diff --git a/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts b/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts index 359f406453..7151f61b94 100644 --- a/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts +++ b/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts @@ -1,13 +1,8 @@ import { default as category } from './categories' import { default as algorithm } from './algorithmCategories' -import AbstractAst from './abstractAstView' -import { ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CompiledContractObj, CompiledContract, VisitFunction, AnalyzerModule} from './../../types' - -interface VisitedContract { - name: string - object: CompiledContract - file: string -} +import { getFunctionDefinitionName, helpers, isVariableTurnedIntoGetter, getMethodParamsSplittedTypeDesc } from './staticAnalysisCommon' +import { ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CompiledContract, AnalyzerModule, + FunctionDefinitionAstNode, VariableDeclarationAstNode } from './../../types' export default class gasCosts implements AnalyzerModule { name: string = `Gas costs: ` @@ -15,58 +10,80 @@ export default class gasCosts implements AnalyzerModule { category: ModuleCategory = category.GAS algorithm: ModuleAlgorithm = algorithm.EXACT - abstractAst: AbstractAst = new AbstractAst() - visit: VisitFunction = this.abstractAst.build_visit((node: any) => false) + warningNodes: any[] = [] + visit (node: FunctionDefinitionAstNode | VariableDeclarationAstNode): void { + if ((node.nodeType === 'FunctionDefinition' && node.kind !== 'constructor' && node.implemented) || + (node.nodeType === 'VariableDeclaration' && isVariableTurnedIntoGetter(node))) + this.warningNodes.push(node) + } report (compilationResults: CompilationResult): ReportObj[] { const report: ReportObj[] = [] - this.visitContracts(compilationResults.contracts, (contract: VisitedContract) => { - if ( - !contract.object.evm.gasEstimates || - !contract.object.evm.gasEstimates.external - ) { - return + const methodsWithSignature: Record[] = this.warningNodes.map(node => { + let signature: string; + if(node.nodeType === 'FunctionDefinition'){ + const functionName: string = getFunctionDefinitionName(node) + signature = helpers.buildAbiSignature(functionName, getMethodParamsSplittedTypeDesc(node, compilationResults.contracts)) } - const fallback: string = contract.object.evm.gasEstimates.external[''] - if (fallback !== undefined) { - if (fallback === null || parseInt(fallback) >= 2100 || fallback === 'infinite') { - report.push({ - warning: `Fallback function of contract ${contract.name} requires too much gas (${fallback}). - If the fallback function requires more than 2300 gas, the contract cannot receive Ether.` - }) - } + else + signature = node.name + '()' + + return { + name: node.name, + src: node.src, + signature: signature } - - for (var functionName in contract.object.evm.gasEstimates.external) { - if (functionName === '') { - continue - } - const gas: string = contract.object.evm.gasEstimates.external[functionName] - const gasString: string = gas === null ? 'unknown or not constant' : 'high: ' + gas - if (gas === null || parseInt(gas) >= 3000000 || gas === 'infinite') { - report.push({ - warning: `Gas requirement of function ${contract.name}.${functionName} ${gasString}. - If the gas requirement of a function is higher than the block gas limit, it cannot be executed. - Please avoid loops in your functions or actions that modify large areas of storage - (this includes clearing or copying arrays in storage)` - }) + }) + for (const method of methodsWithSignature) { + for (const filename in compilationResults.contracts) { + for (const contractName in compilationResults.contracts[filename]) { + const contract: CompiledContract = compilationResults.contracts[filename][contractName] + const methodGas: Record | undefined = this.checkMethodGas(contract, method.signature) + if(methodGas && methodGas.isInfinite) { + if(methodGas.isFallback) { + report.push({ + warning: `Fallback function of contract ${contractName} requires too much gas (${methodGas.msg}). + If the fallback function requires more than 2300 gas, the contract cannot receive Ether.`, + location: method.src + }) + } else { + report.push({ + warning: `Gas requirement of function ${contractName}.${method.name} ${methodGas.msg}. + If the gas requirement of a function is higher than the block gas limit, it cannot be executed. + Please avoid loops in your functions or actions that modify large areas of storage + (this includes clearing or copying arrays in storage)`, + location: method.src + }) + } + } else continue } } - }) + } return report } - /** - * call the given @arg cb (function) for all the contracts. Uses last compilation result - * stop visiting when cb return true - * @param {Function} cb - callback - */ - // @TODO has been copied from remix-ide repo ! should fix that soon ! - private visitContracts (contracts: CompiledContractObj | undefined, cb: ((contract: VisitedContract) => void | undefined)): void { - for (let file in contracts) { - for (let name in contracts[file]) { - if (cb({ name: name, object: contracts[file][name], file: file })) return + private checkMethodGas(contract: CompiledContract, methodSignature: string): Record | undefined { + if(contract.evm && contract.evm.gasEstimates && contract.evm.gasEstimates.external) { + if(methodSignature === '()') { + const fallback: string = contract.evm.gasEstimates.external[''] + if (fallback !== undefined && (fallback === null || parseInt(fallback) >= 2100 || fallback === 'infinite')) { + return { + isInfinite: true, + isFallback: true, + msg: fallback + } + } + } else { + const gas: string = contract.evm.gasEstimates.external[methodSignature] + const gasString: string = gas === null ? 'unknown or not constant' : 'high: ' + gas + if (gas === null || parseInt(gas) >= 3000000 || gas === 'infinite') { + return { + isInfinite: true, + isFallback: false, + msg: gasString + } + } } } - } + } } diff --git a/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts b/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts index e64ffd26b4..0b7e5ae793 100644 --- a/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts +++ b/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts @@ -3,7 +3,7 @@ import { FunctionDefinitionAstNode, ModifierDefinitionAstNode, ParameterListAstNode, ForStatementAstNode, WhileStatementAstNode, VariableDeclarationAstNode, ContractDefinitionAstNode, InheritanceSpecifierAstNode, MemberAccessAstNode, BinaryOperationAstNode, FunctionCallAstNode, ExpressionStatementAstNode, UnaryOperationAstNode, - IdentifierAstNode, IndexAccessAstNode, BlockAstNode, AssignmentAstNode, InlineAssemblyAstNode, IfStatementAstNode } from "types" + IdentifierAstNode, IndexAccessAstNode, BlockAstNode, AssignmentAstNode, InlineAssemblyAstNode, IfStatementAstNode, CompiledContractObj, ABIParameter } from "types" import { util } from 'remix-lib' type SpecialObjDetail = { @@ -641,6 +641,15 @@ function isConstantFunction (node: FunctionDefinitionAstNode): boolean { return node.stateMutability === 'view' || node.stateMutability === 'pure' } +/** +* True if variable decalaration is converted into a getter method + * @node {ASTNode} variable declaration AstNode + * @return {bool} + */ +function isVariableTurnedIntoGetter (varDeclNode: VariableDeclarationAstNode): boolean { + return varDeclNode.stateVariable && varDeclNode.visibility === 'public'; +} + /** * True if is function defintion has payable modifier * @node {ASTNode} some AstNode @@ -1068,6 +1077,51 @@ function buildAbiSignature (funName: string, paramTypes: any[]): string { return funName + '(' + util.concatWithSeperator(paramTypes, ',') + ')' } +// To create the method signature similar to contract.evm.gasEstimates.external object +// For address payable, return address +function getMethodParamsSplittedTypeDesc(node: FunctionDefinitionAstNode, contracts: CompiledContractObj): string[] { + return node.parameters.parameters.map((varNode, varIndex) => { + let finalTypeString; + const typeString = varNode.typeDescriptions.typeString + if(typeString.includes('struct')) { + const fnName = node.name + for (const filename in contracts) { + for (const contractName in contracts[filename]) { + const methodABI = contracts[filename][contractName].abi + .find(e => e.name === fnName && e.inputs?.length && + e.inputs[varIndex]['type'].includes('tuple') && + e.inputs[varIndex]['internalType'] === typeString) + if(methodABI && methodABI.inputs) { + const inputs = methodABI.inputs[varIndex] + let typeStr = getTypeStringFromComponents(inputs['components']) + finalTypeString = typeStr + inputs['type'].replace('tuple', '') + } + } + } + } else + finalTypeString = typeString.split(' ')[0] + return finalTypeString + }) +} + +function getTypeStringFromComponents(components: ABIParameter[]) { + let typeString = '(' + for(var i=0; i < components.length; i++) { + const param = components[i] + if(param.type.includes('tuple') && param.components && param.components.length > 0){ + typeString = typeString + getTypeStringFromComponents(param.components) + typeString = typeString + param.type.replace('tuple', '') + } + else + typeString = typeString + param.type + + if(i !== components.length - 1) + typeString = typeString + ',' + } + typeString = typeString + ')' + return typeString +} + const helpers = { expressionTypeDescription, nodeType, @@ -1103,12 +1157,14 @@ export { getFunctionOrModifierDefinitionParameterPart, getFunctionDefinitionReturnParameterPart, getUnAssignedTopLevelBinOps, + getMethodParamsSplittedTypeDesc, // #################### Complex Node Identification isDeleteOfDynamicArray, isDeleteFromDynamicArray, isAbiNamespaceCall, isSpecialVariableAccess, + isVariableTurnedIntoGetter, isDynamicArrayAccess, isDynamicArrayLengthAccess, isMappingIndexAccess, diff --git a/remix-analyzer/src/types.ts b/remix-analyzer/src/types.ts index 4ba19b23a7..71ee35e289 100644 --- a/remix-analyzer/src/types.ts +++ b/remix-analyzer/src/types.ts @@ -20,11 +20,11 @@ export interface ModuleCategory { export interface ReportObj { warning: string, - location?: string | null, + location: string, more?: string } -// Regarding location, she source mappings inside the AST use the following notation: +// Regarding location, the source mappings inside the AST use the following notation: // s:l:f @@ -33,7 +33,10 @@ export interface ReportObj { // l is the length of the source range in bytes and // f is the source index mentioned above. -export interface AnalysisReportObj extends ReportObj { +export interface AnalysisReportObj { + warning: string, + location?: string, + more?: string error? : string } @@ -51,7 +54,7 @@ export interface CompilationResult { [contractName: string]: CompilationSource } /** This contains the contract-level outputs. It can be limited/filtered by the outputSelection settings */ - contracts?: CompiledContractObj /** If the language used has no contract names, this field should equal to an empty string. */ + contracts: CompiledContractObj /** If the language used has no contract names, this field should equal to an empty string. */ } export interface CompiledContractObj { @@ -834,6 +837,7 @@ export interface CommonYulAstNode { } export interface ABIParameter { + internalType: string /** The name of the parameter */ name: string /** The canonical type of the parameter */ diff --git a/remix-analyzer/test/analysis/astBlocks/funcDefForComplexParams.json b/remix-analyzer/test/analysis/astBlocks/funcDefForComplexParams.json new file mode 100644 index 0000000000..14551bce74 --- /dev/null +++ b/remix-analyzer/test/analysis/astBlocks/funcDefForComplexParams.json @@ -0,0 +1,171 @@ +{ + "nestedStruct": { + "body": + { "id": 330, + "nodeType": "Block", + "src": "5031:2:0", + "statements": [] + }, + "documentation": null, + "id": 331, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "testWithArrayOfStruct", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 328, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 327, + "name": "param", + "nodeType": "VariableDeclaration", + "scope": 331, + "src": "4996:26:0", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_array$_t_struct$_Proposal1_$30_memory_ptr_$dyn_memory_ptr_$dyn_memory_ptr", + "typeString": "struct Ballot.Proposal1[][]" + }, + "typeName": { + "baseType": { + "baseType": { + "contractScope": null, + "id": 332, + "name": "Proposal1", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 30, + "src": "4996:9:0", + "typeDescriptions": { + "typeIdentifier": "t_struct$_Proposal1_$30_storage_ptr", + "typeString": "struct Ballot.Proposal1" + } + }, + "id": 333, + "length": null, + "nodeType": "ArrayTypeName", + "src": "4996:11:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_Proposal1_$30_storage_$dyn_storage_ptr", + "typeString": "struct Ballot.Proposal1[]" + } + }, + "id": 334, + "length": null, + "nodeType": "ArrayTypeName", + "src": "4996:13:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_array$_t_struct$_Proposal1_$30_storage_$dyn_storage_$dyn_storage_ptr", + "typeString": "struct Ballot.Proposal1[][]" + }, + "value": null, + "visibility": "internal" + } + } + ], + "src": "4995:28:0" + }, + "returnParameters": + { "id": 329, + "nodeType": "ParameterList", + "parameters": [], + "src": "5031:0:0" }, + "scope": 332, + "src": "4964:69:0", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "bytesArray": { + "body": + { + "id": 287, + "nodeType": "Block", + "src": "4988:2:0", + "statements": [] + }, + "documentation": null, + "id": 288, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "testWithArray", + "nodeType": "FunctionDefinition", + "parameters": + { "id": 285, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 284, + "name": "param", + "nodeType": "VariableDeclaration", + "scope": 288, + "src": "4957:22:0", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": + { "typeIdentifier": "t_array$_t_bytes32_$dyn_memory_ptr", + "typeString": "bytes32[]" }, + "typeName": + { "baseType": "[Object]", + "id": 283, + "length": null, + "nodeType": "ArrayTypeName", + "src": "4957:9:0", + "typeDescriptions": "[Object]" + }, + "value": null, + "visibility": "internal" + } + ], + "src": "4956:24:0" + }, + "returnParameters": + { "id": 286, + "nodeType": "ParameterList", + "parameters": [], + "src": "4988:0:0" }, + "scope": 289, + "src": "4933:57:0", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "withoutParams" : { + "body": + { + "id": 280, + "nodeType": "Block", + "src": "4864:63:0", + "statements": "[ [Object] ]" + }, + "documentation": null, + "id": 281, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "winnerName", + "nodeType": "FunctionDefinition", + "parameters": + { "id": 268, + "nodeType": "ParameterList", + "parameters": [], + "src": "4804:2:0" + }, + "returnParameters": + { "id": 271, + "nodeType": "ParameterList", + "parameters": "[ [Object] ]", + "src": "4839:20:0" + }, + "scope": 289, + "src": "4785:142:0", + "stateMutability": "view", + "superFunction": null, + "visibility": "public" } +} \ 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 5fe840c03d..0c9e1aa198 100644 --- a/remix-analyzer/test/analysis/astBlocks/index.js +++ b/remix-analyzer/test/analysis/astBlocks/index.js @@ -25,5 +25,6 @@ module.exports = { parameterFunction: require('./parameterFunction.json'), parameterFunctionCall: require('./parameterFunctionCall.json'), inheritance: require('./inheritance.json'), - blockHashAccess: require('./blockHashAccess.json') + blockHashAccess: require('./blockHashAccess.json'), + funcDefForComplexParams: require('./funcDefForComplexParams.json') } diff --git a/remix-analyzer/test/analysis/compilationDetails/CompiledContractObj.json b/remix-analyzer/test/analysis/compilationDetails/CompiledContractObj.json new file mode 100644 index 0000000000..db7cab541e --- /dev/null +++ b/remix-analyzer/test/analysis/compilationDetails/CompiledContractObj.json @@ -0,0 +1,84 @@ +{ "test.sol": + { "Ballot": + { "abi": [ + {"inputs":[{"internalType":"bytes32[]","name":"proposalNames","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}, + {"constant":true,"inputs":[],"name":"chairperson","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"delegate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"internalType":"address","name":"voter","type":"address"}],"name":"giveRightToVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposals","outputs":[{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint256","name":"voteCount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"internalType":"bytes32[]","name":"param","type":"bytes32[]"}],"name":"testWithArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"components":[{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint256","name":"voteCount","type":"uint256"},{"internalType":"uint256[]","name":"arr","type":"uint256[]"},{"internalType":"address payable","name":"sender","type":"address"},{"components":[{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint256","name":"voteCount","type":"uint256"}],"internalType":"struct Ballot.Proposal[]","name":"p","type":"tuple[]"}],"internalType":"struct Ballot.Proposal1[][]","name":"param","type":"tuple[][]"}],"name":"testWithArrayOfStruct","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"internalType":"uint256","name":"proposal","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"voters","outputs":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"bool","name":"voted","type":"bool"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"uint256","name":"vote","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"winnerName","outputs":[{"internalType":"bytes32","name":"winnerName_","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"winningProposal","outputs":[{"internalType":"uint256","name":"winningProposal_","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"} + ], + "evm": "[Object]", + "metadata": + {"compiler": + { + "version":"0.5.17+commit.d19bba13" + }, + "language":"Solidity", + "output":{ + "abi":[ + {"inputs":[{"internalType":"bytes32[]","name":"proposalNames","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}, + {"constant":true,"inputs":[],"name":"chairperson","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"delegate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"internalType":"address","name":"voter","type":"address"}],"name":"giveRightToVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposals","outputs":[{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint256","name":"voteCount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"internalType":"bytes32[]","name":"param","type":"bytes32[]"}],"name":"testWithArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"components":[{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint256","name":"voteCount","type":"uint256"},{"internalType":"uint256[]","name":"arr","type":"uint256[]"},{"internalType":"address payable","name":"sender","type":"address"},{"components":[{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint256","name":"voteCount","type":"uint256"}],"internalType":"struct Ballot.Proposal[]","name":"p","type":"tuple[]"}],"internalType":"struct Ballot.Proposal1[][]","name":"param","type":"tuple[][]"}],"name":"testWithArrayOfStruct","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"internalType":"uint256","name":"proposal","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"voters","outputs":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"bool","name":"voted","type":"bool"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"uint256","name":"vote","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"winnerName","outputs":[{"internalType":"bytes32","name":"winnerName_","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"winningProposal","outputs":[{"internalType":"uint256","name":"winningProposal_","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"} + ], + "devdoc":{ + "details":"Implements voting process along with vote delegation", + "methods":{ + "constructor":{ + "details":"Create a new ballot to choose one of proposalNames", + "params":{"proposalNames":"names of proposals"} + }, + "delegate(address)":{ + "details":"Delegate your vote to the voter", + "params":{"to":"address to which vote is delegated"} + }, + "giveRightToVote(address)":{ + "details":"Give voter the right to vote on this ballot. May only be called by chairperson", + "params":{"voter":"address of voter"} + }, + "vote(uint256)":{ + "details":"Give your vote (including votes delegated to you) to proposal proposals[proposal].name", + "params":{"proposal":"index of proposal in the proposals array"} + }, + "winnerName()":{ + "details":"Calls winningProposal() function to get the index of the winner contained in the proposals array and then", + "return":"winnerName_ the name of the winner" + }, + "winningProposal()":{ + "details":"Computes the winning proposal taking all previous votes into account.", + "return":"winningProposal_ index of winning proposal in the proposals array"} + }, + "title":"Ballot" + }, + "userdoc":{"methods":{}}}, + "settings":{ + "compilationTarget":{"test.sol":"Ballot"}, + "evmVersion":"istanbul", + "libraries":{}, + "optimizer":{"enabled":false,"runs":200}, + "remappings":[]}, + "sources":{ + "test.sol":{ + "keccak256":"0x1008aa4339f4493c8b48dc3d4217403c26a19b1524d1cd79f43d8963e376357e", + "urls":["bzz-raw://0576de9b9e96c2c26296ac3fd9db440d42816bcf3ff03fe50c0323578e089398", + "dweb:/ipfs/QmZ3uBGpFbQ5VsdfVyG8R2KBvwn2oW4SeuFHi1sq9zuMFE"] + } + }, + "version":1 + } + } + } + } \ 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 78fa1abf05..4734ece218 100644 --- a/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts +++ b/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts @@ -3,8 +3,10 @@ import * as common from '../../src/solidity-analyzer/modules/staticAnalysisCommo const { localCall, thisLocalCall, libCall, externalDirect, superLocal, assignment, abiNamespaceCallNodes, inlineAssembly, unaryOperation, nowAst, blockTimestamp, stateVariableContractNode, functionDefinition, requireCall, selfdestruct, storageVariableNodes, dynamicDeleteUnaryOp, - lowlevelCall, parameterFunction, parameterFunctionCall, inheritance, blockHashAccess, contractDefinition } = require('./astBlocks') + lowlevelCall, parameterFunction, parameterFunctionCall, inheritance, blockHashAccess, contractDefinition, funcDefForComplexParams } = require('./astBlocks') + +const compiledContractObj = require('./compilationDetails/CompiledContractObj.json') function escapeRegExp (str) { return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&') } @@ -289,6 +291,13 @@ test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) { t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(parameterFunctionCall, functionDefinition, ['uint256', 'bool']), new RegExp('staticAnalysisCommon.js: not a ContractDefinition Node'), 'throws on wrong nodes') }) +test('staticAnalysisCommon.getSplittedTypeDesc', function (t) { + t.plan(3) + t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.withoutParams, compiledContractObj).length === 0, 'no params, no params type signature') + t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.bytesArray, compiledContractObj)[0] === 'bytes32[]', 'creates right params type signature') + t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.nestedStruct, compiledContractObj)[0] === '(bytes32,uint256,uint256[],address,(bytes32,uint256)[])[][]', 'creates right params type signature') +}) + // #################### Complex Node Identification test('staticAnalysisCommon.isBuiltinFunctionCall', function (t) { diff --git a/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts b/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts index 6ffd646b4c..05c1b0efe1 100644 --- a/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts +++ b/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts @@ -125,7 +125,7 @@ test('Integration test constantFunctions module', function (t: test.Test) { const lengthCheck: Record = { 'KingOfTheEtherThrone.sol': 0, 'assembly.sol': 0, - 'ballot.sol': 0, + 'ballot.sol': 1, 'ballot_reentrant.sol': 0, 'ballot_withoutWarnings.sol': 0, 'cross_contract.sol': 0, @@ -233,7 +233,7 @@ test('Integration test gasCosts module', function (t: test.Test) { const lengthCheck: Record = { 'KingOfTheEtherThrone.sol': 2, 'assembly.sol': 2, - 'ballot.sol': 3, + 'ballot.sol': 4, 'ballot_reentrant.sol': 2, 'ballot_withoutWarnings.sol': 0, 'cross_contract.sol': 1, diff --git a/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ballot.sol b/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ballot.sol index 1681b30c47..ab3bbb75e8 100644 --- a/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ballot.sol +++ b/remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ballot.sol @@ -141,5 +141,7 @@ contract Ballot { { winnerName = proposals[winningProposal()].name; } + + function testWithArray (bytes32[] memory param) public {} }