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

Commit

Permalink
Merge pull request #4363 from trufflesuite/archaeology
Browse files Browse the repository at this point in the history
Bug fix: Repair compiler output from Solidity <0.4.20
  • Loading branch information
haltman-at authored Oct 12, 2021
2 parents 9e51fe6 + c1cb87d commit b31b941
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 2 deletions.
44 changes: 42 additions & 2 deletions packages/compile-solidity/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,15 @@ function processContracts({
originalSourcePaths,
solcVersion
}) {
if (!compilerOutput.contracts) return [];
let { contracts } = compilerOutput;
if (!contracts) return [];
//HACK: versions of Solidity prior to 0.4.20 are confused by our "project:/"
//prefix (or, more generally, by paths containing colons)
//and put contracts in a weird form as a result. we detect
//this case and repair it.
contracts = repairOldContracts(contracts);
return (
Object.entries(compilerOutput.contracts)
Object.entries(contracts)
// map to [[{ source, contractName, contract }]]
.map(([sourcePath, sourceContracts]) =>
Object.entries(sourceContracts).map(([contractName, contract]) => ({
Expand Down Expand Up @@ -418,6 +424,40 @@ function processContracts({
);
}

function repairOldContracts(contracts) {
const contractNames = [].concat(
...Object.values(contracts).map(source => Object.keys(source))
);
if (contractNames.some(name => name.includes(":"))) {
//if any of the "contract names" contains a colon... hack invoked!
//(notionally we could always apply this hack but let's skip it most of the
//time please :P )
let repairedContracts = {};
for (const [sourcePrefix, sourceContracts] of Object.entries(contracts)) {
for (const [mixedPath, contract] of Object.entries(sourceContracts)) {
let sourcePath, contractName;
const lastColonIndex = mixedPath.lastIndexOf(":");
if (lastColonIndex === -1) { //if there is none
sourcePath = sourcePrefix;
contractName = mixedPath;
} else {
contractName = mixedPath.slice(lastColonIndex + 1); //take the part after the final colon
sourcePath = sourcePrefix + ":" + mixedPath.slice(0, lastColonIndex); //the part before the final colon
}
if (!repairedContracts[sourcePath]) {
repairedContracts[sourcePath] = {};
}
repairedContracts[sourcePath][contractName] = contract;
}
}
debug("repaired contracts: %O", repairedContracts);
return repairedContracts;
} else {
//otherwise just return contracts as-is rather than processing
return contracts;
}
}

function formatLinkReferences(linkReferences) {
if (!linkReferences) {
return [];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//SPDX-License-Identifier: MIT
//I've put a colon in the filename to be extra-sure
//that this is indeed testing what it's supposed to
//(that colons in file paths don't screw things up;
//"project:/" should suffice to trigger the issue but
//I want to be extra sure
pragma solidity >=0.4.9 <0.4.20;

contract SimpleContract {
uint x;

function SimpleContract() public {
x = 7;
}
}
77 changes: 77 additions & 0 deletions packages/compile-solidity/test/test_oldversions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const debug = require("debug")("compile:test:test_oldversions");
const fs = require("fs");
const path = require("path");
const { Compile } = require("@truffle/compile-solidity");
const CompilerSupplier = require("../compilerSupplier");
const assert = require("assert");
const { findOne } = require("./helpers");
const workingDirectory = "/home/fakename/truffleproject";
const compileOptions = {
working_directory: workingDirectory,
compilers: {
solc: {
version: "0.4.11",
settings: {
optimizer: {
enabled: false,
runs: 200
}
}
}
},
quiet: true
};
const supplierOptions = {
solcConfig: compileOptions.compilers.solc,
events: {
emit: () => {}
}
};

describe("Compile - solidity <0.4.20", function () {
this.timeout(5000); // solc
let source = null;
let solc = null; // gets loaded via supplier

before("get solc", async function () {
this.timeout(40000);

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

describe("Output repair", function () {
before("get code", function () {
source = fs.readFileSync(
path.join(__dirname, "./sources/v0.4.11/FilenameWith:Colon.sol"),
"utf-8"
);
});

it("produces contract output correctly", async function () {
const sourcePath = `${workingDirectory}/contracts/FilenameWith:Colon.sol`;
const sources = { [sourcePath]: source };

const { compilations } = await Compile.sources({
sources,
options: compileOptions
});
//there should be one compilation
assert.equal(compilations.length, 1);
const compilation = compilations[0];
//it should have contracts
assert.ok(compilation.contracts);
//there should be one
assert.equal(compilation.contracts.length, 1);
const contract = compilation.contracts[0];
//it should have contract name & source set correctly;
//it should have various other properties set at all
assert.equal(contract.contractName, "SimpleContract");
assert.equal(contract.sourcePath, sourcePath);
assert.equal(contract.source, source);
assert.ok(contract.bytecode);
assert.ok(contract.abi);
assert.ok(contract.legacyAST);
});
});
});

0 comments on commit b31b941

Please sign in to comment.