Run lint:fix (#309)

This commit is contained in:
PG Herveou
2025-05-08 12:36:11 +02:00
committed by GitHub
parent 11d47d74ac
commit b8f3073e29
14 changed files with 1127 additions and 1029 deletions
+1 -1
View File
@@ -2,5 +2,5 @@
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": false
"singleQuote": true
}
@@ -1,19 +1,33 @@
import * as path from 'path';
import * as path from 'path'
const outputDir = 'artifacts';
const binExtension = ':C.pvm';
const asmExtension = ':C.pvmasm';
const llvmExtension = '.ll';
const contractSolFilename = 'contract.sol';
const contractYulFilename = 'contract.yul';
const contractOptimizedLLVMFilename = contractSolFilename + '.C.optimized';
const contractUnoptimizedLLVMFilename = contractSolFilename + '.C.unoptimized';
const pathToOutputDir = path.join(__dirname, '..', outputDir);
const pathToContracts = path.join(__dirname, '..', 'src', 'contracts');
const pathToBasicYulContract = path.join(pathToContracts, 'yul', contractYulFilename);
const pathToBasicSolContract = path.join(pathToContracts, 'solidity', contractSolFilename);
const pathToSolBinOutputFile = path.join(pathToOutputDir, contractSolFilename + binExtension);
const pathToSolAsmOutputFile = path.join(pathToOutputDir, contractSolFilename + asmExtension);
const outputDir = 'artifacts'
const binExtension = ':C.pvm'
const asmExtension = ':C.pvmasm'
const llvmExtension = '.ll'
const contractSolFilename = 'contract.sol'
const contractYulFilename = 'contract.yul'
const contractOptimizedLLVMFilename = contractSolFilename + '.C.optimized'
const contractUnoptimizedLLVMFilename = contractSolFilename + '.C.unoptimized'
const pathToOutputDir = path.join(__dirname, '..', outputDir)
const pathToContracts = path.join(__dirname, '..', 'src', 'contracts')
const pathToBasicYulContract = path.join(
pathToContracts,
'yul',
contractYulFilename
)
const pathToBasicSolContract = path.join(
pathToContracts,
'solidity',
contractSolFilename
)
const pathToSolBinOutputFile = path.join(
pathToOutputDir,
contractSolFilename + binExtension
)
const pathToSolAsmOutputFile = path.join(
pathToOutputDir,
contractSolFilename + asmExtension
)
export const paths = {
outputDir: outputDir,
@@ -30,4 +44,4 @@ export const paths = {
pathToBasicYulContract: pathToBasicYulContract,
pathToSolBinOutputFile: pathToSolBinOutputFile,
pathToSolAsmOutputFile: pathToSolAsmOutputFile,
};
}
@@ -1,44 +1,51 @@
import * as shell from 'shelljs';
import * as fs from 'fs';
import { spawnSync } from 'child_process';
import * as shell from 'shelljs'
import * as fs from 'fs'
import { spawnSync } from 'child_process'
interface CommandResult {
output: string;
exitCode: number;
output: string
exitCode: number
}
export const executeCommand = (command: string, stdin?: string): CommandResult => {
if (stdin) {
const process = spawnSync(command, [], {
input: stdin,
shell: true,
encoding: 'utf8',
maxBuffer: 30 * 1024 * 1024
});
export const executeCommand = (
command: string,
stdin?: string
): CommandResult => {
if (stdin) {
const process = spawnSync(command, [], {
input: stdin,
shell: true,
encoding: 'utf8',
maxBuffer: 30 * 1024 * 1024,
})
return {
exitCode: process.status || 0,
output: (process.stdout || process.stderr || '').toString()
};
}
const result = shell.exec(command, { silent: true, async: false });
return {
exitCode: result.code,
output: result.stdout || result.stderr || ''
};
};
exitCode: process.status || 0,
output: (process.stdout || process.stderr || '').toString(),
}
}
const result = shell.exec(command, { silent: true, async: false })
return {
exitCode: result.code,
output: result.stdout || result.stderr || '',
}
}
export const isFolderExist = (folder: string): boolean => {
return shell.test('-d', folder);
};
return shell.test('-d', folder)
}
export const isFileExist = (pathToFileDir: string, fileName: string, fileExtension: string): boolean => {
return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension);
};
export const isFileExist = (
pathToFileDir: string,
fileName: string,
fileExtension: string
): boolean => {
return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension)
}
export const isFileEmpty = (file: string): boolean => {
if (fs.existsSync(file)) {
return (fs.readFileSync(file).length === 0);
}
};
if (fs.existsSync(file)) {
return fs.readFileSync(file).length === 0
}
}
@@ -1,43 +1,44 @@
import {executeCommand} from "../src/helper";
import { paths } from '../src/entities';
import { executeCommand } from '../src/helper'
import { paths } from '../src/entities'
//id1746
describe("Run with --asm by default", () => {
const command = `resolc ${paths.pathToBasicSolContract} --asm`;
const result = executeCommand(command);
const commandInvalid = 'resolc --asm';
const resultInvalid = executeCommand(commandInvalid);
describe('Run with --asm by default', () => {
const command = `resolc ${paths.pathToBasicSolContract} --asm`
const result = executeCommand(command)
const commandInvalid = 'resolc --asm'
const resultInvalid = executeCommand(commandInvalid)
it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it('Valid command exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it("--asm output is presented", () => {
const expectedPatterns = [/(deploy)/i, /(call)/i, /(seal_return)/i];
it('--asm output is presented', () => {
const expectedPatterns = [/(deploy)/i, /(call)/i, /(seal_return)/i]
for (const pattern of expectedPatterns) {
expect(result.output).toMatch(pattern);
expect(result.output).toMatch(pattern)
}
});
})
it("solc exit code == resolc exit code", () => {
const command = `solc ${paths.pathToBasicSolContract} --asm`;
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
it('solc exit code == resolc exit code', () => {
const command = `solc ${paths.pathToBasicSolContract} --asm`
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(result.exitCode)
})
it("run invalid: resolc --asm", () => {
expect(resultInvalid.output).toMatch(/(No input sources specified|Compilation aborted)/i);
});
it("Invalid command exit code = 1", () => {
expect(resultInvalid.exitCode).toBe(1);
});
it('run invalid: resolc --asm', () => {
expect(resultInvalid.output).toMatch(
/(No input sources specified|Compilation aborted)/i
)
})
it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --asm';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
});
});
it('Invalid command exit code = 1', () => {
expect(resultInvalid.exitCode).toBe(1)
})
it('Invalid solc exit code == Invalid resolc exit code', () => {
const command = 'solc --asm'
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
})
})
@@ -1,191 +1,241 @@
import { executeCommand, isFolderExist, isFileExist, isFileEmpty } from "../src/helper";
import { paths } from '../src/entities';
import * as shell from 'shelljs';
import * as path from 'path';
import {
executeCommand,
isFolderExist,
isFileExist,
isFileEmpty,
} from '../src/helper'
import { paths } from '../src/entities'
import * as shell from 'shelljs'
import * as path from 'path'
//id1762
describe("Run resolc without any options", () => {
const command = 'resolc';
const result = executeCommand(command);
describe('Run resolc without any options', () => {
const command = 'resolc'
const result = executeCommand(command)
it("Info with help is presented", () => {
expect(result.output).toMatch(/(Usage: resolc)/i);
});
it('Info with help is presented', () => {
expect(result.output).toMatch(/(Usage: resolc)/i)
})
it("Exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("solc exit code == resolc exit code", () => {
const command = 'solc';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
it('Exit code = 1', () => {
expect(result.exitCode).toBe(1)
})
it('solc exit code == resolc exit code', () => {
const command = 'solc'
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
//#1713
describe("Default run a command from the help", () => {
describe('Default run a command from the help', () => {
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --output-dir "${paths.pathToOutputDir}"` // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command)
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command);
it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i);
});
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
xit("Output file is created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
});
it("the output file is not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
});
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
});
});
it('Compiler run successful', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
xit('Output file is created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.binExtension
)
).toBe(true)
})
it('the output file is not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
})
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i)
})
})
//#1818
describe("Default run a command from the help", () => {
describe('Default run a command from the help', () => {
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"` // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command)
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command);
it('Compiler run successful', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
xit('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.binExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.asmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i)
})
})
it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i);
});
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
xit("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
});
});
describe('Run resolc with source debug information', () => {
const commands = [
`resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
] // potential issue on resolc with full path on Windows cmd`;
describe("Run resolc with source debug information", () => {
const commands = [
`resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) {
const command = commands[idx]
const result = executeCommand(command)
for (var idx in commands) {
const command = commands[idx];
const result = executeCommand(command);
it('Compiler run successful', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
it('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.binExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.asmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i)
})
}
})
it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i);
});
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
it("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
});
}
});
describe('Run resolc with source debug information, check LLVM debug-info', () => {
const commands = [
`resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
] // potential issue on resolc with full path on Windows cmd`;
describe("Run resolc with source debug information, check LLVM debug-info", () => {
const commands = [
`resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) {
const command = commands[idx]
const result = executeCommand(command)
for (var idx in commands) {
const command = commands[idx];
const result = executeCommand(command);
it('Compiler run successful', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
it('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractOptimizedLLVMFilename,
paths.llvmExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractUnoptimizedLLVMFilename,
paths.llvmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i)
})
}
})
it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i);
});
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
it("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractOptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractUnoptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
});
}
});
describe('Standard JSON compilation with path options', () => {
const contractsDir = path.join(shell.tempdir(), 'contracts-test')
const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json')
beforeAll(() => {
shell.mkdir('-p', contractsDir)
const input = JSON.parse(shell.cat(inputFile).toString())
describe("Standard JSON compilation with path options", () => {
const contractsDir = path.join(shell.tempdir(), 'contracts-test');
const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json');
Object.entries(input.sources).forEach(
([sourcePath, source]: [string, any]) => {
const filePath = path.join(contractsDir, sourcePath)
shell.mkdir('-p', path.dirname(filePath))
shell.ShellString(source.content).to(filePath)
}
)
})
afterAll(() => {
shell.rm('-rf', contractsDir)
})
describe('Output with all path options', () => {
let result: { exitCode: number; output: string }
beforeAll(() => {
shell.mkdir('-p', contractsDir);
const tempInputFile = path.join(contractsDir, 'temp-input.json')
shell.cp(inputFile, tempInputFile)
const inputContent = shell.cat(inputFile).toString()
const input = JSON.parse(shell.cat(inputFile).toString());
const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"`
Object.entries(input.sources).forEach(([sourcePath, source]: [string, any]) => {
const filePath = path.join(contractsDir, sourcePath);
shell.mkdir('-p', path.dirname(filePath));
shell.ShellString(source.content).to(filePath);
});
});
result = executeCommand(command, inputContent)
afterAll(() => {
shell.rm('-rf', contractsDir);
});
shell.rm(tempInputFile)
})
describe("Output with all path options", () => {
let result: { exitCode: number; output: string };
beforeAll(() => {
const tempInputFile = path.join(contractsDir, 'temp-input.json');
shell.cp(inputFile, tempInputFile);
const inputContent = shell.cat(inputFile).toString();
const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"`;
result = executeCommand(command, inputContent);
shell.rm(tempInputFile);
});
it("Compiler run successful without emiting warnings", () => {
const parsedResults = JSON.parse(result.output)
expect(parsedResults.errors.filter((error: { type: string; }) => error.type != 'Warning')).toEqual([]);
});
});
});
it('Compiler run successful without emiting warnings', () => {
const parsedResults = JSON.parse(result.output)
expect(
parsedResults.errors.filter(
(error: { type: string }) => error.type != 'Warning'
)
).toEqual([])
})
})
})
@@ -1,40 +1,39 @@
import {executeCommand} from "../src/helper";
import { paths } from '../src/entities';
import { executeCommand } from '../src/helper'
import { paths } from '../src/entities'
//id1743
describe("Run with --yul by default", () => {
const command = `resolc ${paths.pathToBasicYulContract} --yul`;
const result = executeCommand(command);
const commandInvalid = 'resolc --yul';
const resultInvalid = executeCommand(commandInvalid);
describe('Run with --yul by default', () => {
const command = `resolc ${paths.pathToBasicYulContract} --yul`
const result = executeCommand(command)
const commandInvalid = 'resolc --yul'
const resultInvalid = executeCommand(commandInvalid)
it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it('Valid command exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it("--yul output is presented", () => {
expect(result.output).toMatch(/(Compiler run successful)/i);
expect(result.output).toMatch(/(No output requested)/i);
});
it('--yul output is presented', () => {
expect(result.output).toMatch(/(Compiler run successful)/i)
expect(result.output).toMatch(/(No output requested)/i)
})
xit('solc exit code == resolc exit code', () => {
// unknown solc issue for datatype of the contract
const command = `solc ${paths.pathToBasicSolContract} --yul`
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(result.exitCode)
})
xit("solc exit code == resolc exit code", () => { // unknown solc issue for datatype of the contract
const command = `solc ${paths.pathToBasicSolContract} --yul`;
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
it('run invalid: resolc --yul', () => {
expect(resultInvalid.output).toMatch(/(The input file is missing)/i)
})
it('Invalid command exit code = 1', () => {
expect(resultInvalid.exitCode).toBe(1)
})
it("run invalid: resolc --yul", () => {
expect(resultInvalid.output).toMatch(/(The input file is missing)/i);
});
it("Invalid command exit code = 1", () => {
expect(resultInvalid.exitCode).toBe(1);
});
it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --yul';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
});
});
it('Invalid solc exit code == Invalid resolc exit code', () => {
const command = 'solc --yul'
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
})
})
+159 -124
View File
@@ -1,153 +1,188 @@
import { executeCommand } from "../src/helper";
import { paths } from '../src/entities';
import { executeCommand } from '../src/helper'
import { paths } from '../src/entities'
describe('Set of --combined-json tests', () => {
const zksolcCommand = 'zksolc'
const solcCommand = 'solc'
const json_args: string[] = [
`abi`,
`hashes`,
`metadata`,
`devdoc`,
`userdoc`,
`storage-layout`,
`ast`,
`asm`,
`bin`,
`bin-runtime`,
]
describe("Set of --combined-json tests", () => {
const zksolcCommand = 'zksolc';
const solcCommand = 'solc';
const json_args: string[] = [`abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`];
//id1742:I
describe(`Run ${zksolcCommand} with just --combined-json`, () => {
const args = [`--combined-json`]
const result = executeCommand(zksolcCommand, args)
//id1742:I
describe(`Run ${zksolcCommand} with just --combined-json`, () => {
const args = [`--combined-json`];
const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => {
expect(result.exitCode).toBe(1)
})
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it('--combined-json error is presented', () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i)
})
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i);
});
it('solc exit code == zksolc exit code', () => {
const solcResult = executeCommand(solcCommand, args)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
//id1742:II
describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`]
const result = executeCommand(zksolcCommand, args)
//id1742:II
describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`];
const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => {
expect(result.exitCode).toBe(1)
})
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it('--combined-json error is presented', () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i)
})
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i);
});
it('solc exit code == zksolc exit code', () => {
const solcResult = executeCommand(solcCommand, args)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
//id1742:III
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and ARG: ${json_args[i]}`, () => {
const args = [
`${paths.pathToBasicSolContract}`,
`--combined-json`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1742:III
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and ARG: ${json_args[i]}`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it('--combined-json error is presented', () => {
expect(result.output).toMatch(/(contracts)/i)
})
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(contracts)/i);
});
it('solc exit code == zksolc exit code', () => {
const solcResult = executeCommand(solcCommand, args)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1829:I
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and wrong ARG: --${json_args[i]}`, () => {
const args = [
`${paths.pathToBasicSolContract}`,
`--combined-json`,
`--${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1829:I
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and wrong ARG: --${json_args[i]}`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `--${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => {
expect(result.exitCode).toBe(1)
})
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it('--combined-json error is presented', () => {
expect(result.output).toMatch(/(Invalid option|error)/i)
})
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(Invalid option|error)/i);
});
it('solc exit code == zksolc exit code', () => {
const solcResult = executeCommand(solcCommand, args)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1829:II
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and multiple ARG: ${json_args[i]} ${json_args[i]}`, () => {
const args = [
`${paths.pathToBasicSolContract}`,
`--combined-json`,
`${json_args[i]}`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1829:II
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and multiple ARG: ${json_args[i]} ${json_args[i]}`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
xit('Valid command exit code = 1', () => {
expect(result.exitCode).toBe(1)
})
xit("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it('--combined-json error is presented', () => {
expect(result.output).toMatch(
/(No such file or directory|cannot find the file specified)/i
) // Hopefully we should have more precise message here!
})
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(No such file or directory|cannot find the file specified)/i); // Hopefully we should have more precise message here!
});
xit('solc exit code == zksolc exit code', () => {
const solcResult = executeCommand(solcCommand, args)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
xit("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1829:III
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, and multiple (--combined-json ${json_args[i]})`, () => {
const args = [
`${paths.pathToBasicSolContract}`,
`--combined-json`,
`${json_args[i]}`,
`--combined-json`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1829:III
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, and multiple (--combined-json ${json_args[i]})`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => {
expect(result.exitCode).toBe(1)
})
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it('--combined-json error is presented', () => {
expect(result.output).toMatch(/(cannot be used multiple times)/i)
})
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(cannot be used multiple times)/i);
});
it('solc exit code == zksolc exit code', () => {
const solcResult = executeCommand(solcCommand, args)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1830
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Yul, and --combined-json ${json_args[i]}`, () => {
const args = [
`${paths.pathToBasicYulContract}`,
`--combined-json`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1830
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Yul, and --combined-json ${json_args[i]}`, () => {
const args = [`${paths.pathToBasicYulContract}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => {
expect(result.exitCode).toBe(1)
})
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it('--combined-json error is presented', () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i)
})
asd
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i);
});
asd
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
});
it('solc exit code == zksolc exit code', () => {
const solcResult = executeCommand(solcCommand, args)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
})
+4 -4
View File
@@ -4,8 +4,8 @@ import tseslint from 'typescript-eslint'
/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ['**/*.{mjs,ts}'] },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{ files: ['**/*.{mjs,ts}'] },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
]
+106 -110
View File
@@ -1,142 +1,138 @@
import { expect } from "chai";
import { compile } from "../examples/node/revive.js";
import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import { expect } from 'chai'
import { compile } from '../examples/node/revive.js'
import { fileURLToPath } from 'url'
import path from 'path'
import fs from 'fs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`);
return JSON.parse(fs.readFileSync(fixturePath, "utf-8"));
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`)
return JSON.parse(fs.readFileSync(fixturePath, 'utf-8'))
}
describe("Compile Function Tests", function () {
it("should successfully compile valid Solidity code", async function () {
const standardInput = loadFixture("storage.json");
describe('Compile Function Tests', function () {
it('should successfully compile valid Solidity code', async function () {
const standardInput = loadFixture('storage.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/storage.sol"]).to.have.property(
"Storage",
);
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property(
"abi",
);
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property(
"evm",
);
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(output.contracts['fixtures/storage.sol']).to.have.property('Storage')
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property(
'abi'
)
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property(
'evm'
)
expect(
output.contracts["fixtures/storage.sol"].Storage.evm,
).to.have.property("bytecode");
});
output.contracts['fixtures/storage.sol'].Storage.evm
).to.have.property('bytecode')
})
if (typeof globalThis.Bun == "undefined") {
if (typeof globalThis.Bun == 'undefined') {
// Running this test with Bun on a Linux host causes:
// RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)')
// Once this issue is resolved, the test will be re-enabled.
it("should successfully compile large Solidity code", async function () {
const standardInput = loadFixture("token.json");
it('should successfully compile large Solidity code', async function () {
const standardInput = loadFixture('token.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/token.sol"]).to.have.property(
"MyToken",
);
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property(
"abi",
);
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property(
"evm",
);
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(output.contracts['fixtures/token.sol']).to.have.property('MyToken')
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property(
'abi'
)
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property(
'evm'
)
expect(
output.contracts["fixtures/token.sol"].MyToken.evm,
).to.have.property("bytecode");
});
output.contracts['fixtures/token.sol'].MyToken.evm
).to.have.property('bytecode')
})
it("should successfully compile a valid Solidity contract that instantiates the token contracts", async function () {
const standardInput = loadFixture("instantiate_tokens.json");
it('should successfully compile a valid Solidity contract that instantiates the token contracts', async function () {
const standardInput = loadFixture('instantiate_tokens.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(
output.contracts["fixtures/instantiate_tokens.sol"],
).to.have.property("TokensFactory");
output.contracts['fixtures/instantiate_tokens.sol']
).to.have.property('TokensFactory')
expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory,
).to.have.property("abi");
output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property('abi')
expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory,
).to.have.property("evm");
output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property('evm')
expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory.evm,
).to.have.property("bytecode");
});
output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory.evm
).to.have.property('bytecode')
})
}
it("should throw an error for invalid Solidity code", async function () {
const standardInput = loadFixture("invalid_contract_content.json");
it('should throw an error for invalid Solidity code', async function () {
const standardInput = loadFixture('invalid_contract_content.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("errors");
expect(output.errors).to.be.an("array");
expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].type).to.exist;
expect(output.errors[0].type).to.contain("ParserError");
});
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('errors')
expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].type).to.exist
expect(output.errors[0].type).to.contain('ParserError')
})
it("should return not found error for missing imports", async function () {
const standardInput = loadFixture("missing_import.json");
it('should return not found error for missing imports', async function () {
const standardInput = loadFixture('missing_import.json')
const result = await compile(standardInput);
const output = JSON.parse(result);
expect(output).to.have.property("errors");
expect(output.errors).to.be.an("array");
expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].message).to.exist;
const result = await compile(standardInput)
const output = JSON.parse(result)
expect(output).to.have.property('errors')
expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].message).to.exist
expect(output.errors[0].message).to.include(
'Source "nonexistent/console.sol" not found',
);
});
'Source "nonexistent/console.sol" not found'
)
})
it("should successfully compile a valid Solidity contract that instantiates another contract", async function () {
const standardInput = loadFixture("instantiate.json");
it('should successfully compile a valid Solidity contract that instantiates another contract', async function () {
const standardInput = loadFixture('instantiate.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property(
"ChildContract",
);
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
'ChildContract'
)
expect(
output.contracts["fixtures/instantiate.sol"].ChildContract,
).to.have.property("abi");
output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property('abi')
expect(
output.contracts["fixtures/instantiate.sol"].ChildContract,
).to.have.property("evm");
output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property('evm')
expect(
output.contracts["fixtures/instantiate.sol"].ChildContract.evm,
).to.have.property("bytecode");
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property(
"MainContract",
);
output.contracts['fixtures/instantiate.sol'].ChildContract.evm
).to.have.property('bytecode')
expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
'MainContract'
)
expect(
output.contracts["fixtures/instantiate.sol"].MainContract,
).to.have.property("abi");
output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property('abi')
expect(
output.contracts["fixtures/instantiate.sol"].MainContract,
).to.have.property("evm");
output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property('evm')
expect(
output.contracts["fixtures/instantiate.sol"].MainContract.evm,
).to.have.property("bytecode");
});
});
output.contracts['fixtures/instantiate.sol'].MainContract.evm
).to.have.property('bytecode')
})
})
+169 -172
View File
@@ -8,196 +8,193 @@ import * as resolc from '.'
import { SolcInput } from '.'
async function main() {
// hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end
const originalUncaughtExceptionListeners =
process.listeners('uncaughtException')
// FIXME: remove annoying exception catcher of Emscripten
// see https://github.com/chriseth/browser-solidity/issues/167
process.removeAllListeners('uncaughtException')
// hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end
const originalUncaughtExceptionListeners =
process.listeners('uncaughtException')
// FIXME: remove annoying exception catcher of Emscripten
// see https://github.com/chriseth/browser-solidity/issues/167
process.removeAllListeners('uncaughtException')
const program = new commander.Command()
const program = new commander.Command()
program.name('solcjs')
program.version(resolc.version())
program
.option('--bin', 'Binary of the contracts in hex.')
.option('--abi', 'ABI of the contracts.')
.option(
'--base-path <path>',
'Root of the project source tree. ' +
'The import callback will attempt to interpret all import paths as relative to this directory.'
)
.option(
'--include-path <path...>',
'Extra source directories available to the import callback. ' +
'When using a package manager to install libraries, use this option to specify directories where packages are installed. ' +
'Can be used multiple times to provide multiple locations.'
)
.option(
'-o, --output-dir <output-directory>',
'Output directory for the contracts.'
)
.option('-p, --pretty-json', 'Pretty-print all JSON output.', false)
.option('-v, --verbose', 'More detailed console output.', false)
.argument('<files...>')
program.name('solcjs')
program.version(resolc.version())
program
.option('--bin', 'Binary of the contracts in hex.')
.option('--abi', 'ABI of the contracts.')
.option(
'--base-path <path>',
'Root of the project source tree. ' +
'The import callback will attempt to interpret all import paths as relative to this directory.'
)
.option(
'--include-path <path...>',
'Extra source directories available to the import callback. ' +
'When using a package manager to install libraries, use this option to specify directories where packages are installed. ' +
'Can be used multiple times to provide multiple locations.'
)
.option(
'-o, --output-dir <output-directory>',
'Output directory for the contracts.'
)
.option('-p, --pretty-json', 'Pretty-print all JSON output.', false)
.option('-v, --verbose', 'More detailed console output.', false)
.argument('<files...>')
program.parse(process.argv)
const options = program.opts<{
verbose: boolean
abi: boolean
bin: boolean
outputDir?: string
prettyJson: boolean
basePath?: string
includePath?: string[]
}>()
const files: string[] = program.args
const destination = options.outputDir ?? '.'
program.parse(process.argv)
const options = program.opts<{
verbose: boolean
abi: boolean
bin: boolean
outputDir?: string
prettyJson: boolean
basePath?: string
includePath?: string[]
}>()
const files: string[] = program.args
const destination = options.outputDir ?? '.'
function abort(msg: string) {
console.error(msg || 'Error occurred')
process.exit(1)
function abort(msg: string) {
console.error(msg || 'Error occurred')
process.exit(1)
}
function withUnixPathSeparators(filePath: string) {
// On UNIX-like systems forward slashes in paths are just a part of the file name.
if (os.platform() !== 'win32') {
return filePath
}
function withUnixPathSeparators(filePath: string) {
// On UNIX-like systems forward slashes in paths are just a part of the file name.
if (os.platform() !== 'win32') {
return filePath
}
return filePath.replace(/\\/g, '/')
}
return filePath.replace(/\\/g, '/')
}
function makeSourcePathRelativeIfPossible(sourcePath: string) {
const absoluteBasePath = options.basePath
? path.resolve(options.basePath)
: path.resolve('.')
const absoluteIncludePaths = options.includePath
? options.includePath.map((prefix: string) => {
return path.resolve(prefix)
})
: []
// Compared to base path stripping logic in solc this is much simpler because path.resolve()
// handles symlinks correctly (does not resolve them except in work dir) and strips .. segments
// from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it
// ignores less important corner cases: drive letters are not stripped from absolute paths on
// Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has
// very little test coverage so there might be more differences that we are just not aware of.
const absoluteSourcePath = path.resolve(sourcePath)
for (const absolutePrefix of [absoluteBasePath].concat(
absoluteIncludePaths
)) {
const relativeSourcePath = path.relative(
absolutePrefix,
absoluteSourcePath
)
if (!relativeSourcePath.startsWith('../')) {
return withUnixPathSeparators(relativeSourcePath)
}
}
// File is not located inside base path or include paths so use its absolute path.
return withUnixPathSeparators(absoluteSourcePath)
}
function toFormattedJson<T>(input: T) {
return JSON.stringify(input, null, options.prettyJson ? 4 : 0)
}
if (files.length === 0) {
console.error('Must provide a file')
process.exit(1)
}
if (!(options.bin || options.abi)) {
abort('Invalid option selected, must specify either --bin or --abi')
}
const sources: SolcInput = {}
for (let i = 0; i < files.length; i++) {
try {
sources[makeSourcePathRelativeIfPossible(files[i])] = {
content: fs.readFileSync(files[i]).toString(),
}
} catch (e) {
abort('Error reading ' + files[i] + ': ' + e)
}
}
if (options.verbose) {
console.log('>>> Compiling:\n' + toFormattedJson(sources) + '\n')
}
const output = await resolc.compile(sources)
let hasError = false
if (!output) {
abort('No output from compiler')
} else if (output.errors) {
for (const error in output.errors) {
const message = output.errors[error]
if (message.severity === 'warning') {
console.log(message.formattedMessage)
} else {
console.error(message.formattedMessage)
hasError = true
}
}
}
fs.mkdirSync(destination, { recursive: true })
function writeFile(file: string, content: Buffer | string) {
file = path.join(destination, file)
fs.writeFile(file, content, function (err) {
if (err) {
console.error('Failed to write ' + file + ': ' + err)
}
function makeSourcePathRelativeIfPossible(sourcePath: string) {
const absoluteBasePath = options.basePath
? path.resolve(options.basePath)
: path.resolve('.')
const absoluteIncludePaths = options.includePath
? options.includePath.map((prefix: string) => {
return path.resolve(prefix)
})
: []
// Compared to base path stripping logic in solc this is much simpler because path.resolve()
// handles symlinks correctly (does not resolve them except in work dir) and strips .. segments
// from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it
// ignores less important corner cases: drive letters are not stripped from absolute paths on
// Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has
// very little test coverage so there might be more differences that we are just not aware of.
const absoluteSourcePath = path.resolve(sourcePath)
for (const absolutePrefix of [absoluteBasePath].concat(
absoluteIncludePaths
)) {
const relativeSourcePath = path.relative(
absolutePrefix,
absoluteSourcePath
)
if (!relativeSourcePath.startsWith('../')) {
return withUnixPathSeparators(relativeSourcePath)
}
}
for (const fileName in output.contracts) {
for (const contractName in output.contracts[fileName]) {
let contractFileName = fileName + ':' + contractName
contractFileName = contractFileName.replace(/[:./\\]/g, '_')
// File is not located inside base path or include paths so use its absolute path.
return withUnixPathSeparators(absoluteSourcePath)
}
if (options.bin) {
writeFile(
contractFileName + '.polkavm',
Buffer.from(
output.contracts[fileName][contractName].evm.bytecode
.object,
'hex'
)
)
}
function toFormattedJson<T>(input: T) {
return JSON.stringify(input, null, options.prettyJson ? 4 : 0)
}
if (options.abi) {
writeFile(
contractFileName + '.abi',
toFormattedJson(
output.contracts[fileName][contractName].abi
)
)
}
}
if (files.length === 0) {
console.error('Must provide a file')
process.exit(1)
}
if (!(options.bin || options.abi)) {
abort('Invalid option selected, must specify either --bin or --abi')
}
const sources: SolcInput = {}
for (let i = 0; i < files.length; i++) {
try {
sources[makeSourcePathRelativeIfPossible(files[i])] = {
content: fs.readFileSync(files[i]).toString(),
}
} catch (e) {
abort('Error reading ' + files[i] + ': ' + e)
}
}
// Put back original exception handlers.
originalUncaughtExceptionListeners.forEach(function (listener) {
process.addListener('uncaughtException', listener)
if (options.verbose) {
console.log('>>> Compiling:\n' + toFormattedJson(sources) + '\n')
}
const output = await resolc.compile(sources)
let hasError = false
if (!output) {
abort('No output from compiler')
} else if (output.errors) {
for (const error in output.errors) {
const message = output.errors[error]
if (message.severity === 'warning') {
console.log(message.formattedMessage)
} else {
console.error(message.formattedMessage)
hasError = true
}
}
}
fs.mkdirSync(destination, { recursive: true })
function writeFile(file: string, content: Buffer | string) {
file = path.join(destination, file)
fs.writeFile(file, content, function (err) {
if (err) {
console.error('Failed to write ' + file + ': ' + err)
}
})
}
if (hasError) {
process.exit(1)
for (const fileName in output.contracts) {
for (const contractName in output.contracts[fileName]) {
let contractFileName = fileName + ':' + contractName
contractFileName = contractFileName.replace(/[:./\\]/g, '_')
if (options.bin) {
writeFile(
contractFileName + '.polkavm',
Buffer.from(
output.contracts[fileName][contractName].evm.bytecode.object,
'hex'
)
)
}
if (options.abi) {
writeFile(
contractFileName + '.abi',
toFormattedJson(output.contracts[fileName][contractName].abi)
)
}
}
}
// Put back original exception handlers.
originalUncaughtExceptionListeners.forEach(function (listener) {
process.addListener('uncaughtException', listener)
})
if (hasError) {
process.exit(1)
}
}
main().catch((err) => {
console.error('Error:', err)
process.exit(1)
console.error('Error:', err)
process.exit(1)
})
+88 -87
View File
@@ -4,112 +4,113 @@ import assert from 'node:assert'
import { compile, tryResolveImport } from '.'
import { resolve } from 'node:path'
const compileOptions = [{}]
if (existsSync('../../target/release/resolc')) {
compileOptions.push({ bin: '../../target/release/resolc' })
compileOptions.push({ bin: '../../target/release/resolc' })
}
for (const options of compileOptions) {
test(`check Ok output with option ${JSON.stringify(options)}`, async () => {
const contract = 'fixtures/token.sol'
const sources = {
[contract]: {
content: readFileSync('fixtures/storage.sol', 'utf8'),
},
}
test(`check Ok output with option ${JSON.stringify(options)}`, async () => {
const contract = 'fixtures/token.sol'
const sources = {
[contract]: {
content: readFileSync('fixtures/storage.sol', 'utf8'),
},
}
const out = await compile(sources, options)
assert(out.contracts[contract].Storage.abi != null)
assert(out.contracts[contract].Storage.evm.bytecode != null)
})
const out = await compile(sources, options)
assert(out.contracts[contract].Storage.abi != null)
assert(out.contracts[contract].Storage.evm.bytecode != null)
})
}
test('check Err output', async () => {
const sources = {
bad: {
content: readFileSync('fixtures/storage_bad.sol', 'utf8'),
},
}
const sources = {
bad: {
content: readFileSync('fixtures/storage_bad.sol', 'utf8'),
},
}
const out = await compile(sources)
assert(
out?.errors?.[0].message.includes(
'SPDX license identifier not provided in source file'
)
const out = await compile(sources)
assert(
out?.errors?.[0].message.includes(
'SPDX license identifier not provided in source file'
)
assert(
out?.errors?.[1].message.includes(
'Source file does not specify required compiler version'
)
)
assert(
out?.errors?.[1].message.includes(
'Source file does not specify required compiler version'
)
)
})
test('check Err from stderr', async () => {
const sources = {
bad: {
content: readFileSync('fixtures/bad_pragma.sol', 'utf8'),
},
}
const sources = {
bad: {
content: readFileSync('fixtures/bad_pragma.sol', 'utf8'),
},
}
try {
await compile(sources)
assert(false, 'Expected error')
} catch (error) {
assert(
String(error).includes(
'Source file requires different compiler version'
)
)
}
try {
await compile(sources)
assert(false, 'Expected error')
} catch (error) {
assert(
String(error).includes('Source file requires different compiler version')
)
}
})
test('resolve import', () => {
const cases = [
// local
{
file: './fixtures/storage.sol',
expected: resolve('fixtures/storage.sol'),
},
// scopped module with version
{
file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol',
expected: require.resolve('@openzeppelin/contracts/token/ERC20/ERC20.sol'),
},
// scopped module without version
{
file: '@openzeppelin/contracts/token/ERC20/ERC20.sol',
expected: require.resolve('@openzeppelin/contracts/token/ERC20/ERC20.sol'),
},
// scopped module with wrong version
{
file: '@openzeppelin/contracts@4.8.3/token/ERC20/ERC20.sol',
expected: `Error: Version mismatch: Specified @openzeppelin/contracts@4.8.3, but installed version is 5.1.0`,
},
// module without version
{
file: '@openzeppelin/contracts/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'),
},
// scopped module with version
{
file: '@openzeppelin/contracts@5.1.0/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'),
},
]
const cases = [
// local
{
file: './fixtures/storage.sol',
expected: resolve('fixtures/storage.sol'),
},
// scopped module with version
{
file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol',
expected: require.resolve(
'@openzeppelin/contracts/token/ERC20/ERC20.sol'
),
},
// scopped module without version
{
file: '@openzeppelin/contracts/token/ERC20/ERC20.sol',
expected: require.resolve(
'@openzeppelin/contracts/token/ERC20/ERC20.sol'
),
},
// scopped module with wrong version
{
file: '@openzeppelin/contracts@4.8.3/token/ERC20/ERC20.sol',
expected: `Error: Version mismatch: Specified @openzeppelin/contracts@4.8.3, but installed version is 5.1.0`,
},
// module without version
{
file: '@openzeppelin/contracts/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'),
},
// scopped module with version
{
file: '@openzeppelin/contracts@5.1.0/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'),
},
]
for (const { file, expected } of cases) {
try {
const resolved = tryResolveImport(file)
assert(
resolved === expected,
`\nGot:\n${resolved}\nExpected:\n${expected}`
)
} catch (error) {
assert(
String(error) == expected,
`\nGot:\n${String(error)}\nExpected:\n${expected}`
)
}
for (const { file, expected } of cases) {
try {
const resolved = tryResolveImport(file)
assert(
resolved === expected,
`\nGot:\n${resolved}\nExpected:\n${expected}`
)
} catch (error) {
assert(
String(error) == expected,
`\nGot:\n${String(error)}\nExpected:\n${expected}`
)
}
}
})
+151 -153
View File
@@ -5,114 +5,114 @@ import path from 'path'
import { existsSync, readFileSync } from 'fs'
export type SolcInput = {
[contractName: string]: {
content: string
}
[contractName: string]: {
content: string
}
}
export type SolcError = {
component: string
errorCode: string
formattedMessage: string
message: string
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
component: string
errorCode: string
formattedMessage: string
message: string
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}
export type SolcOutput = {
contracts: {
[contractPath: string]: {
[contractName: string]: {
abi: Array<{
name: string
inputs: Array<{ name: string; type: string }>
outputs: Array<{ name: string; type: string }>
stateMutability: string
type: string
}>
evm: {
bytecode: { object: string }
}
}
contracts: {
[contractPath: string]: {
[contractName: string]: {
abi: Array<{
name: string
inputs: Array<{ name: string; type: string }>
outputs: Array<{ name: string; type: string }>
stateMutability: string
type: string
}>
evm: {
bytecode: { object: string }
}
}
}
errors?: Array<SolcError>
}
errors?: Array<SolcError>
}
export function resolveInputs(sources: SolcInput): SolcInput {
const input = {
language: 'Solidity',
sources,
settings: {
outputSelection: {
'*': {
'*': ['evm.bytecode.object'],
},
},
const input = {
language: 'Solidity',
sources,
settings: {
outputSelection: {
'*': {
'*': ['evm.bytecode.object'],
},
}
},
},
}
const out = solc.compile(JSON.stringify(input), {
import: (path: string) => {
return {
contents: readFileSync(tryResolveImport(path), 'utf8'),
}
const out = solc.compile(JSON.stringify(input), {
import: (path: string) => {
return {
contents: readFileSync(tryResolveImport(path), 'utf8'),
}
},
})
const output = JSON.parse(out) as {
sources: { [fileName: string]: { id: number } }
errors: Array<SolcError>
}
if (output.errors && Object.keys(output.sources).length === 0) {
throw new Error(output.errors[0].formattedMessage)
}
return Object.fromEntries(
Object.keys(output.sources).map((fileName) => {
return [
fileName,
sources[fileName] ?? {
content: readFileSync(tryResolveImport(fileName), 'utf8'),
},
]
})
const output = JSON.parse(out) as {
sources: { [fileName: string]: { id: number } }
errors: Array<SolcError>
}
if (output.errors && Object.keys(output.sources).length === 0) {
throw new Error(output.errors[0].formattedMessage)
}
return Object.fromEntries(
Object.keys(output.sources).map((fileName) => {
return [
fileName,
sources[fileName] ?? {
content: readFileSync(tryResolveImport(fileName), 'utf8'),
},
]
})
)
)
}
export function version(): string {
const v = resolcVersion()
return v.split(' ').pop() ?? v
const v = resolcVersion()
return v.split(' ').pop() ?? v
}
export async function compile(
sources: SolcInput,
option: { bin?: string } = {}
sources: SolcInput,
option: { bin?: string } = {}
): Promise<SolcOutput> {
const input = JSON.stringify({
language: 'Solidity',
sources: resolveInputs(sources),
settings: {
optimizer: { enabled: true, runs: 200 },
outputSelection: {
'*': {
'*': ['abi'],
},
},
const input = JSON.stringify({
language: 'Solidity',
sources: resolveInputs(sources),
settings: {
optimizer: { enabled: true, runs: 200 },
outputSelection: {
'*': {
'*': ['abi'],
},
})
},
},
})
if (option.bin) {
return compileWithBin(input, option.bin)
}
if (option.bin) {
return compileWithBin(input, option.bin)
}
return resolc(input)
return resolc(input)
}
/**
@@ -120,83 +120,81 @@ export async function compile(
* @param importPath - The import path to resolve.
*/
export function tryResolveImport(importPath: string) {
// resolve local path
if (existsSync(importPath)) {
return path.resolve(importPath)
// resolve local path
if (existsSync(importPath)) {
return path.resolve(importPath)
}
const importRegex = /^(@?[^@/]+(?:\/[^@/]+)?)(?:@([^/]+))?(\/.+)$/
const match = importPath.match(importRegex)
if (!match) {
throw new Error('Invalid import path format.')
}
const basePackage = match[1] // "foo", "@scope/foo"
const specifiedVersion = match[2] // "1.2.3" (optional)
const relativePath = match[3] // "/path/to/file.sol"
let packageJsonPath
try {
packageJsonPath = require.resolve(path.join(basePackage, 'package.json'))
} catch {
throw new Error(`Could not resolve package ${basePackage}`)
}
// Check if a version was specified and compare with the installed version
if (specifiedVersion) {
const installedVersion = JSON.parse(
readFileSync(packageJsonPath, 'utf-8')
).version
if (installedVersion !== specifiedVersion) {
throw new Error(
`Version mismatch: Specified ${basePackage}@${specifiedVersion}, but installed version is ${installedVersion}`
)
}
}
const importRegex = /^(@?[^@/]+(?:\/[^@/]+)?)(?:@([^/]+))?(\/.+)$/
const match = importPath.match(importRegex)
const packageRoot = path.dirname(packageJsonPath)
if (!match) {
throw new Error('Invalid import path format.')
}
const basePackage = match[1] // "foo", "@scope/foo"
const specifiedVersion = match[2] // "1.2.3" (optional)
const relativePath = match[3] // "/path/to/file.sol"
let packageJsonPath
try {
packageJsonPath = require.resolve(
path.join(basePackage, 'package.json')
)
} catch {
throw new Error(`Could not resolve package ${basePackage}`)
}
// Check if a version was specified and compare with the installed version
if (specifiedVersion) {
const installedVersion = JSON.parse(
readFileSync(packageJsonPath, 'utf-8')
).version
if (installedVersion !== specifiedVersion) {
throw new Error(
`Version mismatch: Specified ${basePackage}@${specifiedVersion}, but installed version is ${installedVersion}`
)
}
}
const packageRoot = path.dirname(packageJsonPath)
// Construct full path to the requested file
const resolvedPath = path.join(packageRoot, relativePath)
if (existsSync(resolvedPath)) {
return resolvedPath
} else {
throw new Error(`Resolved path ${resolvedPath} does not exist.`)
}
// Construct full path to the requested file
const resolvedPath = path.join(packageRoot, relativePath)
if (existsSync(resolvedPath)) {
return resolvedPath
} else {
throw new Error(`Resolved path ${resolvedPath} does not exist.`)
}
}
function compileWithBin(input: string, bin: string): PromiseLike<SolcOutput> {
return new Promise((resolve, reject) => {
const process = spawn(bin, ['--standard-json'])
return new Promise((resolve, reject) => {
const process = spawn(bin, ['--standard-json'])
let output = ''
let error = ''
let output = ''
let error = ''
process.stdin.write(input)
process.stdin.end()
process.stdin.write(input)
process.stdin.end()
process.stdout.on('data', (data) => {
output += data.toString()
})
process.stderr.on('data', (data) => {
error += data.toString()
})
process.on('close', (code) => {
if (code === 0) {
try {
const result: SolcOutput = JSON.parse(output)
resolve(result)
} catch {
reject(new Error(`Failed to parse output`))
}
} else {
reject(new Error(`Process exited with code ${code}: ${error}`))
}
})
process.stdout.on('data', (data) => {
output += data.toString()
})
process.stderr.on('data', (data) => {
error += data.toString()
})
process.on('close', (code) => {
if (code === 0) {
try {
const result: SolcOutput = JSON.parse(output)
resolve(result)
} catch {
reject(new Error(`Failed to parse output`))
}
} else {
reject(new Error(`Process exited with code ${code}: ${error}`))
}
})
})
}
+17 -17
View File
@@ -3,28 +3,28 @@ import Resolc from './resolc/resolc'
import type { SolcOutput } from '.'
export function resolc(input: string): SolcOutput {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson
m.writeToStdin(input)
m.callMain(['--standard-json'])
const err = m.readFromStderr()
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson
m.writeToStdin(input)
m.callMain(['--standard-json'])
const err = m.readFromStderr()
if (err) {
throw new Error(err)
}
if (err) {
throw new Error(err)
}
return JSON.parse(m.readFromStdout()) as SolcOutput
return JSON.parse(m.readFromStdout()) as SolcOutput
}
export function version(): string {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson
m.callMain(['--version'])
const err = m.readFromStderr()
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson
m.callMain(['--version'])
const err = m.readFromStderr()
if (err) {
throw new Error(err)
}
if (err) {
throw new Error(err)
}
return m.readFromStdout()
return m.readFromStdout()
}
+82 -82
View File
@@ -1,95 +1,95 @@
declare module 'solc/soljson' {}
declare module 'solc' {
// Basic types for input/output handling
export interface CompileInput {
language: string
sources: {
[fileName: string]: {
content: string
}
// Basic types for input/output handling
export interface CompileInput {
language: string
sources: {
[fileName: string]: {
content: string
}
}
settings?: {
optimizer?: {
enabled: boolean
runs: number
}
outputSelection: {
[fileName: string]: {
[contractName: string]: string[]
}
settings?: {
optimizer?: {
enabled: boolean
runs: number
}
outputSelection: {
}
}
}
export interface CompileOutput {
errors?: Array<{
component: string
errorCode: string
formattedMessage: string
message: string
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}>
sources?: {
[fileName: string]: {
id: number
ast: object
}
}
contracts?: {
[fileName: string]: {
[contractName: string]: {
abi: object[]
evm: {
bytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[contractName: string]: string[]
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
}
}
export interface CompileOutput {
errors?: Array<{
component: string
errorCode: string
formattedMessage: string
message: string
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}>
sources?: {
[fileName: string]: {
id: number
ast: object
}
}
contracts?: {
[fileName: string]: {
[contractName: string]: {
abi: object[]
evm: {
bytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
deployedBytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
}
deployedBytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
}
}
}
}
}
// Main exported functions
export function compile(
input: string | CompileInput,
options?: {
import: (path: string) =>
| {
contents: string
error?: undefined
}
| {
error: string
contents?: undefined
}
}
): string
// Main exported functions
export function compile(
input: string | CompileInput,
options?: {
import: (path: string) =>
| {
contents: string
error?: undefined
}
| {
error: string
contents?: undefined
}
}
): string
}