From 5defaa6aeba178cc520bc2673f8af22b10dc8e5a Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Wed, 16 Oct 2024 14:18:26 -0400 Subject: [PATCH 1/3] Reuse runtime when compiling source code to bytecode --- crates/core/src/lib.rs | 3 +-- crates/runner/src/lib.rs | 50 +++++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 38a98779..b3c9601b 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -43,8 +43,7 @@ pub extern "C" fn init() { /// * `js_src_ptr` must reference a valid array of unsigned bytes of `js_src_len` length #[export_name = "compile_src"] pub unsafe extern "C" fn compile_src(js_src_ptr: *const u8, js_src_len: usize) -> *const u32 { - // Use fresh runtime to avoid depending on Wizened runtime - let runtime = runtime::new(Config::default()).unwrap(); + let runtime = unsafe { RUNTIME.get().unwrap() }; let js_src = str::from_utf8(slice::from_raw_parts(js_src_ptr, js_src_len)).unwrap(); let bytecode = runtime diff --git a/crates/runner/src/lib.rs b/crates/runner/src/lib.rs index f3cda8b3..16548fc4 100644 --- a/crates/runner/src/lib.rs +++ b/crates/runner/src/lib.rs @@ -670,12 +670,18 @@ impl Runner { let module = Module::from_binary(self.linker.engine(), &self.wasm)?; let instance = self.linker.instantiate(store.as_context_mut(), &module)?; + let bytecode = Self::compile(src.as_bytes(), store.as_context_mut(), &instance)?; + + // Need to use a fresh instance so we get a fresh wizened runtime, + // otherwise the JS in the outermost scope will be executed a second + // time if an exported JS function is invoked. + let instance = self.linker.instantiate(store.as_context_mut(), &module)?; + let bc_ptr = Self::copy_into_instance(&bytecode, &instance, store.as_context_mut())?; - let (bc_ptr, bc_len) = Self::compile(src.as_bytes(), store.as_context_mut(), &instance)?; let res = match use_exported_fn { UseExportedFn::EvalBytecode => instance .get_typed_func::<(u32, u32), ()>(store.as_context_mut(), "eval_bytecode")? - .call(store.as_context_mut(), (bc_ptr, bc_len)), + .call(store.as_context_mut(), (bc_ptr, bytecode.len().try_into()?)), UseExportedFn::Invoke(func) => { let (fn_ptr, fn_len) = match func { Some(func) => Self::copy_func_name(func, &instance, store.as_context_mut())?, @@ -683,7 +689,10 @@ impl Runner { }; instance .get_typed_func::<(u32, u32, u32, u32), ()>(store.as_context_mut(), "invoke")? - .call(store.as_context_mut(), (bc_ptr, bc_len, fn_ptr, fn_len)) + .call( + store.as_context_mut(), + (bc_ptr, bytecode.len().try_into()?, fn_ptr, fn_len), + ) } }; @@ -695,30 +704,30 @@ impl Runner { instance: &Instance, mut store: impl AsContextMut, ) -> Result<(u32, u32)> { + let name_bytes = name.as_bytes(); + let ptr = Self::copy_into_instance(name_bytes, instance, store.as_context_mut())?; + Ok((ptr, name_bytes.len().try_into()?)) + } + + fn copy_into_instance( + data: &[u8], + instance: &Instance, + mut store: impl AsContextMut, + ) -> Result { let memory = instance .get_memory(store.as_context_mut(), "memory") .unwrap(); - let fn_name_bytes = name.as_bytes(); - let fn_name_ptr = Self::allocate_memory( - instance, - store.as_context_mut(), - 1, - fn_name_bytes.len().try_into()?, - )?; - memory.write( - store.as_context_mut(), - fn_name_ptr.try_into()?, - fn_name_bytes, - )?; - - Ok((fn_name_ptr, fn_name_bytes.len().try_into()?)) + let ptr = + Self::allocate_memory(instance, store.as_context_mut(), 1, data.len().try_into()?)?; + memory.write(store.as_context_mut(), ptr.try_into()?, data)?; + Ok(ptr) } fn compile( source: &[u8], mut store: impl AsContextMut, instance: &Instance, - ) -> Result<(u32, u32)> { + ) -> Result> { let memory = instance .get_memory(store.as_context_mut(), "memory") .unwrap(); @@ -742,7 +751,10 @@ impl Runner { let bytecode_ptr = u32::from_le_bytes(ret_buffer[0..4].try_into()?); let bytecode_len = u32::from_le_bytes(ret_buffer[4..8].try_into()?); - Ok((bytecode_ptr, bytecode_len)) + let mut bytecode = vec![0; bytecode_len.try_into()?]; + memory.read(store.as_context(), bytecode_ptr.try_into()?, &mut bytecode)?; + + Ok(bytecode) } fn allocate_memory( From 4df150be92056f3c801824178375dfdd050a9405 Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Thu, 17 Oct 2024 16:59:59 -0400 Subject: [PATCH 2/3] Add comment about why we're using the initialized runtime --- crates/core/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index b3c9601b..fe1194de 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -43,6 +43,19 @@ pub extern "C" fn init() { /// * `js_src_ptr` must reference a valid array of unsigned bytes of `js_src_len` length #[export_name = "compile_src"] pub unsafe extern "C" fn compile_src(js_src_ptr: *const u8, js_src_len: usize) -> *const u32 { + // Use initialized runtime when compiling because certain runtime + // configurations can cause different bytecode to be emitted. + // + // For example, given the following JS: + // ``` + // function foo() { + // "use math" + // 1234 % 32 + // } + // ``` + // + // Setting `config.bignum_extension` to `true` will produce different + // bytecode than if it were set to `false`. let runtime = unsafe { RUNTIME.get().unwrap() }; let js_src = str::from_utf8(slice::from_raw_parts(js_src_ptr, js_src_len)).unwrap(); From 082702121fa32dbefb6cb8ec8804f15d498a9824 Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Fri, 18 Oct 2024 15:13:22 -0400 Subject: [PATCH 3/3] Revert changes to test runner --- crates/runner/src/lib.rs | 50 +++++++++++++++------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/crates/runner/src/lib.rs b/crates/runner/src/lib.rs index 16548fc4..f3cda8b3 100644 --- a/crates/runner/src/lib.rs +++ b/crates/runner/src/lib.rs @@ -670,18 +670,12 @@ impl Runner { let module = Module::from_binary(self.linker.engine(), &self.wasm)?; let instance = self.linker.instantiate(store.as_context_mut(), &module)?; - let bytecode = Self::compile(src.as_bytes(), store.as_context_mut(), &instance)?; - - // Need to use a fresh instance so we get a fresh wizened runtime, - // otherwise the JS in the outermost scope will be executed a second - // time if an exported JS function is invoked. - let instance = self.linker.instantiate(store.as_context_mut(), &module)?; - let bc_ptr = Self::copy_into_instance(&bytecode, &instance, store.as_context_mut())?; + let (bc_ptr, bc_len) = Self::compile(src.as_bytes(), store.as_context_mut(), &instance)?; let res = match use_exported_fn { UseExportedFn::EvalBytecode => instance .get_typed_func::<(u32, u32), ()>(store.as_context_mut(), "eval_bytecode")? - .call(store.as_context_mut(), (bc_ptr, bytecode.len().try_into()?)), + .call(store.as_context_mut(), (bc_ptr, bc_len)), UseExportedFn::Invoke(func) => { let (fn_ptr, fn_len) = match func { Some(func) => Self::copy_func_name(func, &instance, store.as_context_mut())?, @@ -689,10 +683,7 @@ impl Runner { }; instance .get_typed_func::<(u32, u32, u32, u32), ()>(store.as_context_mut(), "invoke")? - .call( - store.as_context_mut(), - (bc_ptr, bytecode.len().try_into()?, fn_ptr, fn_len), - ) + .call(store.as_context_mut(), (bc_ptr, bc_len, fn_ptr, fn_len)) } }; @@ -704,30 +695,30 @@ impl Runner { instance: &Instance, mut store: impl AsContextMut, ) -> Result<(u32, u32)> { - let name_bytes = name.as_bytes(); - let ptr = Self::copy_into_instance(name_bytes, instance, store.as_context_mut())?; - Ok((ptr, name_bytes.len().try_into()?)) - } - - fn copy_into_instance( - data: &[u8], - instance: &Instance, - mut store: impl AsContextMut, - ) -> Result { let memory = instance .get_memory(store.as_context_mut(), "memory") .unwrap(); - let ptr = - Self::allocate_memory(instance, store.as_context_mut(), 1, data.len().try_into()?)?; - memory.write(store.as_context_mut(), ptr.try_into()?, data)?; - Ok(ptr) + let fn_name_bytes = name.as_bytes(); + let fn_name_ptr = Self::allocate_memory( + instance, + store.as_context_mut(), + 1, + fn_name_bytes.len().try_into()?, + )?; + memory.write( + store.as_context_mut(), + fn_name_ptr.try_into()?, + fn_name_bytes, + )?; + + Ok((fn_name_ptr, fn_name_bytes.len().try_into()?)) } fn compile( source: &[u8], mut store: impl AsContextMut, instance: &Instance, - ) -> Result> { + ) -> Result<(u32, u32)> { let memory = instance .get_memory(store.as_context_mut(), "memory") .unwrap(); @@ -751,10 +742,7 @@ impl Runner { let bytecode_ptr = u32::from_le_bytes(ret_buffer[0..4].try_into()?); let bytecode_len = u32::from_le_bytes(ret_buffer[4..8].try_into()?); - let mut bytecode = vec![0; bytecode_len.try_into()?]; - memory.read(store.as_context(), bytecode_ptr.try_into()?, &mut bytecode)?; - - Ok(bytecode) + Ok((bytecode_ptr, bytecode_len)) } fn allocate_memory(