Compare commits

..

4 Commits

Author SHA1 Message Date
xermicus 7233738f45 release resolc v0.1.0-dev.10 (#209)
Signed-off-by: xermicus <cyrill@parity.io>
2025-02-11 15:51:52 +01:00
Sebastian Miasojed 79ec4dd04b Add all resolc dependencies to resolc_web.js file (#176) 2025-02-11 11:55:24 +01:00
xermicus 374563bbe5 integration: add function pointer integration test (#205)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-02-10 18:50:12 +01:00
xermicus a921e425b4 CI: fix and pin geth (#206) 2025-02-10 17:39:44 +01:00
29 changed files with 626 additions and 436 deletions
+1
View File
@@ -87,6 +87,7 @@ jobs:
path: | path: |
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js ${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm ${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc_web.js
retention-days: 1 retention-days: 1
test-revive-wasm: test-revive-wasm:
+5 -4
View File
@@ -30,11 +30,12 @@ jobs:
tar Jxf llvm.tar.xz -C llvm18/ tar Jxf llvm.tar.xz -C llvm18/
echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm18" >> $GITHUB_ENV echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm18" >> $GITHUB_ENV
- name: Install apt dependencies - name: Install geth
run: | run: |
sudo add-apt-repository -y ppa:ethereum/ethereum git clone https://github.com/xermicus/go-ethereum --branch=cl/fix-runner-state-dump --depth=1
sudo apt update cd go-ethereum
sudo apt install -y ethereum make all
echo "$(pwd)/build/bin/" >> $GITHUB_PATH
- name: Machete - name: Machete
uses: bnjbvr/cargo-machete@main uses: bnjbvr/cargo-machete@main
+7
View File
@@ -6,8 +6,15 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50` Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
## v0.1.0-dev.10
This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Added ### Added
- Support for the `coinbase` opcode. - Support for the `coinbase` opcode.
- The resolc web JS version.
### Changed ### Changed
- Missing the `--overwrite` flag emits an error instead of a warning. - Missing the `--overwrite` flag emits an error instead of a warning.
Generated
+51 -20
View File
@@ -129,6 +129,18 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "alloy-eip2124"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675264c957689f0fd75f5993a73123c2cc3b5c235a38f5b9037fe6c826bfb2c0"
dependencies = [
"alloy-primitives",
"alloy-rlp",
"crc",
"thiserror 2.0.11",
]
[[package]] [[package]]
name = "alloy-eip2930" name = "alloy-eip2930"
version = "0.1.0" version = "0.1.0"
@@ -154,25 +166,28 @@ dependencies = [
[[package]] [[package]]
name = "alloy-eips" name = "alloy-eips"
version = "0.9.2" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52dd5869ed09e399003e0e0ec6903d981b2a92e74c5d37e6b40890bad2517526" checksum = "7149e011edbd588f6df6564b369c75f6b538d76db14053d95e0b43b2d92e4266"
dependencies = [ dependencies = [
"alloy-eip2124",
"alloy-eip2930", "alloy-eip2930",
"alloy-eip7702", "alloy-eip7702",
"alloy-primitives", "alloy-primitives",
"alloy-rlp", "alloy-rlp",
"alloy-serde", "alloy-serde",
"auto_impl",
"c-kzg", "c-kzg",
"derive_more 1.0.0", "derive_more 1.0.0",
"once_cell",
"serde", "serde",
] ]
[[package]] [[package]]
name = "alloy-genesis" name = "alloy-genesis"
version = "0.9.2" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d2a7fe5c1a9bd6793829ea21a636f30fc2b3f5d2e7418ba86d96e41dd1f460" checksum = "acaec0cc4c1489d61d6f33d0c3dd522c750025f4b5c8f59cd546221e4df660e5"
dependencies = [ dependencies = [
"alloy-eips", "alloy-eips",
"alloy-primitives", "alloy-primitives",
@@ -244,9 +259,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-serde" name = "alloy-serde"
version = "0.9.2" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae0465c71d4dced7525f408d84873aeebb71faf807d22d74c4a426430ccd9b55" checksum = "86aa42c36e3c0db5bd9a7314e98aa261a61d5e3d6a0bd7e51fb8b0a3d6438481"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"serde", "serde",
@@ -1609,6 +1624,7 @@ dependencies = [
"glob", "glob",
"hex", "hex",
"libc", "libc",
"once_cell",
"serde", "serde",
] ]
@@ -1994,6 +2010,21 @@ dependencies = [
"wasmtime-types", "wasmtime-types",
] ]
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.2" version = "1.4.2"
@@ -4619,7 +4650,7 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
[[package]] [[package]]
name = "lld-sys" name = "lld-sys"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -8244,7 +8275,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-benchmarks" name = "revive-benchmarks"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"criterion", "criterion",
@@ -8256,18 +8287,18 @@ dependencies = [
[[package]] [[package]]
name = "revive-build-utils" name = "revive-build-utils"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
[[package]] [[package]]
name = "revive-builtins" name = "revive-builtins"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"revive-build-utils", "revive-build-utils",
] ]
[[package]] [[package]]
name = "revive-common" name = "revive-common"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"serde", "serde",
@@ -8277,7 +8308,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-differential" name = "revive-differential"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"alloy-genesis", "alloy-genesis",
"alloy-primitives", "alloy-primitives",
@@ -8290,7 +8321,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-integration" name = "revive-integration"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"alloy-sol-types", "alloy-sol-types",
@@ -8305,7 +8336,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-linker" name = "revive-linker"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"libc", "libc",
@@ -8317,7 +8348,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-llvm-builder" name = "revive-llvm-builder"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_cmd", "assert_cmd",
@@ -8338,7 +8369,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-llvm-context" name = "revive-llvm-context"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hex", "hex",
@@ -8358,7 +8389,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-runner" name = "revive-runner"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"hex", "hex",
@@ -8373,7 +8404,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-runtime-api" name = "revive-runtime-api"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"inkwell", "inkwell",
@@ -8383,7 +8414,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-solidity" name = "revive-solidity"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -8409,7 +8440,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-stdlib" name = "revive-stdlib"
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
dependencies = [ dependencies = [
"inkwell", "inkwell",
"revive-build-utils", "revive-build-utils",
+16 -16
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.1.0-dev.9" version = "0.1.0-dev.10"
authors = [ authors = [
"Cyrill Leutwiler <cyrill@parity.io>", "Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>", "Parity Technologies <admin@parity.io>",
@@ -14,19 +14,19 @@ repository = "https://github.com/paritytech/revive"
rust-version = "1.81.0" rust-version = "1.81.0"
[workspace.dependencies] [workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.9", path = "crates/benchmarks" } revive-benchmarks = { version = "0.1.0-dev.10", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.9", path = "crates/builtins" } revive-builtins = { version = "0.1.0-dev.10", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.9", path = "crates/common" } revive-common = { version = "0.1.0-dev.10", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.9", path = "crates/differential" } revive-differential = { version = "0.1.0-dev.10", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.9", path = "crates/integration" } revive-integration = { version = "0.1.0-dev.10", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.9", path = "crates/linker" } revive-linker = { version = "0.1.0-dev.10", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.9", path = "crates/lld-sys" } lld-sys = { version = "0.1.0-dev.10", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.9", path = "crates/llvm-context" } revive-llvm-context = { version = "0.1.0-dev.10", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.9", path = "crates/runtime-api" } revive-runtime-api = { version = "0.1.0-dev.10", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.9", path = "crates/runner" } revive-runner = { version = "0.1.0-dev.10", path = "crates/runner" }
revive-solidity = { version = "0.1.0-dev.9", path = "crates/solidity" } revive-solidity = { version = "0.1.0-dev.10", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.9", path = "crates/stdlib" } revive-stdlib = { version = "0.1.0-dev.10", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.9", path = "crates/build-utils" } revive-build-utils = { version = "0.1.0-dev.10", path = "crates/build-utils" }
hex = "0.4.3" hex = "0.4.3"
cc = "1.0" cc = "1.0"
@@ -53,8 +53,8 @@ polkavm-disassembler = "0.19.0"
polkavm = "0.19.0" polkavm = "0.19.0"
alloy-primitives = { version = "0.8.19", features = ["serde"] } alloy-primitives = { version = "0.8.19", features = ["serde"] }
alloy-sol-types = "0.8.19" alloy-sol-types = "0.8.19"
alloy-genesis = "0.9.2" alloy-genesis = "0.11.0"
alloy-serde = "0.9.2" alloy-serde = "0.11.0"
env_logger = { version = "0.11.6", default-features = false } env_logger = { version = "0.11.6", default-features = false }
serde_stacker = "0.1.11" serde_stacker = "0.1.11"
criterion = { version = "0.5.1", features = ["html_reports"] } criterion = { version = "0.5.1", features = ["html_reports"] }
+1
View File
@@ -30,6 +30,7 @@ install-npm:
install-wasm: install-npm install-wasm: install-npm
cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features
npm run build:package
install-llvm-builder: install-llvm-builder:
cargo install --path crates/llvm-builder cargo install --path crates/llvm-builder
+1 -1
View File
@@ -7,5 +7,5 @@ To create a new pre-release:
1. Merge a release PR which updates the `-dev.X` versions in the workspace `Cargo.toml` and updates the `CHANGELOG.md` accordingly 1. Merge a release PR which updates the `-dev.X` versions in the workspace `Cargo.toml` and updates the `CHANGELOG.md` accordingly
2. Push a release tag to `main` 2. Push a release tag to `main`
3. Create a __pre-release__ from the tag and manually upload the `resolc` binary from docker image 3. Create a __pre-release__ from the tag and manually upload the `resolc` binary from docker image
4. Manually upload `resolc.js` and `resolc.wasm` from the `build-revive-wasm` action artifacts. 4. Manually upload `resolc.js`, `resolc-web.js` and `resolc.wasm` from the `build-revive-wasm` action artifacts.
5. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly 5. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
+8 -1
View File
@@ -16,7 +16,14 @@
"shanghaiTime": 0, "shanghaiTime": 0,
"cancunTime": 0, "cancunTime": 0,
"terminalTotalDifficulty": 0, "terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true "terminalTotalDifficultyPassed": true,
"blobSchedule": {
"cancun": {
"target": 3,
"max": 6,
"baseFeeUpdateFraction": 3338477
}
}
}, },
"coinbase": "0xffffffffffffffffffffffffffffffffffffffff", "coinbase": "0xffffffffffffffffffffffffffffffffffffffff",
"difficulty": "0x20000", "difficulty": "0x20000",
+1 -1
View File
@@ -413,7 +413,7 @@ impl Evm {
let stderr = str::from_utf8(output.stderr.as_slice()) let stderr = str::from_utf8(output.stderr.as_slice())
.unwrap_or_else(|err| panic!("{EXECUTABLE_NAME} stderr failed to parse: {err}")); .unwrap_or_else(|err| panic!("{EXECUTABLE_NAME} stderr failed to parse: {err}"));
let mut log: EvmLog = stdout.into(); let mut log: EvmLog = format!("{stdout}{stderr}").as_str().into();
log.stderr = stderr.into(); log.stderr = stderr.into();
if self.bench { if self.bench {
log.parse_gas_used_from_bench(); log.parse_gas_used_from_bench();
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "FunctionPointer"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "26121ff0"
}
}
]
}
*/
contract FunctionPointer {
bool public flag = false;
function f0() public {
flag = true;
}
function f() public returns (bool) {
function() internal x = f0;
x();
return flag;
}
}
+1
View File
@@ -54,6 +54,7 @@ test_spec!(coinbase, "Coinbase", "Coinbase.sol");
test_spec!(create2, "CreateB", "Create2.sol"); test_spec!(create2, "CreateB", "Create2.sol");
test_spec!(transfer, "Transfer", "Transfer.sol"); test_spec!(transfer, "Transfer", "Transfer.sol");
test_spec!(send, "Send", "Send.sol"); test_spec!(send, "Send", "Send.sol");
test_spec!(function_pointer, "FunctionPointer", "FunctionPointer.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> { fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate { vec![Instantiate {
+53
View File
@@ -0,0 +1,53 @@
const fs = require("fs");
const path = require("path");
const { minify } = require("terser");
const SOLJSON_URI =
"https://binaries.soliditylang.org/wasm/soljson-v0.8.28+commit.7893614a.js";
const RESOLC_WASM_URI = "http://127.0.0.1:8080/resolc.wasm";
const RESOLC_WASM_TARGET_DIR = path.join(
__dirname,
"../target/wasm32-unknown-emscripten/release",
);
const RESOLC_JS = path.join(RESOLC_WASM_TARGET_DIR, "resolc.js");
const RESOLC_WEB_JS = path.join(RESOLC_WASM_TARGET_DIR, "resolc_web.js");
const resolcJs = fs.readFileSync(RESOLC_JS, "utf-8");
const packedJsContent = `
if (typeof importScripts === "function") {
importScripts("${SOLJSON_URI}");
var moduleArgs = {
wasmBinary: (function () {
var xhr = new XMLHttpRequest();
xhr.open("GET", "${RESOLC_WASM_URI}", false);
xhr.responseType = "arraybuffer";
xhr.send(null);
return new Uint8Array(xhr.response);
})(),
soljson: Module
};
} else {
console.log("Not a WebWorker, skipping Soljson and WASM loading.");
}
${resolcJs}
createRevive = createRevive.bind(null, moduleArgs);
`;
minify(packedJsContent)
.then((minifiedJs) => {
if (minifiedJs.error) {
console.error("Error during minification:", minifiedJs.error);
process.exit(1);
}
fs.writeFileSync(RESOLC_WEB_JS, minifiedJs.code, "utf-8");
console.log(`Combined script written to ${RESOLC_WEB_JS}`);
})
.catch((err) => {
console.error("Minification failed:", err);
process.exit(1);
});
+77 -46
View File
@@ -1,16 +1,23 @@
const { test, expect } = require('@playwright/test'); const { test, expect } = require("@playwright/test");
const fs = require('fs'); const fs = require("fs");
const path = require('path'); const path = require("path");
function loadFixture(fixture) { function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`); const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`);
return JSON.parse(fs.readFileSync(fixturePath, 'utf-8')); return JSON.parse(fs.readFileSync(fixturePath, "utf-8"));
}
async function loadTestPage(page) {
await page.goto("http://127.0.0.1:8080");
const outputElement = page.locator("#output");
await outputElement.waitFor({ state: "visible" });
await page.setContent("");
} }
async function runWorker(page, input) { async function runWorker(page, input) {
return await page.evaluate((input) => { return await page.evaluate((input) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const worker = new Worker('worker.js'); const worker = new Worker("worker.js");
worker.postMessage(JSON.stringify(input)); worker.postMessage(JSON.stringify(input));
worker.onmessage = (event) => { worker.onmessage = (event) => {
@@ -26,69 +33,87 @@ async function runWorker(page, input) {
}, input); }, input);
} }
test('should successfully compile valid Solidity code in the browser', async ({ page }) => { test("should successfully compile valid Solidity code in browser", async ({
await page.goto("http://127.0.0.1:8080"); page,
await page.setContent(""); }) => {
const standardInput = loadFixture('storage.json') await loadTestPage(page);
const standardInput = loadFixture("storage.json");
const result = await runWorker(page, standardInput); const result = await runWorker(page, standardInput);
expect(typeof result).toBe('string'); expect(typeof result).toBe("string");
let output = JSON.parse(result); let output = JSON.parse(result);
expect(output).toHaveProperty('contracts'); expect(output).toHaveProperty("contracts");
expect(output.contracts['fixtures/storage.sol']).toHaveProperty('Storage'); expect(output.contracts["fixtures/storage.sol"]).toHaveProperty("Storage");
expect(output.contracts['fixtures/storage.sol'].Storage).toHaveProperty('abi'); expect(output.contracts["fixtures/storage.sol"].Storage).toHaveProperty(
expect(output.contracts['fixtures/storage.sol'].Storage).toHaveProperty('evm'); "abi",
expect(output.contracts['fixtures/storage.sol'].Storage.evm).toHaveProperty('bytecode'); );
expect(output.contracts["fixtures/storage.sol"].Storage).toHaveProperty(
"evm",
);
expect(output.contracts["fixtures/storage.sol"].Storage.evm).toHaveProperty(
"bytecode",
);
}); });
test('should successfully compile large valid Solidity code in the browser', async ({ page }) => { test("should successfully compile large valid Solidity code in browser", async ({
await page.goto("http://127.0.0.1:8080"); page,
await page.setContent(""); browserName,
const standardInput = loadFixture('token.json') }) => {
if (browserName === "firefox") {
// Skipping tests with large contracts on Firefox due to out-of-memory issues.
test.skip();
}
await loadTestPage(page);
const standardInput = loadFixture("token.json");
const result = await runWorker(page, standardInput); const result = await runWorker(page, standardInput);
expect(typeof result).toBe('string'); expect(typeof result).toBe("string");
let output = JSON.parse(result); let output = JSON.parse(result);
expect(output).toHaveProperty('contracts'); expect(output).toHaveProperty("contracts");
expect(output.contracts['fixtures/token.sol']).toHaveProperty('MyToken'); expect(output.contracts["fixtures/token.sol"]).toHaveProperty("MyToken");
expect(output.contracts['fixtures/token.sol'].MyToken).toHaveProperty('abi'); expect(output.contracts["fixtures/token.sol"].MyToken).toHaveProperty("abi");
expect(output.contracts['fixtures/token.sol'].MyToken).toHaveProperty('evm'); expect(output.contracts["fixtures/token.sol"].MyToken).toHaveProperty("evm");
expect(output.contracts['fixtures/token.sol'].MyToken.evm).toHaveProperty('bytecode'); expect(output.contracts["fixtures/token.sol"].MyToken.evm).toHaveProperty(
"bytecode",
);
}); });
test('should throw an error for invalid Solidity code in the browser', async ({ page }) => { test("should throw an error for invalid Solidity code in browser", async ({
await page.goto("http://127.0.0.1:8080"); page,
await page.setContent(""); }) => {
const standardInput = loadFixture('invalid_contract_content.json') await loadTestPage(page);
const standardInput = loadFixture("invalid_contract_content.json");
const result = await runWorker(page, standardInput); const result = await runWorker(page, standardInput);
expect(typeof result).toBe('string'); expect(typeof result).toBe("string");
let output = JSON.parse(result); let output = JSON.parse(result);
expect(output).toHaveProperty('errors'); expect(output).toHaveProperty("errors");
expect(Array.isArray(output.errors)).toBeTruthy(); // Check if it's an array expect(Array.isArray(output.errors)).toBeTruthy(); // Check if it's an array
expect(output.errors.length).toBeGreaterThan(0); expect(output.errors.length).toBeGreaterThan(0);
expect(output.errors[0]).toHaveProperty('type'); expect(output.errors[0]).toHaveProperty("type");
expect(output.errors[0].type).toContain('ParserError'); expect(output.errors[0].type).toContain("ParserError");
}); });
test('should return not found error for missing imports in the browser', async ({page}) => { test("should return not found error for missing imports in browser", async ({
await page.goto("http://127.0.0.1:8080"); page,
await page.setContent(""); }) => {
const standardInput = loadFixture('missing_import.json') await loadTestPage(page);
const standardInput = loadFixture("missing_import.json");
const result = await runWorker(page, standardInput); const result = await runWorker(page, standardInput);
expect(typeof result).toBe('string'); expect(typeof result).toBe("string");
let output = JSON.parse(result); let output = JSON.parse(result);
expect(output).toHaveProperty('errors'); expect(output).toHaveProperty("errors");
expect(Array.isArray(output.errors)).toBeTruthy(); // Check if it's an array expect(Array.isArray(output.errors)).toBeTruthy(); // Check if it's an array
expect(output.errors.length).toBeGreaterThan(0); expect(output.errors.length).toBeGreaterThan(0);
expect(output.errors[0]).toHaveProperty('message'); expect(output.errors[0]).toHaveProperty("message");
expect(output.errors[0].message).toContain('Source "nonexistent/console.sol" not found'); expect(output.errors[0].message).toContain(
'Source "nonexistent/console.sol" not found',
);
}); });
test('should successfully compile a valid Solidity contract that instantiates another contract in the browser', async ({ page }) => { test('should successfully compile a valid Solidity contract that instantiates another contract in the browser', async ({ page }) => {
await page.goto("http://127.0.0.1:8080"); await loadTestPage(page);
await page.setContent("");
const standardInput = loadFixture('instantiate.json') const standardInput = loadFixture('instantiate.json')
const result = await runWorker(page, standardInput); const result = await runWorker(page, standardInput);
@@ -105,9 +130,15 @@ test('should successfully compile a valid Solidity contract that instantiates an
expect(output.contracts['fixtures/instantiate.sol'].MainContract.evm).toHaveProperty('bytecode'); expect(output.contracts['fixtures/instantiate.sol'].MainContract.evm).toHaveProperty('bytecode');
}); });
test('should successfully compile a valid Solidity contract that instantiates the token contracts in the browser', async ({ page }) => { test('should successfully compile a valid Solidity contract that instantiates the token contracts in the browser', async ({
await page.goto("http://127.0.0.1:8080"); page,
await page.setContent(""); browserName,
}) => {
if (browserName === "firefox") {
// Skipping tests with large contracts on Firefox due to out-of-memory issues.
test.skip();
}
await loadTestPage(page);
const standardInput = loadFixture('instantiate_tokens.json') const standardInput = loadFixture('instantiate_tokens.json')
const result = await runWorker(page, standardInput); const result = await runWorker(page, standardInput);
+35 -32
View File
@@ -1,54 +1,57 @@
var Module = { Module.stdinData = null;
stdinData: null, Module.stdinDataPosition = 0;
stdinDataPosition: 0, Module.stdoutData = [];
stdoutData: [], Module.stderrData = [];
stderrData: [],
// Function to read and return all collected stdout data as a string // Method to read all collected stdout data
readFromStdout: function() { Module.readFromStdout = function () {
if (!this.stdoutData.length) return ""; if (!Module.stdoutData.length) return "";
const decoder = new TextDecoder('utf-8'); const decoder = new TextDecoder("utf-8");
const data = decoder.decode(new Uint8Array(this.stdoutData)); const data = decoder.decode(new Uint8Array(Module.stdoutData));
this.stdoutData = []; Module.stdoutData = [];
return data; return data;
}, };
// Function to read and return all collected stderr data as a string // Method to read all collected stderr data
readFromStderr: function() { Module.readFromStderr = function () {
if (!this.stderrData.length) return ""; if (!Module.stderrData.length) return "";
const decoder = new TextDecoder('utf-8'); const decoder = new TextDecoder("utf-8");
const data = decoder.decode(new Uint8Array(this.stderrData)); const data = decoder.decode(new Uint8Array(Module.stderrData));
this.stderrData = []; Module.stderrData = [];
return data; return data;
}, };
// Function to set input data for stdin // Method to write data to stdin
writeToStdin: function(data) { Module.writeToStdin = function (data) {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
this.stdinData = encoder.encode(data); Module.stdinData = encoder.encode(data);
this.stdinDataPosition = 0; Module.stdinDataPosition = 0;
}, };
// `preRun` is called before the program starts running // Override the `preRun` method to customize file system initialization
preRun: function() { Module.preRun = Module.preRun || [];
// Define a custom stdin function Module.preRun.push(function () {
// Custom stdin function
function customStdin() { function customStdin() {
if (!Module.stdinData || Module.stdinDataPosition >= Module.stdinData.length) { if (
!Module.stdinData ||
Module.stdinDataPosition >= Module.stdinData.length
) {
return null; // End of input (EOF) return null; // End of input (EOF)
} }
return Module.stdinData[Module.stdinDataPosition++]; return Module.stdinData[Module.stdinDataPosition++];
} }
// Define a custom stdout function // Custom stdout function
function customStdout(char) { function customStdout(char) {
Module.stdoutData.push(char); Module.stdoutData.push(char);
} }
// Define a custom stderr function // Custom stderr function
function customStderr(char) { function customStderr(char) {
Module.stderrData.push(char); Module.stderrData.push(char);
} }
// Initialize the FS (File System) with custom handlers
FS.init(customStdin, customStdout, customStderr); FS.init(customStdin, customStdout, customStderr);
}, });
};
+9 -4
View File
@@ -1,7 +1,9 @@
mergeInto(LibraryManager.library, { mergeInto(LibraryManager.library, {
soljson_compile: function (inputPtr, inputLen) { soljson_compile: function (inputPtr, inputLen) {
const inputJson = UTF8ToString(inputPtr, inputLen); const inputJson = UTF8ToString(inputPtr, inputLen);
const output = Module.soljson.cwrap('solidity_compile', 'string', ['string'])(inputJson); const output = Module.soljson.cwrap("solidity_compile", "string", [
"string",
])(inputJson);
return stringToNewUTF8(output); return stringToNewUTF8(output);
}, },
soljson_version: function () { soljson_version: function () {
@@ -14,15 +16,18 @@ mergeInto(LibraryManager.library, {
revive.writeToStdin(inputJson); revive.writeToStdin(inputJson);
// Call main on the new instance // Call main on the new instance
const result = revive.callMain(['--recursive-process']); const result = revive.callMain(["--recursive-process"]);
if (result) { if (result) {
const stderrString = revive.readFromStderr(); const stderrString = revive.readFromStderr();
const error = JSON.stringify({ type: 'error', message: stderrString || "Unknown error" }); const error = JSON.stringify({
type: "error",
message: stderrString || "Unknown error",
});
return stringToNewUTF8(error); return stringToNewUTF8(error);
} else { } else {
const stdoutString = revive.readFromStdout(); const stdoutString = revive.readFromStdout();
const json = JSON.stringify({ type: 'success', data: stdoutString }); const json = JSON.stringify({ type: "success", data: stdoutString });
return stringToNewUTF8(json); return stringToNewUTF8(json);
} }
}, },
+4 -4
View File
@@ -1,9 +1,9 @@
const soljson = require('solc/soljson'); const soljson = require("solc/soljson");
const createRevive = require('./resolc.js'); const createRevive = require("./resolc.js");
async function compile(standardJsonInput) { async function compile(standardJsonInput) {
if (!standardJsonInput) { if (!standardJsonInput) {
throw new Error('Input JSON for the Solidity compiler is required.'); throw new Error("Input JSON for the Solidity compiler is required.");
} }
// Initialize the compiler // Initialize the compiler
@@ -14,7 +14,7 @@ async function compile(standardJsonInput) {
compiler.writeToStdin(JSON.stringify(standardJsonInput)); compiler.writeToStdin(JSON.stringify(standardJsonInput));
// Run the compiler // Run the compiler
compiler.callMain(['--standard-json']); compiler.callMain(["--standard-json"]);
// Collect output // Collect output
const stdout = compiler.readFromStdout(); const stdout = compiler.readFromStdout();
+8 -8
View File
@@ -1,9 +1,9 @@
const { compile } = require('./revive.js'); const { compile } = require("./revive.js");
const compilerStandardJsonInput = { const compilerStandardJsonInput = {
language: 'Solidity', language: "Solidity",
sources: { sources: {
'MyContract.sol': { "MyContract.sol": {
content: ` content: `
// SPDX-License-Identifier: UNLICENSED // SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
@@ -21,18 +21,18 @@ const compilerStandardJsonInput = {
runs: 200, runs: 200,
}, },
outputSelection: { outputSelection: {
'*': { "*": {
'*': ['abi'], "*": ["abi"],
}, },
}, },
}, },
}; };
async function runCompiler() { async function runCompiler() {
let output = await compile(compilerStandardJsonInput) let output = await compile(compilerStandardJsonInput);
console.log("Output: " + output); console.log("Output: " + output);
} }
runCompiler().catch(err => { runCompiler().catch((err) => {
console.error('Error:', err); console.error("Error:", err);
}); });
+17 -15
View File
@@ -1,6 +1,5 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Web Worker Example</title> <title>Web Worker Example</title>
@@ -19,14 +18,14 @@
<h1>Revive Compilation Output</h1> <h1>Revive Compilation Output</h1>
<pre id="output"></pre> <pre id="output"></pre>
<script> <script>
var outputElement = document.getElementById('output'); var outputElement = document.getElementById("output");
var worker = new Worker('./worker.js'); var worker = new Worker("./worker.js");
const standardJsonInput = { const standardJsonInput = {
language: 'Solidity', language: "Solidity",
sources: { sources: {
contract: { contract: {
content: 'contract MyContract { function f() public { } }', content: "contract MyContract { function f() public { } }",
} },
}, },
settings: { settings: {
optimizer: { optimizer: {
@@ -34,18 +33,21 @@
runs: 200, runs: 200,
}, },
outputSelection: { outputSelection: {
'*': { "*": {
'*': ['abi'], "*": ["abi"],
} },
} },
} },
}; };
worker.addEventListener('message', function (e) { worker.addEventListener(
"message",
function (e) {
outputElement.textContent = e.data.output; outputElement.textContent = e.data.output;
}, false); },
false,
);
worker.postMessage(JSON.stringify(standardJsonInput)); worker.postMessage(JSON.stringify(standardJsonInput));
</script> </script>
</body> </body>
</html> </html>
-1
View File
@@ -1 +0,0 @@
../../../target/wasm32-unknown-emscripten/release/resolc.js
+1
View File
@@ -0,0 +1 @@
../../../target/wasm32-unknown-emscripten/release/resolc_web.js
+2 -6
View File
@@ -1,18 +1,14 @@
importScripts("./resolc_web.js");
importScripts('./soljson.js');
importScripts('./resolc.js');
// Handle messages from the main thread // Handle messages from the main thread
onmessage = async function (e) { onmessage = async function (e) {
const m = createRevive(); const m = createRevive();
m.soljson = Module;
// Set input data for stdin // Set input data for stdin
m.writeToStdin(e.data); m.writeToStdin(e.data);
// Compile the Solidity source code // Compile the Solidity source code
m.callMain(['--standard-json']); m.callMain(["--standard-json"]);
postMessage({ output: m.readFromStdout() || m.readFromStderr() }); postMessage({ output: m.readFromStdout() || m.readFromStderr() });
}; };
+1 -4
View File
@@ -12,11 +12,8 @@
}, },
"outputSelection": { "outputSelection": {
"*": { "*": {
"*": [ "*": ["abi"]
"abi"
]
} }
} }
} }
} }
+1 -4
View File
@@ -12,11 +12,8 @@
}, },
"outputSelection": { "outputSelection": {
"*": { "*": {
"*": [ "*": ["abi"]
"abi"
]
} }
} }
} }
} }
+1 -4
View File
@@ -12,11 +12,8 @@
}, },
"outputSelection": { "outputSelection": {
"*": { "*": {
"*": [ "*": ["abi"]
"abi"
]
} }
} }
} }
} }
+1 -4
View File
@@ -72,11 +72,8 @@
}, },
"outputSelection": { "outputSelection": {
"*": { "*": {
"*": [ "*": ["abi"]
"abi"
]
} }
} }
} }
} }
+7 -4
View File
@@ -5,17 +5,20 @@
"solc": "^0.8.28" "solc": "^0.8.28"
}, },
"scripts": { "scripts": {
"fetch:soljson": "wget https://binaries.soliditylang.org/wasm/soljson-v0.8.28+commit.7893614a.js -O ./examples/web/soljson.js", "example:web": "http-server ./examples/web/",
"example:web": "npm run fetch:soljson && http-server ./examples/web/",
"example:node": "node ./examples/node/run_revive.js", "example:node": "node ./examples/node/run_revive.js",
"test:node": "mocha --timeout 60000 ./tests", "test:node": "mocha --timeout 60000 ./tests",
"test:bun": "bun test --timeout 60000 node.test", "test:bun": "bun test --timeout 60000 node.test",
"test:all": "npm run test:node && npm run test:bun" "test:all": "npm run test:node && npm run test:bun",
"format": "prettier --write .",
"build:package": "node ./build.js"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.49.1", "@playwright/test": "^1.49.1",
"chai": "^5.1.2", "chai": "^5.1.2",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"mocha": "^11.0.1" "mocha": "^11.0.1",
"prettier": "^3.4.2",
"terser": "^5.37.0"
} }
} }
+14 -15
View File
@@ -1,10 +1,10 @@
const { defineConfig, devices } = require('@playwright/test'); const { defineConfig, devices } = require("@playwright/test");
/** /**
* @see https://playwright.dev/docs/test-configuration * @see https://playwright.dev/docs/test-configuration
*/ */
module.exports = defineConfig({ module.exports = defineConfig({
testDir: './e2e', testDir: "./e2e",
/* Run tests in files in parallel */ /* Run tests in files in parallel */
fullyParallel: true, fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */ /* Fail the build on CI if you accidentally left test.only in the source code. */
@@ -14,39 +14,38 @@ module.exports = defineConfig({
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined, workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'list', reporter: "list",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: { use: {
/* Base URL to use in actions like `await page.goto('/')`. */ /* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://127.0.0.1:8080', baseURL: "http://127.0.0.1:8080",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry', trace: "on-first-retry",
}, },
timeout: 480000, timeout: 480000,
/* Configure projects for major browsers */ /* Configure projects for major browsers */
projects: [ projects: [
{ {
name: 'chromium', name: "chromium",
use: { ...devices['Desktop Chrome'] }, use: { ...devices["Desktop Chrome"] },
}, },
{ {
name: 'firefox', name: "firefox",
use: { ...devices['Desktop Firefox'] }, use: { ...devices["Desktop Firefox"] },
}, },
{ {
name: 'webkit', name: "webkit",
use: { ...devices['Desktop Safari'] }, use: { ...devices["Desktop Safari"] },
} },
], ],
/* Run your local dev server before starting the tests */ /* Run your local dev server before starting the tests */
webServer: { webServer: {
command: 'npm run example:web', command: "npm run example:web",
url: 'http://127.0.0.1:8080', url: "http://127.0.0.1:8080",
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
}, },
}); });
+52 -34
View File
@@ -1,45 +1,61 @@
import { expect } from 'chai'; import { expect } from "chai";
import { compile } from '../examples/node/revive.js'; import { compile } from "../examples/node/revive.js";
import { fileURLToPath } from 'url'; import { fileURLToPath } from "url";
import path from 'path'; import path from "path";
import fs from 'fs'; import fs from "fs";
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
function loadFixture(fixture) { function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`); const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`);
return JSON.parse(fs.readFileSync(fixturePath, 'utf-8')); return JSON.parse(fs.readFileSync(fixturePath, "utf-8"));
} }
describe('Compile Function Tests', function () { describe("Compile Function Tests", function () {
it('should successfully compile valid Solidity code', async function () { it("should successfully compile valid Solidity code", async function () {
const standardInput = loadFixture('storage.json') const standardInput = loadFixture("storage.json");
const result = await compile(standardInput); const result = await compile(standardInput);
expect(result).to.be.a('string'); expect(result).to.be.a("string");
const output = JSON.parse(result); const output = JSON.parse(result);
expect(output).to.have.property('contracts'); expect(output).to.have.property("contracts");
expect(output.contracts['fixtures/storage.sol']).to.have.property('Storage'); expect(output.contracts["fixtures/storage.sol"]).to.have.property(
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property('abi'); "Storage",
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property('evm'); );
expect(output.contracts['fixtures/storage.sol'].Storage.evm).to.have.property('bytecode'); 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");
}); });
if (typeof globalThis.Bun == 'undefined') { if (typeof globalThis.Bun == "undefined") {
// Running this test with Bun on a Linux host causes: // Running this test with Bun on a Linux host causes:
// RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)') // 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. // Once this issue is resolved, the test will be re-enabled.
it('should successfully compile large Solidity code', async function () { it("should successfully compile large Solidity code", async function () {
const standardInput = loadFixture('token.json') const standardInput = loadFixture("token.json");
const result = await compile(standardInput); const result = await compile(standardInput);
expect(result).to.be.a('string'); expect(result).to.be.a("string");
const output = JSON.parse(result); const output = JSON.parse(result);
expect(output).to.have.property('contracts'); expect(output).to.have.property("contracts");
expect(output.contracts['fixtures/token.sol']).to.have.property('MyToken'); expect(output.contracts["fixtures/token.sol"]).to.have.property(
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property('abi'); "MyToken",
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property('evm'); );
expect(output.contracts['fixtures/token.sol'].MyToken.evm).to.have.property('bytecode'); 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");
}); });
it("should successfully compile a valid Solidity contract that instantiates the token contracts", async function () { it("should successfully compile a valid Solidity contract that instantiates the token contracts", async function () {
@@ -64,29 +80,31 @@ describe('Compile Function Tests', function () {
}); });
} }
it('should throw an error for invalid Solidity code', async function () { it("should throw an error for invalid Solidity code", async function () {
const standardInput = loadFixture('invalid_contract_content.json') const standardInput = loadFixture("invalid_contract_content.json");
const result = await compile(standardInput); const result = await compile(standardInput);
expect(result).to.be.a('string'); expect(result).to.be.a("string");
const output = JSON.parse(result); const output = JSON.parse(result);
expect(output).to.have.property('errors'); expect(output).to.have.property("errors");
expect(output.errors).to.be.an('array'); expect(output.errors).to.be.an("array");
expect(output.errors.length).to.be.greaterThan(0); expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].type).to.exist; expect(output.errors[0].type).to.exist;
expect(output.errors[0].type).to.contain("ParserError"); expect(output.errors[0].type).to.contain("ParserError");
}); });
it('should return not found error for missing imports', async function () { it("should return not found error for missing imports", async function () {
const standardInput = loadFixture('missing_import.json') const standardInput = loadFixture("missing_import.json");
const result = await compile(standardInput); const result = await compile(standardInput);
const output = JSON.parse(result); const output = JSON.parse(result);
expect(output).to.have.property('errors'); expect(output).to.have.property("errors");
expect(output.errors).to.be.an('array'); expect(output.errors).to.be.an("array");
expect(output.errors.length).to.be.greaterThan(0); expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].message).to.exist; expect(output.errors[0].message).to.exist;
expect(output.errors[0].message).to.include('Source "nonexistent/console.sol" not found'); expect(output.errors[0].message).to.include(
'Source "nonexistent/console.sol" not found',
);
}); });
it("should successfully compile a valid Solidity contract that instantiates another contract", async function () { it("should successfully compile a valid Solidity contract that instantiates another contract", async function () {
+2 -1
View File
@@ -3,7 +3,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"test:cli": "npm run test -w crates/solidity/src/tests/cli-tests", "test:cli": "npm run test -w crates/solidity/src/tests/cli-tests",
"test:wasm": "npm run test:all -w js" "test:wasm": "npm run test:all -w js",
"build:package": "npm run build:package -w js"
}, },
"workspaces": [ "workspaces": [
"crates/solidity/src/tests/cli-tests", "crates/solidity/src/tests/cli-tests",