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 7 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
52 changes: 44 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,46 @@ 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.requesting) {
//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;
}
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 +305,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 +328,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 +348,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 +370,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 +399,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