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

Commit

Permalink
Move from promise-based decoder to generator-based decoder, and have …
Browse files Browse the repository at this point in the history
…it make requests for unknown storage instead of passing in web3.
  • Loading branch information
haltman-at committed Apr 9, 2019
1 parent 3b46090 commit 1eea6e2
Show file tree
Hide file tree
Showing 21 changed files with 293 additions and 347 deletions.
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
62 changes: 54 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,56 @@ 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);
}

export function* decodeAll() {
let definitions = yield select(data.current.identifiers.definitions);
let refs = yield select(data.current.identifiers.refs);
let decoded = {};
for (let [identifier, ref] of Object.entries(refs)) {
decoded[identifier] = yield* decode(definitions[identifier], ref);
}
return decoded;
}

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 +315,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 +338,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 +358,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 +380,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 +409,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
47 changes: 12 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, decodeAll } 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,10 @@ export default class Session {
return true;
}

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 +226,19 @@ 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");
return await this.runSaga(decodeAll);
}
}
42 changes: 20 additions & 22 deletions packages/truffle-debugger/lib/store/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function abbreviateValues(value, options = {}, depth = 0) {
return "...";
}

const recurse = (child) => abbreviateValues(child, options, depth + 1);
const recurse = child => abbreviateValues(child, options, depth + 1);

if (value instanceof Array) {
if (value.length > options.arrayLimit) {
Expand All @@ -27,27 +27,28 @@ export function abbreviateValues(value, options = {}, depth = 0) {
}

return value.map(recurse);

} else if (value instanceof Object) {
return Object.assign({},
...Object.entries(value).map(
([k, v]) => ({ [recurse(k)]: recurse(v) })
)
return Object.assign(
{},
...Object.entries(value).map(([k, v]) => ({ [recurse(k)]: recurse(v) }))
);

} else if (typeof value === "string" && value.length > options.stringLimit) {
let inner = "...";
let extractAmount = (options.stringLimit - inner.length) / 2;
let leading = value.slice(0, Math.ceil(extractAmount));
let trailing = value.slice(value.length - Math.floor(extractAmount));
return `${leading}${inner}${trailing}`;

} else {
return value;
}
}

export default function configureStore (reducer, saga, initialState, composeEnhancers) {
export default function configureStore(
reducer,
saga,
initialState,
composeEnhancers
) {
const sagaMiddleware = createSagaMiddleware();

if (!composeEnhancers) {
Expand All @@ -56,25 +57,22 @@ export default function configureStore (reducer, saga, initialState, composeEnha

const loggerMiddleware = createLogger({
log: reduxDebug,
stateTransformer: (state) => abbreviateValues(state, {
arrayLimit: 4,
recurseLimit: 3
}),
actionTransformer: abbreviateValues,
stateTransformer: state =>
abbreviateValues(state, {
arrayLimit: 4,
recurseLimit: 3
}),
actionTransformer: abbreviateValues
});

let store = createStore(
reducer, initialState,
reducer,
initialState,

composeEnhancers(
applyMiddleware(
sagaMiddleware,
loggerMiddleware
)
)
composeEnhancers(applyMiddleware(sagaMiddleware, loggerMiddleware))
);

sagaMiddleware.run(saga);

return store;
return { store, sagaMiddleware };
}
Loading

0 comments on commit 1eea6e2

Please sign in to comment.