Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Move from promise-based decoder that makes web3 calls to generator-based decoder that makes requests to caller #1900

Merged
merged 8 commits into from
Apr 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions packages/truffle-debugger/lib/data/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,6 @@ export function mapPathAndAssign(
};
}

export const MAP_KEY_DECODING = "MAP_KEY_DECODING";
export function mapKeyDecoding(started) {
return {
type: MAP_KEY_DECODING,
started
};
}

export const RESET = "DATA_RESET";
export function reset() {
return { type: RESET };
Expand Down
10 changes: 0 additions & 10 deletions packages/truffle-debugger/lib/data/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ function assignments(state = DEFAULT_ASSIGNMENTS, action) {
}

const DEFAULT_PATHS = {
decodingStarted: 0,
byAddress: {}
};

Expand All @@ -166,15 +165,6 @@ const DEFAULT_PATHS = {
//which is fine, as that's all we need it for.
function mappedPaths(state = DEFAULT_PATHS, action) {
switch (action.type) {
case actions.MAP_KEY_DECODING:
debug(
"decoding started: %d",
state.decodingStarted + (action.started ? 1 : -1)
);
return {
...state,
decodingStarted: state.decodingStarted + (action.started ? 1 : -1)
};
case actions.MAP_PATH_AND_ASSIGN:
let { address, slot, typeIdentifier, parentType } = action;
//how this case works: first, we find the spot in our table (based on
Expand Down
54 changes: 46 additions & 8 deletions packages/truffle-debugger/lib/data/sagas/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import debugModule from "debug";
const debug = debugModule("debugger:data:sagas");

import { put, takeEvery, select, call } from "redux-saga/effects";
import { put, takeEvery, select } from "redux-saga/effects";

import { prefixName, stableKeccak256, makeAssignment } from "lib/helpers";

Expand All @@ -19,7 +19,8 @@ import {
getMemoryAllocations,
getCalldataAllocations,
readStack,
storageSize
storageSize,
forEvmState
} from "truffle-decoder";
import BN from "bn.js";

Expand All @@ -42,9 +43,48 @@ function* tickSaga() {
yield* trace.signalTickSagaCompletion();
}

export function* decode(definition, ref) {
let referenceDeclarations = yield select(data.views.referenceDeclarations);
let state = yield select(data.current.state);
let mappingKeys = yield select(data.views.mappingKeys);
let allocations = yield select(data.info.allocations);

let ZERO_WORD = new Uint8Array(DecodeUtils.EVM.WORD_SIZE);
ZERO_WORD.fill(0);

let decoder = forEvmState(definition, ref, {
referenceDeclarations,
state,
mappingKeys,
storageAllocations: allocations.storage,
memoryAllocations: allocations.memory,
calldataAllocations: allocations.calldata
});

let result = decoder.next();
while (!result.done) {
let request = result.value;
let response;
switch (request.type) {
//yes, this is a little silly right now
case "storage":
//the debugger supplies all storage it knows at the beginning.
//any storage it does not know is presumed to be zero.
response = ZERO_WORD;
break;
default:
debug("unrecognized request type!");
}
result = decoder.next(response);
}
//at this point, result.value holds the final value
//note: we're still using the old decoder output format, so we need to clean
//containers before returning something the debugger can use
return DecodeUtils.Conversion.cleanContainers(result.value);
}

function* variablesAndMappingsSaga() {
let node = yield select(data.current.node);
let decode = yield select(data.views.decoder);
let scopes = yield select(data.views.scopes.inlined);
let referenceDeclarations = yield select(data.views.referenceDeclarations);
let allocations = yield select(data.info.allocations.storage);
Expand Down Expand Up @@ -267,7 +307,6 @@ function* variablesAndMappingsSaga() {
//begin subsection: key decoding
//(I tried factoring this out into its own saga but it didn't work when I
//did :P )
yield put(actions.mapKeyDecoding(true));

let indexValue;
let indexDefinition = node.indexExpression;
Expand All @@ -291,7 +330,7 @@ function* variablesAndMappingsSaga() {
//value will go on the stack *left*-padded instead of right-padded,
//so looking for a prior assignment will read the wrong value.
//so instead it's preferable to use the constant directly.
indexValue = yield call(decode, keyDefinition, {
indexValue = yield* decode(keyDefinition, {
definition: indexDefinition
});
} else if (indexReference) {
Expand All @@ -311,7 +350,7 @@ function* variablesAndMappingsSaga() {
} else {
splicedDefinition = keyDefinition;
}
indexValue = yield call(decode, splicedDefinition, indexReference);
indexValue = yield* decode(splicedDefinition, indexReference);
} else if (
indexDefinition.referencedDeclaration &&
scopes[indexDefinition.referenceDeclaration]
Expand All @@ -333,7 +372,7 @@ function* variablesAndMappingsSaga() {
if (
DecodeUtils.Definition.isSimpleConstant(indexConstantDefinition)
) {
indexValue = yield call(decode, keyDefinition, {
indexValue = yield* decode(keyDefinition, {
definition: indexConstantDeclaration.value
});
}
Expand Down Expand Up @@ -362,7 +401,6 @@ function* variablesAndMappingsSaga() {
//now, as mentioned, retry in the typeConversion case
}

yield put(actions.mapKeyDecoding(false));
//end subsection: key decoding

debug("index value %O", indexValue);
Expand Down
52 changes: 0 additions & 52 deletions packages/truffle-debugger/lib/data/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import evm from "lib/evm/selectors";
import solidity from "lib/solidity/selectors";

import * as DecodeUtils from "truffle-decode-utils";
import { forEvmState } from "truffle-decoder";

/**
* @private
Expand Down Expand Up @@ -120,33 +119,6 @@ const data = createSelectorTree({
}
},

/**
* data.views.decoder
*
* selector returns (ast node definition, data reference) => Promise<value>
*/
decoder: createLeaf(
[
"/views/referenceDeclarations",
"/current/state",
"/views/mappingKeys",
"/info/allocations"
],

(referenceDeclarations, state, mappingKeys, allocations) => (
definition,
ref
) =>
forEvmState(definition, ref, {
referenceDeclarations,
state,
mappingKeys,
storageAllocations: allocations.storage,
memoryAllocations: allocations.memory,
calldataAllocations: allocations.calldata
})
),

/*
* data.views.userDefinedTypes
*/
Expand Down Expand Up @@ -717,30 +689,6 @@ const data = createSelectorTree({
}
)
)
),

/**
* data.current.identifiers.decoded
*
* Returns an object with values as Promises
*/
decoded: createLeaf(
["/views/decoder", "./definitions", "./refs"],

async (decode, definitions, refs) => {
debug("setting up keyedPromises");
const keyedPromises = Object.entries(refs).map(
async ([identifier, ref]) => ({
[identifier]: await decode(definitions[identifier], ref)
})
);
debug("set up keyedPromises");
const keyedResults = await Promise.all(keyedPromises);
debug("got keyedResults");
return DecodeUtils.Conversion.cleanContainers(
Object.assign({}, ...keyedResults)
);
}
)
}
},
Expand Down
63 changes: 28 additions & 35 deletions packages/truffle-debugger/lib/session/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import configureStore from "lib/store";
import * as controller from "lib/controller/actions";
import * as actions from "./actions";
import data from "lib/data/selectors";
import { decode } from "lib/data/sagas";
import controllerSelector from "lib/controller/selectors";

import rootSaga from "./sagas";
Expand All @@ -26,7 +27,9 @@ export default class Session {
/**
* @private
*/
this._store = configureStore(reducer, rootSaga);
let { store, sagaMiddleware } = configureStore(reducer, rootSaga);
this._store = store;
this._sagaMiddleware = sagaMiddleware;

let { contexts, sources } = Session.normalize(contracts, files);

Expand Down Expand Up @@ -145,6 +148,16 @@ export default class Session {
return true;
}

/**
* @private
* Allows running any saga -- for internal use only!
* Using this could seriously screw up the debugger state if you
* don't know what you're doing!
haltman-at marked this conversation as resolved.
Show resolved Hide resolved
*/
async _runSaga(saga, ...args) {
return await this._sagaMiddleware.run(saga, ...args).toPromise();
}

async interrupt() {
return this.dispatch(controller.interrupt());
}
Expand Down Expand Up @@ -219,49 +232,29 @@ export default class Session {
return this.dispatch(controller.removeAllBreakpoints());
}

//deprecated -- decode is now *always* ready!
async decodeReady() {
return new Promise(resolve => {
let haveResolved = false;
const unsubscribe = this._store.subscribe(() => {
const subscriptionDecodingStarted = this.view(data.proc.decodingKeys);

debug("following decoding started: %d", subscriptionDecodingStarted);

if (subscriptionDecodingStarted <= 0 && !haveResolved) {
haveResolved = true;
unsubscribe();
resolve();
}
});

const decodingStarted = this.view(data.proc.decodingKeys);

debug("initial decoding started: %d", decodingStarted);

if (decodingStarted <= 0) {
haveResolved = true;
unsubscribe();
resolve();
}
});
return true;
}

async variable(name) {
await this.decodeReady();

const definitions = this.view(data.current.identifiers.definitions);
const refs = this.view(data.current.identifiers.refs);

const decode = this.view(data.views.decoder);
return await decode(definitions[name], refs[name]);
return await this._runSaga(decode, definitions[name], refs[name]);
}

async variables() {
debug("awaiting decodeReady");
await this.decodeReady();
debug("decode now ready");

return await this.view(data.current.identifiers.decoded);
debug("got variables");
let definitions = this.view(data.current.identifiers.definitions);
let refs = this.view(data.current.identifiers.refs);
let decoded = {};
for (let [identifier, ref] of Object.entries(refs)) {
decoded[identifier] = await this._runSaga(
decode,
definitions[identifier],
ref
);
}
return decoded;
}
}
Loading