Commit d68814df authored by Michael Fröwis's avatar Michael Fröwis Committed by chriseth

Static Analysis: more unit tests

parent 11917ffc
...@@ -67,12 +67,12 @@ function getType (node) { ...@@ -67,12 +67,12 @@ function getType (node) {
function getFunctionCallType (func) { function getFunctionCallType (func) {
if (!(isExternalDirectCall(func) || isThisLocalCall(func) || isLocalCall(func))) throw new Error('staticAnalysisCommon.js: not function call Node') if (!(isExternalDirectCall(func) || isThisLocalCall(func) || isLocalCall(func))) throw new Error('staticAnalysisCommon.js: not function call Node')
if (isExternalDirectCall(func)) return func.attributes.type if (isExternalDirectCall(func) || isThisLocalCall(func)) return func.attributes.type
return findFirstSubNodeLTR(func, exactMatch(nodeTypes.IDENTIFIER)).attributes.type return findFirstSubNodeLTR(func, exactMatch(nodeTypes.IDENTIFIER)).attributes.type
} }
function getEffectedVariableName (effectNode) { function getEffectedVariableName (effectNode) {
if (!isEffect(effectNode)) throw new Error('staticAnalysisCommon.js: not an effect Node') if (!isEffect(effectNode) || isInlineAssembly(effectNode)) throw new Error('staticAnalysisCommon.js: not an effect Node or inline assembly')
return findFirstSubNodeLTR(effectNode, exactMatch(nodeTypes.IDENTIFIER)).attributes.value return findFirstSubNodeLTR(effectNode, exactMatch(nodeTypes.IDENTIFIER)).attributes.value
} }
...@@ -91,6 +91,11 @@ function getExternalDirectCallContractName (extDirectCall) { ...@@ -91,6 +91,11 @@ function getExternalDirectCallContractName (extDirectCall) {
return extDirectCall.children[0].attributes.type.replace(new RegExp(basicRegex.CONTRACTTYPE), '') return extDirectCall.children[0].attributes.type.replace(new RegExp(basicRegex.CONTRACTTYPE), '')
} }
function getThisLocalCallContractName (thisLocalCall) {
if (!isThisLocalCall(thisLocalCall)) throw new Error('staticAnalysisCommon.js: not an this local call Node')
return thisLocalCall.children[0].attributes.type.replace(new RegExp(basicRegex.CONTRACTTYPE), '')
}
function getExternalDirectCallMemberName (extDirectCall) { function getExternalDirectCallMemberName (extDirectCall) {
if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node') if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node')
return extDirectCall.attributes.member_name return extDirectCall.attributes.member_name
...@@ -132,7 +137,7 @@ function getFunctionCallTypeParameterType (func) { ...@@ -132,7 +137,7 @@ function getFunctionCallTypeParameterType (func) {
function getFullQualifiedFunctionCallIdent (contract, func) { function getFullQualifiedFunctionCallIdent (contract, func) {
if (isLocalCall(func)) return getContractName(contract) + '.' + getLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' if (isLocalCall(func)) return getContractName(contract) + '.' + getLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
else if (isThisLocalCall(func)) return getContractName(contract) + '.' + getThisLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' else if (isThisLocalCall(func)) return getThisLocalCallContractName(func) + '.' + getThisLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
else if (isExternalDirectCall(func)) return getExternalDirectCallContractName(func) + '.' + getExternalDirectCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' else if (isExternalDirectCall(func)) return getExternalDirectCallContractName(func) + '.' + getExternalDirectCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
else throw new Error('staticAnalysisCommon.js: Can not get function name form non function call node') else throw new Error('staticAnalysisCommon.js: Can not get function name form non function call node')
} }
...@@ -201,6 +206,18 @@ function isConstantFunction (node) { ...@@ -201,6 +206,18 @@ function isConstantFunction (node) {
return isFunctionDefinition(node) && node.attributes.constant === true return isFunctionDefinition(node) && node.attributes.constant === true
} }
function isPlusPlusUnaryOperation (node) {
return nodeType(node, exactMatch(nodeTypes.UNARYOPERATION)) && operator(node, exactMatch(utils.escapeRegExp('++')))
}
function isMinusMinusUnaryOperation (node) {
return nodeType(node, exactMatch(nodeTypes.UNARYOPERATION)) && operator(node, exactMatch(utils.escapeRegExp('--')))
}
function isFullyImplementedContract (node) {
return nodeType(node, exactMatch(nodeTypes.CONTRACTDEFINITION)) && node.attributes.fullyImplemented === true
}
function isCallToNonConstLocalFunction (node) { function isCallToNonConstLocalFunction (node) {
return isLocalCall(node) && !expressionType(node.children[0], basicRegex.CONSTANTFUNCTIONTYPE) return isLocalCall(node) && !expressionType(node.children[0], basicRegex.CONSTANTFUNCTIONTYPE)
} }
...@@ -216,27 +233,11 @@ function isNowAccess (node) { ...@@ -216,27 +233,11 @@ function isNowAccess (node) {
name(node, exactMatch('now')) name(node, exactMatch('now'))
} }
function isPlusPlusUnaryOperation (node) {
return nodeType(node, exactMatch(nodeTypes.UNARYOPERATION)) && operator(node, exactMatch(utils.escapeRegExp('++')))
}
function isMinusMinusUnaryOperation (node) {
return nodeType(node, exactMatch(nodeTypes.UNARYOPERATION)) && operator(node, exactMatch('--'))
}
function isFullyImplementedContract (node) {
return nodeType(node, exactMatch(nodeTypes.CONTRACTDEFINITION)) && node.attributes.fullyImplemented === true
}
// usage of block timestamp // usage of block timestamp
function isBlockTimestampAccess (node) { function isBlockTimestampAccess (node) {
return isSpecialVariableAccess(node, specialVariables.BLOCKTIMESTAMP) return isSpecialVariableAccess(node, specialVariables.BLOCKTIMESTAMP)
} }
function isSpecialVariableAccess (node, varType) {
return isMemberAccess(node, varType.type, varType.obj, varType.obj, varType.member)
}
function isThisLocalCall (node) { function isThisLocalCall (node) {
return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('this'), basicRegex.CONTRACTTYPE, undefined) return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('this'), basicRegex.CONTRACTTYPE, undefined)
} }
...@@ -281,6 +282,8 @@ function isLLDelegatecall (node) { ...@@ -281,6 +282,8 @@ function isLLDelegatecall (node) {
undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident)) undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident))
} }
// #################### Complex Node Identification - Private
function isMemberAccess (node, retType, accessor, accessorType, memberName) { function isMemberAccess (node, retType, accessor, accessorType, memberName) {
return nodeType(node, exactMatch(nodeTypes.MEMBERACCESS)) && return nodeType(node, exactMatch(nodeTypes.MEMBERACCESS)) &&
expressionType(node, retType) && expressionType(node, retType) &&
...@@ -290,6 +293,10 @@ function isMemberAccess (node, retType, accessor, accessorType, memberName) { ...@@ -290,6 +293,10 @@ function isMemberAccess (node, retType, accessor, accessorType, memberName) {
expressionType(node.children[0], accessorType) expressionType(node.children[0], accessorType)
} }
function isSpecialVariableAccess (node, varType) {
return isMemberAccess(node, varType.type, varType.obj, varType.obj, varType.member)
}
// #################### Node Identification Primitives // #################### Node Identification Primitives
function nrOfChildren (node, nr) { function nrOfChildren (node, nr) {
...@@ -337,6 +344,7 @@ function buildFunctionSignature (paramTypes, returnTypes, isPayable) { ...@@ -337,6 +344,7 @@ function buildFunctionSignature (paramTypes, returnTypes, isPayable) {
} }
function findFirstSubNodeLTR (node, type) { function findFirstSubNodeLTR (node, type) {
if (!node || !node.children) return null
for (let i = 0; i < node.children.length; ++i) { for (let i = 0; i < node.children.length; ++i) {
var item = node.children[i] var item = node.children[i]
if (nodeType(item, type)) return item if (nodeType(item, type)) return item
...@@ -360,6 +368,7 @@ module.exports = { ...@@ -360,6 +368,7 @@ module.exports = {
getLocalCallName: getLocalCallName, getLocalCallName: getLocalCallName,
getInheritsFromName: getInheritsFromName, getInheritsFromName: getInheritsFromName,
getExternalDirectCallContractName: getExternalDirectCallContractName, getExternalDirectCallContractName: getExternalDirectCallContractName,
getThisLocalCallContractName: getThisLocalCallContractName,
getExternalDirectCallMemberName: getExternalDirectCallMemberName, getExternalDirectCallMemberName: getExternalDirectCallMemberName,
getFunctionDefinitionName: getFunctionDefinitionName, getFunctionDefinitionName: getFunctionDefinitionName,
getFunctionCallTypeParameterType: getFunctionCallTypeParameterType, getFunctionCallTypeParameterType: getFunctionCallTypeParameterType,
...@@ -385,6 +394,8 @@ module.exports = { ...@@ -385,6 +394,8 @@ module.exports = {
isExternalDirectCall: isExternalDirectCall, isExternalDirectCall: isExternalDirectCall,
isFullyImplementedContract: isFullyImplementedContract, isFullyImplementedContract: isFullyImplementedContract,
isCallToNonConstLocalFunction: isCallToNonConstLocalFunction, isCallToNonConstLocalFunction: isCallToNonConstLocalFunction,
isPlusPlusUnaryOperation: isPlusPlusUnaryOperation,
isMinusMinusUnaryOperation: isMinusMinusUnaryOperation,
// #################### Trivial Node Identification // #################### Trivial Node Identification
isFunctionDefinition: isFunctionDefinition, isFunctionDefinition: isFunctionDefinition,
...@@ -406,9 +417,11 @@ module.exports = { ...@@ -406,9 +417,11 @@ module.exports = {
specialVariables: specialVariables, specialVariables: specialVariables,
helpers: { helpers: {
nrOfChildren: nrOfChildren, nrOfChildren: nrOfChildren,
minNrOfChildren: minNrOfChildren,
expressionType: expressionType, expressionType: expressionType,
nodeType: nodeType, nodeType: nodeType,
name: name, name: name,
operator: operator,
buildFunctionSignature: buildFunctionSignature buildFunctionSignature: buildFunctionSignature
} }
} }
...@@ -3,6 +3,8 @@ var test = require('tape') ...@@ -3,6 +3,8 @@ var test = require('tape')
var common = require('../../src/app/staticanalysis/modules/staticAnalysisCommon') var common = require('../../src/app/staticanalysis/modules/staticAnalysisCommon')
var utils = require('../../src/app/utils') var utils = require('../../src/app/utils')
// #################### helpers Test
test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) { test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) {
t.plan(7) t.plan(7)
...@@ -35,6 +37,8 @@ test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) { ...@@ -35,6 +37,8 @@ test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) {
'check fixed call type') 'check fixed call type')
}) })
// #################### Node Identification Primitives
test('staticAnalysisCommon.helpers.name', function (t) { test('staticAnalysisCommon.helpers.name', function (t) {
t.plan(9) t.plan(9)
var node = { attributes: { value: 'now' } } var node = { attributes: { value: 'now' } }
...@@ -47,6 +51,22 @@ test('staticAnalysisCommon.helpers.name', function (t) { ...@@ -47,6 +51,22 @@ test('staticAnalysisCommon.helpers.name', function (t) {
lowlevelAccessersCommon(t, common.helpers.name, node) lowlevelAccessersCommon(t, common.helpers.name, node)
}) })
test('staticAnalysisCommon.helpers.operator', function (t) {
t.plan(10)
var node = { attributes: { operator: '++' } }
var node2 = { attributes: { operator: '+++' } }
var escapedPP = utils.escapeRegExp('++')
var escapedPPExact = `^${escapedPP}$`
t.ok(common.helpers.operator(node, escapedPPExact), 'should work for ++')
t.notOk(common.helpers.operator(node2, escapedPPExact), 'should not work for +++')
t.ok(common.helpers.operator(node, escapedPP), 'should work for ++')
t.ok(common.helpers.operator(node2, escapedPP), 'should work for +++')
lowlevelAccessersCommon(t, common.helpers.operator, node)
})
test('staticAnalysisCommon.helpers.nodeType', function (t) { test('staticAnalysisCommon.helpers.nodeType', function (t) {
t.plan(9) t.plan(9)
var node = { name: 'Identifier', attributes: { name: 'now' } } var node = { name: 'Identifier', attributes: { name: 'now' } }
...@@ -85,6 +105,23 @@ test('staticAnalysisCommon.helpers.nrOfChildren', function (t) { ...@@ -85,6 +105,23 @@ test('staticAnalysisCommon.helpers.nrOfChildren', function (t) {
lowlevelAccessersCommon(t, common.helpers.nrOfChildren, node) lowlevelAccessersCommon(t, common.helpers.nrOfChildren, node)
}) })
test('staticAnalysisCommon.helpers.minNrOfChildren', function (t) {
t.plan(13)
var node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
var node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
var node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
t.ok(common.helpers.minNrOfChildren(node, 2), 'should work for 2 children')
t.ok(common.helpers.minNrOfChildren(node, 1), 'should work for 1 children')
t.ok(common.helpers.minNrOfChildren(node, 0), 'should work for 0 children')
t.notOk(common.helpers.minNrOfChildren(node, 3), 'has less than 3 children')
t.notOk(common.helpers.minNrOfChildren(node, '1+2'), 'regex should not work')
t.ok(common.helpers.minNrOfChildren(node2, 0), 'should work for 0 children')
t.ok(common.helpers.minNrOfChildren(node3, 0), 'should work without children arr')
lowlevelAccessersCommon(t, common.helpers.minNrOfChildren, node)
})
function lowlevelAccessersCommon (t, f, someNode) { function lowlevelAccessersCommon (t, f, someNode) {
t.ok(f(someNode), 'always ok if type is undefinded') t.ok(f(someNode), 'always ok if type is undefinded')
t.ok(f(someNode, undefined), 'always ok if name is undefinded 2') t.ok(f(someNode, undefined), 'always ok if name is undefinded 2')
...@@ -94,48 +131,302 @@ function lowlevelAccessersCommon (t, f, someNode) { ...@@ -94,48 +131,302 @@ function lowlevelAccessersCommon (t, f, someNode) {
t.notOk(f(), 'false on no params') t.notOk(f(), 'false on no params')
} }
test('staticAnalysisCommon.isLowLevelCall', function (t) { // #################### Trivial Getter Test
t.plan(6)
var sendAst = { name: 'MemberAccess', children: [{attributes: { value: 'd', type: 'address' }}], attributes: { value: 'send', type: 'function (uint256) returns (bool)' } }
var callAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
var callcodeAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'callcode', type: 'function () payable returns (bool)' } }
var delegatecallAst = { name: 'MemberAccess', children: [{attributes: { value: 'g', type: 'address' }}], attributes: { member_name: 'delegatecall', type: 'function () returns (bool)' } }
t.ok(common.isLowLevelSendInst(sendAst) && common.isLowLevelCall(sendAst), 'send is llc should work') test('staticAnalysisCommon.getType', function (t) {
t.ok(common.isLowLevelCallInst(callAst) && common.isLowLevelCall(callAst), 'call is llc should work') t.plan(3)
t.notOk(common.isLowLevelCallInst(callcodeAst), 'callcode is not call') var node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
t.ok(common.isLowLevelCallcodeInst(callcodeAst) && common.isLowLevelCall(callcodeAst), 'callcode is llc should work') var node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
t.notOk(common.isLowLevelCallcodeInst(callAst), 'call is not callcode') var node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
t.ok(common.isLowLevelDelegatecallInst(delegatecallAst) && common.isLowLevelCall(delegatecallAst), 'delegatecall is llc should work')
t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes')
t.ok(common.getType(node2) === 'function () payable returns (bool)', 'gettype should work for different nodes')
t.ok(common.getType(node3) === 'function () payable returns (bool)', 'gettype should work for different nodes')
}) })
test('staticAnalysisCommon.isThisLocalCall', function (t) { // #################### Complex Getter Test
test('staticAnalysisCommon.getFunctionCallType', function (t) {
t.plan(4)
var localCall = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
t.ok(common.getFunctionCallType(thisLocalCall) === 'function (bytes32,address) returns (bool)', 'this local call returns correct type')
t.ok(common.getFunctionCallType(externalDirect) === 'function () payable external returns (uint256)', 'external direct call returns correct type')
t.ok(common.getFunctionCallType(localCall) === 'function (struct Ballot.Voter storage pointer)', 'local call returns correct type')
t.throws(() => common.getFunctionCallType({ name: 'MemberAccess' }), undefined, 'throws on wrong type')
})
test('staticAnalysisCommon.getEffectedVariableName', function (t) {
t.plan(3) t.plan(3)
var node = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } } var assignment = {
t.ok(common.isThisLocalCall(node), 'is this.local_method() used should work') 'attributes': {
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work') 'operator': '=',
t.notOk(common.isNowAccess(node), 'is now used should not work') 'type': 'uint256'
},
'children': [
{
'attributes': {
'type': 'uint256'
},
'children': [
{
'attributes': {
'type': 'mapping(address => uint256)',
'value': 'c'
},
'id': 61,
'name': 'Identifier',
'src': '873:1:0'
},
{
'attributes': {
'member_name': 'sender',
'type': 'address'
},
'children': [
{
'attributes': {
'type': 'msg',
'value': 'msg'
},
'id': 62,
'name': 'Identifier',
'src': '875:3:0'
}
],
'id': 63,
'name': 'MemberAccess',
'src': '875:10:0'
}
],
'id': 64,
'name': 'IndexAccess',
'src': '873:13:0'
},
{
'attributes': {
'hexvalue': '30',
'subdenomination': null,
'token': null,
'type': 'int_const 0',
'value': '0'
},
'id': 65,
'name': 'Literal',
'src': '889:1:0'
}
],
'id': 66,
'name': 'Assignment',
'src': '873:17:0'
}
var inlineAssembly = {
'children': [
],
'id': 21,
'name': 'InlineAssembly',
'src': '809:41:0'
}
t.throws(() => common.getEffectedVariableName(inlineAssembly), 'staticAnalysisCommon.js: not an effect Node or inline assembly', 'get from inline assembly should throw')
t.ok(common.getEffectedVariableName(assignment) === 'c', 'get right name for assignment')
t.throws(() => common.getEffectedVariableName({ name: 'MemberAccess' }), undefined, 'should throw on all other nodes')
}) })
test('staticAnalysisCommon.isBlockTimestampAccess', function (t) { test('staticAnalysisCommon.getLocalCallName', function (t) {
t.plan(3) t.plan(3)
var node = { name: 'MemberAccess', children: [{attributes: { value: 'block', type: 'block' }}], attributes: { value: 'timestamp', type: 'uint256' } } var localCall = {
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work') 'attributes': {
t.ok(common.isBlockTimestampAccess(node), 'is block.timestamp used should work') 'type': 'tuple()',
t.notOk(common.isNowAccess(node), 'is now used should not work') 'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
t.ok(common.getLocalCallName(localCall) === 'bli', 'getLocal call name from node')
t.throws(() => common.getLocalCallName(externalDirect), undefined, 'throws on other nodes')
t.throws(() => common.getLocalCallName(thisLocalCall), undefined, 'throws on other nodes')
}) })
test('staticAnalysisCommon.isNowAccess', function (t) { test('staticAnalysisCommon.getThisLocalCallName', function (t) {
t.plan(3) t.plan(3)
var node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } } var localCall = {
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work') 'attributes': {
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work') 'type': 'tuple()',
t.ok(common.isNowAccess(node), 'is now used should work') 'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
t.ok(common.getThisLocalCallName(thisLocalCall) === 'b', 'get this Local call name from node')
t.throws(() => common.getThisLocalCallName(externalDirect), undefined, 'throws on other nodes')
t.throws(() => common.getThisLocalCallName(localCall), undefined, 'throws on other nodes')
}) })
test('staticAnalysisCommon.isExternalDirectCall', function (t) { test('staticAnalysisCommon.getExternalDirectCallContractName', function (t) {
t.plan(5) t.plan(3)
var node = { var localCall = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: { attributes: {
member_name: 'info', member_name: 'info',
type: 'function () payable external returns (uint256)' type: 'function () payable external returns (uint256)'
...@@ -155,11 +446,1211 @@ test('staticAnalysisCommon.isExternalDirectCall', function (t) { ...@@ -155,11 +446,1211 @@ test('staticAnalysisCommon.isExternalDirectCall', function (t) {
name: 'MemberAccess', name: 'MemberAccess',
src: '405:6:0' src: '405:6:0'
} }
t.ok(common.getExternalDirectCallContractName(externalDirect) === 'InfoFeed', 'external direct call contract name from node')
t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), undefined, 'throws on other nodes')
t.throws(() => common.getExternalDirectCallContractName(localCall), undefined, 'throws on other nodes')
})
var node2 = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } } test('staticAnalysisCommon.getThisLocalCallContractName', function (t) {
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work') t.plan(3)
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work') var localCall = {
t.notOk(common.isNowAccess(node), 'is now used should not work') 'attributes': {
t.ok(common.isExternalDirectCall(node), 'f.info() should be external direct call') 'type': 'tuple()',
t.notOk(common.isExternalDirectCall(node2), 'local call is not an exernal call') 'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'test', 'this local call contract name from node')
t.throws(() => common.getThisLocalCallContractName(localCall), undefined, 'throws on other nodes')
t.throws(() => common.getThisLocalCallContractName(externalDirect), undefined, 'throws on other nodes')
})
test('staticAnalysisCommon.getExternalDirectCallMemberName', function (t) {
t.plan(3)
var localCall = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'info', 'external direct call name from node')
t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), undefined, 'throws on other nodes')
t.throws(() => common.getExternalDirectCallMemberName(localCall), undefined, 'throws on other nodes')
})
test('staticAnalysisCommon.getContractName', function (t) {
t.plan(2)
var contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
t.ok(common.getContractName(contract) === 'baz', 'returns right contract name')
t.throws(() => common.getContractName({ name: 'InheritanceSpecifier' }), undefined, 'throws on other nodes')
})
test('staticAnalysisCommon.getFunctionDefinitionName', function (t) {
t.plan(2)
var func = { name: 'FunctionDefinition', attributes: { name: 'foo' } }
t.ok(common.getFunctionDefinitionName(func) === 'foo', 'returns right contract name')
t.throws(() => common.getFunctionDefinitionName({ name: 'InlineAssembly' }), undefined, 'throws on other nodes')
})
test('staticAnalysisCommon.getInheritsFromName', function (t) {
t.plan(2)
var inh = {
'children': [
{
'attributes': {
'name': 'r'
},
'id': 7,
'name': 'UserDefinedTypeName',
'src': '84:1:0'
}
],
'id': 8,
'name': 'InheritanceSpecifier',
'src': '84:1:0'
}
t.ok(common.getInheritsFromName(inh) === 'r', 'returns right contract name')
t.throws(() => common.getInheritsFromName({ name: 'ElementaryTypeName' }), undefined, 'throws on other nodes')
})
test('staticAnalysisCommon.getDeclaredVariableName', function (t) {
t.plan(2)
var node1 = {
'attributes': {
'name': 'x',
'type': 'struct Ballot.Voter storage pointer'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'id': 43,
'name': 'UserDefinedTypeName',
'src': '604:5:0'
}
],
'id': 44,
'name': 'VariableDeclaration',
'src': '604:15:0'
}
t.ok(common.getDeclaredVariableName(node1) === 'x', 'extract right variable name')
node1.name = 'FunctionCall'
t.throws(() => common.getDeclaredVariableName(node1) === 'x', undefined, 'throw if wrong node')
})
test('staticAnalysisCommon.getStateVariableDeclarationsFormContractNode', function (t) {
t.plan(4)
var contract = {
'attributes': {
'fullyImplemented': true,
'isLibrary': false,
'linearizedBaseContracts': [
274
],
'name': 'Ballot'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'children': [],
'name': 'StructDefinition'
},
{
'attributes': {
'name': 'Proposal'
},
'children': [],
'name': 'StructDefinition'
},
{
'attributes': {
'name': 'chairperson',
'type': 'address'
},
'children': [
{
'attributes': {
'name': 'address'
},
'name': 'ElementaryTypeName'
}
],
'name': 'VariableDeclaration'
},
{
'attributes': {
'name': 'voters',
'type': 'mapping(address => struct Ballot.Voter storage ref)'
},
'children': [
{
'children': [
{
'attributes': {
'name': 'address'
},
'name': 'ElementaryTypeName'
},
{
'attributes': {
'name': 'Voter'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'Mapping'
}
],
'name': 'VariableDeclaration'
},
{
'attributes': {
'name': 'proposals',
'type': 'struct Ballot.Proposal storage ref[] storage ref'
},
'children': [
{
'children': [
{
'attributes': {
'name': 'Proposal'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'ArrayTypeName'
}
],
'name': 'VariableDeclaration'
},
{
'attributes': {
'constant': false,
'name': 'Ballot',
'payable': false,
'visibility': 'public'
},
'children': [],
'name': 'FunctionDefinition'
},
{
'attributes': {
'constant': false,
'name': 'giveRightToVote',
'payable': false,
'visibility': 'public'
},
'children': [],
'name': 'FunctionDefinition'
}
],
'name': 'ContractDefinition'
}
var res = common.getStateVariableDeclarationsFormContractNode(contract).map(common.getDeclaredVariableName)
t.comment(res)
t.ok(res[0] === 'chairperson', 'var 1 should be ')
t.ok(res[1] === 'voters', 'var 2 should be ')
t.ok(res[2] === 'proposals', 'var 3 should be ')
t.ok(res[3] === undefined, 'var 4 should be undefined')
})
test('staticAnalysisCommon.getFunctionOrModifierDefinitionParameterPart', function (t) {
t.plan(2)
var funDef = {
'attributes': {
'constant': true,
'name': 'winnerName',
'payable': false,
'visibility': 'public'
},
'children': [
{
'children': [
],
'name': 'ParameterList'
},
{
'children': [],
'name': 'ParameterList'
},
{
'children': [],
'name': 'Block'
}
],
'name': 'FunctionDefinition'
}
t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(funDef), 'ParameterList'), 'should return a parameterList')
t.throws(() => common.getFunctionOrModifierDefinitionParameterPart({ name: 'SourceUnit' }), undefined, 'throws on other nodes')
})
test('staticAnalysisCommon.getFunctionCallTypeParameterType', function (t) {
t.plan(4)
var localCall = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
t.ok(common.getFunctionCallTypeParameterType(thisLocalCall) === 'bytes32,address', 'this local call returns correct type')
t.ok(common.getFunctionCallTypeParameterType(externalDirect) === '', 'external direct call returns correct type')
t.ok(common.getFunctionCallTypeParameterType(localCall) === 'struct Ballot.Voter storage pointer', 'local call returns correct type')
t.throws(() => common.getFunctionCallTypeParameterType({ name: 'MemberAccess' }), undefined, 'throws on wrong type')
})
test('staticAnalysisCommon.getFullQualifiedFunctionCallIdent', function (t) {
t.plan(4)
var contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
var localCall = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
var externalDirect = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
t.ok(common.getFullQualifiedFunctionCallIdent(contract, thisLocalCall) === 'test.b(bytes32,address)', 'this local call returns correct type')
t.ok(common.getFullQualifiedFunctionCallIdent(contract, externalDirect) === 'InfoFeed.info()', 'external direct call returns correct type')
t.ok(common.getFullQualifiedFunctionCallIdent(contract, localCall) === 'baz.bli(struct Ballot.Voter storage pointer)', 'local call returns correct type')
t.throws(() => common.getFullQualifiedFunctionCallIdent(contract, { name: 'MemberAccess' }), undefined, 'throws on wrong type')
})
test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) {
t.plan(3)
var contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
var funDef = {
'attributes': {
'constant': false,
'name': 'getY',
'payable': false,
'visibility': 'public'
},
'children': [
{
'children': [
{
'attributes': {
'name': 'z',
'type': 'uint256'
},
'children': [
{
'attributes': {
'name': 'uint'
},
'name': 'ElementaryTypeName'
}
],
'name': 'VariableDeclaration'
},
{
'attributes': {
'name': 'r',
'type': 'bool'
},
'children': [
{
'attributes': {
'name': 'bool'
},
'name': 'ElementaryTypeName'
}
],
'name': 'VariableDeclaration'
}
],
'name': 'ParameterList'
},
{
'children': [
{
'attributes': {
'name': '',
'type': 'uint256'
},
'children': [
{
'attributes': {
'name': 'uint'
},
'id': 34,
'name': 'ElementaryTypeName',
'src': '285:4:0'
}
],
'id': 35,
'name': 'VariableDeclaration',
'src': '285:4:0'
}
],
'name': 'ParameterList'
},
{
'children': [],
'name': 'Block'
}
],
'name': 'FunctionDefinition'
}
t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contract, funDef, ['uint256', 'bool']) === 'baz.getY(uint256,bool)', 'creates right signature')
t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(contract, { name: 'MemberAccess' }, ['uint256', 'bool']), undefined, 'throws on wrong nodes')
t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent({ name: 'FunctionCall' }, funDef, ['uint256', 'bool']), undefined, 'throws on wrong nodes')
})
// #################### Trivial Node Identification
test('staticAnalysisCommon.isFunctionDefinition', function (t) {
t.plan(3)
var node1 = { name: 'FunctionDefinition' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'FunctionDefinitionBLABLA' }
t.ok(common.isFunctionDefinition(node1), 'is exact match should work')
t.notOk(common.isFunctionDefinition(node2), 'different node should not work')
t.notOk(common.isFunctionDefinition(node3), 'substring should not work')
})
test('staticAnalysisCommon.isModifierDefinition', function (t) {
t.plan(3)
var node1 = { name: 'ModifierDefinition' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'ModifierDefinitionBLABLA' }
t.ok(common.isModifierDefinition(node1), 'is exact match should work')
t.notOk(common.isModifierDefinition(node2), 'different node should not work')
t.notOk(common.isModifierDefinition(node3), 'substring should not work')
})
test('staticAnalysisCommon.isModifierInvocation', function (t) {
t.plan(3)
var node1 = { name: 'ModifierInvocation' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'ModifierInvocationBLABLA' }
t.ok(common.isModifierInvocation(node1), 'is exact match should work')
t.notOk(common.isModifierInvocation(node2), 'different node should not work')
t.notOk(common.isModifierInvocation(node3), 'substring should not work')
})
test('staticAnalysisCommon.isVariableDeclaration', function (t) {
t.plan(3)
var node1 = { name: 'VariableDeclaration' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'VariableDeclarationBLABLA' }
t.ok(common.isVariableDeclaration(node1), 'is exact match should work')
t.notOk(common.isVariableDeclaration(node2), 'different node should not work')
t.notOk(common.isVariableDeclaration(node3), 'substring should not work')
})
test('staticAnalysisCommon.isInheritanceSpecifier', function (t) {
t.plan(3)
var node1 = { name: 'InheritanceSpecifier' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'InheritanceSpecifierBLABLA' }
t.ok(common.isInheritanceSpecifier(node1), 'is exact match should work')
t.notOk(common.isInheritanceSpecifier(node2), 'different node should not work')
t.notOk(common.isInheritanceSpecifier(node3), 'substring should not work')
})
test('staticAnalysisCommon.isAssignment', function (t) {
t.plan(3)
var node1 = { name: 'Assignment' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'AssignmentBLABLA' }
t.ok(common.isAssignment(node1), 'is exact match should work')
t.notOk(common.isAssignment(node2), 'different node should not work')
t.notOk(common.isAssignment(node3), 'substring should not work')
})
test('staticAnalysisCommon.isContractDefinition', function (t) {
t.plan(3)
var node1 = { name: 'ContractDefinition' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'ContractDefinitionBLABLA' }
t.ok(common.isContractDefinition(node1), 'is exact match should work')
t.notOk(common.isContractDefinition(node2), 'different node should not work')
t.notOk(common.isContractDefinition(node3), 'substring should not work')
})
test('staticAnalysisCommon.isInlineAssembly', function (t) {
t.plan(3)
var node1 = { name: 'InlineAssembly' }
var node2 = { name: 'MemberAccess' }
var node3 = { name: 'InlineAssemblyBLABLA' }
t.ok(common.isInlineAssembly(node1), 'is exact match should work')
t.notOk(common.isInlineAssembly(node2), 'different node should not work')
t.notOk(common.isInlineAssembly(node3), 'substring should not work')
})
// #################### Complex Node Identification
test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) {
t.plan(3)
var node1 = {
'attributes': {
'name': 'x',
'type': 'struct Ballot.Voter storage pointer'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'id': 43,
'name': 'UserDefinedTypeName',
'src': '604:5:0'
}
],
'id': 44,
'name': 'VariableDeclaration',
'src': '604:15:0'
}
var node2 = {
'attributes': {
'name': 'voters',
'type': 'mapping(address => struct Ballot.Voter storage ref)'
},
'children': [
{
'children': [
{
'attributes': {
'name': 'address'
},
'id': 16,
'name': 'ElementaryTypeName',
'src': '235:7:0'
},
{
'attributes': {
'name': 'Voter'
},
'id': 17,
'name': 'UserDefinedTypeName',
'src': '246:5:0'
}
],
'id': 18,
'name': 'Mapping',
'src': '227:25:0'
}
],
'id': 19,
'name': 'VariableDeclaration',
'src': '227:32:0'
}
var node3 = {
'attributes': {
'name': 'voters',
'type': 'bytes32'
},
'children': [
{
'attributes': {
'name': 'bytes'
},
'id': 16,
'name': 'ElementaryTypeName',
'src': '235:7:0'
}
],
'id': 19,
'name': 'VariableDeclaration',
'src': '227:32:0'
}
t.ok(common.isStorageVariableDeclaration(node1), 'struct storage pointer param is storage')
t.ok(common.isStorageVariableDeclaration(node2), 'struct storage pointer mapping param is storage')
t.notOk(common.isStorageVariableDeclaration(node3), 'bytes is not storage')
})
test('staticAnalysisCommon.isInteraction', function (t) {
t.plan(6)
var sendAst = { name: 'MemberAccess', children: [{attributes: { value: 'd', type: 'address' }}], attributes: { value: 'send', type: 'function (uint256) returns (bool)' } }
var callAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
var callcodeAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'callcode', type: 'function () payable returns (bool)' } }
var delegatecallAst = { name: 'MemberAccess', children: [{attributes: { value: 'g', type: 'address' }}], attributes: { member_name: 'delegatecall', type: 'function () returns (bool)' } }
var nodeExtDir = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
var nodeNot = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
t.ok(common.isInteraction(sendAst), 'send is interaction')
t.ok(common.isInteraction(callAst), 'call is interaction')
t.ok(common.isInteraction(nodeExtDir), 'ExternalDirecCall is interaction')
t.notOk(common.isInteraction(callcodeAst), 'callcode is not interaction')
t.notOk(common.isInteraction(delegatecallAst), 'callcode is not interaction')
t.notOk(common.isInteraction(nodeNot), 'local call is not interaction')
})
test('staticAnalysisCommon.isEffect', function (t) {
t.plan(5)
var inlineAssembly = {
'children': [
],
'id': 21,
'name': 'InlineAssembly',
'src': '809:41:0'
}
var assignment = {
'attributes': {
'operator': '=',
'type': 'uint256'
},
'children': [
{
'attributes': {
'type': 'uint256'
},
'children': [
{
'attributes': {
'type': 'mapping(address => uint256)',
'value': 'c'
},
'id': 61,
'name': 'Identifier',
'src': '873:1:0'
},
{
'attributes': {
'member_name': 'sender',
'type': 'address'
},
'children': [
{
'attributes': {
'type': 'msg',
'value': 'msg'
},
'id': 62,
'name': 'Identifier',
'src': '875:3:0'
}
],
'id': 63,
'name': 'MemberAccess',
'src': '875:10:0'
}
],
'id': 64,
'name': 'IndexAccess',
'src': '873:13:0'
},
{
'attributes': {
'hexvalue': '30',
'subdenomination': null,
'token': null,
'type': 'int_const 0',
'value': '0'
},
'id': 65,
'name': 'Literal',
'src': '889:1:0'
}
],
'id': 66,
'name': 'Assignment',
'src': '873:17:0'
}
var unaryOp = { name: 'UnaryOperation', attributes: { operator: '++' } }
t.ok(common.isEffect(inlineAssembly), 'inline assembly is treated as effect')
t.ok(common.isEffect(assignment), 'assignment is treated as effect')
t.ok(common.isEffect(unaryOp), '++ is treated as effect')
unaryOp.attributes.operator = '--'
t.ok(common.isEffect(unaryOp), '-- is treated as effect')
t.notOk(common.isEffect({ name: 'MemberAccess', attributes: { operator: '++' } }), 'MemberAccess not treated as effect')
})
test('staticAnalysisCommon.isWriteOnStateVariable', function (t) {
t.plan(3)
var inlineAssembly = {
'children': [
],
'id': 21,
'name': 'InlineAssembly',
'src': '809:41:0'
}
var assignment = {
'attributes': {
'operator': '=',
'type': 'uint256'
},
'children': [
{
'attributes': {
'type': 'uint256'
},
'children': [
{
'attributes': {
'type': 'mapping(address => uint256)',
'value': 'c'
},
'id': 61,
'name': 'Identifier',
'src': '873:1:0'
},
{
'attributes': {
'member_name': 'sender',
'type': 'address'
},
'children': [
{
'attributes': {
'type': 'msg',
'value': 'msg'
},
'id': 62,
'name': 'Identifier',
'src': '875:3:0'
}
],
'id': 63,
'name': 'MemberAccess',
'src': '875:10:0'
}
],
'id': 64,
'name': 'IndexAccess',
'src': '873:13:0'
},
{
'attributes': {
'hexvalue': '30',
'subdenomination': null,
'token': null,
'type': 'int_const 0',
'value': '0'
},
'id': 65,
'name': 'Literal',
'src': '889:1:0'
}
],
'id': 66,
'name': 'Assignment',
'src': '873:17:0'
}
var node1 = {
'attributes': {
'name': 'x',
'type': 'struct Ballot.Voter storage pointer'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'VariableDeclaration'
}
var node2 = {
'attributes': {
'name': 'y',
'type': 'uint'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'VariableDeclaration'
}
var node3 = {
'attributes': {
'name': 'xx',
'type': 'uint'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'VariableDeclaration'
}
t.notOk(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is not write on state')
t.notOk(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on non state is not write on state')
node3.attributes.name = 'c'
t.ok(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on state is not write on state')
})
test('staticAnalysisCommon.isStateVariable', function (t) {
t.plan(3)
var node1 = {
'attributes': {
'name': 'x',
'type': 'struct Ballot.Voter storage pointer'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'VariableDeclaration'
}
var node2 = {
'attributes': {
'name': 'y',
'type': 'uint'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'VariableDeclaration'
}
var node3 = {
'attributes': {
'name': 'xx',
'type': 'uint'
},
'children': [
{
'attributes': {
'name': 'Voter'
},
'name': 'UserDefinedTypeName'
}
],
'name': 'VariableDeclaration'
}
t.ok(common.isStateVariable('x', [node1, node2]), 'is contained')
t.ok(common.isStateVariable('x', [node2, node1, node1]), 'is contained twice')
t.notOk(common.isStateVariable('x', [node2, node3]), 'not contained')
})
test('staticAnalysisCommon.isConstantFunction', function (t) {
t.plan(3)
var node1 = { name: 'FunctionDefinition', attributes: { constant: true } }
var node2 = { name: 'FunctionDefinition', attributes: { constant: false } }
var node3 = { name: 'MemberAccess', attributes: { constant: true } }
t.ok(common.isConstantFunction(node1), 'should be const func definition')
t.notOk(common.isConstantFunction(node2), 'should not be const func definition')
t.notOk(common.isConstantFunction(node3), 'wrong node should not be const func definition')
})
test('staticAnalysisCommon.isPlusPlusUnaryOperation', function (t) {
t.plan(3)
var node1 = { name: 'UnaryOperation', attributes: { operator: '++' } }
var node2 = { name: 'UnaryOperation', attributes: { operator: '--' } }
var node3 = { name: 'FunctionDefinition', attributes: { operator: '++' } }
t.ok(common.isPlusPlusUnaryOperation(node1), 'should be unary ++')
t.notOk(common.isPlusPlusUnaryOperation(node2), 'should not be unary ++')
t.notOk(common.isPlusPlusUnaryOperation(node3), 'wrong node should not be unary ++')
})
test('staticAnalysisCommon.isMinusMinusUnaryOperation', function (t) {
t.plan(3)
var node1 = { name: 'UnaryOperation', attributes: { operator: '--' } }
var node2 = { name: 'UnaryOperation', attributes: { operator: '++' } }
var node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } }
t.ok(common.isMinusMinusUnaryOperation(node1), 'should be unary --')
t.notOk(common.isMinusMinusUnaryOperation(node2), 'should not be unary --')
t.notOk(common.isMinusMinusUnaryOperation(node3), 'wrong node should not be unary --')
})
test('staticAnalysisCommon.isFullyImplementedContract', function (t) {
t.plan(3)
var node1 = { name: 'ContractDefinition', attributes: { fullyImplemented: true } }
var node2 = { name: 'ContractDefinition', attributes: { fullyImplemented: false } }
var node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } }
t.ok(common.isFullyImplementedContract(node1), 'should be fully implemented contract')
t.notOk(common.isFullyImplementedContract(node2), 'should not be fully implemented contract')
t.notOk(common.isFullyImplementedContract(node3), 'wrong node should not be fully implemented contract')
})
test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) {
t.plan(2)
var node1 = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
t.ok(common.isCallToNonConstLocalFunction(node1), 'should be call to non const Local func')
node1.children[0].attributes.type = 'function (struct Ballot.Voter storage pointer) constant payable (uint256)'
t.notok(common.isCallToNonConstLocalFunction(node1), 'should no longer be call to non const Local func')
})
test('staticAnalysisCommon.isExternalDirectCall', function (t) {
t.plan(5)
var node = {
attributes: {
member_name: 'info',
type: 'function () payable external returns (uint256)'
},
children: [
{
attributes: {
type: 'contract InfoFeed',
value: 'f'
},
id: 30,
name: 'Identifier',
src: '405:1:0'
}
],
id: 32,
name: 'MemberAccess',
src: '405:6:0'
}
var node2 = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
t.notOk(common.isNowAccess(node), 'is now used should not work')
t.ok(common.isExternalDirectCall(node), 'f.info() should be external direct call')
t.notOk(common.isExternalDirectCall(node2), 'local call is not an exernal call')
})
test('staticAnalysisCommon.isNowAccess', function (t) {
t.plan(3)
var node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } }
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
t.ok(common.isNowAccess(node), 'is now used should work')
})
test('staticAnalysisCommon.isBlockTimestampAccess', function (t) {
t.plan(3)
var node = { name: 'MemberAccess', children: [{attributes: { value: 'block', type: 'block' }}], attributes: { value: 'timestamp', type: 'uint256' } }
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
t.ok(common.isBlockTimestampAccess(node), 'is block.timestamp used should work')
t.notOk(common.isNowAccess(node), 'is now used should not work')
})
test('staticAnalysisCommon.isThisLocalCall', function (t) {
t.plan(3)
var node = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
t.ok(common.isThisLocalCall(node), 'is this.local_method() used should work')
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
t.notOk(common.isNowAccess(node), 'is now used should not work')
})
test('staticAnalysisCommon.isLocalCall', function (t) {
t.plan(5)
var node1 = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'id': 37,
'name': 'Identifier',
'src': '540:3:0'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'id': 38,
'name': 'Identifier',
'src': '544:1:0'
}
],
'id': 39,
'name': 'FunctionCall',
'src': '540:6:0'
}
t.ok(common.isLocalCall(node1), 'isLocalCall')
t.notOk(common.isLowLevelCall(node1), 'is not low level call')
t.notOk(common.isExternalDirectCall(node1), 'is not external direct call')
t.notOk(common.isEffect(node1), 'is not effect')
t.notOk(common.isInteraction(node1), 'is not interaction')
})
test('staticAnalysisCommon.isLowLevelCall', function (t) {
t.plan(6)
var sendAst = { name: 'MemberAccess', children: [{attributes: { value: 'd', type: 'address' }}], attributes: { value: 'send', type: 'function (uint256) returns (bool)' } }
var callAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
var callcodeAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'callcode', type: 'function () payable returns (bool)' } }
var delegatecallAst = { name: 'MemberAccess', children: [{attributes: { value: 'g', type: 'address' }}], attributes: { member_name: 'delegatecall', type: 'function () returns (bool)' } }
t.ok(common.isLowLevelSendInst(sendAst) && common.isLowLevelCall(sendAst), 'send is llc should work')
t.ok(common.isLowLevelCallInst(callAst) && common.isLowLevelCall(callAst), 'call is llc should work')
t.notOk(common.isLowLevelCallInst(callcodeAst), 'callcode is not call')
t.ok(common.isLowLevelCallcodeInst(callcodeAst) && common.isLowLevelCall(callcodeAst), 'callcode is llc should work')
t.notOk(common.isLowLevelCallcodeInst(callAst), 'call is not callcode')
t.ok(common.isLowLevelDelegatecallInst(delegatecallAst) && common.isLowLevelCall(delegatecallAst), 'delegatecall is llc should work')
}) })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment