mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-13 16:31:09 +00:00
resolc crate (#328)
- Factor the YUL crate out of `revive-solidity`. - `revive-solidity` is in reality not a Solidity implementation but the revive solidity compiler driver (`resolc`). By renaming we not only get this straight but also a binary with the same name as the crate which should be less confusing. --------- Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites name="jest tests" tests="27" failures="0" errors="0" time="2.146">
|
||||
<testsuite name="Run with --yul by default" errors="0" failures="0" skipped="1" timestamp="2024-10-24T17:08:50" time="1.508" tests="6">
|
||||
<testcase classname="Run with --yul by default Valid command exit code = 0" name="Run with --yul by default Valid command exit code = 0" time="0.003">
|
||||
</testcase>
|
||||
<testcase classname="Run with --yul by default --yul output is presented" name="Run with --yul by default --yul output is presented" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Run with --yul by default solc exit code == resolc exit code" name="Run with --yul by default solc exit code == resolc exit code" time="0">
|
||||
<skipped/>
|
||||
</testcase>
|
||||
<testcase classname="Run with --yul by default run invalid: resolc --yul" name="Run with --yul by default run invalid: resolc --yul" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="Run with --yul by default Invalid command exit code = 1" name="Run with --yul by default Invalid command exit code = 1" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Run with --yul by default Invalid solc exit code == Invalid resolc exit code" name="Run with --yul by default Invalid solc exit code == Invalid resolc exit code" time="0.041">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
<testsuite name="Run with --asm by default" errors="0" failures="0" skipped="0" timestamp="2024-10-24T17:08:50" time="1.512" tests="6">
|
||||
<testcase classname="Run with --asm by default Valid command exit code = 0" name="Run with --asm by default Valid command exit code = 0" time="0.002">
|
||||
</testcase>
|
||||
<testcase classname="Run with --asm by default --asm output is presented" name="Run with --asm by default --asm output is presented" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="Run with --asm by default solc exit code == resolc exit code" name="Run with --asm by default solc exit code == resolc exit code" time="0.044">
|
||||
</testcase>
|
||||
<testcase classname="Run with --asm by default run invalid: resolc --asm" name="Run with --asm by default run invalid: resolc --asm" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Run with --asm by default Invalid command exit code = 1" name="Run with --asm by default Invalid command exit code = 1" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="Run with --asm by default Invalid solc exit code == Invalid resolc exit code" name="Run with --asm by default Invalid solc exit code == Invalid resolc exit code" time="0.04">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
<testsuite name="Run resolc without any options" errors="0" failures="0" skipped="2" timestamp="2024-10-24T17:08:50" time="2.016" tests="15">
|
||||
<testcase classname="Run resolc without any options Info with help is presented" name="Run resolc without any options Info with help is presented" time="0.002">
|
||||
</testcase>
|
||||
<testcase classname="Run resolc without any options Exit code = 1" name="Run resolc without any options Exit code = 1" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Run resolc without any options solc exit code == resolc exit code" name="Run resolc without any options solc exit code == resolc exit code" time="0.044">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Compiler run successful" name="Default run a command from the help Compiler run successful" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Exit code = 0" name="Default run a command from the help Exit code = 0" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Output dir is created" name="Default run a command from the help Output dir is created" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Output file is created" name="Default run a command from the help Output file is created" time="0">
|
||||
<skipped/>
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help the output file is not empty" name="Default run a command from the help the output file is not empty" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help No 'Error'/'Warning'/'Fail' in the output" name="Default run a command from the help No 'Error'/'Warning'/'Fail' in the output" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Compiler run successful" name="Default run a command from the help Compiler run successful" time="0.001">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Exit code = 0" name="Default run a command from the help Exit code = 0" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Output dir is created" name="Default run a command from the help Output dir is created" time="0">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help Output files are created" name="Default run a command from the help Output files are created" time="0">
|
||||
<skipped/>
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help the output files are not empty" name="Default run a command from the help the output files are not empty" time="0.003">
|
||||
</testcase>
|
||||
<testcase classname="Default run a command from the help No 'Error'/'Warning'/'Fail' in the output" name="Default run a command from the help No 'Error'/'Warning'/'Fail' in the output" time="0">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
+3812
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "cli-tests",
|
||||
"version": "1.0.0",
|
||||
"title": "resolc CLI Tests",
|
||||
"description": "Auto tests for verifying resolc CLI",
|
||||
"repository": "https://github.com/paritytech/revive",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "npx jest --verbose --testPathPattern="
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Matter Labs",
|
||||
"contributors": [
|
||||
"cyrill@parity.io"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/shelljs": "^0.8.15",
|
||||
"jest": "^29.7.0",
|
||||
"shelljs": "^0.8.5",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.0;
|
||||
contract C {}
|
||||
@@ -0,0 +1,54 @@
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
mstore(64, 128)
|
||||
if callvalue() { revert(0, 0) }
|
||||
let _1 := datasize("Test_deployed")
|
||||
codecopy(0, dataoffset("Test_deployed"), _1)
|
||||
return(0, _1)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
mstore(64, 128)
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
let _1 := 0
|
||||
switch shr(224, calldataload(_1))
|
||||
case 0x3df4ddf4 {
|
||||
if callvalue() { revert(_1, _1) }
|
||||
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
||||
let memPos := allocate_memory(_1)
|
||||
mstore(memPos, 0x2a)
|
||||
return(memPos, 32)
|
||||
}
|
||||
case 0x5a8ac02d {
|
||||
if callvalue() { revert(_1, _1) }
|
||||
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
||||
let memPos_1 := allocate_memory(_1)
|
||||
return(memPos_1, sub(abi_encode_uint256(memPos_1, 0x63), memPos_1))
|
||||
}
|
||||
}
|
||||
revert(0, 0)
|
||||
}
|
||||
function abi_encode_uint256(headStart, value0) -> tail
|
||||
{
|
||||
tail := add(headStart, 32)
|
||||
mstore(headStart, value0)
|
||||
}
|
||||
function allocate_memory(size) -> memPtr
|
||||
{
|
||||
memPtr := mload(64)
|
||||
let newFreePtr := add(memPtr, and(add(size, 31), not(31)))
|
||||
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr))
|
||||
{
|
||||
mstore(0, shl(224, 0x4e487b71))
|
||||
mstore(4, 0x41)
|
||||
revert(0, 0x24)
|
||||
}
|
||||
mstore(64, newFreePtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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
|
||||
)
|
||||
|
||||
export const paths = {
|
||||
outputDir: outputDir,
|
||||
binExtension: binExtension,
|
||||
asmExtension: asmExtension,
|
||||
llvmExtension: llvmExtension,
|
||||
contractSolFilename: contractSolFilename,
|
||||
contractYulFilename: contractYulFilename,
|
||||
contractOptimizedLLVMFilename: contractOptimizedLLVMFilename,
|
||||
contractUnoptimizedLLVMFilename: contractUnoptimizedLLVMFilename,
|
||||
pathToOutputDir: pathToOutputDir,
|
||||
pathToContracts: pathToContracts,
|
||||
pathToBasicSolContract: pathToBasicSolContract,
|
||||
pathToBasicYulContract: pathToBasicYulContract,
|
||||
pathToSolBinOutputFile: pathToSolBinOutputFile,
|
||||
pathToSolAsmOutputFile: pathToSolAsmOutputFile,
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as shell from 'shelljs'
|
||||
import * as fs from 'fs'
|
||||
import { spawnSync } from 'child_process'
|
||||
|
||||
interface CommandResult {
|
||||
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,
|
||||
})
|
||||
|
||||
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 || '',
|
||||
}
|
||||
}
|
||||
|
||||
export const isFolderExist = (folder: string): boolean => {
|
||||
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 isFileEmpty = (file: string): boolean => {
|
||||
if (fs.existsSync(file)) {
|
||||
return fs.readFileSync(file).length === 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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)
|
||||
|
||||
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]
|
||||
|
||||
for (const pattern of expectedPatterns) {
|
||||
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('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('Invalid solc exit code == Invalid resolc exit code', () => {
|
||||
const command = 'solc --asm'
|
||||
const solcResult = executeCommand(command)
|
||||
expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +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'
|
||||
|
||||
//id1762
|
||||
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('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', () => {
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
//#1818
|
||||
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)
|
||||
|
||||
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`;
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
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())
|
||||
|
||||
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(() => {
|
||||
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([])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
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)
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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('Invalid solc exit code == Invalid resolc exit code', () => {
|
||||
const command = 'solc --yul'
|
||||
const solcResult = executeCommand(command)
|
||||
expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "CommonJS",
|
||||
"outDir": "./dist",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
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`,
|
||||
]
|
||||
|
||||
//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('--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)
|
||||
})
|
||||
})
|
||||
|
||||
//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('--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)
|
||||
})
|
||||
})
|
||||
|
||||
//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('--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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//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('--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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//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('--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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//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('--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)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,88 @@
|
||||
//! The Solidity compiler unit tests for factory dependencies.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub const MAIN_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
import "./callable.sol";
|
||||
|
||||
contract Main {
|
||||
function main() external returns(uint256) {
|
||||
Callable callable = new Callable();
|
||||
|
||||
callable.set(10);
|
||||
return callable.get();
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
pub const CALLABLE_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
contract Callable {
|
||||
uint256 value;
|
||||
|
||||
function set(uint256 x) external {
|
||||
value = x;
|
||||
}
|
||||
|
||||
function get() external view returns(uint256) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("main.sol".to_owned(), MAIN_CODE.to_owned());
|
||||
sources.insert("callable.sol".to_owned(), CALLABLE_CODE.to_owned());
|
||||
|
||||
let output = super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
|
||||
assert_eq!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("main.sol")
|
||||
.expect("Missing file `main.sol`")
|
||||
.get("Main")
|
||||
.expect("Missing contract `main.sol:Main`")
|
||||
.factory_dependencies
|
||||
.as_ref()
|
||||
.expect("Missing field `factory_dependencies`")
|
||||
.len(),
|
||||
1,
|
||||
"Expected 1 factory dependency in `main.sol:Main`"
|
||||
);
|
||||
assert_eq!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("callable.sol")
|
||||
.expect("Missing file `callable.sol`")
|
||||
.get("Callable")
|
||||
.expect("Missing contract `callable.sol:Callable`")
|
||||
.factory_dependencies
|
||||
.as_ref()
|
||||
.expect("Missing field `factory_dependencies`")
|
||||
.len(),
|
||||
0,
|
||||
"Expected 0 factory dependencies in `callable.sol:Callable`"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//! The Solidity compiler unit tests for IR artifacts.
|
||||
//! The tests check if the IR artifacts are kept in the final output.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
fn yul() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract Test {
|
||||
function main() public view returns (uint) {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), source_code.to_owned());
|
||||
|
||||
let build = super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
|
||||
assert!(
|
||||
build
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("Test")
|
||||
.expect("Always exists")
|
||||
.ir_optimized
|
||||
.is_some(),
|
||||
"Yul IR is missing"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//! The Solidity compiler unit tests for libraries.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub const LIBRARY_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
// A simple library with at least one external method
|
||||
library SimpleLibrary {
|
||||
function add(uint256 a, uint256 b) external pure returns (uint256) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
// A contract calling that library
|
||||
contract SimpleContract {
|
||||
using SimpleLibrary for uint256;
|
||||
|
||||
function performAlgorithm(uint256 a, uint256 b) public pure returns (uint256) {
|
||||
uint sum = 0;
|
||||
if (a > b) {
|
||||
while (true) {
|
||||
sum += a.add(b);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn not_specified() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned());
|
||||
|
||||
let output =
|
||||
super::build_solidity_and_detect_missing_libraries(sources.clone(), BTreeMap::new())
|
||||
.expect("Test failure");
|
||||
assert!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("SimpleContract")
|
||||
.expect("Always exists")
|
||||
.missing_libraries
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.contains("test.sol:SimpleLibrary"),
|
||||
"Missing library not detected"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn specified() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned());
|
||||
|
||||
let mut libraries = BTreeMap::new();
|
||||
libraries
|
||||
.entry("test.sol".to_string())
|
||||
.or_insert_with(BTreeMap::new)
|
||||
.entry("SimpleLibrary".to_string())
|
||||
.or_insert("0x00000000000000000000000000000000DEADBEEF".to_string());
|
||||
|
||||
let output =
|
||||
super::build_solidity_and_detect_missing_libraries(sources.clone(), libraries.clone())
|
||||
.expect("Test failure");
|
||||
assert!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("SimpleContract")
|
||||
.expect("Always exists")
|
||||
.missing_libraries
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.is_empty(),
|
||||
"The list of missing libraries must be empty"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
//! The Solidity compiler unit tests for messages.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use revive_solc_json_interface::warning::Warning;
|
||||
|
||||
pub const ECRECOVER_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ECRecoverExample {
|
||||
function recoverAddress(
|
||||
bytes32 messageHash,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) public pure returns (address) {
|
||||
return ecrecover(messageHash, v, r, s);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn ecrecover() {
|
||||
assert!(
|
||||
super::check_solidity_warning(
|
||||
ECRECOVER_TEST_SOURCE,
|
||||
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
None,
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecrecover_suppressed() {
|
||||
assert!(
|
||||
!super::check_solidity_warning(
|
||||
ECRECOVER_TEST_SOURCE,
|
||||
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
Some(vec![Warning::EcRecover]),
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
pub const SEND_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract SendExample {
|
||||
address payable public recipient;
|
||||
|
||||
constructor(address payable _recipient) {
|
||||
recipient = _recipient;
|
||||
}
|
||||
|
||||
function forwardEther() external payable {
|
||||
bool success = recipient.send(msg.value);
|
||||
require(success, "Failed to send Ether");
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
pub const BALANCE_CALLS_MESSAGE: &str =
|
||||
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)'";
|
||||
|
||||
#[test]
|
||||
fn send() {
|
||||
assert!(super::check_solidity_warning(
|
||||
SEND_TEST_SOURCE,
|
||||
BALANCE_CALLS_MESSAGE,
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
SEND_TEST_SOURCE,
|
||||
BALANCE_CALLS_MESSAGE,
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
Some(vec![Warning::SendTransfer]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
pub const TRANSFER_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract TransferExample {
|
||||
address payable public recipient;
|
||||
|
||||
constructor(address payable _recipient) {
|
||||
recipient = _recipient;
|
||||
}
|
||||
|
||||
function forwardEther() external payable {
|
||||
recipient.transfer(msg.value);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn transfer() {
|
||||
assert!(super::check_solidity_warning(
|
||||
TRANSFER_TEST_SOURCE,
|
||||
BALANCE_CALLS_MESSAGE,
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
TRANSFER_TEST_SOURCE,
|
||||
BALANCE_CALLS_MESSAGE,
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
Some(vec![Warning::SendTransfer]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
pub const EXTCODESIZE_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ExternalCodeSize {
|
||||
function getExternalCodeSize(address target) public view returns (uint256) {
|
||||
uint256 codeSize;
|
||||
assembly {
|
||||
codeSize := extcodesize(target)
|
||||
}
|
||||
return codeSize;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn extcodesize() {
|
||||
assert!(super::check_solidity_warning(
|
||||
EXTCODESIZE_TEST_SOURCE,
|
||||
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extcodesize_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
EXTCODESIZE_TEST_SOURCE,
|
||||
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
Some(vec![Warning::ExtCodeSize]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
pub const TX_ORIGIN_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract TxOriginExample {
|
||||
function isOriginSender() public view returns (bool) {
|
||||
return tx.origin == msg.sender;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn tx_origin() {
|
||||
assert!(super::check_solidity_warning(
|
||||
TX_ORIGIN_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_origin_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
TX_ORIGIN_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
Some(vec![Warning::TxOrigin]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
pub const TX_ORIGIN_ASSEMBLY_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract TxOriginExample {
|
||||
function isOriginSender() public view returns (bool) {
|
||||
address txOrigin;
|
||||
address sender = msg.sender;
|
||||
|
||||
assembly {
|
||||
txOrigin := origin() // Get the transaction origin using the 'origin' instruction
|
||||
}
|
||||
|
||||
return txOrigin == sender;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn tx_origin_assembly() {
|
||||
assert!(super::check_solidity_warning(
|
||||
TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_origin_assembly_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
false,
|
||||
Some(vec![Warning::TxOrigin]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//! The Solidity compiler unit tests.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
mod factory_dependency;
|
||||
mod ir_artifacts;
|
||||
mod libraries;
|
||||
mod messages;
|
||||
mod optimizer;
|
||||
mod remappings;
|
||||
mod runtime_code;
|
||||
mod unsupported_opcodes;
|
||||
|
||||
pub(crate) use super::test_utils::*;
|
||||
@@ -0,0 +1,130 @@
|
||||
//! The Solidity compiler unit tests for the optimizer.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub const SOURCE_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.5.0;
|
||||
|
||||
contract Test {
|
||||
uint8 constant ARRAY_SIZE = 40;
|
||||
uint128 constant P = 257;
|
||||
uint128 constant MODULO = 1000000007;
|
||||
|
||||
function complex() public pure returns(uint64) {
|
||||
uint8[ARRAY_SIZE] memory array;
|
||||
// generate array where first half equals second
|
||||
for(uint8 i = 0; i < ARRAY_SIZE; i++) {
|
||||
array[i] = (i % (ARRAY_SIZE / 2)) * (255 / (ARRAY_SIZE / 2 - 1));
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
for(uint8 i = 0; i < ARRAY_SIZE/2; i++) {
|
||||
result = result && hash(array, 0, i + 1) == hash(array, ARRAY_SIZE/2, ARRAY_SIZE/2 + i + 1)
|
||||
&& hash(array, i, ARRAY_SIZE/2) == hash(array, i + ARRAY_SIZE/2, ARRAY_SIZE);
|
||||
}
|
||||
if (result) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function hash(uint8[ARRAY_SIZE] memory array, uint8 begin, uint8 end) private pure returns(uint128) {
|
||||
uint128 h = 0;
|
||||
for(uint8 i = begin; i < end; i++) {
|
||||
h = (h * P + array[i]) % MODULO;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn optimizer() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), SOURCE_CODE.to_owned());
|
||||
|
||||
let build_unoptimized = super::build_solidity(
|
||||
sources.clone(),
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::none(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
let build_optimized_for_cycles = super::build_solidity(
|
||||
sources.clone(),
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
let build_optimized_for_size = super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::size(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
|
||||
let size_when_unoptimized = build_unoptimized
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("test.sol")
|
||||
.expect("Missing file `test.sol`")
|
||||
.get("Test")
|
||||
.expect("Missing contract `test.sol:Test`")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("Missing EVM data")
|
||||
.bytecode
|
||||
.as_ref()
|
||||
.expect("Missing bytecode")
|
||||
.object
|
||||
.len();
|
||||
let size_when_optimized_for_cycles = build_optimized_for_cycles
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("test.sol")
|
||||
.expect("Missing file `test.sol`")
|
||||
.get("Test")
|
||||
.expect("Missing contract `test.sol:Test`")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("Missing EVM data")
|
||||
.bytecode
|
||||
.as_ref()
|
||||
.expect("Missing bytecode")
|
||||
.object
|
||||
.len();
|
||||
let size_when_optimized_for_size = build_optimized_for_size
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("test.sol")
|
||||
.expect("Missing file `test.sol`")
|
||||
.get("Test")
|
||||
.expect("Missing contract `test.sol:Test`")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("Missing EVM data")
|
||||
.bytecode
|
||||
.as_ref()
|
||||
.expect("Missing bytecode")
|
||||
.object
|
||||
.len();
|
||||
|
||||
assert!(
|
||||
size_when_optimized_for_cycles < size_when_unoptimized,
|
||||
"Expected the cycles-optimized bytecode to be smaller than the unoptimized. Optimized: {}B, Unoptimized: {}B", size_when_optimized_for_cycles, size_when_unoptimized,
|
||||
);
|
||||
assert!(
|
||||
size_when_optimized_for_size < size_when_unoptimized,
|
||||
"Expected the size-optimized bytecode to be smaller than the unoptimized. Optimized: {}B, Unoptimized: {}B", size_when_optimized_for_size, size_when_unoptimized,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//! The Solidity compiler unit tests for remappings.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
pub const CALLEE_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
contract Callable {
|
||||
function f(uint a) public pure returns(uint) {
|
||||
return a * 2;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
pub const CALLER_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
import "libraries/default/callable.sol";
|
||||
|
||||
contract Main {
|
||||
function main(Callable callable) public returns(uint) {
|
||||
return callable.f(5);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("./test.sol".to_owned(), CALLER_TEST_SOURCE.to_owned());
|
||||
sources.insert("./callable.sol".to_owned(), CALLEE_TEST_SOURCE.to_owned());
|
||||
|
||||
let mut remappings = BTreeSet::new();
|
||||
remappings.insert("libraries/default/=./".to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
Some(remappings),
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//! The Solidity compiler unit tests for runtime code.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "runtimeCode is not supported")]
|
||||
fn default() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract A {}
|
||||
|
||||
contract Test {
|
||||
function main() public pure returns(bytes memory) {
|
||||
return type(A).runtimeCode;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), source_code.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
//! The Solidity compiler unit tests for unsupported opcodes.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `CODECOPY` instruction is not supported")]
|
||||
fn codecopy_yul_runtime() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract FixedCodeCopy {
|
||||
function copyCode() public view returns (bytes memory) {
|
||||
uint256 fixedCodeSize = 64;
|
||||
bytes memory code = new bytes(fixedCodeSize);
|
||||
|
||||
assembly {
|
||||
codecopy(add(code, 0x20), 0, fixedCodeSize)
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), source_code.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
pub const CALLCODE_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract CallcodeTest {
|
||||
function testCallcode(address target, bytes4 signature, uint256 inputValue) public returns (bool) {
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
let input := mload(0x40)
|
||||
mstore(input, signature)
|
||||
mstore(add(input, 0x04), inputValue)
|
||||
|
||||
let callResult := callcode(gas(), target, 0, input, 0x24, 0, 0)
|
||||
|
||||
success := and(callResult, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `CALLCODE` instruction is not supported")]
|
||||
fn callcode_yul() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), CALLCODE_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `PC` instruction is not supported")]
|
||||
fn pc_yul() {
|
||||
let source_code = r#"
|
||||
object "ProgramCounter" {
|
||||
code {
|
||||
datacopy(0, dataoffset("ProgramCounter_deployed"), datasize("ProgramCounter_deployed"))
|
||||
return(0, datasize("ProgramCounter_deployed"))
|
||||
}
|
||||
object "ProgramCounter_deployed" {
|
||||
code {
|
||||
function getPC() -> programCounter {
|
||||
programCounter := pc()
|
||||
}
|
||||
|
||||
let pcValue := getPC()
|
||||
sstore(0, pcValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
super::build_yul(source_code).expect("Test failure");
|
||||
}
|
||||
|
||||
pub const EXTCODECOPY_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ExternalCodeCopy {
|
||||
function copyExternalCode(address target, uint256 codeSize) public view returns (bytes memory) {
|
||||
bytes memory code = new bytes(codeSize);
|
||||
|
||||
assembly {
|
||||
extcodecopy(target, add(code, 0x20), 0, codeSize)
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `EXTCODECOPY` instruction is not supported")]
|
||||
fn extcodecopy_yul() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), EXTCODECOPY_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
pub const SELFDESTRUCT_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract MinimalDestructible {
|
||||
address payable public owner;
|
||||
|
||||
constructor() {
|
||||
owner = payable(msg.sender);
|
||||
}
|
||||
|
||||
function destroy() public {
|
||||
require(msg.sender == owner, "Only the owner can call this function.");
|
||||
selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `SELFDESTRUCT` instruction is not supported")]
|
||||
fn selfdestruct_yul() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), SELFDESTRUCT_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
Reference in New Issue
Block a user