diff options
-rw-r--r-- | packages/sol-cov/package.json | 1 | ||||
-rw-r--r-- | packages/sol-cov/src/ast_visitor.ts | 90 | ||||
-rw-r--r-- | packages/sol-cov/src/collect_coverage_entries.ts | 10 | ||||
-rw-r--r-- | packages/sol-cov/src/coverage_subprovider.ts | 2 | ||||
-rw-r--r-- | packages/sol-cov/src/globals.d.ts | 317 | ||||
-rw-r--r-- | yarn.lock | 4 |
6 files changed, 361 insertions, 63 deletions
diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index 6a0b61ce9..af6be13f2 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -28,6 +28,7 @@ "lodash": "^4.17.4", "semaphore-async-await": "^1.5.1", "solidity-coverage": "^0.4.10", + "solidity-parser-antlr": "^0.2.7", "solidity-parser-sc": "^0.4.4", "web3": "^0.20.0" }, diff --git a/packages/sol-cov/src/ast_visitor.ts b/packages/sol-cov/src/ast_visitor.ts index 701cbbc88..0dddbb16e 100644 --- a/packages/sol-cov/src/ast_visitor.ts +++ b/packages/sol-cov/src/ast_visitor.ts @@ -1,5 +1,5 @@ import * as _ from 'lodash'; -import * as SolidityParser from 'solidity-parser-sc'; +import * as Parser from 'solidity-parser-antlr'; import { BranchMap, FnMap, LocationByOffset, SingleFileSourceRange, StatementMap } from './types'; @@ -23,29 +23,9 @@ export class ASTVisitor { private _modifiersStatementIds: number[] = []; private _statementMap: StatementMap = {}; private _locationByOffset: LocationByOffset; - private static _doesLookLikeAnASTNode(ast: any): boolean { - const isAST = _.isObject(ast) && _.isString(ast.type) && _.isNumber(ast.start) && _.isNumber(ast.end); - return isAST; - } constructor(locationByOffset: LocationByOffset) { this._locationByOffset = locationByOffset; } - public walkAST(astNode: SolidityParser.AST): void { - if (_.isArray(astNode) || _.isObject(astNode)) { - if (ASTVisitor._doesLookLikeAnASTNode(astNode)) { - const nodeType = astNode.type; - const visitorFunctionName = `_visit${nodeType}`; - // tslint:disable-next-line:no-this-assignment - const self: { [visitorFunctionName: string]: (ast: SolidityParser.AST) => void } = this as any; - if (_.isFunction(self[visitorFunctionName])) { - self[visitorFunctionName](astNode); - } - } - _.forEach(astNode, subtree => { - this.walkAST(subtree); - }); - } - } public getCollectedCoverageEntries(): CoverageEntriesDescription { const coverageEntriesDescription = { fnMap: this._fnMap, @@ -55,43 +35,38 @@ export class ASTVisitor { }; return coverageEntriesDescription; } - private _visitConditionalExpression(ast: SolidityParser.AST): void { - this._visitBinaryBranch(ast, ast.consequent, ast.alternate, BranchType.ConditionalExpression); - } - private _visitFunctionDeclaration(ast: SolidityParser.AST): void { - const loc = this._getExpressionRange(ast); - this._fnMap[this._entryId++] = { - name: ast.name, - line: loc.start.line, - loc, - }; + public IfStatement(ast: Parser.IfStatement): void { + this._visitStatement(ast); + this._visitBinaryBranch(ast, ast.trueBody, ast.falseBody || ast, BranchType.If); } - private _visitBinaryExpression(ast: SolidityParser.AST): void { - this._visitBinaryBranch(ast, ast.left, ast.right, BranchType.BinaryExpression); + public FunctionDefinition(ast: Parser.FunctionDefinition): void { + this._visitFunctionLikeDefinition(ast); } - private _visitIfStatement(ast: SolidityParser.AST): void { - this._visitStatement(ast); - this._visitBinaryBranch(ast, ast.consequent, ast.alternate || ast, BranchType.If); + public ModifierDefinition(ast: Parser.ModifierDefinition): void { + this._visitFunctionLikeDefinition(ast); } - private _visitBreakStatement(ast: SolidityParser.AST): void { + public ForStatement(ast: Parser.ForStatement): void { this._visitStatement(ast); } - private _visitContractStatement(ast: SolidityParser.AST): void { + public ReturnStatement(ast: Parser.ReturnStatement): void { this._visitStatement(ast); } - private _visitExpressionStatement(ast: SolidityParser.AST): void { + public BreakStatement(ast: Parser.BreakStatement): void { this._visitStatement(ast); } - private _visitForStatement(ast: SolidityParser.AST): void { - this._visitStatement(ast); + public ExpressionStatement(ast: Parser.ExpressionStatement): void { + this._visitStatement(ast.expression); } - private _visitPlaceholderStatement(ast: SolidityParser.AST): void { - this._visitStatement(ast); + public BinaryOperation(ast: Parser.BinaryOperation): void { + const BRANCHING_BIN_OPS = ['&&', '||']; + if (_.includes(BRANCHING_BIN_OPS, ast.operator)) { + this._visitBinaryBranch(ast, ast.left, ast.right, BranchType.BinaryExpression); + } } - private _visitReturnStatement(ast: SolidityParser.AST): void { - this._visitStatement(ast); + public Conditional(ast: Parser.Conditional): void { + this._visitBinaryBranch(ast, ast.trueExpression, ast.falseExpression, BranchType.ConditionalExpression); } - private _visitModifierArgument(ast: SolidityParser.AST): void { + public ModifierInvocation(ast: Parser.ModifierInvocation): void { const BUILTIN_MODIFIERS = ['public', 'view', 'payable', 'external', 'internal', 'pure', 'constant']; if (!_.includes(BUILTIN_MODIFIERS, ast.name)) { this._modifiersStatementIds.push(this._entryId); @@ -99,9 +74,9 @@ export class ASTVisitor { } } private _visitBinaryBranch( - ast: SolidityParser.AST, - left: SolidityParser.AST, - right: SolidityParser.AST, + ast: Parser.ASTNode, + left: Parser.ASTNode, + right: Parser.ASTNode, type: BranchType, ): void { this._branchMap[this._entryId++] = { @@ -110,16 +85,25 @@ export class ASTVisitor { locations: [this._getExpressionRange(left), this._getExpressionRange(right)], }; } - private _visitStatement(ast: SolidityParser.AST): void { + private _visitStatement(ast: Parser.ASTNode): void { this._statementMap[this._entryId++] = this._getExpressionRange(ast); } - private _getExpressionRange(ast: SolidityParser.AST): SingleFileSourceRange { - const start = this._locationByOffset[ast.start - 1]; - const end = this._locationByOffset[ast.end - 1]; + private _getExpressionRange(ast: Parser.ASTNode): SingleFileSourceRange { + const start = this._locationByOffset[ast.range[0] - 1]; + const end = this._locationByOffset[ast.range[1]]; const range = { start, end, }; return range; } + private _visitFunctionLikeDefinition(ast: Parser.ModifierDefinition | Parser.FunctionDefinition): void { + const loc = this._getExpressionRange(ast); + this._fnMap[this._entryId++] = { + name: ast.name, + line: loc.start.line, + loc, + }; + this._visitStatement(ast); + } } diff --git a/packages/sol-cov/src/collect_coverage_entries.ts b/packages/sol-cov/src/collect_coverage_entries.ts index d29fa2c37..6da81fbfc 100644 --- a/packages/sol-cov/src/collect_coverage_entries.ts +++ b/packages/sol-cov/src/collect_coverage_entries.ts @@ -2,7 +2,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as fs from 'fs'; import * as _ from 'lodash'; import * as path from 'path'; -import * as SolidityParser from 'solidity-parser-sc'; +import * as parser from 'solidity-parser-antlr'; import { ASTVisitor, CoverageEntriesDescription } from './ast_visitor'; import { getLocationByOffset } from './source_maps'; @@ -13,11 +13,11 @@ const coverageEntriesBySourceHash: { [sourceHash: string]: CoverageEntriesDescri export const collectCoverageEntries = (contractSource: string, fileName: string) => { const sourceHash = ethUtil.sha3(contractSource).toString('hex'); if (_.isUndefined(coverageEntriesBySourceHash[sourceHash])) { - const ast = SolidityParser.parse(contractSource); + const ast = parser.parse(contractSource, { range: true }); const locationByOffset = getLocationByOffset(contractSource); - const astVisitor = new ASTVisitor(locationByOffset); - astVisitor.walkAST(ast); - coverageEntriesBySourceHash[sourceHash] = astVisitor.getCollectedCoverageEntries(); + const visitor = new ASTVisitor(locationByOffset); + parser.visit(ast, visitor); + coverageEntriesBySourceHash[sourceHash] = visitor.getCollectedCoverageEntries(); } const coverageEntriesDescription = coverageEntriesBySourceHash[sourceHash]; return coverageEntriesDescription; diff --git a/packages/sol-cov/src/coverage_subprovider.ts b/packages/sol-cov/src/coverage_subprovider.ts index ba7552fe6..71d90bba7 100644 --- a/packages/sol-cov/src/coverage_subprovider.ts +++ b/packages/sol-cov/src/coverage_subprovider.ts @@ -137,9 +137,9 @@ export class CoverageSubprovider extends Subprovider { await this._lock.acquire(); const snapshotId = Number((await this.emitPayloadAsync({ method: 'evm_snapshot' })).result); const fakeTxData: MaybeFakeTxData = { - from: this._defaultFromAddress, isFakeTransaction: true, // This transaction (and only it) is allowed to come through when the lock is locked ...callData, + from: callData.from || this._defaultFromAddress, }; try { await this.emitPayloadAsync({ diff --git a/packages/sol-cov/src/globals.d.ts b/packages/sol-cov/src/globals.d.ts index f807730cc..54ee64684 100644 --- a/packages/sol-cov/src/globals.d.ts +++ b/packages/sol-cov/src/globals.d.ts @@ -1,6 +1,315 @@ // tslint:disable:completed-docs -declare module 'solidity-parser-sc' { - // This is too time-consuming to define and we don't rely on it anyway - export type AST = any; - export function parse(sourceCode: string): AST; +declare module 'solidity-parser-antlr' { + export interface BaseASTNode { + range: [number, number]; + } + export interface SourceUnit extends BaseASTNode {} + export interface PragmaDirective extends BaseASTNode {} + export interface PragmaName extends BaseASTNode {} + export interface PragmaValue extends BaseASTNode {} + export interface Version extends BaseASTNode {} + export interface VersionOperator extends BaseASTNode {} + export interface VersionConstraint extends BaseASTNode {} + export interface ImportDeclaration extends BaseASTNode {} + export interface ImportDirective extends BaseASTNode {} + export interface ContractDefinition extends BaseASTNode {} + export interface InheritanceSpecifier extends BaseASTNode {} + export interface ContractPart extends BaseASTNode {} + export interface StateVariableDeclaration extends BaseASTNode { + variables: VariableDeclaration[]; + } + export interface UsingForDeclaration extends BaseASTNode {} + export interface StructDefinition extends BaseASTNode {} + export interface ModifierDefinition extends BaseASTNode { + name: string; + } + export interface ModifierInvocation extends BaseASTNode { + name: string; + } + export interface FunctionDefinition extends BaseASTNode { + name: string; + } + export interface ReturnParameters extends BaseASTNode {} + export interface ModifierList extends BaseASTNode {} + export interface EventDefinition extends BaseASTNode {} + export interface EnumValue extends BaseASTNode {} + export interface EnumDefinition extends BaseASTNode {} + export interface ParameterList extends BaseASTNode {} + export interface Parameter extends BaseASTNode {} + export interface EventParameterList extends BaseASTNode {} + export interface EventParameter extends BaseASTNode {} + export interface FunctionTypeParameterList extends BaseASTNode {} + export interface FunctionTypeParameter extends BaseASTNode {} + export interface VariableDeclaration extends BaseASTNode { + visibility: 'public' | 'private'; + isStateVar: boolean; + } + export interface TypeName extends BaseASTNode {} + export interface UserDefinedTypeName extends BaseASTNode {} + export interface Mapping extends BaseASTNode {} + export interface FunctionTypeName extends BaseASTNode {} + export interface StorageLocation extends BaseASTNode {} + export interface StateMutability extends BaseASTNode {} + export interface Block extends BaseASTNode {} + export interface Statement extends BaseASTNode {} + export interface ExpressionStatement extends BaseASTNode { + expression: ASTNode; + } + export interface IfStatement extends BaseASTNode { + trueBody: ASTNode; + falseBody: ASTNode; + } + export interface WhileStatement extends BaseASTNode {} + export interface SimpleStatement extends BaseASTNode {} + export interface ForStatement extends BaseASTNode {} + export interface InlineAssemblyStatement extends BaseASTNode {} + export interface DoWhileStatement extends BaseASTNode {} + export interface ContinueStatement extends BaseASTNode {} + export interface BreakStatement extends BaseASTNode {} + export interface ReturnStatement extends BaseASTNode {} + export interface ThrowStatement extends BaseASTNode {} + export interface VariableDeclarationStatement extends BaseASTNode {} + export interface IdentifierList extends BaseASTNode {} + export interface ElementaryTypeName extends BaseASTNode {} + export interface Expression extends BaseASTNode {} + export interface PrimaryExpression extends BaseASTNode {} + export interface ExpressionList extends BaseASTNode {} + export interface NameValueList extends BaseASTNode {} + export interface NameValue extends BaseASTNode {} + export interface FunctionCallArguments extends BaseASTNode {} + export interface AssemblyBlock extends BaseASTNode {} + export interface AssemblyItem extends BaseASTNode {} + export interface AssemblyExpression extends BaseASTNode {} + export interface AssemblyCall extends BaseASTNode {} + export interface AssemblyLocalDefinition extends BaseASTNode {} + export interface AssemblyAssignment extends BaseASTNode {} + export interface AssemblyIdentifierOrList extends BaseASTNode {} + export interface AssemblyIdentifierList extends BaseASTNode {} + export interface AssemblyStackAssignment extends BaseASTNode {} + export interface LabelDefinition extends BaseASTNode {} + export interface AssemblySwitch extends BaseASTNode {} + export interface AssemblyCase extends BaseASTNode {} + export interface AssemblyFunctionDefinition extends BaseASTNode {} + export interface AssemblyFunctionReturns extends BaseASTNode {} + export interface AssemblyFor extends BaseASTNode {} + export interface AssemblyIf extends BaseASTNode {} + export interface AssemblyLiteral extends BaseASTNode {} + export interface SubAssembly extends BaseASTNode {} + export interface TupleExpression extends BaseASTNode {} + export interface ElementaryTypeNameExpression extends BaseASTNode {} + export interface NumberLiteral extends BaseASTNode {} + export interface Identifier extends BaseASTNode {} + export type BinOp = + | '+' + | '-' + | '*' + | '/' + | '**' + | '%' + | '<<' + | '>>' + | '&&' + | '||' + | '&' + | '|' + | '^' + | '<' + | '>' + | '<=' + | '>=' + | '==' + | '!=' + | '=' + | '|=' + | '^=' + | '&=' + | '<<=' + | '>>=' + | '+=' + | '-=' + | '*=' + | '/=' + | '%='; + export interface BinaryOperation extends BaseASTNode { + left: ASTNode; + right: ASTNode; + operator: BinOp; + } + export interface Conditional extends BaseASTNode { + trueExpression: ASTNode; + falseExpression: ASTNode; + } + + export type ASTNode = + | SourceUnit + | PragmaDirective + | PragmaName + | PragmaValue + | Version + | VersionOperator + | VersionConstraint + | ImportDeclaration + | ImportDirective + | ContractDefinition + | InheritanceSpecifier + | ContractPart + | StateVariableDeclaration + | UsingForDeclaration + | StructDefinition + | ModifierDefinition + | ModifierInvocation + | FunctionDefinition + | ReturnParameters + | ModifierList + | EventDefinition + | EnumValue + | EnumDefinition + | ParameterList + | Parameter + | EventParameterList + | EventParameter + | FunctionTypeParameterList + | FunctionTypeParameter + | VariableDeclaration + | TypeName + | UserDefinedTypeName + | Mapping + | FunctionTypeName + | StorageLocation + | StateMutability + | Block + | Statement + | ExpressionStatement + | IfStatement + | WhileStatement + | SimpleStatement + | ForStatement + | InlineAssemblyStatement + | DoWhileStatement + | ContinueStatement + | BreakStatement + | ReturnStatement + | ThrowStatement + | VariableDeclarationStatement + | IdentifierList + | ElementaryTypeName + | Expression + | PrimaryExpression + | ExpressionList + | NameValueList + | NameValue + | FunctionCallArguments + | AssemblyBlock + | AssemblyItem + | AssemblyExpression + | AssemblyCall + | AssemblyLocalDefinition + | AssemblyAssignment + | AssemblyIdentifierOrList + | AssemblyIdentifierList + | AssemblyStackAssignment + | LabelDefinition + | AssemblySwitch + | AssemblyCase + | AssemblyFunctionDefinition + | AssemblyFunctionReturns + | AssemblyFor + | AssemblyIf + | AssemblyLiteral + | SubAssembly + | TupleExpression + | ElementaryTypeNameExpression + | NumberLiteral + | Identifier + | BinaryOperation + | Conditional; + export interface Visitor { + SourceUnit?: (node: SourceUnit) => void; + PragmaDirective?: (node: PragmaDirective) => void; + PragmaName?: (node: PragmaName) => void; + PragmaValue?: (node: PragmaValue) => void; + Version?: (node: Version) => void; + VersionOperator?: (node: VersionOperator) => void; + VersionConstraint?: (node: VersionConstraint) => void; + ImportDeclaration?: (node: ImportDeclaration) => void; + ImportDirective?: (node: ImportDirective) => void; + ContractDefinition?: (node: ContractDefinition) => void; + InheritanceSpecifier?: (node: InheritanceSpecifier) => void; + ContractPart?: (node: ContractPart) => void; + StateVariableDeclaration?: (node: StateVariableDeclaration) => void; + UsingForDeclaration?: (node: UsingForDeclaration) => void; + StructDefinition?: (node: StructDefinition) => void; + ModifierDefinition?: (node: ModifierDefinition) => void; + ModifierInvocation?: (node: ModifierInvocation) => void; + FunctionDefinition?: (node: FunctionDefinition) => void; + ReturnParameters?: (node: ReturnParameters) => void; + ModifierList?: (node: ModifierList) => void; + EventDefinition?: (node: EventDefinition) => void; + EnumValue?: (node: EnumValue) => void; + EnumDefinition?: (node: EnumDefinition) => void; + ParameterList?: (node: ParameterList) => void; + Parameter?: (node: Parameter) => void; + EventParameterList?: (node: EventParameterList) => void; + EventParameter?: (node: EventParameter) => void; + FunctionTypeParameterList?: (node: FunctionTypeParameterList) => void; + FunctionTypeParameter?: (node: FunctionTypeParameter) => void; + VariableDeclaration?: (node: VariableDeclaration) => void; + TypeName?: (node: TypeName) => void; + UserDefinedTypeName?: (node: UserDefinedTypeName) => void; + Mapping?: (node: Mapping) => void; + FunctionTypeName?: (node: FunctionTypeName) => void; + StorageLocation?: (node: StorageLocation) => void; + StateMutability?: (node: StateMutability) => void; + Block?: (node: Block) => void; + Statement?: (node: Statement) => void; + ExpressionStatement?: (node: ExpressionStatement) => void; + IfStatement?: (node: IfStatement) => void; + WhileStatement?: (node: WhileStatement) => void; + SimpleStatement?: (node: SimpleStatement) => void; + ForStatement?: (node: ForStatement) => void; + InlineAssemblyStatement?: (node: InlineAssemblyStatement) => void; + DoWhileStatement?: (node: DoWhileStatement) => void; + ContinueStatement?: (node: ContinueStatement) => void; + BreakStatement?: (node: BreakStatement) => void; + ReturnStatement?: (node: ReturnStatement) => void; + ThrowStatement?: (node: ThrowStatement) => void; + VariableDeclarationStatement?: (node: VariableDeclarationStatement) => void; + IdentifierList?: (node: IdentifierList) => void; + ElementaryTypeName?: (node: ElementaryTypeName) => void; + Expression?: (node: Expression) => void; + PrimaryExpression?: (node: PrimaryExpression) => void; + ExpressionList?: (node: ExpressionList) => void; + NameValueList?: (node: NameValueList) => void; + NameValue?: (node: NameValue) => void; + FunctionCallArguments?: (node: FunctionCallArguments) => void; + AssemblyBlock?: (node: AssemblyBlock) => void; + AssemblyItem?: (node: AssemblyItem) => void; + AssemblyExpression?: (node: AssemblyExpression) => void; + AssemblyCall?: (node: AssemblyCall) => void; + AssemblyLocalDefinition?: (node: AssemblyLocalDefinition) => void; + AssemblyAssignment?: (node: AssemblyAssignment) => void; + AssemblyIdentifierOrList?: (node: AssemblyIdentifierOrList) => void; + AssemblyIdentifierList?: (node: AssemblyIdentifierList) => void; + AssemblyStackAssignment?: (node: AssemblyStackAssignment) => void; + LabelDefinition?: (node: LabelDefinition) => void; + AssemblySwitch?: (node: AssemblySwitch) => void; + AssemblyCase?: (node: AssemblyCase) => void; + AssemblyFunctionDefinition?: (node: AssemblyFunctionDefinition) => void; + AssemblyFunctionReturns?: (node: AssemblyFunctionReturns) => void; + AssemblyFor?: (node: AssemblyFor) => void; + AssemblyIf?: (node: AssemblyIf) => void; + AssemblyLiteral?: (node: AssemblyLiteral) => void; + SubAssembly?: (node: SubAssembly) => void; + TupleExpression?: (node: TupleExpression) => void; + ElementaryTypeNameExpression?: (node: ElementaryTypeNameExpression) => void; + NumberLiteral?: (node: NumberLiteral) => void; + Identifier?: (node: Identifier) => void; + BinaryOperation?: (node: BinaryOperation) => void; + Conditional?: (node: Conditional) => void; + } + export interface ParserOpts { + range?: boolean; + } + export function parse(sourceCode: string, parserOpts: ParserOpts): ASTNode; + export function visit(ast: ASTNode, visitor: Visitor): void; } @@ -9939,6 +9939,10 @@ solidity-coverage@^0.4.10: solidity-parser-sc "0.4.5" web3 "^0.18.4" +solidity-parser-antlr@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.2.7.tgz#4c72e5f052fdd54fec363f1156533703e84a8325" + solidity-parser-sc@0.4.5, solidity-parser-sc@^0.4.4: version "0.4.5" resolved "https://registry.yarnpkg.com/solidity-parser-sc/-/solidity-parser-sc-0.4.5.tgz#dab766567edc690bb7c3fa987b04982609d5cae5" |