Compare commits

..

2 Commits

Author SHA1 Message Date
xermicus bec5d60b7c release resolc-0.1.0-dev.9 (#178)
Signed-off-by: xermicus <cyrill@parity.io>
2025-01-29 12:49:54 +01:00
xermicus 3608a5a143 runtime-api: pass call arguments in registers instead of spilling to stack (#174)
Companion to paritytech/polkadot-sdk#7319

Signed-off-by: xermicus <cyrill@parity.io>
2025-01-28 12:45:54 +01:00
30 changed files with 910 additions and 1270 deletions
-1
View File
@@ -87,7 +87,6 @@ jobs:
path: |
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc_packed.js
retention-days: 1
test-revive-wasm:
+12
View File
@@ -2,6 +2,18 @@
## Unreleased
## v0.1.0-dev.9
This is a development pre-release.
### Added
### Changed
- Syscalls with more than 6 arguments now pack them into registers.
### Fixed
- Remove reloading of the resolc.js file (fix issue with relative path in web worker)
## v0.1.0-dev.8
This is a development pre-release.
Generated
+380 -396
View File
File diff suppressed because it is too large Load Diff
+18 -18
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "0.1.0-dev.8"
version = "0.1.0-dev.9"
authors = [
"Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>",
@@ -14,29 +14,29 @@ repository = "https://github.com/paritytech/revive"
rust-version = "1.81.0"
[workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.8", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.8", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.8", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.8", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.8", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.8", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.8", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.8", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.8", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.8", path = "crates/runner" }
revive-solidity = { version = "0.1.0-dev.8", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.8", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.8", path = "crates/build-utils" }
revive-benchmarks = { version = "0.1.0-dev.9", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.9", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.9", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.9", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.9", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.9", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.9", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.9", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.9", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.9", path = "crates/runner" }
revive-solidity = { version = "0.1.0-dev.9", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.9", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.9", path = "crates/build-utils" }
hex = "0.4.3"
cc = "1.0"
libc = "0.2.169"
tempfile = "3.8"
anyhow = "1.0"
semver = { version = "1.0", features = [ "serde" ] }
semver = { version = "1.0", features = ["serde"] }
itertools = "0.14"
serde = { version = "1.0", features = [ "derive" ] }
serde_json = { version = "1.0", features = [ "arbitrary_precision" ] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
regex = "1.10"
once_cell = "1.19"
num = "0.4.3"
@@ -73,7 +73,7 @@ assert_fs = "1.1.2"
# polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.6", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "d62a90c8c729acd98c7e9a5cab9803b8b211ffc5" }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "4302f74f7874e6a894578731142a7b310a1449b0" }
# llvm
[workspace.dependencies.inkwell]
-1
View File
@@ -30,7 +30,6 @@ install-npm:
install-wasm: install-npm
cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features
npm run build:package
install-llvm-builder:
cargo install --path crates/llvm-builder
@@ -1339,11 +1339,17 @@ where
self.llvm.custom_width_int_type(bit_length as u32)
}
/// Returns the register witdh sized type.
/// Returns the XLEN witdh sized type.
pub fn xlen_type(&self) -> inkwell::types::IntType<'ctx> {
self.llvm.custom_width_int_type(crate::polkavm::XLEN as u32)
}
/// Returns the PolkaVM native register width sized type.
pub fn register_type(&self) -> inkwell::types::IntType<'ctx> {
self.llvm
.custom_width_int_type(revive_common::BIT_LENGTH_X64 as u32)
}
/// Returns the sentinel pointer value.
pub fn sentinel_pointer(&self) -> Pointer<'ctx> {
let sentinel_pointer = self
+67 -55
View File
@@ -58,39 +58,48 @@ where
};
let flags = context.xlen_type().const_int(flags as u64, false);
let argument_type = revive_runtime_api::calling_convention::call(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "call_arguments");
let arguments = &[
flags.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
deposit_pointer.value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
output_pointer.value.as_basic_value_enum(),
output_length_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
argument_pointer.value,
argument_type,
arguments,
context.llvm(),
flags,
address_pointer.to_int(context),
"address_and_callee",
)?;
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
deposit_pointer.to_int(context),
value_pointer.to_int(context),
"deposit_and_value",
)?;
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
input_length,
input_pointer.to_int(context),
"input_data",
)?;
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
output_length_pointer.to_int(context),
output_pointer.to_int(context),
"output_data",
)?;
let name = revive_runtime_api::polkavm_imports::CALL;
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"call_argument_pointer",
)?;
let success = context
.build_runtime_call(name, &[argument_pointer.into()])
.build_runtime_call(
name,
&[
flags_and_callee.into(),
context.register_type().const_all_ones().into(),
context.register_type().const_all_ones().into(),
deposit_and_value.into(),
input_data.into(),
output_data.into(),
],
)
.unwrap_or_else(|| panic!("{name} should return a value"))
.into_int_value();
@@ -144,38 +153,41 @@ where
let flags = context.xlen_type().const_int(0u64, false);
let argument_type = revive_runtime_api::calling_convention::delegate_call(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "delegate_call_arguments");
let arguments = &[
flags.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
deposit_pointer.value.as_basic_value_enum(),
input_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
output_pointer.value.as_basic_value_enum(),
output_length_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
argument_pointer.value,
argument_type,
arguments,
context.llvm(),
flags,
address_pointer.to_int(context),
"address_and_callee",
)?;
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
input_length,
input_pointer.to_int(context),
"input_data",
)?;
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
output_length_pointer.to_int(context),
output_pointer.to_int(context),
"output_data",
)?;
let name = revive_runtime_api::polkavm_imports::DELEGATE_CALL;
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"delegate_call_argument_pointer",
)?;
let success = context
.build_runtime_call(name, &[argument_pointer.into()])
.build_runtime_call(
name,
&[
flags_and_callee.into(),
context.register_type().const_all_ones().into(),
context.register_type().const_all_ones().into(),
deposit_pointer.to_int(context).into(),
input_data.into(),
output_data.into(),
],
)
.unwrap_or_else(|| panic!("{name} should return a value"))
.into_int_value();
+27 -38
View File
@@ -26,15 +26,6 @@ where
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
let input_data_pointer = context.build_gep(
code_hash_pointer,
&[context
.xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
context.byte_type(),
"input_ptr_parameter_offset",
);
let value_pointer = context.build_alloca_at_entry(context.value_type(), "transferred_value");
context.build_store(value_pointer, value)?;
@@ -56,40 +47,38 @@ where
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
context.build_store(deposit_pointer, context.word_type().const_all_ones())?;
let argument_type = revive_runtime_api::calling_convention::instantiate(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "instantiate_arguments");
let arguments = &[
code_hash_pointer.value.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
deposit_pointer.value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_data_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
salt_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
argument_pointer.value,
argument_type,
arguments,
context.llvm(),
deposit_pointer.to_int(context),
value_pointer.to_int(context),
"deposit_and_value",
)?;
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
input_length,
code_hash_pointer.to_int(context),
"input_data",
)?;
let address_and_salt = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
address_pointer.to_int(context),
salt_pointer.to_int(context),
"output_data",
)?;
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"instantiate_argument_pointer",
)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::INSTANTIATE,
&[argument_pointer.into()],
&[
context.register_type().const_all_ones().into(),
context.register_type().const_all_ones().into(),
deposit_and_value.into(),
input_data.into(),
context.register_type().const_all_ones().into(),
address_and_salt.into(),
],
);
let address = context.build_byte_swap(context.build_load(address_pointer, "address")?)?;
+1
View File
@@ -85,6 +85,7 @@ impl ExtBuilder {
.unwrap();
pallet_balances::GenesisConfig::<Runtime> {
balances: self.balance_genesis_config,
dev_accounts: None,
}
.assimilate_storage(&mut t)
.unwrap();
-1
View File
@@ -87,6 +87,5 @@ impl pallet_revive::Config for Runtime {
type UploadOrigin = EnsureSigned<AccountId32>;
type InstantiateOrigin = EnsureSigned<AccountId32>;
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type Debug = ();
type ChainId = ConstU64<420_420_420>;
}
+18 -116
View File
@@ -1,10 +1,4 @@
use inkwell::{
builder::Builder,
context::Context,
module::Module,
types::{BasicType, StructType},
values::{BasicValueEnum, PointerValue},
};
use inkwell::{builder::Builder, context::Context, module::Module, values::IntValue};
/// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in.
pub fn min_stack_size<'context>(
@@ -21,115 +15,23 @@ pub fn min_stack_size<'context>(
module
}
/// Helper for building function calls with stack spilled arguments.
/// - `pointer`: points to a struct of the packed argument struct type
/// - `type`: the packed argument struct type
/// - `arguments`: a correctly ordered list of the struct field values
pub fn spill<'ctx>(
/// Helper for packing two 32 bit integer values into a 64 bit integer value.
pub fn pack_hi_lo_reg<'ctx>(
builder: &Builder<'ctx>,
pointer: PointerValue<'ctx>,
r#type: StructType<'ctx>,
arguments: &[BasicValueEnum<'ctx>],
) -> anyhow::Result<()> {
for index in 0..r#type.get_field_types().len() {
let field_pointer = builder.build_struct_gep(
r#type,
pointer,
index as u32,
&format!("spill_parameter_{}", index),
)?;
let field_value = arguments
.get(index)
.ok_or_else(|| anyhow::anyhow!("invalid index {index} for struct type {}", r#type))?;
builder.build_store(field_pointer, *field_value)?;
}
context: &'ctx Context,
hi: IntValue<'ctx>,
lo: IntValue<'ctx>,
name: &str,
) -> anyhow::Result<IntValue<'ctx>> {
assert_eq!(hi.get_type(), context.i32_type());
assert_eq!(lo.get_type(), context.i32_type());
Ok(())
}
/// Returns a packed struct argument type for the `instantiate` API.
pub fn instantiate(context: &Context) -> StructType {
context.struct_type(
&[
// code_hash_ptr: u32,
context.i32_type().as_basic_type_enum(),
// ref_time_limit: u64,
context.i64_type().as_basic_type_enum(),
// proof_size_limit: u64,
context.i64_type().as_basic_type_enum(),
// deposit_ptr: u32,
context.i32_type().as_basic_type_enum(),
// value_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_len: u32,
context.i32_type().as_basic_type_enum(),
// address_ptr: u32,
context.i32_type().as_basic_type_enum(),
// output_ptr: u32,
context.i32_type().as_basic_type_enum(),
// output_len_ptr: u32,
context.i32_type().as_basic_type_enum(),
// salt_ptr: u32,
context.i32_type().as_basic_type_enum(),
],
false,
)
}
/// Returns a packed struct argument type for the `call` API.
pub fn call(context: &Context) -> StructType {
context.struct_type(
&[
// flags: u32,
context.i32_type().as_basic_type_enum(),
// address_ptr:
context.i32_type().as_basic_type_enum(),
// ref_time_limit: u64,
context.i64_type().as_basic_type_enum(),
// proof_size_limit: u64,
context.i64_type().as_basic_type_enum(),
// deposit_ptr: u32,
context.i32_type().as_basic_type_enum(),
// value_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_len: u32,
context.i32_type().as_basic_type_enum(),
// output_ptr: u32,
context.i32_type().as_basic_type_enum(),
// output_len_ptr: u32,
context.i32_type().as_basic_type_enum(),
],
false,
)
}
/// Returns a packed struct argument type for the `delegate_call` API.
pub fn delegate_call(context: &Context) -> StructType {
context.struct_type(
&[
// flags: u32,
context.i32_type().as_basic_type_enum(),
// address_ptr:
context.i32_type().as_basic_type_enum(),
// ref_time_limit: u64,
context.i64_type().as_basic_type_enum(),
// proof_size_limit: u64,
context.i64_type().as_basic_type_enum(),
// deposit_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_len: u32,
context.i32_type().as_basic_type_enum(),
// output_ptr: u32,
context.i32_type().as_basic_type_enum(),
// output_len_ptr: u32,
context.i32_type().as_basic_type_enum(),
],
false,
)
let lo_part = builder.build_int_z_extend(lo, context.i64_type(), &format!("{name}_lo_part"))?;
let hi_part = builder.build_int_z_extend(hi, context.i64_type(), &format!("{name}_hi_part"))?;
let hi_part_shifted = builder.build_left_shift(
hi_part,
context.i64_type().const_int(32, false),
&format!("{name}_hi_part_shifted"),
)?;
Ok(builder.build_or(hi_part_shifted, lo_part, name)?)
}
+3 -3
View File
@@ -76,7 +76,7 @@ POLKAVM_IMPORT(void, block_hash, uint32_t, uint32_t)
POLKAVM_IMPORT(void, block_number, uint32_t)
POLKAVM_IMPORT(uint64_t, call, uint32_t)
POLKAVM_IMPORT(uint64_t, call, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)
POLKAVM_IMPORT(uint64_t, call_data_copy, uint32_t, uint32_t, uint32_t)
@@ -92,7 +92,7 @@ POLKAVM_IMPORT(uint64_t, code_size, uint32_t)
POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, delegate_call, uint32_t)
POLKAVM_IMPORT(uint64_t, delegate_call, uint64_t, uint64_t, uint64_t, uint32_t, uint64_t, uint64_t)
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
@@ -106,7 +106,7 @@ POLKAVM_IMPORT(uint64_t, get_storage, uint32_t, uint32_t, uint32_t, uint32_t, ui
POLKAVM_IMPORT(void, hash_keccak_256, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, instantiate, uint32_t)
POLKAVM_IMPORT(uint64_t, instantiate, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)
POLKAVM_IMPORT(void, now, uint32_t)
-63
View File
@@ -1,63 +0,0 @@
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const { minify } = require("terser");
const RESOLC_WASM_TARGET_DIR = path.join(
__dirname,
"../target/wasm32-unknown-emscripten/release",
);
const RESOLC_WASM = path.join(RESOLC_WASM_TARGET_DIR, "resolc.wasm");
const RESOLC_JS = path.join(RESOLC_WASM_TARGET_DIR, "resolc.js");
const RESOLC_JS_PACKED = path.join(RESOLC_WASM_TARGET_DIR, "resolc_packed.js");
const execShellCommand = (cmd) => {
return execSync(cmd, {
encoding: "utf-8",
maxBuffer: 1024 * 1024 * 100,
}).trim();
};
const wasmBase64 = execShellCommand(
`lz4c --no-frame-crc --best --favor-decSpeed "${RESOLC_WASM}" - | tail -c +8 | base64 -w 0`,
);
const wasmSize = fs.statSync(RESOLC_WASM).size;
const miniLz4 = fs.readFileSync(
path.join(__dirname, "utils/mini-lz4.js"),
"utf-8",
);
const base64DecToArr = fs.readFileSync(
path.join(__dirname, "utils/base64DecToArr.js"),
"utf-8",
);
const resolcJs = fs.readFileSync(RESOLC_JS, "utf-8");
const packedJsContent = `
let moduleArgs = { wasmBinary: (function(source, uncompressedSize) {
${miniLz4}
${base64DecToArr}
return uncompress(base64DecToArr(source), uncompressedSize);
})("${wasmBase64}", ${wasmSize}),
};
${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_JS_PACKED, minifiedJs.code, "utf-8");
console.log(`Combined script written to ${RESOLC_JS_PACKED}`);
})
.catch((err) => {
console.error("Minification failed:", err);
process.exit(1);
});
+33 -51
View File
@@ -1,16 +1,16 @@
const { test, expect } = require("@playwright/test");
const fs = require("fs");
const path = require("path");
const { test, expect } = require('@playwright/test');
const fs = require('fs');
const path = require('path');
function loadFixture(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 runWorker(page, input) {
return await page.evaluate((input) => {
return new Promise((resolve, reject) => {
const worker = new Worker("worker.js");
const worker = new Worker('worker.js');
worker.postMessage(JSON.stringify(input));
worker.onmessage = (event) => {
@@ -26,80 +26,62 @@ async function runWorker(page, input) {
}, input);
}
test("should successfully compile valid Solidity code in browser", async ({
page,
}) => {
test('should successfully compile valid Solidity code in browser', async ({ page }) => {
await page.goto("http://127.0.0.1:8080");
await page.setContent("");
const standardInput = loadFixture("storage.json");
const standardInput = loadFixture('storage.json')
const result = await runWorker(page, standardInput);
expect(typeof result).toBe("string");
expect(typeof result).toBe('string');
let output = JSON.parse(result);
expect(output).toHaveProperty("contracts");
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(
"evm",
);
expect(output.contracts["fixtures/storage.sol"].Storage.evm).toHaveProperty(
"bytecode",
);
expect(output).toHaveProperty('contracts');
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('evm');
expect(output.contracts['fixtures/storage.sol'].Storage.evm).toHaveProperty('bytecode');
});
test("should successfully compile large valid Solidity code in browser", async ({
page,
}) => {
test('should successfully compile large valid Solidity code in browser', async ({ page }) => {
await page.goto("http://127.0.0.1:8080");
await page.setContent("");
const standardInput = loadFixture("token.json");
const standardInput = loadFixture('token.json')
const result = await runWorker(page, standardInput);
expect(typeof result).toBe("string");
expect(typeof result).toBe('string');
let output = JSON.parse(result);
expect(output).toHaveProperty("contracts");
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("evm");
expect(output.contracts["fixtures/token.sol"].MyToken.evm).toHaveProperty(
"bytecode",
);
expect(output).toHaveProperty('contracts');
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('evm');
expect(output.contracts['fixtures/token.sol'].MyToken.evm).toHaveProperty('bytecode');
});
test("should throw an error for invalid Solidity code in browser", async ({
page,
}) => {
test('should throw an error for invalid Solidity code in browser', async ({ page }) => {
await page.goto("http://127.0.0.1:8080");
await page.setContent("");
const standardInput = loadFixture("invalid_contract_content.json");
const standardInput = loadFixture('invalid_contract_content.json')
const result = await runWorker(page, standardInput);
expect(typeof result).toBe("string");
expect(typeof result).toBe('string');
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(output.errors.length).toBeGreaterThan(0);
expect(output.errors[0]).toHaveProperty("type");
expect(output.errors[0].type).toContain("ParserError");
expect(output.errors[0]).toHaveProperty('type');
expect(output.errors[0].type).toContain('ParserError');
});
test("should return not found error for missing imports in browser", async ({
page,
}) => {
test('should return not found error for missing imports in browser', async ({page}) => {
await page.goto("http://127.0.0.1:8080");
await page.setContent("");
const standardInput = loadFixture("missing_import.json");
const standardInput = loadFixture('missing_import.json')
const result = await runWorker(page, standardInput);
expect(typeof result).toBe("string");
expect(typeof result).toBe('string');
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(output.errors.length).toBeGreaterThan(0);
expect(output.errors[0]).toHaveProperty("message");
expect(output.errors[0].message).toContain(
'Source "nonexistent/console.sol" not found',
);
expect(output.errors[0]).toHaveProperty('message');
expect(output.errors[0].message).toContain('Source "nonexistent/console.sol" not found');
});
+52 -55
View File
@@ -1,57 +1,54 @@
Module.stdinData = null;
Module.stdinDataPosition = 0;
Module.stdoutData = [];
Module.stderrData = [];
var Module = {
stdinData: null,
stdinDataPosition: 0,
stdoutData: [],
stderrData: [],
// Method to read all collected stdout data
Module.readFromStdout = function () {
if (!Module.stdoutData.length) return "";
const decoder = new TextDecoder("utf-8");
const data = decoder.decode(new Uint8Array(Module.stdoutData));
Module.stdoutData = [];
return data;
// Function to read and return all collected stdout data as a string
readFromStdout: function() {
if (!this.stdoutData.length) return "";
const decoder = new TextDecoder('utf-8');
const data = decoder.decode(new Uint8Array(this.stdoutData));
this.stdoutData = [];
return data;
},
// Function to read and return all collected stderr data as a string
readFromStderr: function() {
if (!this.stderrData.length) return "";
const decoder = new TextDecoder('utf-8');
const data = decoder.decode(new Uint8Array(this.stderrData));
this.stderrData = [];
return data;
},
// Function to set input data for stdin
writeToStdin: function(data) {
const encoder = new TextEncoder();
this.stdinData = encoder.encode(data);
this.stdinDataPosition = 0;
},
// `preRun` is called before the program starts running
preRun: function() {
// Define a custom stdin function
function customStdin() {
if (!Module.stdinData || Module.stdinDataPosition >= Module.stdinData.length) {
return null; // End of input (EOF)
}
return Module.stdinData[Module.stdinDataPosition++];
}
// Define a custom stdout function
function customStdout(char) {
Module.stdoutData.push(char);
}
// Define a custom stderr function
function customStderr(char) {
Module.stderrData.push(char);
}
FS.init(customStdin, customStdout, customStderr);
},
};
// Method to read all collected stderr data
Module.readFromStderr = function () {
if (!Module.stderrData.length) return "";
const decoder = new TextDecoder("utf-8");
const data = decoder.decode(new Uint8Array(Module.stderrData));
Module.stderrData = [];
return data;
};
// Method to write data to stdin
Module.writeToStdin = function (data) {
const encoder = new TextEncoder();
Module.stdinData = encoder.encode(data);
Module.stdinDataPosition = 0;
};
// Override the `preRun` method to customize file system initialization
Module.preRun = Module.preRun || [];
Module.preRun.push(function () {
// Custom stdin function
function customStdin() {
if (
!Module.stdinData ||
Module.stdinDataPosition >= Module.stdinData.length
) {
return null; // End of input (EOF)
}
return Module.stdinData[Module.stdinDataPosition++];
}
// Custom stdout function
function customStdout(char) {
Module.stdoutData.push(char);
}
// Custom stderr function
function customStderr(char) {
Module.stderrData.push(char);
}
// Initialize the FS (File System) with custom handlers
FS.init(customStdin, customStdout, customStderr);
});
+25 -30
View File
@@ -1,34 +1,29 @@
mergeInto(LibraryManager.library, {
soljson_compile: function (inputPtr, inputLen) {
const inputJson = UTF8ToString(inputPtr, inputLen);
const output = Module.soljson.cwrap("solidity_compile", "string", [
"string",
])(inputJson);
return stringToNewUTF8(output);
},
soljson_version: function () {
const version = Module.soljson.cwrap("solidity_version", "string", [])();
return stringToNewUTF8(version);
},
resolc_compile: function (inputPtr, inputLen) {
const inputJson = UTF8ToString(inputPtr, inputLen);
var revive = createRevive();
revive.writeToStdin(inputJson);
soljson_compile: function(inputPtr, inputLen) {
const inputJson = UTF8ToString(inputPtr, inputLen);
const output = Module.soljson.cwrap('solidity_compile', 'string', ['string'])(inputJson);
return stringToNewUTF8(output);
},
soljson_version: function() {
const version = Module.soljson.cwrap("solidity_version", "string", [])();
return stringToNewUTF8(version);
},
resolc_compile: function(inputPtr, inputLen) {
const inputJson = UTF8ToString(inputPtr, inputLen);
var revive = createRevive();
revive.writeToStdin(inputJson);
// Call main on the new instance
const result = revive.callMain(["--recursive-process"]);
// Call main on the new instance
const result = revive.callMain(['--recursive-process']);
if (result) {
const stderrString = revive.readFromStderr();
const error = JSON.stringify({
type: "error",
message: stderrString || "Unknown error",
});
return stringToNewUTF8(error);
} else {
const stdoutString = revive.readFromStdout();
const json = JSON.stringify({ type: "success", data: stdoutString });
return stringToNewUTF8(json);
}
},
if (result) {
const stderrString = revive.readFromStderr();
const error = JSON.stringify({ type: 'error', message: stderrString || "Unknown error" });
return stringToNewUTF8(error);
} else {
const stdoutString = revive.readFromStdout();
const json = JSON.stringify({ type: 'success', data: stdoutString });
return stringToNewUTF8(json);
}
},
});
+4 -4
View File
@@ -1,9 +1,9 @@
const soljson = require("solc/soljson");
const createRevive = require("./resolc.js");
const soljson = require('solc/soljson');
const createRevive = require('./resolc.js');
async function compile(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
@@ -14,7 +14,7 @@ async function compile(standardJsonInput) {
compiler.writeToStdin(JSON.stringify(standardJsonInput));
// Run the compiler
compiler.callMain(["--standard-json"]);
compiler.callMain(['--standard-json']);
// Collect output
const stdout = compiler.readFromStdout();
+20 -20
View File
@@ -1,10 +1,10 @@
const { compile } = require("./revive.js");
const { compile } = require('./revive.js');
const compilerStandardJsonInput = {
language: "Solidity",
sources: {
"MyContract.sol": {
content: `
language: 'Solidity',
sources: {
'MyContract.sol': {
content: `
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract MyContract {
@@ -13,26 +13,26 @@ const compilerStandardJsonInput = {
}
}
`,
},
},
settings: {
optimizer: {
enabled: true,
runs: 200,
},
outputSelection: {
"*": {
"*": ["abi"],
},
},
},
};
settings: {
optimizer: {
enabled: true,
runs: 200,
},
outputSelection: {
'*': {
'*': ['abi'],
},
},
},
};
async function runCompiler() {
let output = await compile(compilerStandardJsonInput);
let output = await compile(compilerStandardJsonInput)
console.log("Output: " + output);
}
runCompiler().catch((err) => {
console.error("Error:", err);
runCompiler().catch(err => {
console.error('Error:', err);
});
+47 -49
View File
@@ -1,53 +1,51 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Web Worker Example</title>
<style>
/* Ensure the pre tag wraps long lines */
pre {
white-space: pre-wrap; /* Wrap long lines */
word-wrap: break-word; /* Break long words */
max-width: 100%; /* Optional: Ensures it doesn't overflow container */
overflow-wrap: break-word; /* Another method for wrapping */
}
</style>
</head>
<body>
<h1>Revive Compilation Output</h1>
<pre id="output"></pre>
<script>
var outputElement = document.getElementById("output");
var worker = new Worker("./worker.js");
const standardJsonInput = {
language: "Solidity",
sources: {
contract: {
content: "contract MyContract { function f() public { } }",
},
},
settings: {
optimizer: {
enabled: true,
runs: 200,
},
outputSelection: {
"*": {
"*": ["abi"],
},
},
},
};
worker.addEventListener(
"message",
function (e) {
outputElement.textContent = e.data.output;
},
false,
);
<head>
<meta charset="utf-8" />
<title>Web Worker Example</title>
<style>
/* Ensure the pre tag wraps long lines */
pre {
white-space: pre-wrap; /* Wrap long lines */
word-wrap: break-word; /* Break long words */
max-width: 100%; /* Optional: Ensures it doesn't overflow container */
overflow-wrap: break-word; /* Another method for wrapping */
}
</style>
</head>
<body>
<h1>Revive Compilation Output</h1>
<pre id="output"></pre>
<script>
var outputElement = document.getElementById('output');
var worker = new Worker('./worker.js');
const standardJsonInput = {
language: 'Solidity',
sources: {
contract: {
content: 'contract MyContract { function f() public { } }',
}
},
settings: {
optimizer: {
enabled: true,
runs: 200,
},
outputSelection: {
'*': {
'*': ['abi'],
}
}
}
};
worker.addEventListener('message', function (e) {
outputElement.textContent = e.data.output;
}, false);
worker.postMessage(JSON.stringify(standardJsonInput));
</script>
</body>
worker.postMessage(JSON.stringify(standardJsonInput));
</script>
</body>
</html>
+11 -9
View File
@@ -1,16 +1,18 @@
importScripts("./soljson.js");
importScripts("./resolc.js");
importScripts('./soljson.js');
importScripts('./resolc.js');
// Handle messages from the main thread
onmessage = async function (e) {
const m = createRevive();
m.soljson = Module;
const m = createRevive();
// Set input data for stdin
m.writeToStdin(e.data);
m.soljson = Module;
// Compile the Solidity source code
m.callMain(["--standard-json"]);
// Set input data for stdin
m.writeToStdin(e.data);
postMessage({ output: m.readFromStdout() || m.readFromStderr() });
// Compile the Solidity source code
m.callMain(['--standard-json']);
postMessage({output: m.readFromStdout() || m.readFromStderr()});
};
+17 -14
View File
@@ -1,19 +1,22 @@
{
"language": "Solidity",
"sources": {
"fixtures/storage.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\nimport \"nonexistent/console.sol\";\ncontract MyContract { function greet() public pure returns (string memory) { return \"Hello\" // Missing semicolon }}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
"language": "Solidity",
"sources": {
"fixtures/storage.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\nimport \"nonexistent/console.sol\";\ncontract MyContract { function greet() public pure returns (string memory) { return \"Hello\" // Missing semicolon }}"
}
},
"outputSelection": {
"*": {
"*": ["abi"]
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi"
]
}
}
}
}
}
+17 -14
View File
@@ -1,19 +1,22 @@
{
"language": "Solidity",
"sources": {
"fixtures/storage.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\nimport \"nonexistent/console.sol\";\ncontract MyContract { function f() public { } }"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
"language": "Solidity",
"sources": {
"fixtures/storage.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\nimport \"nonexistent/console.sol\";\ncontract MyContract { function f() public { } }"
}
},
"outputSelection": {
"*": {
"*": ["abi"]
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi"
]
}
}
}
}
}
+17 -14
View File
@@ -1,19 +1,22 @@
{
"language": "Solidity",
"sources": {
"fixtures/storage.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.8.2 <0.9.0;\ncontract Storage {\n uint256 number;\n function store(uint256 num) public { number = num; }\n function retrieve() public view returns (uint256){ return number; }\n}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
"language": "Solidity",
"sources": {
"fixtures/storage.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.8.2 <0.9.0;\ncontract Storage {\n uint256 number;\n function store(uint256 num) public { number = num; }\n function retrieve() public view returns (uint256){ return number; }\n}"
}
},
"outputSelection": {
"*": {
"*": ["abi"]
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi"
]
}
}
}
}
}
+77 -74
View File
File diff suppressed because one or more lines are too long
+2 -6
View File
@@ -10,16 +10,12 @@
"example:node": "node ./examples/node/run_revive.js",
"test:node": "mocha --timeout 60000 ./tests",
"test:bun": "bun test --timeout 60000 node.test",
"test:all": "npm run test:node && npm run test:bun",
"format": "prettier --write .",
"build:package": "node ./build.js"
"test:all": "npm run test:node && npm run test:bun"
},
"devDependencies": {
"@playwright/test": "^1.49.1",
"chai": "^5.1.2",
"http-server": "^14.1.1",
"mocha": "^11.0.1",
"prettier": "^3.4.2",
"terser": "^5.37.0"
"mocha": "^11.0.1"
}
}
+15 -14
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
*/
module.exports = defineConfig({
testDir: "./e2e",
testDir: './e2e',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
@@ -14,38 +14,39 @@ module.exports = defineConfig({
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* 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. */
use: {
/* 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 */
trace: "on-first-retry",
trace: 'on-first-retry',
},
timeout: 480000,
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
name: 'webkit',
use: { ...devices['Desktop Safari'] },
}
],
/* Run your local dev server before starting the tests */
webServer: {
command: "npm run example:web",
url: "http://127.0.0.1:8080",
command: 'npm run example:web',
url: 'http://127.0.0.1:8080',
reuseExistingServer: !process.env.CI,
},
});
+34 -52
View File
@@ -1,88 +1,70 @@
import { expect } from "chai";
import { compile } from "../examples/node/revive.js";
import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";
import { expect } from 'chai';
import { compile } from '../examples/node/revive.js';
import { fileURLToPath } from 'url';
import path from 'path';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`);
return JSON.parse(fs.readFileSync(fixturePath, "utf-8"));
return JSON.parse(fs.readFileSync(fixturePath, 'utf-8'));
}
describe("Compile Function Tests", function () {
it("should successfully compile valid Solidity code", async function () {
const standardInput = loadFixture("storage.json");
describe('Compile Function Tests', function () {
it('should successfully compile valid Solidity code', async function () {
const standardInput = loadFixture('storage.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
expect(result).to.be.a('string');
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/storage.sol"]).to.have.property(
"Storage",
);
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property(
"abi",
);
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property(
"evm",
);
expect(
output.contracts["fixtures/storage.sol"].Storage.evm,
).to.have.property("bytecode");
expect(output).to.have.property('contracts');
expect(output.contracts['fixtures/storage.sol']).to.have.property('Storage');
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property('abi');
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property('evm');
expect(output.contracts['fixtures/storage.sol'].Storage.evm).to.have.property('bytecode');
});
if (typeof globalThis.Bun == "undefined") {
if (typeof globalThis.Bun == 'undefined') {
// Running this test with Bun on a Linux host causes:
// RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)')
// Once this issue is resolved, the test will be re-enabled.
it("should successfully compile large Solidity code", async function () {
const standardInput = loadFixture("token.json");
it('should successfully compile large Solidity code', async function () {
const standardInput = loadFixture('token.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
expect(result).to.be.a('string');
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/token.sol"]).to.have.property(
"MyToken",
);
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property(
"abi",
);
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property(
"evm",
);
expect(
output.contracts["fixtures/token.sol"].MyToken.evm,
).to.have.property("bytecode");
expect(output).to.have.property('contracts');
expect(output.contracts['fixtures/token.sol']).to.have.property('MyToken');
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property('abi');
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property('evm');
expect(output.contracts['fixtures/token.sol'].MyToken.evm).to.have.property('bytecode');
});
}
it("should throw an error for invalid Solidity code", async function () {
const standardInput = loadFixture("invalid_contract_content.json");
it('should throw an error for invalid Solidity code', async function () {
const standardInput = loadFixture('invalid_contract_content.json')
const result = await compile(standardInput);
expect(result).to.be.a("string");
expect(result).to.be.a('string');
const output = JSON.parse(result);
expect(output).to.have.property("errors");
expect(output.errors).to.be.an("array");
expect(output).to.have.property('errors');
expect(output.errors).to.be.an('array');
expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].type).to.exist;
expect(output.errors[0].type).to.contain("ParserError");
});
it("should return not found error for missing imports", async function () {
const standardInput = loadFixture("missing_import.json");
it('should return not found error for missing imports', async function () {
const standardInput = loadFixture('missing_import.json')
const result = await compile(standardInput);
const output = JSON.parse(result);
expect(output).to.have.property("errors");
expect(output.errors).to.be.an("array");
expect(output).to.have.property('errors');
expect(output.errors).to.be.an('array');
expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].message).to.exist;
expect(output.errors[0].message).to.include(
'Source "nonexistent/console.sol" not found',
);
expect(output.errors[0].message).to.include('Source "nonexistent/console.sol" not found');
});
});
-46
View File
@@ -1,46 +0,0 @@
function base64DecToArr (sBase64) {
/*\
|*|
|*| Base64 / binary data / UTF-8 strings utilities
|*|
|*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
|*|
\*/
/* Array of bytes to Base64 string decoding */
function b64ToUint6 (nChr) {
return nChr > 64 && nChr < 91 ?
nChr - 65
: nChr > 96 && nChr < 123 ?
nChr - 71
: nChr > 47 && nChr < 58 ?
nChr + 4
: nChr === 43 ?
62
: nChr === 47 ?
63
:
0;
}
var
nInLen = sBase64.length,
nOutLen = nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sBase64.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
}
nUint24 = 0;
}
}
return taBytes;
}
-118
View File
@@ -1,118 +0,0 @@
function uncompress(source, uncompressedSize) {
/*
Source https://github.com/ethereum/solidity/blob/develop/scripts/ci/mini-lz4.js
====
based off https://github.com/emscripten-core/emscripten/blob/main/third_party/mini-lz4.js
The license only applies to the body of this function (``uncompress``).
====
MiniLZ4: Minimal LZ4 block decoding and encoding.
based off of node-lz4, https://github.com/pierrec/node-lz4
====
Copyright (c) 2012 Pierre Curto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
====
changes have the same license
*/
/**
* Decode a block. Assumptions: input contains all sequences of a
* chunk, output is large enough to receive the decoded data.
* If the output buffer is too small, an error will be thrown.
* If the returned value is negative, an error occurred at the returned offset.
*
* @param {ArrayBufferView} input input data
* @param {ArrayBufferView} output output data
* @param {number=} sIdx
* @param {number=} eIdx
* @return {number} number of decoded bytes
* @private
*/
function uncompressBlock (input, output, sIdx, eIdx) {
sIdx = sIdx || 0
eIdx = eIdx || (input.length - sIdx)
// Process each sequence in the incoming data
for (var i = sIdx, n = eIdx, j = 0; i < n;) {
var token = input[i++]
// Literals
var literals_length = (token >> 4)
if (literals_length > 0) {
// length of literals
var l = literals_length + 240
while (l === 255) {
l = input[i++]
literals_length += l
}
// Copy the literals
var end = i + literals_length
while (i < end) output[j++] = input[i++]
// End of buffer?
if (i === n) return j
}
// Match copy
// 2 bytes offset (little endian)
var offset = input[i++] | (input[i++] << 8)
// XXX 0 is an invalid offset value
if (offset === 0) return j
if (offset > j) return -(i-2)
// length of match copy
var match_length = (token & 0xf)
var l = match_length + 240
while (l === 255) {
l = input[i++]
match_length += l
}
// Copy the match
var pos = j - offset // position of the match copy in the current output
var end = j + match_length + 4 // minmatch = 4
while (j < end) output[j++] = output[pos++]
}
return j
}
var result = new ArrayBuffer(uncompressedSize);
var sourceIndex = 0;
var destIndex = 0;
var blockSize;
while((blockSize = (source[sourceIndex] | (source[sourceIndex + 1] << 8) | (source[sourceIndex + 2] << 16) | (source[sourceIndex + 3] << 24))) > 0)
{
sourceIndex += 4;
if (blockSize & 0x80000000)
{
blockSize &= 0x7FFFFFFFF;
for (var i = 0; i < blockSize; i++) {
result[destIndex++] = source[sourceIndex++];
}
}
else
{
destIndex += uncompressBlock(source, new Uint8Array(result, destIndex, uncompressedSize - destIndex), sourceIndex, sourceIndex + blockSize);
sourceIndex += blockSize;
}
}
return new Uint8Array(result, 0, uncompressedSize);
}
+1 -2
View File
@@ -3,8 +3,7 @@
"private": true,
"scripts": {
"test:cli": "npm run test -w crates/solidity/src/tests/cli-tests",
"test:wasm": "npm run test:all -w js",
"build:package": "npm run build:package -w js"
"test:wasm": "npm run test:all -w js"
},
"workspaces": [
"crates/solidity/src/tests/cli-tests",