From 2ce9ca6bb0a6eb206af65817d42f116402a23573 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Thu, 20 Jun 2019 10:16:25 -0500 Subject: [PATCH] fix(@embark/coverage): function types and single statement ifs --- packages/embark-coverage/README.md | 2 + packages/embark-coverage/package.json | 9 +- packages/embark-coverage/src/injector.ts | 42 --- .../embark-coverage/src/instrumentWalker.ts | 63 ---- packages/embark-coverage/src/instrumenter.ts | 77 ---- .../src/{ => lib}/contractEnhanced.ts | 24 +- .../embark-coverage/src/{ => lib}/eventId.ts | 0 .../embark-coverage/src/{ => lib}/index.ts | 3 +- packages/embark-coverage/src/lib/injector.ts | 83 +++++ .../src/lib/instrumentWalker.ts | 74 ++++ .../embark-coverage/src/lib/instrumenter.ts | 112 ++++++ .../embark-coverage/src/{ => lib}/path.ts | 0 packages/embark-coverage/src/lib/printer.ts | 32 ++ .../embark-coverage/src/{ => lib}/types.ts | 6 +- .../src/test/fixtures/contracts/ERC20.sol | 228 ++++++++++++ .../contracts/GnosisStandardToken.sol | 111 ++++++ .../fixtures/contracts/SignatureBouncer.sol | 122 +++++++ .../fixtures/contracts/SignedSafeMath.sol | 60 ++++ .../embark-coverage/src/test/printer.spec.ts | 45 +++ packages/embark-solidity/src/index.js | 1 + packages/embark-typings/index.d.ts | 2 +- .../src/prettier-plugin-solidity/index.d.ts | 1 + .../src/solidity-parser-antlr/index.d.ts | 332 ------------------ packages/embark-utils/src/index.js | 2 - packages/embark-utils/src/solidity/code.ts | 15 - .../embark-utils/src/solidity/remapImports.ts | 8 +- packages/embark/package.json | 1 - yarn.lock | 64 +++- 28 files changed, 963 insertions(+), 556 deletions(-) delete mode 100644 packages/embark-coverage/src/injector.ts delete mode 100644 packages/embark-coverage/src/instrumentWalker.ts delete mode 100644 packages/embark-coverage/src/instrumenter.ts rename packages/embark-coverage/src/{ => lib}/contractEnhanced.ts (88%) rename packages/embark-coverage/src/{ => lib}/eventId.ts (100%) rename packages/embark-coverage/src/{ => lib}/index.ts (97%) create mode 100644 packages/embark-coverage/src/lib/injector.ts create mode 100644 packages/embark-coverage/src/lib/instrumentWalker.ts create mode 100644 packages/embark-coverage/src/lib/instrumenter.ts rename packages/embark-coverage/src/{ => lib}/path.ts (100%) create mode 100644 packages/embark-coverage/src/lib/printer.ts rename packages/embark-coverage/src/{ => lib}/types.ts (76%) create mode 100644 packages/embark-coverage/src/test/fixtures/contracts/ERC20.sol create mode 100644 packages/embark-coverage/src/test/fixtures/contracts/GnosisStandardToken.sol create mode 100644 packages/embark-coverage/src/test/fixtures/contracts/SignatureBouncer.sol create mode 100644 packages/embark-coverage/src/test/fixtures/contracts/SignedSafeMath.sol create mode 100644 packages/embark-coverage/src/test/printer.spec.ts create mode 100644 packages/embark-typings/src/prettier-plugin-solidity/index.d.ts delete mode 100644 packages/embark-typings/src/solidity-parser-antlr/index.d.ts delete mode 100644 packages/embark-utils/src/solidity/code.ts diff --git a/packages/embark-coverage/README.md b/packages/embark-coverage/README.md index 64b3dfe14..ef3a18ee7 100644 --- a/packages/embark-coverage/README.md +++ b/packages/embark-coverage/README.md @@ -4,3 +4,5 @@ Visit [embark.status.im](https://embark.status.im/) to get started with [Embark](https://github.com/embark-framework/embark). + +Contracts in `test/fixture/contracts` are from [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-solidity) and [Gnosis Prediction Markets](https://github.com/gnosis/pm-contracts) diff --git a/packages/embark-coverage/package.json b/packages/embark-coverage/package.json index 4f6e11e80..4ba1153ab 100644 --- a/packages/embark-coverage/package.json +++ b/packages/embark-coverage/package.json @@ -24,7 +24,7 @@ "type": "git", "url": "https://github.com/embark-framework/embark.git" }, - "main": "./dist/index.js", + "main": "./dist/lib/index.js", "scripts": { "build": "cross-env BABEL_ENV=node babel src --extensions \".ts\" --out-dir dist --root-mode upward --source-maps", "ci": "npm run qa", @@ -35,6 +35,7 @@ "qa": "npm-run-all lint typecheck build package", "reset": "npx rimraf dist embark-*.tgz package", "start": "npm run watch", + "test": "nyc --reporter=html --reporter=json mocha dist/test/**/*.js --exit --no-timeouts --require source-map-support/register", "typecheck": "tsc", "watch": "run-p watch:*", "watch:build": "npm run build -- --verbose --watch", @@ -46,16 +47,20 @@ "embark-utils": "^4.1.0-beta.3", "fs-extra": "7.0.1", "globule": "1.2.1", + "prettier-plugin-solidity": "1.0.0-alpha.25", "semver": "5.6.0", - "solidity-parser-antlr": "0.4.2", + "solidity-parser-antlr": "0.4.5", "web3-eth-contract": "1.0.0-beta.37" }, "devDependencies": { "@babel/cli": "7.2.3", "@babel/core": "7.2.2", + "@types/mocha": "5.2.0", + "@types/prettier": "1.16.4", "@types/web3": "1.0.12", "cross-env": "5.2.0", "eslint": "5.7.0", + "mocha": "5.2.0", "npm-run-all": "4.1.5", "rimraf": "2.6.3", "tslint": "5.16.0", diff --git a/packages/embark-coverage/src/injector.ts b/packages/embark-coverage/src/injector.ts deleted file mode 100644 index c6fb66e3f..000000000 --- a/packages/embark-coverage/src/injector.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as semver from "semver"; -import { ContractEnhanced } from "./contractEnhanced"; -import { encrypt } from "./eventId"; -import { InjectionPoint } from "./types"; - -const EMIT_VERSION = "0.4.21"; - -export class Injector { - private isEmitSupported: boolean; - - constructor(private contract: ContractEnhanced) { - this.isEmitSupported = semver.gte(this.contract.solcVersion, EMIT_VERSION); - } - - public process(injectionPoint: InjectionPoint) { - switch (injectionPoint.type) { - case "statement": - return this.statement(injectionPoint); - case "contractDefinition": - return this.contractDefinition(injectionPoint); - } - } - - private statement(injectionPoint: InjectionPoint) { - const data = `${this.isEmitSupported ? "emit" : ""} __StatementCoverage(${encrypt(this.contract.id, injectionPoint.id)});`; - this.insertAt(injectionPoint.location.start.line - 1, data); - } - - private contractDefinition(injectionPoint: InjectionPoint) { - const data = [ - "event __StatementCoverage(uint32 value);", - ].join("\n"); - - this.insertAt(injectionPoint.location.start.line, data); - } - - private insertAt(line: number, data: string) { - const lines = this.contract.source.split("\n"); - lines.splice(line, 0, data); - this.contract.source = lines.join("\n"); - } -} diff --git a/packages/embark-coverage/src/instrumentWalker.ts b/packages/embark-coverage/src/instrumentWalker.ts deleted file mode 100644 index 24e495857..000000000 --- a/packages/embark-coverage/src/instrumentWalker.ts +++ /dev/null @@ -1,63 +0,0 @@ -import parser, { - ASTNode, - BreakStatement, - ContinueStatement, - ContractDefinition, - EmitStatement, - ExpressionStatement, - FunctionDefinition, - IfStatement, - ModifierDefinition, - ReturnStatement, - ThrowStatement, - VariableDeclarationStatement, - WhileStatement, -} from "solidity-parser-antlr"; -import { Instrumenter } from "./instrumenter"; - -export class InstrumentWalker { - constructor(private instrumenter: Instrumenter) { - } - - public walk(ast: ASTNode) { - parser.visit(ast, { - BreakStatement: (node: BreakStatement) => { - this.instrumenter.instrumentStatement(node); - }, - ContinueStatement: (node: ContinueStatement) => { - this.instrumenter.instrumentStatement(node); - }, - ContractDefinition: (node: ContractDefinition) => { - this.instrumenter.instrumentContractDefinition(node); - }, - EmitStatement: (node: EmitStatement) => { - this.instrumenter.instrumentStatement(node); - }, - ExpressionStatement: (node: ExpressionStatement) => { - this.instrumenter.instrumentStatement(node); - }, - FunctionDefinition: (node: FunctionDefinition) => { - this.instrumenter.instrumentFunction(node); - }, - IfStatement: (node: IfStatement) => { - this.instrumenter.instrumentStatement(node); - this.instrumenter.instrumentIfStatement(node); - }, - ModifierDefinition: (node: ModifierDefinition) => { - this.instrumenter.instrumentFunction(node); - }, - ReturnStatement: (node: ReturnStatement) => { - this.instrumenter.instrumentStatement(node); - }, - ThrowStatement: (node: ThrowStatement) => { - this.instrumenter.instrumentStatement(node); - }, - VariableDeclarationStatement: (node: VariableDeclarationStatement) => { - this.instrumenter.instrumentStatement(node); - }, - WhileStatement: (node: WhileStatement) => { - this.instrumenter.instrumentStatement(node); - }, - }); - } -} diff --git a/packages/embark-coverage/src/instrumenter.ts b/packages/embark-coverage/src/instrumenter.ts deleted file mode 100644 index 01188386f..000000000 --- a/packages/embark-coverage/src/instrumenter.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - ContractDefinition, - FunctionDefinition, - IfStatement, - Location, - Statement, -} from "solidity-parser-antlr"; - -import { ContractEnhanced } from "./contractEnhanced"; -import { InjectionPoint, InjectionPointType } from "./types"; - -export class Instrumenter { - private injectionPoints: InjectionPoint[] = []; - - constructor(private contract: ContractEnhanced) { - } - - public getInjectionPoints() { - return this.injectionPoints.sort((a, b) => b.location.start.line - a.location.start.line); - } - - public instrumentContractDefinition(node: ContractDefinition) { - if (!node.loc) { - return; - } - - if (node.loc.start.line === node.loc.end.line) { - return; - } - - this.addInjectionPoints("contractDefinition", 1, node.loc); - } - - public instrumentFunction(node: FunctionDefinition) { - if (!node.loc) { - return; - } - - if (!node.body || !node.body.loc) { - return; - } - - if (node.body.loc.start.line === node.body.loc.end.line) { - return; - } - - this.contract.addFunction(node.loc, node.name, node.body.loc); - } - - public instrumentStatement(node: Statement) { - if (!node.loc) { - return; - } - - const id = this.contract.addStatement(node.loc); - this.addInjectionPoints("statement", id, node.loc); - } - - public instrumentIfStatement(node: IfStatement) { - if (!node.loc) { - return; - } - - const locations = [node.trueBody, node.falseBody].reduce((acc: Location[], body) => { - if (body && body.loc) { - acc.push(body.loc); - } - return acc; - }, []); - - this.contract.addBranch(node.loc.start.line, "if", locations); - } - - private addInjectionPoints(type: InjectionPointType, id: number, location: Location) { - this.injectionPoints.push({type, id, location}); - } -} diff --git a/packages/embark-coverage/src/contractEnhanced.ts b/packages/embark-coverage/src/lib/contractEnhanced.ts similarity index 88% rename from packages/embark-coverage/src/contractEnhanced.ts rename to packages/embark-coverage/src/lib/contractEnhanced.ts index f3bd3131d..9848d8501 100644 --- a/packages/embark-coverage/src/contractEnhanced.ts +++ b/packages/embark-coverage/src/lib/contractEnhanced.ts @@ -1,7 +1,7 @@ import { File } from "embark-utils"; import * as fs from "fs-extra"; import * as path from "path"; -import parser, { LineColumn, Location } from "solidity-parser-antlr"; +import parser, { LineColumn, Location, SourceUnit } from "solidity-parser-antlr"; import { EventLog } from "web3/types"; import { decrypt } from "./eventId"; @@ -9,6 +9,7 @@ import { Injector } from "./injector"; import { Instrumenter } from "./instrumenter"; import { InstrumentWalker } from "./instrumentWalker"; import { coverageContractsPath } from "./path"; +import { Printer } from "./printer"; import { BranchType, Coverage } from "./types"; const STATEMENT_EVENT = "__StatementCoverage"; @@ -26,7 +27,7 @@ export class ContractEnhanced { public coverageFilepath: string; public originalSource: string; public source: string; - private ast!: parser.ASTNode; + private ast!: SourceUnit; private functionsBodyLocation: {[id: number]: Location} = {}; constructor(public filepath: string, public solcVersion: string) { @@ -38,7 +39,7 @@ export class ContractEnhanced { try { this.source = fs.readFileSync(filepath, "utf-8"); this.originalSource = this.source; - this.ast = parser.parse(this.source, {loc: true, range: true}); + this.ast = parser.parse(this.source, {loc: true, range: true}) as SourceUnit; } catch (error) { const {line, column, message} = error.errors[0]; console.warn(`Error on ${this.filepath}:${line}:${column}: "${message}". Could not setup for coverage.`); @@ -67,13 +68,26 @@ export class ContractEnhanced { const instrumentWalker = new InstrumentWalker(instrumenter); instrumentWalker.walk(this.ast); - const injector = new Injector(this); + const injector = new Injector(this, this.solcVersion); instrumenter.getInjectionPoints().forEach(injector.process.bind(injector)); } public save() { fs.ensureFileSync(this.coverageFilepath); - fs.writeFileSync(this.coverageFilepath, this.source); + + if (!this.ast) { + return fs.writeFileSync(this.coverageFilepath, this.source); + } + + try { + const printer = new Printer(this.ast); + this.source = printer.print(); + } catch (err) { + // Something might have happened with the printer. Write the original source. + console.warn(`Error with coverage printer for ${this.filepath}. Please submit an issue with this code.`); + } finally { + fs.writeFileSync(this.coverageFilepath, this.source); + } } public updateCoverage(events: EventLog[]) { diff --git a/packages/embark-coverage/src/eventId.ts b/packages/embark-coverage/src/lib/eventId.ts similarity index 100% rename from packages/embark-coverage/src/eventId.ts rename to packages/embark-coverage/src/lib/eventId.ts diff --git a/packages/embark-coverage/src/index.ts b/packages/embark-coverage/src/lib/index.ts similarity index 97% rename from packages/embark-coverage/src/index.ts rename to packages/embark-coverage/src/lib/index.ts index a48bba391..e5a583357 100644 --- a/packages/embark-coverage/src/index.ts +++ b/packages/embark-coverage/src/lib/index.ts @@ -1,4 +1,4 @@ -import { dappPath, File, removePureView } from "embark-utils"; +import { dappPath, File } from "embark-utils"; import * as globule from "globule"; import * as path from "path"; import Web3Contract from "web3/eth/contract"; @@ -44,7 +44,6 @@ export default class Coverage { contract.save(); }); await Promise.all(promises); - removePureView(coverageContractsPath()); } private swapContracts() { diff --git a/packages/embark-coverage/src/lib/injector.ts b/packages/embark-coverage/src/lib/injector.ts new file mode 100644 index 000000000..3d6258458 --- /dev/null +++ b/packages/embark-coverage/src/lib/injector.ts @@ -0,0 +1,83 @@ +import { ContractEnhanced } from "./contractEnhanced"; +import { encrypt } from "./eventId"; +import { CoverageEmitNodeType, InjectionPoint } from "./types"; + +import * as semver from "semver"; +import parser, { ASTNode, ContractDefinition, EmitStatement, EventDefinition, ExpressionStatement } from "solidity-parser-antlr"; + +const EMIT_VERSION = "0.4.21"; + +const EVENT_NODE = { + name: "__StatementCoverage", + parameters: { + parameters: [ + { + name: "value", + type: "VariableDeclaration", + typeName: { name: "uint32", type: "ElementaryTypeName" }, + }, + ], + type: "ParameterList", + }, + type: "EventDefinition", +}; + +export class Injector { + private isEmitSupported: boolean; + + constructor(private contract: ContractEnhanced, private solcVersion: string) { + this.solcVersion = solcVersion; + this.isEmitSupported = semver.gte(this.solcVersion, EMIT_VERSION); + } + + public process(injectionPoint: InjectionPoint) { + switch (injectionPoint.type) { + case "statement": + return this.statement(injectionPoint); + case "contractDefinition": + return this.contractDefinition(injectionPoint); + } + } + + private emitStatementNode(id: number): CoverageEmitNodeType { + // Depending on what solidity version we're handling, "emit" statements might not + // be supported, so we check here. + const functionCall = { + arguments: [{ number: id.toString(), type: "NumberLiteral", subdenomination: null }], + expression: { name: "__StatementCoverage", type: "Identifier" }, + names: [], + type: "FunctionCall", + }; + + if (this.isEmitSupported) { + return { eventCall: functionCall, type: "EmitStatement" } as EmitStatement; + } + + return { expression: functionCall, type: "ExpressionStatement" } as ExpressionStatement; + } + + private statement(injectionPoint: InjectionPoint) { + const eventId = encrypt(this.contract.id, injectionPoint.id); + + // Get the proper node that we're going to inject instead of building a string. + // There _might_ be a better way to do this. + const node = this.emitStatementNode(eventId); + this.insertAt(node, injectionPoint); + } + + private contractDefinition(injectionPoint: InjectionPoint) { + const contractNode = injectionPoint.node as ContractDefinition; + contractNode.subNodes.splice(0, 0, EVENT_NODE as EventDefinition); + } + + private insertAt(emit: CoverageEmitNodeType, injectionPoint: InjectionPoint) { + const { parent, node } = injectionPoint; + + if (!parent) { + return; + } + + const idx = parent.statements.findIndex((s: ASTNode) => s.loc === node.loc); + parent.statements.splice(idx, 0, emit); + } +} diff --git a/packages/embark-coverage/src/lib/instrumentWalker.ts b/packages/embark-coverage/src/lib/instrumentWalker.ts new file mode 100644 index 000000000..d35367bef --- /dev/null +++ b/packages/embark-coverage/src/lib/instrumentWalker.ts @@ -0,0 +1,74 @@ +import parser, { + ASTNode, + Block, + BreakStatement, + ContinueStatement, + ContractDefinition, + EmitStatement, + ExpressionStatement, + FunctionDefinition, + IfStatement, + ReturnStatement, + SourceUnit, + ThrowStatement, + VariableDeclarationStatement, + WhileStatement, +} from "solidity-parser-antlr"; +import { Instrumenter } from "./instrumenter"; + +export class InstrumentWalker { + private blockStack: Block[]; + + constructor(private instrumenter: Instrumenter) { + this.blockStack = []; + } + + private blockTail(): Block { + return this.blockStack[this.blockStack.length - 1]; + } + + public walk(ast: ASTNode) { + parser.visit(ast, { + "Block": (node: Block) => { + this.blockStack.push(node); + }, + "Block:exit": (node: Block) => { + this.blockStack.pop(); + }, + "BreakStatement": (node: BreakStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + "ContinueStatement": (node: ContinueStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + "ContractDefinition": (node: ContractDefinition) => { + this.instrumenter.instrumentContractDefinition(node); + }, + "EmitStatement": (node: EmitStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + "ExpressionStatement": (node: ExpressionStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + "FunctionDefinition": (node: FunctionDefinition) => { + this.instrumenter.instrumentFunction(node); + }, + "IfStatement": (node: IfStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + this.instrumenter.instrumentIfStatement(node); + }, + "ReturnStatement": (node: ReturnStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + "ThrowStatement": (node: ThrowStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + "VariableDeclarationStatement": (node: VariableDeclarationStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + "WhileStatement": (node: WhileStatement) => { + this.instrumenter.instrumentStatement(node, this.blockTail()); + }, + }); + } +} diff --git a/packages/embark-coverage/src/lib/instrumenter.ts b/packages/embark-coverage/src/lib/instrumenter.ts new file mode 100644 index 000000000..0089c1da4 --- /dev/null +++ b/packages/embark-coverage/src/lib/instrumenter.ts @@ -0,0 +1,112 @@ +import { + ASTNode, + Block, + ContractDefinition, + FunctionDefinition, + IfStatement, + Location, + ModifierDefinition, + Statement, +} from "solidity-parser-antlr"; + +import {ContractEnhanced} from "./contractEnhanced"; +import {InjectionPoint, InjectionPointType} from "./types"; + +export class Instrumenter { + private injectionPoints: InjectionPoint[] = []; + + constructor(private contract: ContractEnhanced) { + } + + public getInjectionPoints() { + return this.injectionPoints.sort((a, b) => { + return (a.node.loc && b.node.loc) ? b.node.loc.start.line - a.node.loc.start.line : 0; + }); + } + + public instrumentContractDefinition(node: ContractDefinition) { + if (!node.loc) { + return; + } + + if (node.loc.start.line === node.loc.end.line) { + return; + } + + this.addInjectionPoints("contractDefinition", 1, node); + } + + public instrumentFunction(node: FunctionDefinition) { + // Remove stateMutability so that the function is not exported + // as "view" or "pure", seen as it will contain some emit + // statements and those require a transaction. + if (node.stateMutability === "view" || node.stateMutability === "pure") { + node.stateMutability = undefined; + } + + if (!node.loc) { + return; + } + + if (!node.body || !node.body.loc) { + return; + } + + if (node.body.loc.start.line === node.body.loc.end.line) { + return; + } + + this.contract.addFunction(node.loc, node.name || "", node.body.loc); + } + + public instrumentStatement(node: Statement, parent: Block) { + if (!node.loc) { + return; + } + + const id = this.contract.addStatement(node.loc); + this.addInjectionPoints("statement", id, node, parent); + } + + public instrumentIfStatement(node: IfStatement) { + if (!node.loc) { + return; + } + + // Ensure that the trueBody and falseBody are Blocks. This prevents single + // statement `if` checks from breaking when we try to add an emit statement. + node.trueBody = this.wrapInBlock(node.trueBody); + + if (node.falseBody) { + node.falseBody = this.wrapInBlock(node.falseBody); + } + + const locations = [node.trueBody, node.falseBody].reduce((acc: Location[], body) => { + if (body && body.loc) { + acc.push(body.loc); + } + return acc; + }, []); + + this.contract.addBranch(node.loc.start.line, "if", locations); + } + + private wrapInBlock(node: Statement): Block { + if (node.type === "Block") { + return node; + } + + const newNode: Block = { + loc: node.loc, + range: node.range, + statements: [node], + type: "Block", + }; + + return newNode; + } + + private addInjectionPoints(type: InjectionPointType, id: number, node: ASTNode, parent?: Block | undefined) { + this.injectionPoints.push({type, id, node, parent}); + } +} diff --git a/packages/embark-coverage/src/path.ts b/packages/embark-coverage/src/lib/path.ts similarity index 100% rename from packages/embark-coverage/src/path.ts rename to packages/embark-coverage/src/lib/path.ts diff --git a/packages/embark-coverage/src/lib/printer.ts b/packages/embark-coverage/src/lib/printer.ts new file mode 100644 index 000000000..9c0936339 --- /dev/null +++ b/packages/embark-coverage/src/lib/printer.ts @@ -0,0 +1,32 @@ +import fs from "fs"; + +import prettier from "prettier"; +import { printers } from "prettier-plugin-solidity"; +import { SourceUnit } from "solidity-parser-antlr"; + +export class Printer { + constructor(private ast: SourceUnit) {} + + public print() { + const { ast } = this; + + // Yes, yes. This is ridiculous. But go and try and write + // an AST to code writer and then tell me it's ridiculous. + let code = prettier.format("", { + parser(_, parsers, options) { + // The types on this library don't account for Options.printer, + // so we'll ask the typechecker to ignore this one, as it errors. + // + // @ts-ignore + options.printer = printers["solidity-ast"]; + return ast; + }, + }); + + // Fix some weird cases with the library. + code = code.replace(/;\)/gm, ")"); + code = code.replace(/;;/gm, ";"); + + return code; + } +} diff --git a/packages/embark-coverage/src/types.ts b/packages/embark-coverage/src/lib/types.ts similarity index 76% rename from packages/embark-coverage/src/types.ts rename to packages/embark-coverage/src/lib/types.ts index 2427446d1..c9d774188 100644 --- a/packages/embark-coverage/src/types.ts +++ b/packages/embark-coverage/src/lib/types.ts @@ -1,12 +1,14 @@ -import { Location } from "solidity-parser-antlr"; +import { ASTNode, Block, EmitStatement, ExpressionStatement, Location } from "solidity-parser-antlr"; export type InjectionPointType = "statement" | "contractDefinition"; export type BranchType = "if" | "switch"; +export type CoverageEmitNodeType = EmitStatement | ExpressionStatement; export interface InjectionPoint { type: InjectionPointType; id: number; - location: Location; + node: ASTNode; + parent: Block | undefined; } export interface Coverage { diff --git a/packages/embark-coverage/src/test/fixtures/contracts/ERC20.sol b/packages/embark-coverage/src/test/fixtures/contracts/ERC20.sol new file mode 100644 index 000000000..23cbcb845 --- /dev/null +++ b/packages/embark-coverage/src/test/fixtures/contracts/ERC20.sol @@ -0,0 +1,228 @@ +pragma solidity ^0.5.0; + +import "./IERC20.sol"; +import "../../math/SafeMath.sol"; + +/** + * @dev Implementation of the `IERC20` interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using `_mint`. + * For a generic mechanism see `ERC20Mintable`. + * + * *For a detailed writeup see our guide [How to implement supply + * mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).* + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an `Approval` event is emitted on calls to `transferFrom`. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard `decreaseAllowance` and `increaseAllowance` + * functions have been added to mitigate the well-known issues around setting + * allowances. See `IERC20.approve`. + */ +contract ERC20 is IERC20 { + using SafeMath for uint256; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowances; + + uint256 private _totalSupply; + + /** + * @dev See `IERC20.totalSupply`. + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev See `IERC20.balanceOf`. + */ + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + + /** + * @dev See `IERC20.transfer`. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public returns (bool) { + _transfer(msg.sender, recipient, amount); + return true; + } + + /** + * @dev See `IERC20.allowance`. + */ + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See `IERC20.approve`. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 value) public returns (bool) { + _approve(msg.sender, spender, value); + return true; + } + + /** + * @dev See `IERC20.transferFrom`. + * + * Emits an `Approval` event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of `ERC20`; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `value`. + * - the caller must have allowance for `sender`'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount)); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to `approve` that can be used as a mitigation for + * problems described in `IERC20.approve`. + * + * Emits an `Approval` event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { + _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to `approve` that can be used as a mitigation for + * problems described in `IERC20.approve`. + * + * Emits an `Approval` event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { + _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue)); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to `transfer`, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a `Transfer` event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _balances[sender] = _balances[sender].sub(amount); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a `Transfer` event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal { + require(account != address(0), "ERC20: mint to the zero address"); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destoys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a `Transfer` event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 value) internal { + require(account != address(0), "ERC20: burn from the zero address"); + + _totalSupply = _totalSupply.sub(value); + _balances[account] = _balances[account].sub(value); + emit Transfer(account, address(0), value); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an `Approval` event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 value) internal { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = value; + emit Approval(owner, spender, value); + } + + /** + * @dev Destoys `amount` tokens from `account`.`amount` is then deducted + * from the caller's allowance. + * + * See `_burn` and `_approve`. + */ + function _burnFrom(address account, uint256 amount) internal { + _burn(account, amount); + _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount)); + } +} diff --git a/packages/embark-coverage/src/test/fixtures/contracts/GnosisStandardToken.sol b/packages/embark-coverage/src/test/fixtures/contracts/GnosisStandardToken.sol new file mode 100644 index 000000000..014e673c2 --- /dev/null +++ b/packages/embark-coverage/src/test/fixtures/contracts/GnosisStandardToken.sol @@ -0,0 +1,111 @@ +pragma solidity ^0.4.21; +import "./Token.sol"; +import "./Math.sol"; +import "./Proxy.sol"; + +/** + * Deprecated: Use Open Zeppeling one instead + */ +contract StandardTokenData { + + /* + * Storage + */ + mapping (address => uint) balances; + mapping (address => mapping (address => uint)) allowances; + uint totalTokens; +} + +/** + * Deprecated: Use Open Zeppeling one instead + */ +/// @title Standard token contract with overflow protection +contract GnosisStandardToken is Token, StandardTokenData { + using Math for *; + + /* + * Public functions + */ + /// @dev Transfers sender's tokens to a given address. Returns success + /// @param to Address of token receiver + /// @param value Number of tokens to transfer + /// @return Was transfer successful? + function transfer(address to, uint value) + public + returns (bool) + { + if ( !balances[msg.sender].safeToSub(value) + || !balances[to].safeToAdd(value)) + return false; + balances[msg.sender] -= value; + balances[to] += value; + emit Transfer(msg.sender, to, value); + return true; + } + + /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success + /// @param from Address from where tokens are withdrawn + /// @param to Address to where tokens are sent + /// @param value Number of tokens to transfer + /// @return Was transfer successful? + function transferFrom(address from, address to, uint value) + public + returns (bool) + { + if ( !balances[from].safeToSub(value) + || !allowances[from][msg.sender].safeToSub(value) + || !balances[to].safeToAdd(value)) + return false; + balances[from] -= value; + allowances[from][msg.sender] -= value; + balances[to] += value; + emit Transfer(from, to, value); + return true; + } + + /// @dev Sets approved amount of tokens for spender. Returns success + /// @param spender Address of allowed account + /// @param value Number of approved tokens + /// @return Was approval successful? + function approve(address spender, uint value) + public + returns (bool) + { + allowances[msg.sender][spender] = value; + emit Approval(msg.sender, spender, value); + return true; + } + + /// @dev Returns number of allowed tokens for given address + /// @param owner Address of token owner + /// @param spender Address of token spender + /// @return Remaining allowance for spender + function allowance(address owner, address spender) + public + view + returns (uint) + { + return allowances[owner][spender]; + } + + /// @dev Returns number of tokens owned by given address + /// @param owner Address of token owner + /// @return Balance of owner + function balanceOf(address owner) + public + view + returns (uint) + { + return balances[owner]; + } + + /// @dev Returns total supply of tokens + /// @return Total supply + function totalSupply() + public + view + returns (uint) + { + return totalTokens; + } +} diff --git a/packages/embark-coverage/src/test/fixtures/contracts/SignatureBouncer.sol b/packages/embark-coverage/src/test/fixtures/contracts/SignatureBouncer.sol new file mode 100644 index 000000000..b3c9304f6 --- /dev/null +++ b/packages/embark-coverage/src/test/fixtures/contracts/SignatureBouncer.sol @@ -0,0 +1,122 @@ +pragma solidity ^0.5.0; + +import "../access/roles/SignerRole.sol"; +import "../cryptography/ECDSA.sol"; + +/** + * @title SignatureBouncer + * @author PhABC, Shrugs and aflesher + * @dev SignatureBouncer allows users to submit a signature as a permission to + * do an action. + * If the signature is from one of the authorized signer addresses, the + * signature is valid. + * Note that SignatureBouncer offers no protection against replay attacks, users + * must add this themselves! + * + * Signer addresses can be individual servers signing grants or different + * users within a decentralized club that have permission to invite other + * members. This technique is useful for whitelists and airdrops; instead of + * putting all valid addresses on-chain, simply sign a grant of the form + * keccak256(abi.encodePacked(`:contractAddress` + `:granteeAddress`)) using a + * valid signer address. + * Then restrict access to your crowdsale/whitelist/airdrop using the + * `onlyValidSignature` modifier (or implement your own using _isValidSignature). + * In addition to `onlyValidSignature`, `onlyValidSignatureAndMethod` and + * `onlyValidSignatureAndData` can be used to restrict access to only a given + * method or a given method with given parameters respectively. + * See the tests in SignatureBouncer.test.js for specific usage examples. + * + * @notice A method that uses the `onlyValidSignatureAndData` modifier must make + * the _signature parameter the "last" parameter. You cannot sign a message that + * has its own signature in it so the last 128 bytes of msg.data (which + * represents the length of the _signature data and the _signature data itself) + * is ignored when validating. Also non fixed sized parameters make constructing + * the data in the signature much more complex. + * See https://ethereum.stackexchange.com/a/50616 for more details. + */ +contract SignatureBouncer is SignerRole { + using ECDSA for bytes32; + + // Function selectors are 4 bytes long, as documented in + // https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector + uint256 private constant _METHOD_ID_SIZE = 4; + // Signature size is 65 bytes (tightly packed v + r + s), but gets padded to 96 bytes + uint256 private constant _SIGNATURE_SIZE = 96; + + constructor () internal { + // solhint-disable-previous-line no-empty-blocks + } + + /** + * @dev Requires that a valid signature of a signer was provided. + */ + modifier onlyValidSignature(bytes memory signature) { + require(_isValidSignature(msg.sender, signature), "SignatureBouncer: invalid signature for caller"); + _; + } + + /** + * @dev Requires that a valid signature with a specified method of a signer was provided. + */ + modifier onlyValidSignatureAndMethod(bytes memory signature) { + // solhint-disable-next-line max-line-length + require(_isValidSignatureAndMethod(msg.sender, signature), "SignatureBouncer: invalid signature for caller and method"); + _; + } + + /** + * @dev Requires that a valid signature with a specified method and params of a signer was provided. + */ + modifier onlyValidSignatureAndData(bytes memory signature) { + // solhint-disable-next-line max-line-length + require(_isValidSignatureAndData(msg.sender, signature), "SignatureBouncer: invalid signature for caller and data"); + _; + } + + /** + * @dev is the signature of `this + account` from a signer? + * @return bool + */ + function _isValidSignature(address account, bytes memory signature) internal view returns (bool) { + return _isValidDataHash(keccak256(abi.encodePacked(address(this), account)), signature); + } + + /** + * @dev is the signature of `this + account + methodId` from a signer? + * @return bool + */ + function _isValidSignatureAndMethod(address account, bytes memory signature) internal view returns (bool) { + bytes memory data = new bytes(_METHOD_ID_SIZE); + for (uint i = 0; i < data.length; i++) { + data[i] = msg.data[i]; + } + return _isValidDataHash(keccak256(abi.encodePacked(address(this), account, data)), signature); + } + + /** + * @dev is the signature of `this + account + methodId + params(s)` from a signer? + * @notice the signature parameter of the method being validated must be the "last" parameter + * @return bool + */ + function _isValidSignatureAndData(address account, bytes memory signature) internal view returns (bool) { + require(msg.data.length > _SIGNATURE_SIZE, "SignatureBouncer: data is too short"); + + bytes memory data = new bytes(msg.data.length - _SIGNATURE_SIZE); + for (uint i = 0; i < data.length; i++) { + data[i] = msg.data[i]; + } + + return _isValidDataHash(keccak256(abi.encodePacked(address(this), account, data)), signature); + } + + /** + * @dev Internal function to convert a hash to an eth signed message + * and then recover the signature and check it against the signer role. + * @return bool + */ + function _isValidDataHash(bytes32 hash, bytes memory signature) internal view returns (bool) { + address signer = hash.toEthSignedMessageHash().recover(signature); + + return signer != address(0) && isSigner(signer); + } +} diff --git a/packages/embark-coverage/src/test/fixtures/contracts/SignedSafeMath.sol b/packages/embark-coverage/src/test/fixtures/contracts/SignedSafeMath.sol new file mode 100644 index 000000000..95a68df0a --- /dev/null +++ b/packages/embark-coverage/src/test/fixtures/contracts/SignedSafeMath.sol @@ -0,0 +1,60 @@ +pragma solidity ^0.5.0; + +/** + * @title SignedSafeMath + * @dev Signed math operations with safety checks that revert on error. + */ +library SignedSafeMath { + int256 constant private INT256_MIN = -2**255; + + /** + * @dev Multiplies two signed integers, reverts on overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + require(!(a == -1 && b == INT256_MIN), "SignedSafeMath: multiplication overflow"); + + int256 c = a * b; + require(c / a == b, "SignedSafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + require(b != 0, "SignedSafeMath: division by zero"); + require(!(b == -1 && a == INT256_MIN), "SignedSafeMath: division overflow"); + + int256 c = a / b; + + return c; + } + + /** + * @dev Subtracts two signed integers, reverts on overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + int256 c = a - b; + require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); + + return c; + } + + /** + * @dev Adds two signed integers, reverts on overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + int256 c = a + b; + require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); + + return c; + } +} diff --git a/packages/embark-coverage/src/test/printer.spec.ts b/packages/embark-coverage/src/test/printer.spec.ts new file mode 100644 index 000000000..c22a6df3e --- /dev/null +++ b/packages/embark-coverage/src/test/printer.spec.ts @@ -0,0 +1,45 @@ +import assert from "assert"; +import fs from "fs"; +import { describe, it } from "mocha"; +import path from "path"; +import parser, { SourceUnit } from "solidity-parser-antlr"; + +import { Printer } from "../lib/printer"; + +const FIXTURE_PATH = path.join(__dirname, "../", "../", "src", "test", "fixtures", "contracts"); +const FIXTURES = fs.readdirSync(FIXTURE_PATH).map((f) => { + const fp = path.join(FIXTURE_PATH, f); + + return { + basename: f, + path: fp, + source: fs.readFileSync(fp, "utf8"), + }; +}); + +describe("Printer", () => { + describe("printing an AST", () => { + describe("prints equivalent code", () => { + for (const fixture of FIXTURES) { + it(fixture.basename, () => { + const astBefore = parser.parse(fixture.source, {loc: false, range: false}) as SourceUnit; + const printer = new Printer(astBefore); + + const source = printer.print(); + const astAfter = parser.parse(source, {loc: false, range: false}) as SourceUnit; + + // Remove .tokens from the AST as it gets walked and processed by + // prettier. This is not consequential for anything else. Also, the + // tokens property is added without types, and as we can't remove it + // without Typescript doing some type checks, we'll have to ask it to + // ignore those here. + // + // @ts-ignore + delete astBefore.tokens; + + assert.deepEqual(astBefore, astAfter); + }); + } + }); + }); +}); diff --git a/packages/embark-solidity/src/index.js b/packages/embark-solidity/src/index.js index 46d411a8b..790d619d7 100644 --- a/packages/embark-solidity/src/index.js +++ b/packages/embark-solidity/src/index.js @@ -214,6 +214,7 @@ class Solidity { fileCb(); }).catch((e) => { self.logger.error(__('Error while loading the content of ') + filename); + self.logger.error(JSON.stringify(e)); self.logger.debug(e); fileCb(); }); diff --git a/packages/embark-typings/index.d.ts b/packages/embark-typings/index.d.ts index 35a22e02b..54e6cf61d 100644 --- a/packages/embark-typings/index.d.ts +++ b/packages/embark-typings/index.d.ts @@ -1,5 +1,5 @@ +import "./src/prettier-plugin-solidity"; import "./src/remix-debug-debugtest"; -import "./src/solidity-parser-antlr"; export * from "./src/callbacks"; export * from "./src/contract"; diff --git a/packages/embark-typings/src/prettier-plugin-solidity/index.d.ts b/packages/embark-typings/src/prettier-plugin-solidity/index.d.ts new file mode 100644 index 000000000..12ea8e2ce --- /dev/null +++ b/packages/embark-typings/src/prettier-plugin-solidity/index.d.ts @@ -0,0 +1 @@ +declare module "prettier-plugin-solidity"; diff --git a/packages/embark-typings/src/solidity-parser-antlr/index.d.ts b/packages/embark-typings/src/solidity-parser-antlr/index.d.ts deleted file mode 100644 index 31a7c1daf..000000000 --- a/packages/embark-typings/src/solidity-parser-antlr/index.d.ts +++ /dev/null @@ -1,332 +0,0 @@ -declare module "solidity-parser-antlr" { - export interface LineColumn { - line: number; - column: number; - } - export interface Location { - start: LineColumn; - end: LineColumn; - } - export interface BaseASTNode { - type: string; - range?: [number, number]; - loc?: Location; - } - export interface SourceUnit extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface PragmaDirective extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface PragmaName extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface PragmaValue extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface Version extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface VersionOperator extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface VersionConstraint extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ImportDeclaration extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ImportDirective extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ContractDefinition extends BaseASTNode { - name: string; - } - export interface InheritanceSpecifier extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ContractPart extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface StateVariableDeclaration extends BaseASTNode { - variables: VariableDeclaration[]; - } - export interface UsingForDeclaration extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface StructDefinition extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ModifierDefinition extends BaseASTNode { - name: string; - } - export interface ModifierInvocation extends BaseASTNode { - name: string; - } - export interface FunctionDefinition extends BaseASTNode { - name: string; - body?: Block; - } - export interface ReturnParameters extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ModifierList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface EventDefinition extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface EnumValue extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface EnumDefinition extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ParameterList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface Parameter extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface EventParameterList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface EventParameter extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface FunctionTypeParameterList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface FunctionTypeParameter extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface VariableDeclaration extends BaseASTNode { - visibility: "public" | "private"; - isStateVar: boolean; - } - export interface TypeName extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface UserDefinedTypeName extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface Mapping extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface FunctionTypeName extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface StorageLocation extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface StateMutability extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface Block extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface Statement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface EmitStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ExpressionStatement extends BaseASTNode { - expression: ASTNode; - } - export interface IfStatement extends BaseASTNode { - trueBody: ASTNode; - falseBody: ASTNode; - } - export interface WhileStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface SimpleStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ForStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface InlineAssemblyStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface DoWhileStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ContinueStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface BreakStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ReturnStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ThrowStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface VariableDeclarationStatement extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface IdentifierList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ElementaryTypeName extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface Expression extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface PrimaryExpression extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ExpressionList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface NameValueList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface NameValue extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface FunctionCallArguments extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyBlock extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyItem extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyExpression extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyCall extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyLocalDefinition extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyAssignment extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyIdentifierOrList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyIdentifierList extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyStackAssignment extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface LabelDefinition extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblySwitch extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyCase extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyFunctionDefinition extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyFunctionReturns extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyFor extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyIf extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface AssemblyLiteral extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface SubAssembly extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface TupleExpression extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface ElementaryTypeNameExpression extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface NumberLiteral extends BaseASTNode {} // tslint:disable-line:no-empty-interface - export interface Identifier extends BaseASTNode {} // tslint:disable-line:no-empty-interface - 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 - | EmitStatement - | 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; - EmitStatement?: (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 { - tolerant?: boolean; - range?: boolean; - loc?: boolean; - } - export function parse(sourceCode: string, parserOpts: ParserOpts): ASTNode; - export function visit(ast: ASTNode, visitor: Visitor): void; -} diff --git a/packages/embark-utils/src/index.js b/packages/embark-utils/src/index.js index a814ce427..6850e01e2 100644 --- a/packages/embark-utils/src/index.js +++ b/packages/embark-utils/src/index.js @@ -48,7 +48,6 @@ const { extendZeroAddressShorthand, replaceZeroAddressShorthand } = AddressUtils import { compact, last, recursiveMerge, groupBy } from './collections'; import { prepareForCompilation } from './solidity/remapImports'; -import { removePureView } from './solidity/code'; import { File, getExternalContractUrl, Types } from './file'; function timer(ms) { @@ -320,7 +319,6 @@ const Utils = { Types, unitRegex, urlJoin, - removePureView, runCmd, escapeHtml: logUtils.escapeHtml, normalizeInput: logUtils.normalizeInput, diff --git a/packages/embark-utils/src/solidity/code.ts b/packages/embark-utils/src/solidity/code.ts deleted file mode 100644 index 9646cfbca..000000000 --- a/packages/embark-utils/src/solidity/code.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as fs from "fs-extra"; -import * as globule from "globule"; -import * as path from "path"; - -export const removePureView = (dir: string) => { - globule.find(path.join(dir, "**/*.sol")).forEach((filepath) => { - let source = fs.readFileSync(filepath, "utf-8"); - source = replacePureView(source); - fs.writeFileSync(filepath, source); - }); -}; - -export const replacePureView = (source: string) => { - return source.replace(/pure/g, "").replace(/view/g, ""); -}; diff --git a/packages/embark-utils/src/solidity/remapImports.ts b/packages/embark-utils/src/solidity/remapImports.ts index 5da350865..96b6e1d6b 100644 --- a/packages/embark-utils/src/solidity/remapImports.ts +++ b/packages/embark-utils/src/solidity/remapImports.ts @@ -3,7 +3,6 @@ import * as path from "path"; import { groupBy } from "../collections"; import { File, Types } from "../file"; import { dappPath, embarkPath, urlJoin } from "../pathUtils"; -import { removePureView, replacePureView } from "./code"; const FIND_IMPORTS_REGEX = /^import[\s]*(['"])(.*)\1;/gm; const FIND_FILE_REGEX = /import[\s]*(['"])(.*)\1;/; @@ -197,10 +196,5 @@ export const prepareForCompilation = async (file: File, isCoverage = false) => { content = await file.content; } - if (!isCoverage) { - return content; - } - - removePureView(dappPath(".embark")); - return replacePureView(content); + return content; }; diff --git a/packages/embark/package.json b/packages/embark/package.json index 3c934bf62..1ed1c1127 100644 --- a/packages/embark/package.json +++ b/packages/embark/package.json @@ -177,7 +177,6 @@ "shelljs": "0.5.3", "simples": "0.8.8", "solc": "0.5.0", - "solidity-parser-antlr": "0.4.2", "source-map-support": "0.5.9", "stream-json": "1.1.3", "string-replace-async": "1.2.1", diff --git a/yarn.lock b/yarn.lock index 58ddfc8a3..2ba6a0083 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2823,6 +2823,11 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/mocha@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.0.tgz#b3c8e69f038835db1a7fdc0b3d879fc50506e29e" + integrity sha512-YeDiSEzznwZwwp766SJ6QlrTyBYUGPSIwmREHVTmktUYiT/WADdWtpt9iH0KuUSf8lZLdI4lP0X6PBzPo5//JQ== + "@types/node-fetch@^1.6.8": version "1.6.9" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-1.6.9.tgz#a750fb0f4cf2960bf72b462e4c86908022dd69c5" @@ -2845,6 +2850,11 @@ resolved "https://registry.yarnpkg.com/@types/os-locale/-/os-locale-2.1.0.tgz#0ded736612a79e900fa76f02c6ad566d046ad17a" integrity sha512-1FI8uCzD//cYu6eDMrik91BevmE8xQLeV5Br2+dDjiluhTM5vtxTDXSaVY20rRV8V/XT6l+A1H12g5+hBtSQZg== +"@types/prettier@1.16.4": + version "1.16.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.16.4.tgz#5e5e97702cb68498aaba7349b941648daaf2385c" + integrity sha512-MG7ExKBo7AQ5UrL1awyYLNinNM/kyXgE4iP4Ul9fB+T7n768Z5Xem8IZeP6Bna0xze8gkDly49Rgge2HOEw4xA== + "@types/pretty-ms@3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@types/pretty-ms/-/pretty-ms-3.2.0.tgz#cdd35f7edac2310bbe2af86f5244625db7a101b1" @@ -6789,6 +6799,11 @@ dir-glob@^2.0.0: dependencies: path-type "^3.0.0" +dir-to-object@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-to-object/-/dir-to-object-2.0.0.tgz#29723e9bd1c3e58e4f307bd04ff634c0370c8f8a" + integrity sha512-sXs0JKIhymON7T1UZuO2Ud6VTNAx/VTBXIl4+3mjb2RgfOpt+hectX0x04YqPOPdkeOAKoJuKqwqnXXURNPNEA== + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -7095,7 +7110,7 @@ emoji-regex@^6.5.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2" integrity sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ== -emoji-regex@^7.0.1: +emoji-regex@^7.0.1, emoji-regex@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== @@ -7487,6 +7502,13 @@ espree@^4.0.0: acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" +esprima-extract-comments@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esprima-extract-comments/-/esprima-extract-comments-1.1.0.tgz#0dacab567a5900240de6d344cf18c33617becbc9" + integrity sha512-sBQUnvJwpeE9QnPrxh7dpI/dp67erYG4WXEAreAMoelPRpMR7NWb4YtwRPn9b+H1uLQKl/qS8WYmyaljTpjIsw== + dependencies: + esprima "^4.0.0" + esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -8042,6 +8064,14 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-comments@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/extract-comments/-/extract-comments-1.1.0.tgz#b90bca033a056bd69b8ba1c6b6b120fc2ee95c18" + integrity sha512-dzbZV2AdSSVW/4E7Ti5hZdHWbA+Z80RJsJhr5uiL10oyjl/gy7/o+HI1HwK4/WSZhlq4SNKU3oUzXlM13Qx02Q== + dependencies: + esprima-extract-comments "^1.1.0" + parse-code-context "^1.0.0" + extract-opts@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/extract-opts/-/extract-opts-2.2.0.tgz#1fa28eba7352c6db480f885ceb71a46810be6d7d" @@ -13562,6 +13592,11 @@ parse-asn1@^5.0.0: evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" +parse-code-context@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-code-context/-/parse-code-context-1.0.0.tgz#718c295c593d0d19a37f898473268cc75e98de1e" + integrity sha512-OZQaqKaQnR21iqhlnPfVisFjBWjhnMl5J9MgbP8xC+EwoVqbXrq78lp+9Zb3ahmLzrIX5Us/qbvBnaS3hkH6OA== + parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" @@ -14520,11 +14555,30 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +prettier-plugin-solidity@1.0.0-alpha.25: + version "1.0.0-alpha.25" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-alpha.25.tgz#45b5c7aa68f69f2aa160cd1d2590c459cd89e6d4" + integrity sha512-NsjhpK/td17apZz8Jnp5FDzIvXyrTGqKIg7PNrpLcKMi+XBDoMV/sFrWiN1VLI4oqjHnDJo/e4VhALiUCBHfBQ== + dependencies: + dir-to-object "^2.0.0" + emoji-regex "^7.0.3" + escape-string-regexp "^1.0.5" + extract-comments "^1.1.0" + prettier "^1.15.3" + semver "^5.6.0" + solidity-parser-antlr "^0.4.4" + string-width "^3.0.0" + prettier@^1.14.2: version "1.16.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.1.tgz#534c2c9d7853f8845e5e078384e71973bd74089f" integrity sha512-XXUITwIkGb3CPJ2hforHah/zTINRyie5006Jd2HKy2qz7snEJXl0KLfsJZW/wst9g6R2rFvqba3VpNYdu1hDcA== +prettier@^1.15.3: + version "1.18.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" + integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== + pretty-bytes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" @@ -16868,10 +16922,10 @@ solc@^0.4.13, solc@^0.4.25: semver "^5.3.0" yargs "^4.7.1" -solidity-parser-antlr@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.4.2.tgz#b862eba5936e7a90b4f5f1c8eb1d33fe86650f78" - integrity sha512-0OKT2YKZAqPe14HN7Nbo24hjmnyUYh92UjyZG0Zz2rpQhl/w8asX8qHb+ASSXfayQaiW8g9zGIupXEE355tOQQ== +solidity-parser-antlr@0.4.5, solidity-parser-antlr@^0.4.4: + version "0.4.5" + resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.4.5.tgz#4de1867f1a12df4553886209225bc80328aeb0c1" + integrity sha512-5GzhIs4hpY3XnGwMndgrOksD567O5WdUQPbpy2n2WA1m3algzUKYMf+Ne/QHd36TTUNvVV+iTWD5tKaCZfaOjg== sort-keys@^2.0.0: version "2.0.0"