Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Enhancement: Enable expedient solc-js parsing when using docker or native solc #2067

Merged
merged 27 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2efb227
throw away nonImportErrors in parser.js (caught elsewhere)
CruzMolina May 31, 2019
f4eb56e
only return matches & filter out non-matches (parser.js)
CruzMolina May 31, 2019
4a67417
pass options to resolveAllSources & getImports (profiler.js)
CruzMolina May 31, 2019
799e465
add setSolcParser method to handle passed parser config setting (prof…
CruzMolina May 31, 2019
338ca0c
make getImports async, destructure passed config options, & implement…
CruzMolina May 31, 2019
a22e8c6
await getImports (profiler.js)
CruzMolina May 31, 2019
7090f36
fix wonky async.whilst error swallowing! (profiler.js)
CruzMolina May 31, 2019
88a12d1
add profiler tests for solcjs parsing usage (test_profiler.js)
CruzMolina May 31, 2019
bd0b2db
add test sources for native solcjs parsing test
CruzMolina May 31, 2019
ffa05b8
update test/test_parser.js
CruzMolina May 31, 2019
b316710
refactor & add checkParser method to compilerSupplier (compilerSuppli…
CruzMolina Jun 8, 2019
d5ec590
add loadParserSolc method to check parser, normalize solcVersion, & r…
CruzMolina Jun 8, 2019
8170b81
integrate loadParserSolc into CompilerSupplier.load (compilerSupplier…
CruzMolina Jun 8, 2019
d692053
refactor profiler to use CompilerSupplier parsedSolc for resolving im…
CruzMolina Jun 8, 2019
e01caa0
rm setSolcParser method (profiler.js)
CruzMolina Jun 8, 2019
c56f8d3
update test_ordering.js
CruzMolina Jun 8, 2019
b0e5e70
update test_parser.js
CruzMolina Jun 8, 2019
fca0bc7
call solc.version in loadParserSolc
CruzMolina Jun 8, 2019
3c5c141
update compilerSupplier/index.js tests
CruzMolina Jun 10, 2019
9f64064
rename & update JSparser tests
CruzMolina Jun 10, 2019
66a17c8
Merge branch 'develop' of https://github.com/trufflesuite/truffle int…
CruzMolina Jul 5, 2019
e133205
integrate CompilerSupplier breaking change into truffle-compile (run.js)
CruzMolina Jul 5, 2019
e32c0fa
make .load() resolve an object instead of an array
CruzMolina Jul 5, 2019
853c17f
align JSparser tests with legacy truffle-compile (test_JSparser.js)
CruzMolina Jul 5, 2019
cec3b79
keep getImports sync (profiler/getImports)
CruzMolina Jul 5, 2019
06991b4
add 'native' keyword to skip docker/native tests in local dev env (te…
CruzMolina Jul 5, 2019
512a5fb
up JSparser timeouts for Travis (test_JSparser)
CruzMolina Jul 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion packages/truffle-compile/compilerSupplier/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ class CompilerSupplier {
if (strategy) {
try {
const solc = await strategy.load(userSpecification);
resolve(solc);
const parserSolc = await this.loadParserSolc(
this.config.parser,
solc
);
resolve({ solc, parserSolc });
} catch (error) {
reject(error);
}
Expand All @@ -74,6 +78,25 @@ class CompilerSupplier {
});
}

async loadParserSolc(parser, solc) {
if (parser) {
this.checkParser(parser);
const solcVersion = solc.version();
const normalizedSolcVersion = semver.coerce(solcVersion).version;
return await new VersionRange({ version: normalizedSolcVersion }).load(
normalizedSolcVersion
);
}
return false;
}

checkParser(parser) {
if (parser !== "solcjs")
throw new Error(
`Unsupported parser "${parser}" found in truffle-config.js`
);
}

fileExists(localPath) {
return fs.existsSync(localPath) || path.isAbsolute(localPath);
}
Expand Down
22 changes: 3 additions & 19 deletions packages/truffle-compile/parser.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const debug = require("debug")("compile:parser"); // eslint-disable-line no-unused-vars
const CompileError = require("./compileerror");

// Warning issued by a pre-release compiler version, ignored by this component.
const preReleaseCompilerWarning =
Expand All @@ -17,9 +16,6 @@ module.exports = {
// statement right on the end; just to ensure it will error and we can parse
// the imports speedily without doing extra work.

// Helper to detect import errors with an easy regex.
const importErrorKey = "not found: File";

// Inject failing import.
const failingImportFileName = "__Truffle__NotFound.sol";

Expand Down Expand Up @@ -50,19 +46,6 @@ module.exports = {
({ message }) => !message.includes(preReleaseCompilerWarning)
);

// If the import error key is not found, we must not have an import error.
// This means we have a *different* parsing error which we should show to the user.
// Note: solc can return multiple parsing errors at once.
const nonImportErrors = errors.filter(
({ formattedMessage }) => !formattedMessage.includes(importErrorKey)
);

// Should we try to throw more than one? (aside; we didn't before)
if (nonImportErrors.length > 0) {
throw new CompileError(nonImportErrors[0].formattedMessage);
}

// Now, all errors must be import errors.
// Filter out our forced import, then get the import paths of the rest.
const imports = errors
.filter(({ message }) => !message.includes(failingImportFileName))
Expand All @@ -72,8 +55,9 @@ module.exports = {
);

// Return the item between the quotes.
return matches[2];
});
if (matches) return matches[2];
})
.filter(match => match !== undefined);

return imports;
}
Expand Down
16 changes: 10 additions & 6 deletions packages/truffle-compile/profiler/getImports.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ const { isExplicitlyRelative } = require("./isExplicitlyRelative");
const Parser = require("../parser");
const path = require("path");

