diff --git a/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts b/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts index 4313ad7325..10fbe4561b 100644 --- a/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts +++ b/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts @@ -1,4 +1,4 @@ -import { getStateVariableDeclarationsFormContractNode, +import { getStateVariableDeclarationsFromContractNode, getInheritsFromName, getContractName, getFunctionOrModifierDefinitionParameterPart, getType, getDeclaredVariableName, getFunctionDefinitionReturnParameterPart } from './staticAnalysisCommon' @@ -57,7 +57,7 @@ export default class abstractAstView { relevantNodes: [], modifiers: [], inheritsFrom: [], - stateVariables: getStateVariableDeclarationsFormContractNode(node) + stateVariables: getStateVariableDeclarationsFromContractNode(node) }) } else if (node.nodeType === "InheritanceSpecifier") { const currentContract = that.getCurrentContract(that) diff --git a/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts b/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts index 4b2476c36b..8bbc37b1ac 100644 --- a/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts +++ b/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts @@ -47,7 +47,7 @@ const basicRegex = { FUNCTIONTYPE: '^function \\(', EXTERNALFUNCTIONTYPE: '^function \\(.*\\).* external', CONSTANTFUNCTIONTYPE: '^function \\(.*\\).* (view|pure)', - REFTYPE: '( storage )|(mapping\\()|(\\[\\])', + REFTYPE: '(storage)|(mapping\\()|(\\[\\])', FUNCTIONSIGNATURE: '^function \\(([^\\(]*)\\)', LIBRARYTYPE: '^type\\(library (.*)\\)' } @@ -169,9 +169,9 @@ function getLocalCallName (localCallNode: FunctionCallAstNode): string { * @localCallNode {ASTNode} Function call node * @return {string} name of the function called */ -function getThisLocalCallName (localCallNode: FunctionCallAstNode): string { - if (!isThisLocalCall(localCallNode.expression)) throw new Error('staticAnalysisCommon.js: not an this local call Node') - return localCallNode.expression.memberName +function getThisLocalCallName (thisLocalCallNode: FunctionCallAstNode): string { + if (!isThisLocalCall(thisLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not an this local call Node') + return thisLocalCallNode.expression.memberName } /** @@ -180,9 +180,9 @@ function getThisLocalCallName (localCallNode: FunctionCallAstNode): string { * @localCallNode {ASTNode} Function call node * @return {string} name of the function called */ -function getSuperLocalCallName (localCallNode: FunctionCallAstNode): string { - if (!isSuperLocalCall(localCallNode.expression)) throw new Error('staticAnalysisCommon.js: not an super local call Node') - return localCallNode.expression.memberName +function getSuperLocalCallName (superLocalCallNode: FunctionCallAstNode): string { + if (!isSuperLocalCall(superLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not an super local call Node') + return superLocalCallNode.expression.memberName } /** @@ -256,8 +256,8 @@ function getFunctionDefinitionName (funcDef: FunctionDefinitionAstNode): string * @func {ASTNode} Inheritance specifier * @return {string} name of contract inherited from */ -function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): UserDefinedTypeNameAstNode { - return inheritsNode.baseName +function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): string { + return inheritsNode.baseName.name } /** @@ -292,7 +292,7 @@ function getDeclaredVariableType (varDeclNode: VariableDeclarationAstNode): stri * @contractNode {ASTNode} Contract Definition node * @return {list variable declaration} state variable node list */ -function getStateVariableDeclarationsFormContractNode (contractNode: ContractDefinitionAstNode): VariableDeclarationAstNode[] { +function getStateVariableDeclarationsFromContractNode (contractNode: ContractDefinitionAstNode): VariableDeclarationAstNode[] { return contractNode.nodes.filter(el => el.nodeType === "VariableDeclaration") } @@ -355,9 +355,9 @@ function getFunctionCallTypeParameterType (func: FunctionCallAstNode): string | * @funcCall {ASTNode} function call node * @return {string} name of the lib defined */ -function getLibraryCallContractName (node: MemberAccessAstNode): string | undefined { - if (!isLibraryCall(node)) throw new Error('staticAnalysisCommon.js: not an this library call Node') - const types: RegExpExecArray | null = new RegExp(basicRegex.LIBRARYTYPE).exec(node.expression.typeDescriptions.typeString) +function getLibraryCallContractName (node: FunctionCallAstNode): string | undefined { + if (!isLibraryCall(node.expression)) throw new Error('staticAnalysisCommon.js: not an this library call Node') + const types: RegExpExecArray | null = new RegExp(basicRegex.LIBRARYTYPE).exec(node.expression.expression.typeDescriptions.typeString) if(types) return types[1] } @@ -522,6 +522,9 @@ function isLocalCallGraphRelevantNode (node: FunctionCallAstNode): boolean { * @return {bool} */ function isBuiltinFunctionCall (node: FunctionCallAstNode): boolean { + // console.log('isBuiltinFunctionCall isLocalCall', isLocalCall(node)) + // console.log('isBuiltinFunctionCall getLocalCallName', getLocalCallName(node)) + // console.log('isBuiltinFunctionCall getFunctionCallTypeParameterType', getFunctionCallTypeParameterType(node)) return (isLocalCall(node) && builtinFunctions[getLocalCallName(node) + '(' + getFunctionCallTypeParameterType(node) + ')'] === true) || isAbiNamespaceCall(node) } @@ -567,7 +570,7 @@ function isRequireCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isStorageVariableDeclaration (node: VariableDeclarationAstNode): boolean { - return expressionType(node, basicRegex.REFTYPE) + return node.storageLocation === 'storage' && new RegExp(basicRegex.REFTYPE).test(node.typeDescriptions.typeIdentifier) } /** @@ -731,7 +734,7 @@ function isLibrary (node: ContractDefinitionAstNode): boolean { * @return {bool} */ function isCallToNonConstLocalFunction (node: FunctionCallAstNode): boolean { - return isLocalCall(node) && !expressionType(node, basicRegex.CONSTANTFUNCTIONTYPE) + return isLocalCall(node) && !expressionTypeDescription(node, basicRegex.CONSTANTFUNCTIONTYPE) } /** @@ -862,8 +865,8 @@ function isLLSend (node: MemberAccessAstNode): boolean { */ function isLLCall (node: MemberAccessAstNode): boolean { return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.CALL.type)), - undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident)) + exactMatch(util.escapeRegExp(lowLevelCallTypes['CALL-v0.5'].type)), + undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes['CALL-v0.5'].ident)) } /** @@ -895,8 +898,8 @@ function isLLCallcode (node: MemberAccessAstNode): boolean { */ function isLLDelegatecall (node: MemberAccessAstNode): boolean { return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.DELEGATECALL.type)), - undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident)) + exactMatch(util.escapeRegExp(lowLevelCallTypes['DELEGATECALL-v0.5'].type)), + undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes['DELEGATECALL-v0.5'].ident)) } /** @@ -959,10 +962,15 @@ function isBytesLengthCheck (node: MemberAccessAstNode): boolean { // #################### Complex Node Identification - Private function isMemberAccess (node: MemberAccessAstNode, retType: string, accessor: string| undefined, accessorType: string, memberName: string | undefined): boolean { - return typeDescription(node, retType) && - memName(node, memberName) && - memName(node.expression, accessor) && - expressionTypeDescription(node.expression, accessorType) + const nodeTypeDef: boolean = typeDescription(node, retType) + console.log('MemberAccess typeDef ->',nodeTypeDef) + const nodeMemName: boolean = memName(node, memberName) + console.log('MemberAccess nodeMemName ->',nodeMemName) + const nodeExpMemName: boolean = memName(node.expression, accessor) + console.log('MemberAccess nodeExpMemName ->',nodeExpMemName) + const nodeExpTypeDef: boolean = expressionTypeDescription(node, accessorType) + console.log('MemberAccess nodeExpTypeDef ->',nodeExpTypeDef) + return nodeTypeDef && nodeMemName && nodeExpTypeDef && nodeExpMemName } function isSpecialVariableAccess (node: MemberAccessAstNode, varType: any): boolean { @@ -979,9 +987,9 @@ function isSpecialVariableAccess (node: MemberAccessAstNode, varType: any): bool // return (node && (nr === undefined || nr === null)) || (node && nr === 0 && !node.children) || (node && node.children && node.children.length >= nr) // } -function expressionType (node, typeRegex) { - return new RegExp(typeRegex).test(node.expression.typeDescriptions.typeString) -} +// function expressionType (node, typeRegex) { +// return new RegExp(typeRegex).test(node.expression.typeDescriptions.typeString) +// } function expressionTypeDescription (node, typeRegex) { return new RegExp(typeRegex).test(node.expression.typeDescriptions.typeString) @@ -996,8 +1004,8 @@ function nodeType (node, typeRegex) { } function memName (node, memNameRegex) { - const regex = new RegExp(memNameRegex) - return regex.test(node.name) || regex.test(node.memberName) + // const regex = new RegExp(memNameRegex) + return (node && !memNameRegex) || new RegExp(memNameRegex).test(node.name) || new RegExp(memNameRegex).test(node.memberName) } function operator (node, opRegex) { @@ -1056,7 +1064,7 @@ function buildAbiSignature (funName: string, paramTypes: any[]): string { const helpers = { // nrOfChildren, // minNrOfChildren, - expressionType, + expressionTypeDescription, nodeType, memName, operator, @@ -1086,7 +1094,7 @@ export { getLibraryCallMemberName, getFullQualifiedFunctionCallIdent, getFullQuallyfiedFuncDefinitionIdent, - getStateVariableDeclarationsFormContractNode, + getStateVariableDeclarationsFromContractNode, getFunctionOrModifierDefinitionParameterPart, getFunctionDefinitionReturnParameterPart, getUnAssignedTopLevelBinOps, diff --git a/remix-analyzer/test/analysis/astBlocks/contractDefinition.json b/remix-analyzer/test/analysis/astBlocks/contractDefinition.json new file mode 100644 index 0000000000..31229fcabb --- /dev/null +++ b/remix-analyzer/test/analysis/astBlocks/contractDefinition.json @@ -0,0 +1,102 @@ +{ + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 8, + "linearizedBaseContracts": + [ + 8 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 6, + "nodeType": "Block", + "src": "42:23:0", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "name": "a", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 6, + "src": "52:6:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "52:4:0", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": null, + "visibility": "internal" + } + ], + "id": 5, + "initialValue": null, + "nodeType": "VariableDeclarationStatement", + "src": "52:6:0" + } + ] + }, + "documentation": null, + "functionSelector": "26121ff0", + "id": 7, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "overrides": null, + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:0" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:0" + }, + "scope": 8, + "src": "17:48:0", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "scope": 9, + "src": "0:67:0" + } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/externalDirect.json b/remix-analyzer/test/analysis/astBlocks/externalDirect.json index ceb1bf2cd2..ebf7625571 100644 --- a/remix-analyzer/test/analysis/astBlocks/externalDirect.json +++ b/remix-analyzer/test/analysis/astBlocks/externalDirect.json @@ -1,20 +1,99 @@ { - "attributes": { - "member_name": "info", - "type": "function () payable external returns (uint256)" - }, - "children": [ + "argumentTypes": null, + "arguments": [], + "expression": + { + "argumentTypes": [], + "expression": { - "attributes": { - "type": "contract InfoFeed", - "value": "f" + "argumentTypes": null, + "arguments": + [ + { + "argumentTypes": null, + "hexValue": "30", + "id": 9, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "83:1:0", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + } + ], + "expression": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + } + ], + "id": 8, + "name": "c", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 5, + "src": "81:1:0", + "typeDescriptions": + { + "typeIdentifier": "t_type$_t_contract$_c_$5_$", + "typeString": "type(contract c)" + } }, - "id": 30, - "name": "Identifier", - "src": "405:1:0" + "id": 10, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "81:4:0", + "tryCall": false, + "typeDescriptions": + { + "typeIdentifier": "t_contract$_c_$5", + "typeString": "contract c" + } + }, + "id": 11, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "f", + "nodeType": "MemberAccess", + "referencedDeclaration": 4, + "src": "81:6:0", + "typeDescriptions": + { + "typeIdentifier": "t_function_external_nonpayable$__$returns$__$", + "typeString": "function () external" } - ], - "id": 32, - "name": "MemberAccess", - "src": "405:6:0" + }, + "id": 12, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "81:8:0", + "tryCall": false, + "typeDescriptions": + { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/index.js b/remix-analyzer/test/analysis/astBlocks/index.js index f952ad63c5..fb1d9d6114 100644 --- a/remix-analyzer/test/analysis/astBlocks/index.js +++ b/remix-analyzer/test/analysis/astBlocks/index.js @@ -1,5 +1,8 @@ module.exports = { localCall: require('./localCall.json'), + contractDefinition: require('./contractDefinition.json'), + unaryOperation: require('./unaryOperation.json'), + nowAst: require('./nowAst.json'), thisLocalCall: require('./thisLocalCall.json'), libCall: require('./libCall.json'), externalDirect: require('./externalDirect.json'), diff --git a/remix-analyzer/test/analysis/astBlocks/libCall.json b/remix-analyzer/test/analysis/astBlocks/libCall.json index f99e793e6f..12df4999aa 100644 --- a/remix-analyzer/test/analysis/astBlocks/libCall.json +++ b/remix-analyzer/test/analysis/astBlocks/libCall.json @@ -1,32 +1,59 @@ -{ - "argumentTypes": null, - "expression": - { - "argumentTypes": null, - "id": 33, - "name": "L", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": 21, - "src": "244:1:0", - "typeDescriptions": - { - "typeIdentifier": "t_type$_t_contract$_L_$21_$", - "typeString": "type(library L)" - } - }, - "id": 34, +{ "argumentTypes": null, + "arguments": + [ { "argumentTypes": null, + "id": 95, + "name": "knownValues", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 79, + "src": "1392:11:0", + "typeDescriptions": { "typeIdentifier": "t_struct$_Data_$6_storage", + "typeString": "struct Set.Data storage ref" } }, + { "argumentTypes": null, + "id": 96, + "name": "value", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 81, + "src": "1405:5:0", + "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } ], + "expression": + { "argumentTypes": [ { "typeIdentifier": "t_struct$_Data_$6_storage", + "typeString": "struct Set.Data storage ref" }, + { "typeIdentifier": "t_uint256', typeString: 'uint256" } ], + "expression": + { "argumentTypes": null, + "id": 93, + "name": "Set", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 77, + "src": "1381:3:0", + "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_Set_$77_$", + "typeString": "type(library Set)" } }, + "id": 94, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "insert", + "nodeType": "MemberAccess", + "referencedDeclaration": 33, + "src": "1381:10:0", + "typeDescriptions": + { "typeIdentifier": + "t_function_delegatecall_nonpayable$_t_struct$_Data_$6_storage_ptr_$_t_uint256_$returns$_t_bool_$", + "typeString": + "function (struct Set.Data storage pointer,uint256) returns (bool)" } }, + "id": 97, "isConstant": false, "isLValue": false, "isPure": false, + "kind": "functionCall", "lValueRequested": false, - "memberName": "f", - "nodeType": "MemberAccess", - "referencedDeclaration": 6, - "src": "244:3:0", - "typeDescriptions": - { - "typeIdentifier": "t_function_delegatecall_nonpayable$_t_uint256_$returns$__$", - "typeString": "function (uint256)" - } + "names": [], + "nodeType": "FunctionCall", + "src": "1381:30:0", + "tryCall": false, + "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/localCall.json b/remix-analyzer/test/analysis/astBlocks/localCall.json index 0963998caa..d954eba389 100644 --- a/remix-analyzer/test/analysis/astBlocks/localCall.json +++ b/remix-analyzer/test/analysis/astBlocks/localCall.json @@ -1,34 +1,88 @@ { - "argumentTypes": null, - "arguments": [], - "expression": + "argumentTypes": null, + "arguments": + [ { - "argumentTypes": [], - "id": 13, - "name": "sha3", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": 8, - "src": "129:4:0", + "argumentTypes": null, + "hexValue": "616263", + "id": 15, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "115:5:0", + "subdenomination": null, "typeDescriptions": { - "typeIdentifier": "t_function_internal_pure$__$returns$_t_bool_$", - "typeString": "function () pure returns (bool)" - } + "typeIdentifier": "t_stringliteral_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", + "typeString": "literal_string \"abc\"" + }, + "value": "abc" }, + { + "argumentTypes": null, + "hexValue": "38", + "id": 16, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "125:1:0", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_8_by_1", + "typeString": "int_const 8" + }, + "value": "8" + } + ], + "expression": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_stringliteral_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", + "typeString": "literal_string \"abc\"" + }, + { + "typeIdentifier": "t_rational_8_by_1", + "typeString": "int_const 8" + } + ], "id": 14, - "isConstant": false, - "isLValue": false, - "isPure": false, - "kind": "functionCall", - "lValueRequested": false, - "names": [], - "nodeType": "FunctionCall", - "src": "129:6:0", - "tryCall": false, + "name": "e", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 6, + "src": "109:1:0", "typeDescriptions": { - "typeIdentifier": "t_bool", - "typeString": "bool" + "typeIdentifier": "t_function_event_nonpayable$_t_uint256_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (uint256,string memory)" } + }, + "id": 17, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": + [ + "b", + "a" + ], + "nodeType": "FunctionCall", + "src": "109:19:0", + "tryCall": false, + "typeDescriptions": + { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/nowAst.json b/remix-analyzer/test/analysis/astBlocks/nowAst.json new file mode 100644 index 0000000000..1fdc9a5397 --- /dev/null +++ b/remix-analyzer/test/analysis/astBlocks/nowAst.json @@ -0,0 +1,14 @@ +{ + "argumentTypes": null, + "id": 11, + "name": "now", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -17, + "src": "110:3:0", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } +} \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/stateVariableContractNode.json b/remix-analyzer/test/analysis/astBlocks/stateVariableContractNode.json index 68bd3f3985..e24f5308eb 100644 --- a/remix-analyzer/test/analysis/astBlocks/stateVariableContractNode.json +++ b/remix-analyzer/test/analysis/astBlocks/stateVariableContractNode.json @@ -1,336 +1,210 @@ -{ - "abstract": false, - "baseContracts": [], - "contractDependencies": [], - "contractKind": "contract", - "documentation": null, - "fullyImplemented": true, - "id": 24, - "linearizedBaseContracts": - [ - 24 - ], - "name": "C", - "nodeType": "ContractDefinition", - "nodes": - [ - { - "body": + + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 17, + "linearizedBaseContracts": + [ + 17 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ { - "id": 22, - "nodeType": "Block", - "src": "52:69:0", - "statements": - [ + "constant": true, + "id": 5, + "name": "x", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "17:23:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "17:4:0", + "typeDescriptions": { - "assignments": - [ - 10 - ], - "declarations": - [ - { - "constant": false, - "id": 10, - "name": "z", - "nodeType": "VariableDeclaration", - "overrides": null, - "scope": 22, - "src": "62:17:0", - "stateVariable": false, - "storageLocation": "memory", - "typeDescriptions": - { - "typeIdentifier": "t_array$_t_uint8_$4_memory_ptr", - "typeString": "uint8[4]" - }, - "typeName": - { - "baseType": - { - "id": 8, - "name": "uint8", - "nodeType": "ElementaryTypeName", - "src": "62:5:0", - "typeDescriptions": - { - "typeIdentifier": "t_uint8", - "typeString": "uint8" - } - }, - "id": 9, - "length": - { - "argumentTypes": null, - "hexValue": "34", - "id": 7, - "isConstant": false, - "isLValue": false, - "isPure": true, - "kind": "number", - "lValueRequested": false, - "nodeType": "Literal", - "src": "68:1:0", - "subdenomination": null, - "typeDescriptions": - { - "typeIdentifier": "t_rational_4_by_1", - "typeString": "int_const 4" - }, - "value": "4" - }, - "nodeType": "ArrayTypeName", - "src": "62:8:0", - "typeDescriptions": - { - "typeIdentifier": "t_array$_t_uint8_$4_storage_ptr", - "typeString": "uint8[4]" - } - }, - "value": null, - "visibility": "internal" - } - ], - "id": 16, - "initialValue": + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": + { + "argumentTypes": null, + "commonType": + { + "typeIdentifier": "t_rational_1048576_by_1", + "typeString": "int_const 1048576" + }, + "id": 4, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "leftExpression": + { + "argumentTypes": null, + "hexValue": "32", + "id": 2, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "35:1:0", + "subdenomination": null, + "typeDescriptions": { - "argumentTypes": null, - "components": - [ - { - "argumentTypes": null, - "hexValue": "31", - "id": 11, - "isConstant": false, - "isLValue": false, - "isPure": true, - "kind": "number", - "lValueRequested": false, - "nodeType": "Literal", - "src": "83:1:0", - "subdenomination": null, - "typeDescriptions": - { - "typeIdentifier": "t_rational_1_by_1", - "typeString": "int_const 1" - }, - "value": "1" - }, - { - "argumentTypes": null, - "hexValue": "32", - "id": 12, - "isConstant": false, - "isLValue": false, - "isPure": true, - "kind": "number", - "lValueRequested": false, - "nodeType": "Literal", - "src": "85:1:0", - "subdenomination": null, - "typeDescriptions": - { - "typeIdentifier": "t_rational_2_by_1", - "typeString": "int_const 2" - }, - "value": "2" - }, - { - "argumentTypes": null, - "hexValue": "33", - "id": 13, - "isConstant": false, - "isLValue": false, - "isPure": true, - "kind": "number", - "lValueRequested": false, - "nodeType": "Literal", - "src": "87:1:0", - "subdenomination": null, - "typeDescriptions": - { - "typeIdentifier": "t_rational_3_by_1", - "typeString": "int_const 3" - }, - "value": "3" - }, - { - "argumentTypes": null, - "hexValue": "35", - "id": 14, - "isConstant": false, - "isLValue": false, - "isPure": true, - "kind": "number", - "lValueRequested": false, - "nodeType": "Literal", - "src": "89:1:0", - "subdenomination": null, - "typeDescriptions": - { - "typeIdentifier": "t_rational_5_by_1", - "typeString": "int_const 5" - }, - "value": "5" - } - ], - "id": 15, - "isConstant": false, - "isInlineArray": true, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "nodeType": "TupleExpression", - "src": "82:9:0", - "typeDescriptions": - { - "typeIdentifier": "t_array$_t_uint8_$4_memory_ptr", - "typeString": "uint8[4] memory" - } + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" }, - "nodeType": "VariableDeclarationStatement", - "src": "62:29:0" + "value": "2" }, + "nodeType": "BinaryOperation", + "operator": "**", + "rightExpression": { - "expression": + "argumentTypes": null, + "hexValue": "3230", + "id": 3, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "38:2:0", + "subdenomination": null, + "typeDescriptions": { - "argumentTypes": null, - "components": - [ - { - "argumentTypes": null, - "baseExpression": - { - "argumentTypes": null, - "id": 17, - "name": "z", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": 10, - "src": "109:1:0", - "typeDescriptions": - { - "typeIdentifier": "t_array$_t_uint8_$4_memory_ptr", - "typeString": "uint8[4] memory" - } - }, - "id": 19, - "indexExpression": - { - "argumentTypes": null, - "hexValue": "30", - "id": 18, - "isConstant": false, - "isLValue": false, - "isPure": true, - "kind": "number", - "lValueRequested": false, - "nodeType": "Literal", - "src": "111:1:0", - "subdenomination": null, - "typeDescriptions": - { - "typeIdentifier": "t_rational_0_by_1", - "typeString": "int_const 0" - }, - "value": "0" - }, - "isConstant": false, - "isLValue": true, - "isPure": false, - "lValueRequested": false, - "nodeType": "IndexAccess", - "src": "109:4:0", - "typeDescriptions": - { - "typeIdentifier": "t_uint8", - "typeString": "uint8" - } - } - ], - "id": 20, - "isConstant": false, - "isInlineArray": false, - "isLValue": false, - "isPure": false, - "lValueRequested": false, - "nodeType": "TupleExpression", - "src": "108:6:0", - "typeDescriptions": - { - "typeIdentifier": "t_uint8", - "typeString": "uint8" - } + "typeIdentifier": "t_rational_20_by_1", + "typeString": "int_const 20" }, - "functionReturnParameters": 4, - "id": 21, - "nodeType": "Return", - "src": "101:13:0" + "value": "20" + }, + "src": "35:5:0", + "typeDescriptions": + { + "typeIdentifier": "t_rational_1048576_by_1", + "typeString": "int_const 1048576" } - ] + }, + "visibility": "internal" }, - "documentation": null, - "functionSelector": "26121ff0", - "id": 23, - "implemented": true, - "kind": "function", - "modifiers": [], - "name": "f", - "nodeType": "FunctionDefinition", - "overrides": null, - "parameters": { - "id": 1, - "nodeType": "ParameterList", - "parameters": [], - "src": "27:2:0" + "constant": true, + "id": 8, + "name": "b", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "46:22:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": + { + "id": 6, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "46:4:0", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "value": + { + "argumentTypes": null, + "hexValue": "74727565", + "id": 7, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "64:4:0", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "true" + }, + "visibility": "internal" }, - "returnParameters": { - "id": 4, - "nodeType": "ParameterList", - "parameters": - [ + "constant": true, + "id": 11, + "name": "s", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "74:24:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_bytes4", + "typeString": "bytes4" + }, + "typeName": + { + "id": 9, + "name": "bytes4", + "nodeType": "ElementaryTypeName", + "src": "74:6:0", + "typeDescriptions": { - "constant": false, - "id": 3, - "name": "", - "nodeType": "VariableDeclaration", - "overrides": null, - "scope": 23, - "src": "46:4:0", - "stateVariable": false, - "storageLocation": "default", - "typeDescriptions": - { - "typeIdentifier": "t_uint256", - "typeString": "uint256" - }, - "typeName": - { - "id": 2, - "name": "uint", - "nodeType": "ElementaryTypeName", - "src": "46:4:0", - "typeDescriptions": - { - "typeIdentifier": "t_uint256", - "typeString": "uint256" - } - }, - "value": null, - "visibility": "internal" + "typeIdentifier": "t_bytes4", + "typeString": "bytes4" } - ], - "src": "45:6:0" - }, - "scope": 24, - "src": "17:104:0", - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - } - ], - "scope": 25, - "src": "0:123:0" -} \ No newline at end of file + }, + "value": + { + "argumentTypes": null, + "hexValue": "6162", + "id": 10, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "94:4:0", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_stringliteral_67fad3bfa1e0321bd021ca805ce14876e50acac8ca8532eda8cbf924da565160", + "typeString": "literal_string \"ab\"" + }, + "value": "ab" + }, + "visibility": "internal" + } + ], + "scope": 18, + "src": "0:239:0" + } diff --git a/remix-analyzer/test/analysis/astBlocks/storageVariableNodes.json b/remix-analyzer/test/analysis/astBlocks/storageVariableNodes.json index 4448202b60..f023acd13b 100644 --- a/remix-analyzer/test/analysis/astBlocks/storageVariableNodes.json +++ b/remix-analyzer/test/analysis/astBlocks/storageVariableNodes.json @@ -1,74 +1,127 @@ { "node1": { - "attributes": { - "name": "x", - "type": "struct Ballot.Voter storage pointer" + "constant": false, + "id": 20, + "name": "c", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 33, + "src": "174:11:0", + "stateVariable": false, + "storageLocation": "storage", + "typeDescriptions": + { + "typeIdentifier": "t_struct$_S_$3_storage_ptr", + "typeString": "struct C.S" }, - "children": [ + "typeName": + { + "contractScope": null, + "id": 19, + "name": "S", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 3, + "src": "174:1:0", + "typeDescriptions": { - "attributes": { - "name": "Voter" - }, - "id": 43, - "name": "UserDefinedTypeName", - "src": "604:5:0" + "typeIdentifier": "t_struct$_S_$3_storage_ptr", + "typeString": "struct C.S" } - ], - "id": 44, - "name": "VariableDeclaration", - "src": "604:15:0" + }, + "value": null, + "visibility": "internal" }, "node2": { - "attributes": { - "name": "voters", - "type": "mapping(address => struct Ballot.Voter storage ref)" + "constant": false, + "id": 11, + "name": "", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 16, + "src": "82:29:0", + "stateVariable": false, + "storageLocation": "storage", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_mapping$_t_uint256_$_t_uint256_$_$dyn_storage_ptr", + "typeString": "mapping(uint256 => uint256)[]" }, - "children": [ + "typeName": + { + "baseType": { - "children": [ + "id": 9, + "keyType": + { + "id": 7, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "90:4:0", + "typeDescriptions": { - "attributes": { - "name": "address" - }, - "id": 16, - "name": "ElementaryTypeName", - "src": "235:7:0" - }, + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Mapping", + "src": "82:19:0", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_uint256_$_t_uint256_$", + "typeString": "mapping(uint256 => uint256)" + }, + "valueType": + { + "id": 8, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "96:4:0", + "typeDescriptions": { - "attributes": { - "name": "Voter" - }, - "id": 17, - "name": "UserDefinedTypeName", - "src": "246:5:0" + "typeIdentifier": "t_uint256", + "typeString": "uint256" } - ], - "id": 18, - "name": "Mapping", - "src": "227:25:0" + } + }, + "id": 10, + "length": null, + "nodeType": "ArrayTypeName", + "src": "82:21:0", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_mapping$_t_uint256_$_t_uint256_$_$dyn_storage_ptr", + "typeString": "mapping(uint256 => uint256)[]" } - ], - "id": 19, - "name": "VariableDeclaration", - "src": "227:32:0" + } }, "node3": { - "attributes": { - "name": "voters", - "type": "bytes32" + "constant": false, + "id": 125, + "name": "f", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 160, + "src": "1005:14:0", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": + { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes" }, - "children": [ + "typeName": + { + "id": 124, + "name": "bytes", + "nodeType": "ElementaryTypeName", + "src": "1005:5:0", + "typeDescriptions": { - "attributes": { - "name": "bytes" - }, - "id": 16, - "name": "ElementaryTypeName", - "src": "235:7:0" + "typeIdentifier": "t_bytes_storage_ptr", + "typeString": "bytes" } - ], - "id": 19, - "name": "VariableDeclaration", - "src": "227:32:0" + }, + "value": null, + "visibility": "internal" } } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/superLocal.json b/remix-analyzer/test/analysis/astBlocks/superLocal.json index 7655966f1d..d87be6477b 100644 --- a/remix-analyzer/test/analysis/astBlocks/superLocal.json +++ b/remix-analyzer/test/analysis/astBlocks/superLocal.json @@ -1,32 +1,52 @@ { - "argumentTypes": [], + "argumentTypes": null, + "arguments": [], "expression": { - "argumentTypes": null, - "id": 10, - "name": "super", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": -25, - "src": "162:5:0", + "argumentTypes": [], + "expression": + { + "argumentTypes": null, + "id": 10, + "name": "super", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -25, + "src": "162:5:0", + "typeDescriptions": + { + "typeIdentifier": "t_super$_B_$17", + "typeString": "contract super B" + } + }, + "id": 12, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "x", + "nodeType": "MemberAccess", + "referencedDeclaration": 4, + "src": "162:7:0", "typeDescriptions": { - "typeIdentifier": "t_super$_B_$17", - "typeString": "contract super B" + "typeIdentifier": "t_function_internal_pure$__$returns$__$", + "typeString": "function () pure" } }, - "id": 12, + "id": 13, "isConstant": false, "isLValue": false, "isPure": false, + "kind": "functionCall", "lValueRequested": false, - "memberName": "x", - "nodeType": "MemberAccess", - "referencedDeclaration": 4, - "src": "162:7:0", + "names": [], + "nodeType": "FunctionCall", + "src": "162:9:0", + "tryCall": false, "typeDescriptions": { - "typeIdentifier": "t_function_internal_pure$__$returns$__$", - "typeString": "function () pure" + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" } } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/thisLocalCall.json b/remix-analyzer/test/analysis/astBlocks/thisLocalCall.json index 5cbcebe82a..214edff07f 100644 --- a/remix-analyzer/test/analysis/astBlocks/thisLocalCall.json +++ b/remix-analyzer/test/analysis/astBlocks/thisLocalCall.json @@ -1,32 +1,52 @@ { "argumentTypes": null, + "arguments": [], "expression": { - "argumentTypes": null, - "id": 13, - "name": "this", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": -28, - "src": "138:4:0", + "argumentTypes": [], + "expression": + { + "argumentTypes": null, + "id": 10, + "name": "this", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -28, + "src": "99:4:0", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_C_$26", + "typeString": "contract C" + } + }, + "id": 11, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "f", + "nodeType": "MemberAccess", + "referencedDeclaration": 25, + "src": "99:6:0", "typeDescriptions": { - "typeIdentifier": "t_contract$_C_$21", - "typeString": "contract C" + "typeIdentifier": "t_function_external_nonpayable$__$returns$_t_uint256_$_t_uint256_$", + "typeString": "function () external returns (uint256,uint256)" } }, - "id": 14, + "id": 12, "isConstant": false, "isLValue": false, "isPure": false, + "kind": "functionCall", "lValueRequested": false, - "memberName": "h", - "nodeType": "MemberAccess", - "referencedDeclaration": 4, - "src": "138:6:0", + "names": [], + "nodeType": "FunctionCall", + "src": "99:8:0", + "tryCall": true, "typeDescriptions": { - "typeIdentifier": "t_function_external_payable$__$returns$__$", - "typeString": "function () payable external" + "typeIdentifier": "t_tuple$_t_uint256_$_t_uint256_$", + "typeString": "tuple(uint256,uint256)" } } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/astBlocks/unaryOperation.json b/remix-analyzer/test/analysis/astBlocks/unaryOperation.json new file mode 100644 index 0000000000..60fa3a805f --- /dev/null +++ b/remix-analyzer/test/analysis/astBlocks/unaryOperation.json @@ -0,0 +1,32 @@ +{ + "argumentTypes": null, + "id": 13, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "95:3:0", + "subExpression": + { + "argumentTypes": null, + "id": 12, + "name": "x", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4, + "src": "95:1:0", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } \ No newline at end of file diff --git a/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts b/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts index 8de2780cf3..c1c71dc4fd 100644 --- a/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts +++ b/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts @@ -1,9 +1,9 @@ import { default as test} from "tape" -import * as common from '../../dist/src/solidity-analyzer/modules/staticAnalysisCommon' +import * as common from '../../src/solidity-analyzer/modules/staticAnalysisCommon' const { localCall, thisLocalCall, libCall, externalDirect, superLocal, assignment, - inlineAssembly, forLoopNode, whileLoopNode, doWhileLoopNode, stateVariableContractNode, + inlineAssembly, unaryOperation, nowAst, doWhileLoopNode, stateVariableContractNode, functionDefinition, fullyQualifiedFunctionDefinition, selfdestruct, storageVariableNodes, - lowlevelCall, parameterFunction, parameterFunctionCall, inheritance, blockHashAccess } = require('./astBlocks') + lowlevelCall, parameterFunction, parameterFunctionCall, inheritance, blockHashAccess, contractDefinition } = require('./astBlocks') function escapeRegExp (str) { return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&') @@ -51,57 +51,86 @@ test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) { // #################### Node Identification Primitives -// test('staticAnalysisCommon.helpers.name', function (t) { -// t.plan(9) -// const node = { attributes: { value: 'now' } } -// const node2 = { attributes: { member_name: 'call' } } +test('staticAnalysisCommon.helpers.name', function (t) { + t.plan(3) + const node = { name: 'now' } + const node2 = { memberName: 'call' } -// t.ok(common.helpers.memName(node, 'now'), 'should work for values') -// t.ok(common.helpers.memName(node2, 'call'), 'should work for member_name') -// t.ok(common.helpers.memName(node2, '.all'), 'regex should work') + t.ok(common.helpers.memName(node, 'now'), 'should work for names') + t.ok(common.helpers.memName(node2, 'call'), 'should work for memberName') + t.ok(common.helpers.memName(node2, '.all'), 'regex should work') -// lowlevelAccessersCommon(t, common.helpers.memName, node) -// }) - -// test('staticAnalysisCommon.helpers.operator', function (t) { -// t.plan(10) -// const node = { attributes: { operator: '++' } } -// const node2 = { attributes: { operator: '+++' } } - -// const escapedPP = escapeRegExp('++') -// const escapedPPExact = `^${escapedPP}$` + // lowlevelAccessersCommon(t, common.helpers.memName, node) +}) -// 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 +++') +test('staticAnalysisCommon.helpers.operator', function (t) { + t.plan(4) + const node = { operator: '++' } + const node2 = { operator: '+++' } -// lowlevelAccessersCommon(t, common.helpers.operator, node) -// }) + const escapedPP = escapeRegExp('++') + const escapedPPExact = `^${escapedPP}$` -// test('staticAnalysisCommon.helpers.nodeType', function (t) { -// t.plan(9) -// const node = { name: 'Identifier', attributes: { name: 'now' } } -// const node2 = { name: 'FunctionCall', attributes: { member_name: 'call' } } + 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 +++') -// t.ok(common.helpers.nodeType(node, common.nodeTypes.IDENTIFIER), 'should work for ident') -// t.ok(common.helpers.nodeType(node2, common.nodeTypes.FUNCTIONCALL), 'should work for funcall') -// t.ok(common.helpers.nodeType(node2, '^F'), 'regex should work for funcall') + // lowlevelAccessersCommon(t, common.helpers.operator, node) +}) -// lowlevelAccessersCommon(t, common.helpers.nodeType, node) -// }) +test('staticAnalysisCommon.helpers.nodeType', function (t) { + t.plan(3) + const node = { nodeType: 'Identifier', name: 'now'} + const node2 = { nodeType: 'FunctionCall', memberName: 'call' } -// test('staticAnalysisCommon.helpers.expressionType', function (t) { -// t.plan(9) -// const node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } } -// const node2 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } } + t.ok(common.helpers.nodeType(node, common.nodeTypes.IDENTIFIER), 'should work for identifier') + t.ok(common.helpers.nodeType(node2, common.nodeTypes.FUNCTIONCALL), 'should work for function call') + t.ok(common.helpers.nodeType(node2, '^F'), 'regex should work for function call') -// t.ok(common.helpers.expressionType(node, common.basicTypes.UINT), 'should work for ident') -// t.ok(common.helpers.expressionType(node2, escapeRegExp(common.basicFunctionTypes.CALL)), 'should work for funcall') -// t.ok(common.helpers.expressionType(node2, '^function \\('), 'regex should work') + // lowlevelAccessersCommon(t, common.helpers.nodeType, node) +}) -// lowlevelAccessersCommon(t, common.helpers.expressionType, node) -// }) +test('staticAnalysisCommon.helpers.expressionTypeDescription', function (t) { + t.plan(3) + const node = { + "expression": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_stringliteral_c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "typeString": "literal_string \"\"" + } + ], + "expression": + { + "name": "addr", + "nodeType": "Identifier", + "src": "132:4:0", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "memberName": "call", + "nodeType": "MemberAccess", + "typeDescriptions": + { + "typeIdentifier": "t_function_barecall_payable$_t_bytes_memory_ptr_$returns$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "function (bytes memory) payable returns (bool,bytes memory)" + } + }, + "nodeType": "FunctionCall", + } + + t.ok(common.helpers.expressionTypeDescription(node.expression, common.basicTypes.PAYABLE_ADDRESS), 'should work for ident') + t.ok(common.helpers.expressionTypeDescription(node, escapeRegExp(common.basicFunctionTypes['CALL-v0.5'])), 'should work for funcall') + t.ok(common.helpers.expressionTypeDescription(node, '^function \\('), 'regex should work') + + // lowlevelAccessersCommon(t, common.helpers.expressionType, node) +}) // // test('staticAnalysisCommon.helpers.nrOfChildren', function (t) { // // t.plan(10) @@ -135,8 +164,8 @@ test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) { // // }) // function lowlevelAccessersCommon (t, f, someNode) { -// 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), 'always ok if type is undefined') +// t.ok(f(someNode, undefined), 'always ok if name is undefined 2') // t.notOk(f(null, undefined), 'false on no node') // t.notOk(f(null, 'call'), 'false on no node') // t.notOk(f(undefined, null), 'false on no node') @@ -145,290 +174,257 @@ test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) { // // #################### Trivial Getter Test -// test('staticAnalysisCommon.getType', function (t) { -// t.plan(2) -// const node = { "argumentTypes": null, -// "id": 3, -// "name": "a", -// "nodeType": "Identifier", -// "overloadedDeclarations": [], -// "referencedDeclaration": 22, -// "src": "52:1:0", -// "typeDescriptions": -// { -// "typeIdentifier": "t_uint256", -// "typeString": "uint256" -// } -// } -// t.ok(common.getType(blockHashAccess) === 'function (uint256) view returns (bytes32)', 'gettype should work for different nodes') -// t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes') -// }) +test('staticAnalysisCommon.getType', function (t) { + t.plan(2) + const node = { "argumentTypes": null, + "id": 3, + "name": "a", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 22, + "src": "52:1:0", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + t.ok(common.getType(blockHashAccess) === 'function (uint256) view returns (bytes32)', 'gettype should work for different nodes') + t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes') +}) // // #################### Complex Getter Test -// test('staticAnalysisCommon.getFunctionCallType', function (t) { -// t.plan(5) -// t.equal(common.getFunctionCallType(libCall), 'function (struct Set.Data storage pointer,uint256) returns (bool)', 'this lib call returns correct type') -// t.equal(common.getFunctionCallType(thisLocalCall), 'function (bytes32,address) returns (bool)', 'this local call returns correct type') -// t.equal(common.getFunctionCallType(externalDirect), 'function () payable external returns (uint256)', 'external direct call returns correct type') -// t.equal(common.getFunctionCallType(localCall), 'function (struct Ballot.Voter storage pointer)', 'local call returns correct type') -// t.throws(() => common.getFunctionCallType({ name: 'MemberAccess' }), Error, 'throws on wrong type') -// }) - -// test('staticAnalysisCommon.getEffectedVariableName', function (t) { -// t.plan(3) -// t.throws(() => common.getEffectedVariableName(inlineAssembly), Error, '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' }), Error, 'should throw on all other nodes') -// }) - -// test('staticAnalysisCommon.getLocalCallName', function (t) { -// t.plan(3) -// t.ok(common.getLocalCallName(localCall) === 'bli', 'getLocal call name from node') -// t.throws(() => common.getLocalCallName(externalDirect), Error, 'throws on other nodes') -// t.throws(() => common.getLocalCallName(thisLocalCall), Error, 'throws on other nodes') -// }) - -// test('staticAnalysisCommon.getThisLocalCallName', function (t) { -// t.plan(3) -// t.ok(common.getThisLocalCallName(thisLocalCall) === 'b', 'get this Local call name from node') -// t.throws(() => common.getThisLocalCallName(externalDirect), Error, 'throws on other nodes') -// t.throws(() => common.getThisLocalCallName(localCall), Error, 'throws on other nodes') -// }) - -// test('staticAnalysisCommon.getSuperLocalCallName', function (t) { -// t.plan(4) -// t.equal(common.getSuperLocalCallName(superLocal), 'duper', 'get local name from super local call') -// t.throws(() => common.getSuperLocalCallName(thisLocalCall), 'throws on other nodes') -// t.throws(() => common.getSuperLocalCallName(externalDirect), 'throws on other nodes') -// t.throws(() => common.getSuperLocalCallName(localCall), 'throws on other nodes') -// }) +test('staticAnalysisCommon.getFunctionCallType', function (t) { + t.plan(4) + t.equal(common.getFunctionCallType(libCall), 'function (struct Set.Data storage pointer,uint256) returns (bool)', 'this lib call returns correct type') + t.equal(common.getFunctionCallType(thisLocalCall), 'function () external returns (uint256,uint256)', 'this local call returns correct type') + t.equal(common.getFunctionCallType(localCall), 'function (uint256,string memory)', 'local call returns correct type') + t.equal(common.getFunctionCallType(externalDirect), 'function () external', 'external call returns correct type') +}) -// test('staticAnalysisCommon.getExternalDirectCallContractName', function (t) { -// t.plan(3) -// t.ok(common.getExternalDirectCallContractName(externalDirect) === 'InfoFeed', 'external direct call contract name from node') -// t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), Error, 'throws on other nodes') -// t.throws(() => common.getExternalDirectCallContractName(localCall), Error, 'throws on other nodes') -// }) +test('staticAnalysisCommon.getEffectedVariableName', function (t) { + t.plan(3) + t.throws(() => common.getEffectedVariableName(inlineAssembly), Error, 'staticAnalysisCommon.js: not an effect Node or inline assembly, get from inline assembly should throw') + t.ok(common.getEffectedVariableName(assignment) === 'a', 'get right name for assignment') + t.throws(() => common.getEffectedVariableName(externalDirect), Error, 'should throw on all other nodes') +}) -// test('staticAnalysisCommon.getThisLocalCallContractName', function (t) { -// t.plan(3) -// t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'test', 'this local call contract name from node') -// t.throws(() => common.getThisLocalCallContractName(localCall), Error, 'throws on other nodes') -// t.throws(() => common.getThisLocalCallContractName(externalDirect), Error, 'throws on other nodes') -// }) +test('staticAnalysisCommon.getLocalCallName', function (t) { + t.plan(3) + t.ok(common.getLocalCallName(localCall) === 'e', 'getLocal call name from node') + t.throws(() => common.getLocalCallName(externalDirect), Error, 'throws on other nodes') + t.throws(() => common.getLocalCallName(thisLocalCall), Error, 'throws on other nodes') +}) -// test('staticAnalysisCommon.getExternalDirectCallMemberName', function (t) { -// t.plan(3) -// t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'info', 'external direct call name from node') -// t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), Error, 'throws on other nodes') -// t.throws(() => common.getExternalDirectCallMemberName(localCall), Error, 'throws on other nodes') -// }) +test('staticAnalysisCommon.getThisLocalCallName', function (t) { + t.plan(3) + t.ok(common.getThisLocalCallName(thisLocalCall) === 'f', 'get this Local call name from node') + t.throws(() => common.getThisLocalCallName(externalDirect), Error, 'throws on other nodes') + t.throws(() => common.getThisLocalCallName(localCall), Error, 'throws on other nodes') +}) -// test('staticAnalysisCommon.getContractName', function (t) { -// t.plan(2) -// const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } } -// t.ok(common.getContractName(contract) === 'baz', 'returns right contract name') -// t.throws(() => common.getContractName({ name: 'InheritanceSpecifier' }), Error, 'throws on other nodes') -// }) +test('staticAnalysisCommon.getSuperLocalCallName', function (t) { + t.plan(4) + t.equal(common.getSuperLocalCallName(superLocal), 'x', 'get local name from super local call') + t.throws(() => common.getSuperLocalCallName(thisLocalCall), 'throws on other nodes') + t.throws(() => common.getSuperLocalCallName(externalDirect), 'throws on other nodes') + t.throws(() => common.getSuperLocalCallName(localCall), 'throws on other nodes') +}) -// test('staticAnalysisCommon.getFunctionDefinitionName', function (t) { -// t.plan(2) -// const func = { name: 'FunctionDefinition', attributes: { name: 'foo' } } -// t.ok(common.getFunctionDefinitionName(func) === 'foo', 'returns right contract name') -// t.throws(() => common.getFunctionDefinitionName({ name: 'InlineAssembly' }), Error, 'throws on other nodes') -// }) +test('staticAnalysisCommon.getExternalDirectCallContractName', function (t) { + t.plan(3) + t.ok(common.getExternalDirectCallContractName(externalDirect) === 'c', 'external direct call contract name from node') + t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), Error, 'throws on other nodes') + t.throws(() => common.getExternalDirectCallContractName(localCall), Error, 'throws on other nodes') +}) -// test('staticAnalysisCommon.getInheritsFromName', function (t) { -// t.plan(2) -// t.ok(common.getInheritsFromName(inheritance) === 'r', 'returns right contract name') -// t.throws(() => common.getInheritsFromName({ name: 'ElementaryTypeName' }), Error, 'throws on other nodes') -// }) +test('staticAnalysisCommon.getThisLocalCallContractName', function (t) { + t.plan(3) + t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'C', 'this local call contract name from node') + t.throws(() => common.getThisLocalCallContractName(localCall), Error, 'throws on other nodes') + t.throws(() => common.getThisLocalCallContractName(externalDirect), Error, 'throws on other nodes') +}) -// test('staticAnalysisCommon.getDeclaredVariableName', function (t) { -// t.plan(2) -// t.ok(common.getDeclaredVariableName(storageVariableNodes.node1) === 'x', 'extract right variable name') -// let node1 = JSON.parse(JSON.stringify(storageVariableNodes)) -// node1.node1.name = 'FunctionCall' -// t.throws(() => common.getDeclaredVariableName(node1) === 'x', Error, 'throw if wrong node') -// }) +test('staticAnalysisCommon.getExternalDirectCallMemberName', function (t) { + t.plan(3) + t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'f', 'external direct call name from node') + t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), Error, 'throws on other nodes') + t.throws(() => common.getExternalDirectCallMemberName(localCall), Error, 'throws on other nodes') +}) -// test('staticAnalysisCommon.getStateVariableDeclarationsFormContractNode', function (t) { -// t.plan(4) -// const res = common.getStateVariableDeclarationsFormContractNode(stateVariableContractNode).map(common.getDeclaredVariableName) -// 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.getContractName', function (t) { + t.plan(1) + t.ok(common.getContractName(contractDefinition) === 'C', 'returns right contract name') +}) -// test('staticAnalysisCommon.getFunctionOrModifierDefinitionParameterPart', function (t) { -// t.plan(2) -// t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(functionDefinition), 'ParameterList'), 'should return a parameterList') -// t.throws(() => common.getFunctionOrModifierDefinitionParameterPart({ name: 'SourceUnit' }), Error, 'throws on other nodes') -// }) +test('staticAnalysisCommon.getFunctionDefinitionName', function (t) { + t.plan(1) + t.ok(common.getFunctionDefinitionName(functionDefinition) === 'f', 'returns right function name') +}) -// test('staticAnalysisCommon.getFunctionCallTypeParameterType', function (t) { -// t.plan(4) -// 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' }), Error, 'throws on wrong type') -// }) +test('staticAnalysisCommon.getInheritsFromName', function (t) { + t.plan(1) + t.ok(common.getInheritsFromName(inheritance) === 'A', 'returns right contract name') +}) -// test('staticAnalysisCommon.getLibraryCallContractName', function (t) { -// t.plan(2) -// t.equal(common.getLibraryCallContractName(libCall), 'Set', 'should return correct contract name') -// t.throws(() => common.getLibraryCallContractName({ name: 'Identifier' }), Error, 'should throw on wrong node') -// }) +test('staticAnalysisCommon.getDeclaredVariableName', function (t) { + t.plan(1) + t.ok(common.getDeclaredVariableName(storageVariableNodes.node1) === 'c', 'extract right variable name') +}) -// test('staticAnalysisCommon.getLibraryCallMemberName', function (t) { -// t.plan(2) -// t.equal(common.getLibraryCallMemberName(libCall), 'insert', 'should return correct member name') -// t.throws(() => common.getLibraryCallMemberName({ name: 'Identifier' }), Error, 'should throw on wrong node') -// }) +test('staticAnalysisCommon.getStateVariableDeclarationsFromContractNode', function (t) { + t.plan(3) + const res = common.getStateVariableDeclarationsFromContractNode(stateVariableContractNode).map(common.getDeclaredVariableName) + t.ok(res[0] === 'x', 'var 1 should be ') + t.ok(res[1] === 'b', 'var 2 should be ') + t.ok(res[2] === 's', 'var 3 should be ') +}) -// test('staticAnalysisCommon.getFullQualifiedFunctionCallIdent', function (t) { -// t.plan(4) -// const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } } -// 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' }), Error, 'throws on wrong type') -// }) +test('staticAnalysisCommon.getFunctionOrModifierDefinitionParameterPart', function (t) { + t.plan(1) + t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(functionDefinition), 'ParameterList'), 'should return a parameterList') +}) -// test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) { -// t.plan(3) -// const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } } -// t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contract, fullyQualifiedFunctionDefinition, ['uint256', 'bool']) === 'baz.getY(uint256,bool)', 'creates right signature') -// t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(contract, { name: 'MemberAccess' }, ['uint256', 'bool']), Error, 'throws on wrong nodes') -// t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent({ name: 'FunctionCall' }, fullyQualifiedFunctionDefinition, ['uint256', 'bool']), Error, 'throws on wrong nodes') -// }) +test('staticAnalysisCommon.getFunctionCallTypeParameterType', function (t) { + t.plan(3) + t.ok(common.getFunctionCallTypeParameterType(thisLocalCall) === '', 'this local call returns correct type') + t.ok(common.getFunctionCallTypeParameterType(externalDirect) === '', 'external direct call returns correct type') + t.ok(common.getFunctionCallTypeParameterType(localCall) === 'uint256,string memory', 'local call returns correct type') +}) -// // #################### Complex Node Identification +test('staticAnalysisCommon.getLibraryCallContractName', function (t) { + t.plan(1) + t.equal(common.getLibraryCallContractName(libCall), 'Set', 'should return correct contract name') +}) -// test('staticAnalysisCommon.isBuiltinFunctionCall', function (t) { -// t.plan(2) -// t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin') -// t.notOk(common.isBuiltinFunctionCall(localCall), 'local call is not builtin') -// }) +test('staticAnalysisCommon.getLibraryCallMemberName', function (t) { + t.plan(1) + t.equal(common.getLibraryCallMemberName(libCall), 'insert', 'should return correct member name') +}) -// test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) { -// t.plan(3) -// t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node1), 'struct storage pointer param is storage') -// t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node2), 'struct storage pointer mapping param is storage') -// t.notOk(common.isStorageVariableDeclaration(storageVariableNodes.node3), 'bytes is not storage') -// }) +test('staticAnalysisCommon.getFullQualifiedFunctionCallIdent', function (t) { + t.plan(3) + t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, thisLocalCall) === 'C.f()', 'this local call returns correct type') + t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, externalDirect) === 'c.f()', 'external direct call returns correct type') + t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, localCall) === 'C.e(uint256,string memory)', 'local call returns correct type') +}) -// test('staticAnalysisCommon.isInteraction', function (t) { -// t.plan(6) -// t.ok(common.isInteraction(lowlevelCall.sendAst), 'send is interaction') -// t.ok(common.isInteraction(lowlevelCall.callAst), 'call is interaction') -// t.ok(common.isInteraction(externalDirect), 'ExternalDirecCall is interaction') -// t.notOk(common.isInteraction(lowlevelCall.callcodeAst), 'callcode is not interaction') -// t.notOk(common.isInteraction(lowlevelCall.delegatecallAst), 'callcode is not interaction') -// t.notOk(common.isInteraction(localCall), 'local call is not interaction') -// }) +test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) { + t.plan(1) + t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contractDefinition, functionDefinition, ['uint256', 'bool']) === 'C.f(uint256,bool)', 'creates right signature') +}) -// test('staticAnalysisCommon.isEffect', function (t) { -// t.plan(5) -// const 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') -// }) +// #################### Complex Node Identification -// test('staticAnalysisCommon.isWriteOnStateVariable', function (t) { -// t.plan(3) -// const node1 = JSON.parse(JSON.stringify(storageVariableNodes.node1)) -// const node2 = node1 -// const node3 = node1 -// node2.attributes.name = 'y' -// node3.attributes.name = 'xx' -// t.ok(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is 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.isBuiltinFunctionCall', function (t) { + t.plan(2) + t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin') + t.throws(() => common.isBuiltinFunctionCall(localCall), Error, 'local call is not builtin') +}) -// test('staticAnalysisCommon.isStateVariable', function (t) { -// t.plan(3) -// t.ok(common.isStateVariable('x', [storageVariableNodes.node1, storageVariableNodes.node2]), 'is contained') -// t.ok(common.isStateVariable('x', [storageVariableNodes.node2, storageVariableNodes.node1, storageVariableNodes.node1]), 'is contained twice') -// t.notOk(common.isStateVariable('x', [storageVariableNodes.node2, storageVariableNodes.node3]), 'not contained') -// }) +test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) { + t.plan(3) + t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node1), 'struct storage pointer param is storage') + t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node2), 'struct storage pointer mapping param is storage') + t.notOk(common.isStorageVariableDeclaration(storageVariableNodes.node3), 'bytes is not storage') +}) -// test('staticAnalysisCommon.isConstantFunction', function (t) { -// t.plan(3) -// const node1 = { name: 'FunctionDefinition', attributes: { constant: true, stateMutability: 'view' } } -// const node2 = { name: 'FunctionDefinition', attributes: { constant: false, stateMutability: 'nonpayable' } } -// const node3 = { name: 'MemberAccess', attributes: { constant: true, stateMutability: 'view' } } +test('staticAnalysisCommon.isInteraction', function (t) { + t.plan(6) + t.ok(common.isInteraction(lowlevelCall.sendAst), 'send is interaction') + t.ok(common.isInteraction(lowlevelCall.callAst), 'call is interaction') + t.ok(common.isInteraction(externalDirect.expression), 'ExternalDirecCall is interaction') + t.notOk(common.isInteraction(lowlevelCall.callcodeAst), 'callcode is not interaction') + t.notOk(common.isInteraction(lowlevelCall.delegatecallAst), 'delegatecall is not interaction') + t.notOk(common.isInteraction(localCall), 'local call is not interaction') +}) -// 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.isEffect', function (t) { + t.plan(5) + 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(unaryOperation), '++ is treated as effect') + const node = JSON.parse(JSON.stringify(unaryOperation)) + node.operator = '--' + t.ok(common.isEffect(node), '-- is treated as effect') + t.notOk(common.isEffect(externalDirect.expression), 'MemberAccess not treated as effect') +}) -// test('staticAnalysisCommon.isPlusPlusUnaryOperation', function (t) { -// t.plan(3) -// const node1 = { name: 'UnaryOperation', attributes: { operator: '++' } } -// const node2 = { name: 'UnaryOperation', attributes: { operator: '--' } } -// const node3 = { name: 'FunctionDefinition', attributes: { operator: '++' } } +test('staticAnalysisCommon.isWriteOnStateVariable', function (t) { + t.plan(3) + const node1 = JSON.parse(JSON.stringify(storageVariableNodes.node1)) + const node2 = node1 + const node3 = node1 + node2.name = 'y' + node3.name = 'xx' + t.ok(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is write on state') + t.notOk(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on non state is not write on state') + node3.name = 'a' // same as assignment left hand side var name + t.ok(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on state is write on state') +}) -// 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.isStateVariable', function (t) { + t.plan(3) + t.ok(common.isStateVariable('c', [storageVariableNodes.node1, storageVariableNodes.node2]), 'is contained') + t.ok(common.isStateVariable('c', [storageVariableNodes.node2, storageVariableNodes.node1, storageVariableNodes.node1]), 'is contained twice') + t.notOk(common.isStateVariable('c', [storageVariableNodes.node2, storageVariableNodes.node3]), 'not contained') +}) -// test('staticAnalysisCommon.isMinusMinusUnaryOperation', function (t) { -// t.plan(3) -// const node1 = { name: 'UnaryOperation', attributes: { operator: '--' } } -// const node2 = { name: 'UnaryOperation', attributes: { operator: '++' } } -// const node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } } +test('staticAnalysisCommon.isConstantFunction', function (t) { + t.plan(3) + t.ok(common.isConstantFunction(functionDefinition), 'should be const func definition') + functionDefinition.stateMutability = 'view' + t.ok(common.isConstantFunction(functionDefinition), 'should be const func definition') + functionDefinition.stateMutability = 'nonpayable' + t.notOk(common.isConstantFunction(functionDefinition), 'should not be const func definition') +}) -// 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.isPlusPlusUnaryOperation', function (t) { + t.plan(2) + t.ok(common.isPlusPlusUnaryOperation(unaryOperation), 'should be unary ++') + const node = JSON.parse(JSON.stringify(unaryOperation)) + node.operator = '--' + t.notOk(common.isPlusPlusUnaryOperation(node), 'should not be unary ++') +}) -// test('staticAnalysisCommon.isFullyImplementedContract', function (t) { -// t.plan(3) -// const node1 = { name: 'ContractDefinition', attributes: { fullyImplemented: true } } -// const node2 = { name: 'ContractDefinition', attributes: { fullyImplemented: false } } -// const node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } } +test('staticAnalysisCommon.isMinusMinusUnaryOperation', function (t) { + t.plan(2) + unaryOperation.operator = '--' + t.ok(common.isMinusMinusUnaryOperation(unaryOperation), 'should be unary --') + unaryOperation.operator = '++' + t.notOk(common.isMinusMinusUnaryOperation(unaryOperation), 'should not be unary --') +}) -// 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.isFullyImplementedContract', function (t) { + t.plan(2) + t.ok(common.isFullyImplementedContract(contractDefinition), 'should be fully implemented contract') + const node = JSON.parse(JSON.stringify(contractDefinition)) + node.fullyImplemented = false + t.notOk(common.isFullyImplementedContract(node), 'should not be fully implemented contract') +}) -// test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) { -// t.plan(2) -// t.ok(common.isCallToNonConstLocalFunction(localCall), 'should be call to non const Local func') -// localCall.children[0].attributes.type = 'function (struct Ballot.Voter storage pointer) view payable (uint256)' -// t.notok(common.isCallToNonConstLocalFunction(localCall), 'should no longer be call to non const Local func') -// }) +test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) { + t.plan(2) + t.ok(common.isCallToNonConstLocalFunction(localCall), 'should be call to non const Local func') + const node = JSON.parse(JSON.stringify(localCall)) + node.expression.typeDescriptions.typeString = 'function (struct Ballot.Voter storage pointer) view payable (uint256)' + t.notok(common.isCallToNonConstLocalFunction(node), 'should no longer be call to non const Local func') +}) -// test('staticAnalysisCommon.isExternalDirectCall', function (t) { -// t.plan(5) -// const node2 = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } } -// t.notOk(common.isThisLocalCall(externalDirect), 'is this.local_method() used should not work') -// t.notOk(common.isBlockTimestampAccess(externalDirect), 'is block.timestamp used should not work') -// t.notOk(common.isNowAccess(externalDirect), 'is now used should not work') -// t.ok(common.isExternalDirectCall(externalDirect), 'f.info() should be external direct call') -// t.notOk(common.isExternalDirectCall(node2), 'local call is not an exernal call') -// }) +test('staticAnalysisCommon.isExternalDirectCall', function (t) { + t.plan(5) + t.notOk(common.isThisLocalCall(externalDirect), 'is this.local_method() used should not work') + t.notOk(common.isBlockTimestampAccess(externalDirect), 'is block.timestamp used should not work') + t.notOk(common.isNowAccess(externalDirect), 'is now used should not work') + t.ok(common.isExternalDirectCall(externalDirect.expression), 'c.f() should be external direct call') + t.notOk(common.isExternalDirectCall(thisLocalCall.expression), 'this local call is not an exernal call') +}) -// test('staticAnalysisCommon.isNowAccess', function (t) { -// t.plan(3) -// const 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.isNowAccess', function (t) { + t.plan(1) + t.ok(common.isNowAccess(nowAst), 'is now used should work') +}) // test('staticAnalysisCommon.isBlockTimestampAccess', function (t) { // t.plan(3) diff --git a/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/library.sol b/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/library.sol index c94f993e43..8294c9e46f 100644 --- a/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/library.sol +++ b/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/library.sol @@ -1,9 +1,9 @@ pragma solidity >=0.4.9 <0.7.0; -contract Set { +library Set { // We define a new struct datatype that will be used to // hold its data in the calling contract. -// struct Data { uint flags; } + struct Data { mapping(uint => bool) flags; } // Note that the first parameter is of type "storage // reference" and thus only its storage address and not @@ -11,35 +11,35 @@ contract Set { // special feature of library functions. It is idiomatic // to call the first parameter 'self', if the function can // be seen as a method of that object. - //function insert(Data memory self, uint value) public - // returns (bool) - //{ -// if (self.flags[value]) -// return false; // already there -// self.flags[value] = true; + function insert(Data storage self, uint value) public + returns (bool) + { + if (self.flags[value]) + return false; // already there + self.flags[value] = true; -// return true; -// } + return true; + } -// function remove(Data memory self, uint value) public -// returns (bool) -// { -// if (!self.flags[value]) -// return false; // not there -// self.flags[value] = false; -// return true; -// } + function remove(Data storage self, uint value) public + returns (bool) + { + if (!self.flags[value]) + return false; // not there + self.flags[value] = false; + return true; + } - function contains(uint value) public pure - returns (uint) + function contains(Data storage self, uint value) public + returns (bool) { - return value; + return self.flags[value]; } } contract C { - Set x; + Set.Data knownValues; function register(uint value) public { // The library functions can be called without a @@ -47,18 +47,8 @@ contract C { // "instance" will be the current contract. address payable a; a.send(10 wei); - //if (!Set.insert(knownValues, value)) - // revert(); - } - - function tests2() public { - x = Set(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c); - uint y = x.contains(103); - if(y == 103){ - y++; - } else { - y--; - } + if (!Set.insert(knownValues, value)) + revert(); } // In this contract, we can also directly access knownValues.flags, if we want. } \ No newline at end of file