lib/data/selectors/index.js
import debugModule from "debug";
const debug = debugModule("debugger:data:selectors");
import { createSelectorTree, createLeaf } from "reselect-tree";
import jsonpointer from "json-pointer";
import ast from "lib/ast/selectors";
import evm from "lib/evm/selectors";
import solidity from "lib/solidity/selectors";
import decode from "../decode";
import * as decodeUtils from "../decode/utils";
import { BigNumber } from "bignumber.js";
function createStateSelectors({ stack, memory, storage }) {
return {
/**
* .stack
*/
stack: createLeaf(
[stack],
(words) => (words || []).map(
(word) => decodeUtils.toBytes(decodeUtils.toBigNumber(word, decodeUtils.WORD_SIZE))
)
),
/**
* .memory
*/
memory: createLeaf(
[memory],
(words) => new Uint8Array(
(words.join("").match(/.{1,2}/g) || [])
.map( (byte) => parseInt(byte, 16) )
)
),
/**
* .storage
*/
storage: createLeaf(
[storage],
(mapping) => Object.assign(
{}, ...Object.entries(mapping).map( ([ address, word ]) =>
({
[`0x${address}`]: new Uint8Array(
(word.match(/.{1,2}/g) || [])
.map( (byte) => parseInt(byte, 16) )
)
})
)
)
)
};
}
const data = createSelectorTree({
state: (state) => state.data,
/**
* data.views
*/
views: {
ast: createLeaf(
[ast.current], (tree) => tree
),
atLastInstructionForSourceRange: createLeaf(
[solidity.current.isSourceRangeFinal], (final) => final
),
/**
* data.views.scopes
*/
scopes: {
/**
* data.views.scopes.inlined
*/
inlined: createLeaf(
["/info/scopes", solidity.info.sources],
(scopes, sources) => Object.assign({},
...Object.entries(scopes).map(
([id, entry]) => ({
[id]: {
...entry,
definition: jsonpointer.get(
sources[entry.sourceId].ast, entry.pointer
)
}
})
)
)
)
},
/**
* data.views.decoder
*
* selector returns (ast node definition, data reference) => value
*/
decoder: createLeaf(
["/views/scopes/inlined", "/current/state"],
(scopes, state) => {
return (definition, ref) => decode(definition, ref, state, scopes)
}
)
},
/**
* data.info
*/
info: {
/**
* data.info.scopes
*/
scopes: createLeaf(["/state"], (state) => state.info.scopes.byId)
},
/**
* data.proc
*/
proc: {
/**
* data.proc.assignments
*/
assignments: createLeaf(["/state"], (state) => state.proc.assignments.byId)
},
/**
* data.current
*/
current: {
/**
*
* data.current.scope
*/
scope: {
/**
* data.current.scope.id
*/
id: createLeaf(
[ast.current.node], (node) => node.id
)
},
/**
* data.current.state
*/
state: createStateSelectors(evm.current.state),
/**
* data.current.identifiers (namespace)
*/
identifiers: {
/**
* data.current.identifiers (selector)
*
* returns identifers and corresponding definition node ID
*/
_: createLeaf(
[
"/views/scopes/inlined",
"/current/scope",
],
(scopes, scope) => {
let cur = scope.id;
let variables = {};
do {
variables = Object.assign(
variables,
...(scopes[cur].variables || [])
.filter( (v) => variables[v.name] == undefined )
.map( (v) => ({ [v.name]: v.id }) )
);
cur = scopes[cur].parentId;
} while (cur != null);
return variables;
}
),
/**
* data.current.identifiers.definitions
*
* current variable definitions
*/
definitions: createLeaf(
[
"/views/scopes/inlined",
"./_"
],
(scopes, identifiers) => Object.assign({},
...Object.entries(identifiers)
.map( ([identifier, id]) => {
let { definition } = scopes[id];
return { [identifier]: definition };
})
)
),
/**
* data.current.identifiers.refs
*
* current variables' value refs
*/
refs: createLeaf(
[
"/proc/assignments",
"./_"
],
(assignments, identifiers) => Object.assign({},
...Object.entries(identifiers)
.map( ([identifier, id]) => {
let { ref } = (assignments[id] || {})
if (!ref) { return undefined };
return {
[identifier]: ref
};
})
)
),
decoded: createLeaf(
[
"/views/decoder",
"./definitions",
"./refs",
],
(decode, definitions, refs) => Object.assign({},
...Object.entries(refs)
.map( ([identifier, ref]) => ({
[identifier]: decode(definitions[identifier], ref)
}) )
)
),
native: createLeaf(['./decoded'], decodeUtils.cleanBigNumbers)
}
},
/**
* data.next
*/
next: {
/**
* data.next.state
*/
state: createStateSelectors(evm.next.state)
}
});
export default data;