const getImports = (file, { body, source }, solc) => {
const getImports = (file, { body, source }, solc, parserSolc) => {
let imports;

// No imports in vyper!
if (path.extname(file) === ".vy") return [];

const imports = Parser.parseImports(body, solc);
if (parserSolc) imports = Parser.parseImports(body, parserSolc);
else imports = Parser.parseImports(body, solc);

// Convert explicitly relative dependencies of modules back into module paths.
return imports.map(dependencyPath =>
isExplicitlyRelative(dependencyPath)
? source.resolve_dependency_path(file, dependencyPath)
: dependencyPath
return imports.map(
dependencyPath =>
isExplicitlyRelative(dependencyPath)
? source.resolve_dependency_path(file, dependencyPath)
: dependencyPath
);
};

Expand Down
22 changes: 16 additions & 6 deletions packages/truffle-compile/profiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,14 @@ module.exports = {
const supplier = new CompilerSupplier(options.compilers.solc);
return supplier.load();
})
.then(async solc => {
.then(async ({ solc, parserSolc }) => {
// Get all the source code
const resolved = await this.resolveAllSources(resolver, allPaths, solc);
const resolved = await this.resolveAllSources(
resolver,
allPaths,
solc,
parserSolc
);
// Generate hash of all sources including external packages - passed to solc inputs.
const resolvedPaths = Object.keys(resolved);
resolvedPaths.forEach(file => {
Expand Down Expand Up @@ -132,7 +137,12 @@ module.exports = {

let imports;
try {
imports = getImports(currentFile, resolved[currentFile], solc);
imports = await getImports(
currentFile,
resolved[currentFile],
solc,
parserSolc
);
} catch (err) {
err.message = `Error parsing ${currentFile}: ${err.message}`;
throw err;
Expand All @@ -156,7 +166,7 @@ module.exports = {

// Resolves sources in several async passes. For each resolved set it detects unknown
// imports from external packages and adds them to the set of files to resolve.
async resolveAllSources(resolver, initialPaths, solc) {
async resolveAllSources(resolver, initialPaths, solc, parserSolc) {
const mapping = {};
const allPaths = initialPaths.slice();

Expand Down Expand Up @@ -190,7 +200,7 @@ module.exports = {

// Resolve everything known and add it to the map, then inspect each file's
// imports and add those to the list of paths to resolve if we don't have it.
return Promise.all(promises).then(results => {
return Promise.all(promises).then(async results => {
// Generate the sources mapping
results.forEach(item => (mapping[item.file] = Object.assign({}, item)));

Expand All @@ -201,7 +211,7 @@ module.exports = {
// Inspect the imports
let imports;
try {
imports = getImports(result.file, result, solc);
imports = await getImports(result.file, result, solc, parserSolc);
} catch (err) {
if (err.message.includes("requires different compiler version")) {
const contractSolcPragma = err.message.match(
Expand Down
2 changes: 1 addition & 1 deletion packages/truffle-compile/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ function prepareOutputSelection({ targets = [] }) {
async function invokeCompiler({ compilerInput, options }) {
// load solc
const supplier = new CompilerSupplier(options.compilers.solc);
const solc = await supplier.load();
const { solc } = await supplier.load();
const solcVersion = solc.version();

// perform compilation
Expand Down
24 changes: 12 additions & 12 deletions packages/truffle-compile/test/compilerSupplier/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ describe("CompilerSupplier", () => {
it("calls load on the Docker strategy", done => {
supplier
.load()
.then(result => {
assert(result === "called Docker");
.then(({ solc }) => {
assert(solc === "called Docker");
done();
})
.catch(() => {
Expand All @@ -53,8 +53,8 @@ describe("CompilerSupplier", () => {
it("calls load on the Native strategy", done => {
supplier
.load()
.then(result => {
assert(result === "called Native");
.then(({ solc }) => {
assert(solc === "called Native");
done();
})
.catch(() => {
Expand All @@ -78,8 +78,8 @@ describe("CompilerSupplier", () => {
it("calls load on the VersionRange strategy", done => {
supplier
.load()
.then(result => {
assert(result === "called VersionRange");
.then(({ solc }) => {
assert(solc === "called VersionRange");
done();
})
.catch(() => {
Expand All @@ -104,8 +104,8 @@ describe("CompilerSupplier", () => {
it("calls load on the VersionRange strategy", done => {
supplier
.load()
.then(result => {
assert(result === "called VersionRange");
.then(({ solc }) => {
assert(solc === "called VersionRange");
done();
})
.catch(() => {
Expand Down Expand Up @@ -178,8 +178,8 @@ describe("CompilerSupplier", () => {
it("calls load on the VersionRange strategy", done => {
supplier
.load()
.then(result => {
assert(result === "called VersionRange");
.then(({ solc }) => {
assert(solc === "called VersionRange");
done();
})
.catch(() => {
Expand All @@ -204,8 +204,8 @@ describe("CompilerSupplier", () => {
it("calls load on the Local strategy", done => {
supplier
.load()
.then(result => {
assert(result === "called Local");
.then(({ solc }) => {
assert(solc === "called Local");
done();
})
.catch(() => {
Expand Down
19 changes: 19 additions & 0 deletions packages/truffle-compile/test/sources/v0.5.x/ComplexOrdered.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pragma solidity ^0.5.0;

import "./InheritB.sol";

// These are out of alphabetic order
// Solc will alphabetize them, we should restore source-order.
contract InheritA is InheritB {
event LogB();
event LogA();
constructor() public {}
}

contract ComplexOrdered is InheritA {
function theFirst() public pure {}
function second() public pure {}
function andThird() public pure {}
}

contract Empty {}
9 changes: 9 additions & 0 deletions packages/truffle-compile/test/sources/v0.5.x/InheritB.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity ^0.5.0;

// These are out of alphabetic order
// Solc will alphabetize them, we should restore source-order.
contract InheritB {
event LogD();
event LogC();
constructor() public {}
}
93 changes: 93 additions & 0 deletions packages/truffle-compile/test/test_JSparser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const path = require("path");
const assert = require("assert");
const Resolver = require("truffle-resolver");
const compile = require("../index");

describe("JSparser", () => {
const options = {
compilers: {
solc: {
parser: "solcjs",
settings: {
optimizer: {
enabled: false,
runs: 200
}
}
}
},
quiet: true,
contracts_build_directory: path.join(__dirname, "./build"),
working_directory: __dirname
};

it("resolves imports quickly when using solcjs parser instead of docker [ @native ]", done => {
options.compilers.solc.version = "0.4.22";
options.compilers.solc.docker = true;
options.contracts_directory = path.join(__dirname, "./sources/v0.4.x");

const paths = [];
paths.push(path.join(__dirname, "./sources/v0.4.x/ComplexOrdered.sol"));
paths.push(path.join(__dirname, "./sources/v0.4.x/InheritB.sol"));

options.paths = paths;
options.resolver = new Resolver(options);

compile.with_dependencies(options, (err, result) => {
if (err) return done(err);

// This contract imports / inherits
assert(
result["ComplexOrdered"].contract_name === "ComplexOrdered",
"Should have compiled"
);
done();
});
}).timeout(5000);

it("resolves imports quickly when using solcjs parser instead of native solc", done => {
options.compilers.solc.version = "native";
delete options.compilers.solc.docker;
options.contracts_directory = path.join(__dirname, "./sources/v0.5.x");

const paths = [];
paths.push(path.join(__dirname, "./sources/v0.5.x/ComplexOrdered.sol"));
paths.push(path.join(__dirname, "./sources/v0.5.x/InheritB.sol"));

options.paths = paths;
options.resolver = new Resolver(options);

compile.with_dependencies(options, (err, result) => {
if (err) return done(err);

// This contract imports / inherits
assert(
result["ComplexOrdered"].contract_name === "ComplexOrdered",
"Should have compiled"
);
done();
});
}).timeout(5000);

it("properly throws when passed an invalid parser value", done => {
options.compilers.solc.parser = "badParser";
options.contracts_directory = path.join(__dirname, "./sources/v0.5.x");

const paths = [];
paths.push(path.join(__dirname, "./sources/v0.5.x/ComplexOrdered.sol"));
paths.push(path.join(__dirname, "./sources/v0.5.x/InheritB.sol"));

options.paths = paths;
options.resolver = new Resolver(options);

compile.with_dependencies(options, (err, result) => {
if (result) {
assert(false, "should have failed!");
done();
}

assert(err.message.match(/(Unsupported parser)/));
done();
});
}).timeout(3000);
});
2 changes: 1 addition & 1 deletion packages/truffle-compile/test/test_ordering.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("Compile - solidity ^0.4.0", function() {
this.timeout(40000);

const supplier = new CompilerSupplier(compileOptions.compilers.solc);
solc = await supplier.load();
({ solc } = await supplier.load());
});

describe("ABI Ordering", function() {
Expand Down
Loading