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

Commit b31b941

Browse files
authored
Merge pull request #4363 from trufflesuite/archaeology
Bug fix: Repair compiler output from Solidity <0.4.20
2 parents 9e51fe6 + c1cb87d commit b31b941

File tree

3 files changed

+134
-2
lines changed

3 files changed

+134
-2
lines changed

packages/compile-solidity/run.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,15 @@ function processContracts({
335335
originalSourcePaths,
336336
solcVersion
337337
}) {
338-
if (!compilerOutput.contracts) return [];
338+
let { contracts } = compilerOutput;
339+
if (!contracts) return [];
340+
//HACK: versions of Solidity prior to 0.4.20 are confused by our "project:/"
341+
//prefix (or, more generally, by paths containing colons)
342+
//and put contracts in a weird form as a result. we detect
343+
//this case and repair it.
344+
contracts = repairOldContracts(contracts);
339345
return (
340-
Object.entries(compilerOutput.contracts)
346+
Object.entries(contracts)
341347
// map to [[{ source, contractName, contract }]]
342348
.map(([sourcePath, sourceContracts]) =>
343349
Object.entries(sourceContracts).map(([contractName, contract]) => ({
@@ -418,6 +424,40 @@ function processContracts({
418424
);
419425
}
420426

427+
function repairOldContracts(contracts) {
428+
const contractNames = [].concat(
429+
...Object.values(contracts).map(source => Object.keys(source))
430+
);
431+
if (contractNames.some(name => name.includes(":"))) {
432+
//if any of the "contract names" contains a colon... hack invoked!
433+
//(notionally we could always apply this hack but let's skip it most of the
434+
//time please :P )
435+
let repairedContracts = {};
436+
for (const [sourcePrefix, sourceContracts] of Object.entries(contracts)) {
437+
for (const [mixedPath, contract] of Object.entries(sourceContracts)) {
438+
let sourcePath, contractName;
439+
const lastColonIndex = mixedPath.lastIndexOf(":");
440+
if (lastColonIndex === -1) { //if there is none
441+
sourcePath = sourcePrefix;
442+
contractName = mixedPath;
443+
} else {
444+
contractName = mixedPath.slice(lastColonIndex + 1); //take the part after the final colon
445+
sourcePath = sourcePrefix + ":" + mixedPath.slice(0, lastColonIndex); //the part before the final colon
446+
}
447+
if (!repairedContracts[sourcePath]) {
448+
repairedContracts[sourcePath] = {};
449+
}
450+
repairedContracts[sourcePath][contractName] = contract;
451+
}
452+
}
453+
debug("repaired contracts: %O", repairedContracts);
454+
return repairedContracts;
455+
} else {
456+
//otherwise just return contracts as-is rather than processing
457+
return contracts;
458+
}
459+
}
460+
421461
function formatLinkReferences(linkReferences) {
422462
if (!linkReferences) {
423463
return [];
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//SPDX-License-Identifier: MIT
2+
//I've put a colon in the filename to be extra-sure
3+
//that this is indeed testing what it's supposed to
4+
//(that colons in file paths don't screw things up;
5+
//"project:/" should suffice to trigger the issue but
6+
//I want to be extra sure
7+
pragma solidity >=0.4.9 <0.4.20;
8+
9+
contract SimpleContract {
10+
uint x;
11+
12+
function SimpleContract() public {
13+
x = 7;
14+
}
15+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
const debug = require("debug")("compile:test:test_oldversions");
2+
const fs = require("fs");
3+
const path = require("path");
4+
const { Compile } = require("@truffle/compile-solidity");
5+
const CompilerSupplier = require("../compilerSupplier");
6+
const assert = require("assert");
7+
const { findOne } = require("./helpers");
8+
const workingDirectory = "/home/fakename/truffleproject";
9+
const compileOptions = {
10+
working_directory: workingDirectory,
11+
compilers: {
12+
solc: {
13+
version: "0.4.11",
14+
settings: {
15+
optimizer: {
16+
enabled: false,
17+
runs: 200
18+
}
19+
}
20+
}
21+
},
22+
quiet: true
23+
};
24+
const supplierOptions = {
25+
solcConfig: compileOptions.compilers.solc,
26+
events: {
27+
emit: () => {}
28+
}
29+
};
30+
31+
describe("Compile - solidity <0.4.20", function () {
32+
this.timeout(5000); // solc
33+
let source = null;
34+
let solc = null; // gets loaded via supplier
35+
36+
before("get solc", async function () {
37+
this.timeout(40000);
38+
39+
const supplier = new CompilerSupplier(supplierOptions);
40+
({ solc } = await supplier.load());
41+
});
42+
43+
describe("Output repair", function () {
44+
before("get code", function () {
45+
source = fs.readFileSync(
46+
path.join(__dirname, "./sources/v0.4.11/FilenameWith:Colon.sol"),
47+
"utf-8"
48+
);
49+
});
50+
51+
it("produces contract output correctly", async function () {
52+
const sourcePath = `${workingDirectory}/contracts/FilenameWith:Colon.sol`;
53+
const sources = { [sourcePath]: source };
54+
55+
const { compilations } = await Compile.sources({
56+
sources,
57+
options: compileOptions
58+
});
59+
//there should be one compilation
60+
assert.equal(compilations.length, 1);
61+
const compilation = compilations[0];
62+
//it should have contracts
63+
assert.ok(compilation.contracts);
64+
//there should be one
65+
assert.equal(compilation.contracts.length, 1);
66+
const contract = compilation.contracts[0];
67+
//it should have contract name & source set correctly;
68+
//it should have various other properties set at all
69+
assert.equal(contract.contractName, "SimpleContract");
70+
assert.equal(contract.sourcePath, sourcePath);
71+
assert.equal(contract.source, source);
72+
assert.ok(contract.bytecode);
73+
assert.ok(contract.abi);
74+
assert.ok(contract.legacyAST);
75+
});
76+
});
77+
});

0 commit comments

Comments
 (0)