From 4e024655a10fc0e5951a5fd0d4ec6bfb23bf9ac5 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 25 Nov 2024 10:25:15 +0100 Subject: [PATCH] Add support for esm and cjs modules --- .github/workflows/build-revive-wasm.yml | 3 +- .gitignore | 3 +- Makefile | 16 +++-- crates/solidity/Cargo.toml | 4 -- crates/solidity/src/process/mod.rs | 54 ++++++++++++++++- crates/solidity/src/process/native_process.rs | 52 ----------------- crates/solidity/src/process/worker_process.rs | 58 ------------------- js/{ => embed}/pre.js | 0 js/{ => embed}/soljson_interface.js | 34 +---------- js/package.json | 12 +++- js/rollup.config.js | 35 +++++++++++ js/run_revive.js | 2 +- js/src/worker.js | 32 ++++++++++ package.json | 1 + 14 files changed, 149 insertions(+), 157 deletions(-) rename js/{ => embed}/pre.js (100%) rename js/{ => embed}/soljson_interface.js (63%) create mode 100644 js/rollup.config.js create mode 100644 js/src/worker.js diff --git a/.github/workflows/build-revive-wasm.yml b/.github/workflows/build-revive-wasm.yml index 90bc2f8..0cd7c91 100644 --- a/.github/workflows/build-revive-wasm.yml +++ b/.github/workflows/build-revive-wasm.yml @@ -8,7 +8,7 @@ on: env: CARGO_TERM_COLOR: always - REVIVE_WASM_INSTALL_DIR: ${{ github.workspace }}/target/wasm32-unknown-emscripten/release + REVIVE_WASM_INSTALL_DIR: ${{ github.workspace }}/js/dist/revive-cjs EMSCRIPTEN_VERSION: 3.1.64 jobs: @@ -75,5 +75,6 @@ jobs: name: revive-wasm path: | ${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js + ${{ env.REVIVE_WASM_INSTALL_DIR }}/worker.js ${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm retention-days: 1 diff --git a/.gitignore b/.gitignore index 142c03e..1c4929d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,6 @@ artifacts tmp package-lock.json /*.html -/js/resolc.* +/js/src/resolc.* +/js/dist/ /build diff --git a/Makefile b/Makefile index b998a0d..96749a9 100644 --- a/Makefile +++ b/Makefile @@ -11,20 +11,22 @@ RUSTFLAGS_EMSCRIPTEN := \ -Clink-arg=-sMODULARIZE \ -Clink-arg=-sEXPORT_ES6 \ -Clink-arg=-sEXPORT_NAME=createRevive \ - -Clink-arg=--js-library=js/soljson_interface.js \ - -Clink-arg=--pre-js=js/pre.js + -Clink-arg=--js-library=js/embed/soljson_interface.js \ + -Clink-arg=--pre-js=js/embed/pre.js install: install-bin install-npm install-bin: cargo install --path crates/solidity -install-wasm: - RUSTFLAGS='$(RUSTFLAGS_EMSCRIPTEN)' cargo install --target wasm32-unknown-emscripten --path crates/solidity - install-npm: npm install && npm fund +install-wasm: + RUSTFLAGS='$(RUSTFLAGS_EMSCRIPTEN)' cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features + npm install + npm run build:revive + # install-revive: Build and install to the directory specified in REVIVE_INSTALL_DIR ifeq ($(origin REVIVE_INSTALL_DIR), undefined) REVIVE_INSTALL_DIR=`pwd`/release/revive-debian @@ -75,4 +77,6 @@ clean: rm -rf node_modules ; \ rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \ cargo uninstall revive-solidity ; \ - rm -f package-lock.json + rm -f package-lock.json ; \ + rm -rf js/dist ; \ + rm -f js/src/resolc.{wasm,js} diff --git a/crates/solidity/Cargo.toml b/crates/solidity/Cargo.toml index ee92201..7af977e 100644 --- a/crates/solidity/Cargo.toml +++ b/crates/solidity/Cargo.toml @@ -49,9 +49,5 @@ libc = { workspace = true } inkwell = { workspace = true, features = ["target-riscv", "llvm18-0-no-llvm-linking"]} [features] -default = [] parallel = ["rayon"] - -# Enable parallel by default only for non-emscripten targets -[target.'cfg(not(target_os = "emscripten"))'.features] default = ["parallel"] diff --git a/crates/solidity/src/process/mod.rs b/crates/solidity/src/process/mod.rs index 18244d6..883349f 100644 --- a/crates/solidity/src/process/mod.rs +++ b/crates/solidity/src/process/mod.rs @@ -7,10 +7,62 @@ pub mod output; #[cfg(target_os = "emscripten")] pub mod worker_process; +use std::io::{Read, Write}; + use self::input::Input; use self::output::Output; pub trait Process { - fn run(input_file: Option<&mut std::fs::File>) -> anyhow::Result<()>; + /// Read input from `stdin`, compile a contract, and write the output to `stdout`. + fn run(input_file: Option<&mut std::fs::File>) -> anyhow::Result<()> { + let mut stdin = std::io::stdin(); + let mut stdout = std::io::stdout(); + let mut stderr = std::io::stderr(); + + let mut buffer = Vec::with_capacity(16384); + match input_file { + Some(ins) => { + if let Err(error) = ins.read_to_end(&mut buffer) { + anyhow::bail!("Failed to read recursive process input file: {:?}", error); + } + } + None => { + if let Err(error) = stdin.read_to_end(&mut buffer) { + anyhow::bail!( + "Failed to read recursive process input from stdin: {:?}", + error + ) + } + } + } + + let input: Input = revive_common::deserialize_from_slice(buffer.as_slice())?; + let result = input.contract.compile( + input.project, + input.optimizer_settings, + input.include_metadata_hash, + input.debug_config, + ); + + match result { + Ok(build) => { + let output = Output::new(build); + let json = serde_json::to_vec(&output).expect("Always valid"); + stdout + .write_all(json.as_slice()) + .expect("Stdout writing error"); + Ok(()) + } + Err(error) => { + let message = error.to_string(); + stderr + .write_all(message.as_bytes()) + .expect("Stderr writing error"); + Err(error) + } + } + } + + /// Runs this process recursively to compile a single contract. fn call(input: Input) -> anyhow::Result; } diff --git a/crates/solidity/src/process/native_process.rs b/crates/solidity/src/process/native_process.rs index c449ed7..f6d4cfd 100644 --- a/crates/solidity/src/process/native_process.rs +++ b/crates/solidity/src/process/native_process.rs @@ -1,6 +1,5 @@ //! Process for compiling a single compilation unit. -use std::io::Read; use std::io::Write; use std::path::PathBuf; use std::process::Command; @@ -17,57 +16,6 @@ pub static EXECUTABLE: OnceCell = OnceCell::new(); pub struct NativeProcess; impl Process for NativeProcess { - /// Read input from `stdin`, compile a contract, and write the output to `stdout`. - fn run(input_file: Option<&mut std::fs::File>) -> anyhow::Result<()> { - let mut stdin = std::io::stdin(); - let mut stdout = std::io::stdout(); - let mut stderr = std::io::stderr(); - - let mut buffer = Vec::with_capacity(16384); - match input_file { - Some(ins) => { - if let Err(error) = ins.read_to_end(&mut buffer) { - anyhow::bail!("Failed to read recursive process input file: {:?}", error); - } - } - None => { - if let Err(error) = stdin.read_to_end(&mut buffer) { - anyhow::bail!( - "Failed to read recursive process input from stdin: {:?}", - error - ) - } - } - } - - let input: Input = revive_common::deserialize_from_slice(buffer.as_slice())?; - let result = input.contract.compile( - input.project, - input.optimizer_settings, - input.include_metadata_hash, - input.debug_config, - ); - - match result { - Ok(build) => { - let output = Output::new(build); - let json = serde_json::to_vec(&output).expect("Always valid"); - stdout - .write_all(json.as_slice()) - .expect("Stdout writing error"); - Ok(()) - } - Err(error) => { - let message = error.to_string(); - stderr - .write_all(message.as_bytes()) - .expect("Stderr writing error"); - Err(error) - } - } - } - - /// Runs this process recursively to compile a single contract. fn call(input: Input) -> anyhow::Result { let input_json = serde_json::to_vec(&input).expect("Always valid"); diff --git a/crates/solidity/src/process/worker_process.rs b/crates/solidity/src/process/worker_process.rs index 1ebc9f2..00a332b 100644 --- a/crates/solidity/src/process/worker_process.rs +++ b/crates/solidity/src/process/worker_process.rs @@ -1,9 +1,6 @@ //! Process for compiling a single compilation unit using Web Workers. use std::ffi::{c_char, c_void, CStr, CString}; -use std::fs::File; -use std::io::Read; -use std::io::Write; use super::Input; use super::Output; @@ -32,61 +29,6 @@ enum Response { pub struct WorkerProcess; impl Process for WorkerProcess { - /// Read input from `stdin`, compile a contract, and write the output to `stdout`. - fn run(input_file: Option<&mut std::fs::File>) -> anyhow::Result<()> { - let mut buffer = Vec::with_capacity(16384); - // TODO: Init correctly stdin in emscripten - preload FS conf before module init - let mut stdin = File::open("/in") - .map_err(|error| anyhow::anyhow!("File /in openning error: {}", error))?; - let mut stdout = File::create("/out") - .map_err(|error| anyhow::anyhow!("File /out creating error: {}", error))?; - let mut stderr = File::create("/err") - .map_err(|error| anyhow::anyhow!("File /err creating error: {}", error))?; - - match input_file { - Some(ins) => { - if let Err(error) = ins.read_to_end(&mut buffer) { - anyhow::bail!("Failed to read recursive process input file: {:?}", error); - } - } - None => { - if let Err(error) = stdin.read_to_end(&mut buffer) { - anyhow::bail!( - "Failed to read recursive process input from stdin: {:?}", - error - ) - } - } - } - - let input: Input = revive_common::deserialize_from_slice(buffer.as_slice())?; - let result = input.contract.compile( - input.project, - input.optimizer_settings, - input.include_metadata_hash, - input.debug_config, - ); - - match result { - Ok(build) => { - let output = Output::new(build); - let json = serde_json::to_vec(&output).expect("Always valid"); - stdout - .write_all(json.as_slice()) - .expect("Stdout writing error"); - Ok(()) - } - Err(error) => { - let message = error.to_string(); - stderr - .write_all(message.as_bytes()) - .expect("Stderr writing error"); - Err(error) - } - } - } - - /// Runs this process recursively to compile a single contract. fn call(input: Input) -> anyhow::Result { let input_json = serde_json::to_vec(&input).expect("Always valid"); let input_str = String::from_utf8(input_json).expect("Input shall be valid"); diff --git a/js/pre.js b/js/embed/pre.js similarity index 100% rename from js/pre.js rename to js/embed/pre.js diff --git a/js/soljson_interface.js b/js/embed/soljson_interface.js similarity index 63% rename from js/soljson_interface.js rename to js/embed/soljson_interface.js index c7e514f..44f1a64 100644 --- a/js/soljson_interface.js +++ b/js/embed/soljson_interface.js @@ -14,39 +14,11 @@ mergeInto(LibraryManager.library, { var inputJson = UTF8ToString(inputPtr, inputLen); - // Inline worker code - const workerCode = ` - // worker.js - // nodejs version - const { parentPort } = require('worker_threads'); - - parentPort.on('message', async (inputJson) => { - const { default: ModuleFactory } = await import('./resolc.js'); - const newModule = await ModuleFactory(); - - // Create a virtual file for stdin - newModule.FS.writeFile('/in', inputJson); - - // Call main on the new instance - const output = newModule.callMain(['--recursive-process']); - - // Check the /err file content - const errorMessage = newModule.FS.readFile('/err', { encoding: 'utf8' }); - - if (errorMessage.length > 0) { - // If /err is not empty, throw an error with its content - throw new Error(errorMessage); - } else { - // If no error, read the output file - let outputFile = newModule.FS.readFile('/out', { encoding: 'utf8' }); - parentPort.postMessage({ output: outputFile }); - } - });`; - function compileWithWorker(inputJson, callback) { return new Promise((resolve, reject) => { - // Create a new Worker - const worker = new Worker(workerCode, { eval: true }); + const worker = new Worker(new URL('./worker.js', import.meta.url), { + type: 'module', + }); // Listen for messages from the worker worker.on('message', (message) => { diff --git a/js/package.json b/js/package.json index 5315662..41525e4 100644 --- a/js/package.json +++ b/js/package.json @@ -9,7 +9,15 @@ "solc": "^0.8.28" }, "scripts": { - "init": "cp -r ../target/wasm32-unknown-emscripten/release/resolc.{js,wasm} .", - "test": "npm run init && node run_revive.js" + "build": "cp -r ../target/wasm32-unknown-emscripten/release/resolc.{js,wasm} ./src && npx rollup -c", + "test": "npm run build && node run_revive.js" + }, + "devDependencies": { + "@babel/core": "^7.26.0", + "@babel/preset-env": "^7.26.0", + "@rollup/plugin-babel": "^6.0.4", + "@rollup/plugin-node-resolve": "^15.3.0", + "rollup": "^4.27.3", + "rollup-plugin-copy": "^3.5.0" } } diff --git a/js/rollup.config.js b/js/rollup.config.js new file mode 100644 index 0000000..b46d773 --- /dev/null +++ b/js/rollup.config.js @@ -0,0 +1,35 @@ +import babel from '@rollup/plugin-babel'; +import copy from 'rollup-plugin-copy'; +import resolve from '@rollup/plugin-node-resolve'; + +const outputDirCJS = 'dist/revive-cjs'; +const outputDirESM = 'dist/revive-esm'; + +export default { + input: ['src/resolc.js', 'src/worker.js'], // Adjust this to your main entry file + output: [ + { + dir: outputDirCJS, + format: 'cjs', + exports: 'auto', + }, + { + dir: outputDirESM, + format: 'esm', + }, + ], + plugins: [ + babel({ + exclude: 'node_modules/**', + presets: ['@babel/preset-env'], + babelHelpers: 'inline', + }), + resolve(), + copy({ + targets: [ + { src: 'src/resolc.wasm', dest: outputDirCJS }, + { src: 'src/resolc.wasm', dest: outputDirESM }, + ], + }) + ], +}; diff --git a/js/run_revive.js b/js/run_revive.js index 94ee6c3..3c16085 100644 --- a/js/run_revive.js +++ b/js/run_revive.js @@ -1,6 +1,6 @@ import solc from 'solc'; // Import the Emscripten module -import createRevive from './resolc.js'; +import createRevive from './dist/revive-esm/resolc.js'; const compilerStandardJsonInput = { language: 'Solidity', diff --git a/js/src/worker.js b/js/src/worker.js new file mode 100644 index 0000000..24a7b00 --- /dev/null +++ b/js/src/worker.js @@ -0,0 +1,32 @@ +import { parentPort } from 'worker_threads'; + +parentPort.on('message', async (inputJson) => { + const { default: createRevive } = await import(new URL('./resolc.js', import.meta.url)); + const revive = await createRevive(); + + revive.setStdinData(inputJson); + + let stdoutString = ""; + revive.setStdoutCallback(function(char) { + if (char.charCodeAt(0) === '\n') { + console.log("new line") + exit + } + stdoutString += char; + }); + + let stderrString = ""; + revive.setStderrCallback(function(char) { + stderrString += char; + }); + + // Call main on the new instance + const output = revive.callMain(['--recursive-process']); + + if (stderrString.length > 0) { + // If /err is not empty, throw an error with its content + throw new Error(stderrString); + } else { + parentPort.postMessage({ output: stdoutString }); + } +}); diff --git a/package.json b/package.json index 7409c54..ebee435 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "private": true, "scripts": { "test:cli": "npm run test -w crates/solidity/src/tests/cli-tests", + "build:revive": "npm run build -w js", "test:revive": "npm run test -w js" }, "workspaces": [