commit
a3b953ecd4
@ -0,0 +1,64 @@ |
||||
'use strict' |
||||
var AstWalker = require('../util/astWalker') |
||||
|
||||
/** |
||||
* return all contract definitions of the given @astList |
||||
* |
||||
* @param {Object} sourcesList - sources list (containing root AST node) |
||||
* @return {Object} - returns a mapping from AST node ids to AST nodes for the contracts |
||||
*/ |
||||
function extractContractDefinitions (sourcesList) { |
||||
var ret = { |
||||
contractsById: {}, |
||||
contractsByName: {} |
||||
} |
||||
var walker = new AstWalker() |
||||
walker.walkAstList(sourcesList, { 'ContractDefinition': function (node) { |
||||
ret.contractsById[node.id] = node |
||||
ret.contractsByName[node.attributes.name] = node |
||||
return false |
||||
}}) |
||||
return ret |
||||
} |
||||
|
||||
/** |
||||
* returns the linearized base contracts of the contract @arg id |
||||
* |
||||
* @param {Int} id - contract id to resolve |
||||
* @param {Map} contracts - all contracts defined in the current context |
||||
* @return {Array} - array of base contracts in derived to base order as AST nodes. |
||||
*/ |
||||
function getLinearizedBaseContracts (id, contractsById) { |
||||
return contractsById[id].attributes.linearizedBaseContracts.map(function (id) { return contractsById[id] }) |
||||
} |
||||
|
||||
/** |
||||
* return state var and type definition of the given contract |
||||
* |
||||
* @param {String} contractName - contract for which state var should be resolved |
||||
* @param {Object} sourcesList - sources list (containing root AST node) |
||||
* @return {Array} - return an array of AST node of all state variables (including inherited) (this will include all enum/struct declarations) |
||||
*/ |
||||
function extractStateVariables (contractName, sourcesList) { |
||||
var contracts = extractContractDefinitions(sourcesList) |
||||
var node = contracts.contractsByName[contractName] |
||||
if (node) { |
||||
var stateVar = [] |
||||
var baseContracts = getLinearizedBaseContracts(node.id, contracts.contractsById) |
||||
baseContracts.reverse() |
||||
for (var k in baseContracts) { |
||||
var ctr = baseContracts[k] |
||||
for (var i in ctr.children) { |
||||
stateVar.push(ctr.children[i]) |
||||
} |
||||
} |
||||
return stateVar |
||||
} |
||||
return null |
||||
} |
||||
|
||||
module.exports = { |
||||
extractStateVariables: extractStateVariables, |
||||
extractContractDefinitions: extractContractDefinitions, |
||||
getLinearizedBaseContracts: getLinearizedBaseContracts |
||||
} |
@ -0,0 +1,302 @@ |
||||
'use strict' |
||||
/** |
||||
* Uint decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g uint256, uint32) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName} |
||||
*/ |
||||
function Uint (type) { |
||||
type === 'uint' ? 'uint256' : type |
||||
return { |
||||
needsFreeStorageSlot: false, |
||||
storageBytes: parseInt(type.replace('uint', '')) / 8, |
||||
typeName: type |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Int decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g int256, int32) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName} |
||||
*/ |
||||
function Int (type) { |
||||
type === 'int' ? 'int256' : type |
||||
return { |
||||
needsFreeStorageSlot: false, |
||||
storageBytes: parseInt(type.replace('int', '')) / 8, |
||||
typeName: type |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Address decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g address) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName} |
||||
*/ |
||||
function Address (type) { |
||||
return { |
||||
needsFreeStorageSlot: false, |
||||
storageBytes: 20, |
||||
typeName: 'address' |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Bool decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g bool) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName} |
||||
*/ |
||||
function Bool (type) { |
||||
return { |
||||
needsFreeStorageSlot: false, |
||||
storageBytes: 1, |
||||
typeName: 'bool' |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* DynamicByteArray decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g bytes storage ref) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName} |
||||
*/ |
||||
function DynamicByteArray (type) { |
||||
return { |
||||
needsFreeStorageSlot: true, |
||||
storageBytes: 32, |
||||
typeName: 'bytes' |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* FixedByteArray decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g bytes16) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName} |
||||
*/ |
||||
function FixedByteArray (type) { |
||||
return { |
||||
needsFreeStorageSlot: false, |
||||
storageBytes: parseInt(type.replace('bytes', '')), |
||||
typeName: type |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* StringType decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g string storage ref) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName} |
||||
*/ |
||||
function StringType (type) { |
||||
return { |
||||
needsFreeStorageSlot: true, |
||||
storageBytes: 32, |
||||
typeName: 'string' |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* ArrayType decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g int256[] storage ref, int256[] storage ref[] storage ref) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, arraySize, subArray} |
||||
*/ |
||||
function ArrayType (type, stateDefinitions) { |
||||
var arraySize |
||||
var storageBytes |
||||
|
||||
var match = type.match(/(.*)\[(.*?)\]( storage ref| storage pointer| memory| calldata)?$/) |
||||
if (!match || match.length < 3) { |
||||
console.log('unable to parse type ' + type) |
||||
return null |
||||
} |
||||
|
||||
arraySize = match[2] === '' ? 'dynamic' : parseInt(match[2]) |
||||
|
||||
var underlyingType = decode(match[1], stateDefinitions) |
||||
if (underlyingType === null) { |
||||
console.log('unable to parse type ' + type) |
||||
return null |
||||
} |
||||
|
||||
if (arraySize === 'dynamic') { |
||||
storageBytes = 32 |
||||
} else { |
||||
storageBytes = underlyingType.storageBytes |
||||
if (storageBytes > 32) { |
||||
storageBytes = 32 * arraySize * Math.ceil(storageBytes / 32) |
||||
} else { |
||||
storageBytes = 32 * (arraySize / Math.floor(32 / storageBytes)) |
||||
} |
||||
} |
||||
|
||||
return { |
||||
needsFreeStorageSlot: true, |
||||
storageBytes: storageBytes, |
||||
typeName: type, |
||||
arraySize: arraySize, |
||||
underlyingType: underlyingType |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Enum decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g enum enumDef) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, enum} |
||||
*/ |
||||
function Enum (type, stateDefinitions) { |
||||
var enumDef = getEnum(type, stateDefinitions) |
||||
if (enumDef === null) { |
||||
console.log('unable to retrieve decode info of ' + type) |
||||
return null |
||||
} |
||||
var length = enumDef.children.length |
||||
var storageBytes = 0 |
||||
while (length > 1) { |
||||
length = length / 256 |
||||
storageBytes++ |
||||
} |
||||
return { |
||||
needsFreeStorageSlot: false, |
||||
storageBytes: storageBytes, |
||||
typeName: type, |
||||
enum: enumDef |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Struct decode the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g struct structDef storage ref) |
||||
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, members} |
||||
*/ |
||||
function Struct (type, stateDefinitions) { |
||||
var match = type.match(/struct (.*?)( storage ref| storage pointer| memory| calldata)?$/) |
||||
if (!match) { |
||||
return null |
||||
} |
||||
var memberDetails = getStructMembers(match[1], stateDefinitions) // type is used to extract the ast struct definition
|
||||
if (!memberDetails) return null |
||||
return { |
||||
needsFreeStorageSlot: true, |
||||
storageBytes: memberDetails.storageBytes, |
||||
typeName: type, |
||||
members: memberDetails.members |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* retrieve enum declaration of the given @arg type |
||||
* |
||||
* @param {String} type - type given by the AST (e.g enum enumDef) |
||||
* @param {Object} stateDefinitions - all state declarations given by the AST (including struct and enum type declaration) |
||||
* @return {Array} - containing all value declaration of the current enum type |
||||
*/ |
||||
function getEnum (type, stateDefinitions) { |
||||
for (var k in stateDefinitions) { |
||||
var dec = stateDefinitions[k] |
||||
if (type === 'enum ' + dec.attributes.name) { |
||||
return dec |
||||
} |
||||
} |
||||
return null |
||||
} |
||||
|
||||
/** |
||||
* retrieve memebers declared in the given @arg tye |
||||
* |
||||
* @param {String} typeName - name of the struct type (e.g struct <name>) |
||||
* @param {Object} stateDefinitions - all state definition given by the AST (including struct and enum type declaration) |
||||
* @return {Array} containing all members of the current struct type |
||||
*/ |
||||
function getStructMembers (typeName, stateDefinitions) { |
||||
var members = [] |
||||
var storageBytes = 0 |
||||
for (var k in stateDefinitions) { |
||||
var dec = stateDefinitions[k] |
||||
if (dec.name === 'StructDefinition' && typeName === dec.attributes.name) { |
||||
for (var i in dec.children) { |
||||
var member = dec.children[i] |
||||
var decoded = decode(member.attributes.type, stateDefinitions) |
||||
if (!decoded) { |
||||
console.log('unable to retrieve decode info of ' + member.attributes.type) |
||||
return null |
||||
} |
||||
members.push(decoded) |
||||
if (decoded.needsFreeStorageSlot) { |
||||
storageBytes = Math.ceil(storageBytes / 32) * 32 |
||||
} |
||||
storageBytes += decoded.storageBytes |
||||
} |
||||
break |
||||
} |
||||
} |
||||
return { |
||||
members: members, |
||||
storageBytes: storageBytes |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* parse the full type |
||||
* |
||||
* @param {String} fullType - type given by the AST (ex: uint[2] storage ref[2]) |
||||
* @return {String} returns the token type (used to instanciate the right decoder) (uint[2] storage ref[2] will return 'array', uint256 will return uintX) |
||||
*/ |
||||
function typeClass (fullType) { |
||||
if (fullType.indexOf(']') !== -1) { |
||||
return 'array' |
||||
} |
||||
if (fullType.indexOf(' ') !== -1) { |
||||
fullType = fullType.split(' ')[0] |
||||
} |
||||
var char = fullType.indexOf('bytes') === 0 ? 'X' : '' |
||||
return fullType.replace(/[0-9]+/g, char) |
||||
} |
||||
|
||||
/** |
||||
* parse the type and return an object representing the type |
||||
* |
||||
* @param {Object} type - type name given by the ast node |
||||
* @param {Object} stateDefinitions - all state stateDefinitions given by the AST (including struct and enum type declaration) |
||||
* @return {Object} - return the corresponding decoder or null on error |
||||
*/ |
||||
function decode (type, stateDefinitions) { |
||||
var decodeInfos = { |
||||
'address': Address, |
||||
'array': ArrayType, |
||||
'bool': Bool, |
||||
'bytes': DynamicByteArray, |
||||
'bytesX': FixedByteArray, |
||||
'enum': Enum, |
||||
'string': StringType, |
||||
'struct': Struct, |
||||
'int': Int, |
||||
'uint': Uint |
||||
} |
||||
var currentType = typeClass(type) |
||||
if (currentType === null) { |
||||
console.log('unable to retrieve decode info of ' + type) |
||||
return null |
||||
} |
||||
return decodeInfos[currentType](type, stateDefinitions) |
||||
} |
||||
|
||||
module.exports = { |
||||
decode: decode, |
||||
Uint: Uint, |
||||
Address: Address, |
||||
Bool: Bool, |
||||
DynamicByteArray: DynamicByteArray, |
||||
FixedByteArray: FixedByteArray, |
||||
Int: Int, |
||||
StringType: StringType, |
||||
ArrayType: ArrayType, |
||||
Enum: Enum, |
||||
Struct: Struct |
||||
} |
@ -0,0 +1,104 @@ |
||||
'use strict' |
||||
var tape = require('tape') |
||||
var compiler = require('solc') |
||||
var index = require('../src/index') |
||||
|
||||
tape('solidity', function (t) { |
||||
t.test('astHelper, decodeInfo', function (st) { |
||||
var output = compiler.compile(contracts, 0) |
||||
|
||||
var stateDec = index.solidity.astHelper.extractStateVariables('contractUint', output.sources) |
||||
var decodeInfo = index.solidity.decodeInfo.decode(stateDec[0].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 1, 'uint8') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[2].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 32, 'uint256') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[3].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 32, 'uint256') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[4].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 16, 'bytes16') |
||||
|
||||
stateDec = index.solidity.astHelper.extractStateVariables('contractStructAndArray', output.sources) |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[1].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, true, 64, 'struct structDef storage ref') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[2].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, true, 192, 'struct structDef storage ref[3] storage ref') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[3].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, true, 64, 'bytes12[4] storage ref') |
||||
|
||||
stateDec = index.solidity.astHelper.extractStateVariables('contractArray', output.sources) |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[0].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, true, 4 * 5, 'uint32[5] storage ref') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[1].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, true, 32, 'int8[] storage ref') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[2].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, true, 4 * 32, 'int16[] storage ref[3] storage ref[] storage ref[4] storage ref') |
||||
|
||||
stateDec = index.solidity.astHelper.extractStateVariables('contractEnum', output.sources) |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[1].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 2, 'enum enumDef') |
||||
|
||||
stateDec = index.solidity.astHelper.extractStateVariables('contractSmallVariable', output.sources) |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[0].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 1, 'int8') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[1].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 1, 'uint8') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[2].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 2, 'uint16') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[3].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 4, 'int32') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[4].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 32, 'uint256') |
||||
decodeInfo = index.solidity.decodeInfo.decode(stateDec[5].attributes.type, stateDec) |
||||
checkDecodeInfo(st, decodeInfo, false, 2, 'int16') |
||||
|
||||
st.end() |
||||
}) |
||||
}) |
||||
|
||||
function checkDecodeInfo (st, decodeInfo, freeSlot, storageBytes, typeName, name) { |
||||
st.equal(decodeInfo.needsFreeStorageSlot, freeSlot) |
||||
st.equal(decodeInfo.storageBytes, storageBytes) |
||||
st.equal(decodeInfo.typeName, typeName) |
||||
} |
||||
|
||||
var contracts = ` |
||||
contract baseContract { |
||||
uint8 u; |
||||
} |
||||
|
||||
contract contractUint is baseContract { |
||||
uint256 ui; |
||||
uint ui1; |
||||
bytes16 b; |
||||
}
|
||||
|
||||
contract contractStructAndArray {
|
||||
struct structDef { |
||||
uint8 ui; |
||||
string str; |
||||
} |
||||
structDef structDec; |
||||
structDef[3] array; |
||||
bytes12[4] bytesArray; |
||||
} |
||||
|
||||
contract contractArray { |
||||
uint32[5] i32st; |
||||
int8[] i8dyn; |
||||
int16[][3][][4] i16dyn;
|
||||
}
|
||||
|
||||
contract contractEnum { |
||||
enum enumDef {item0,item1,item2,item3,item4,item5,item6,item7,item8,item9,item10,item11,item12,item13,item14,item15,item16,item17,item18,item19,item20,item21,item22,item23,item24,item25,item26,item27,item28,item29,item30,item31,item32,item33,item34,item35,item36,item37,item38,item39,item40,item41,item42,item43,item44,item45,item46,item47,item48,item49,item50,item51,item52,item53,item54,item55,item56,item57,item58,item59,item60,item61,item62,item63,item64,item65,item66,item67,item68,item69,item70,item71,item72,item73,item74,item75,item76,item77,item78,item79,item80,item81,item82,item83,item84,item85,item86,item87,item88,item89,item90,item91,item92,item93,item94,item95,item96,item97,item98,item99,item100,item101,item102,item103,item104,item105,item106,item107,item108,item109,item110,item111,item112,item113,item114,item115,item116,item117,item118,item119,item120,item121,item122,item123,item124,item125,item126,item127,item128,item129,item130,item131,item132,item133,item134,item135,item136,item137,item138,item139,item140,item141,item142,item143,item144,item145,item146,item147,item148,item149,item150,item151,item152,item153,item154,item155,item156,item157,item158,item159,item160,item161,item162,item163,item164,item165,item166,item167,item168,item169,item170,item171,item172,item173,item174,item175,item176,item177,item178,item179,item180,item181,item182,item183,item184,item185,item186,item187,item188,item189,item190,item191,item192,item193,item194,item195,item196,item197,item198,item199,item200,item201,item202,item203,item204,item205,item206,item207,item208,item209,item210,item211,item212,item213,item214,item215,item216,item217,item218,item219,item220,item221,item222,item223,item224,item225,item226,item227,item228,item229,item230,item231,item232,item233,item234,item235,item236,item237,item238,item239,item240,item241,item242,item243,item244,item245,item246,item247,item248,item249,item250,item251,item252,item253,item254,item255,item256,item257,item258,item259,item260,item261,item262,item263,item264,item265,item266,item267,item268,item269,item270,item271,item272,item273,item274,item275,item276,item277,item278,item279,item280,item281,item282,item283,item284,item285,item286,item287,item288,item289,item290,item291,item292,item293,item294,item295,item296,item297,item298,item299,item100000000} |
||||
enumDef enum1; |
||||
} |
||||
|
||||
contract contractSmallVariable { |
||||
int8 i8; |
||||
uint8 iu8; |
||||
uint16 iu18; |
||||
int32 i32; |
||||
uint ui32; |
||||
int16 i16; |
||||
}
|
||||
` |
Loading…
Reference in new issue