diff --git a/Makefile b/Makefile index 27445f6..5d9b059 100644 --- a/Makefile +++ b/Makefile @@ -13,11 +13,12 @@ else endif ARCH := $(shell uname -m) - ifeq ($(ARCH),x86_64) build_arch = amd64 + else ifeq ($(ARCH),arm64) + build_arch = arm64 else - $(error Architecture not supported yet) + $(error Architecture not supported yet) endif endif @@ -30,7 +31,7 @@ build: build-headers build-rust build-java build-rust: build-rust-$(build_arch)-$(build_os) # Compile the Rust part. -build-rust-all-targets: build-rust-amd64-darwin build-rust-amd64-linux build-rust-amd64-windows +build-rust-all-targets: build-rust-amd64-darwin build-rust-arm64-darwin build-rust-amd64-linux build-rust-amd64-windows build-rust-amd64-darwin: rustup target add x86_64-apple-darwin @@ -40,6 +41,14 @@ build-rust-amd64-darwin: install_name_tool -id "@rpath/libwasmer_jni.dylib" ./artifacts/darwin-amd64/libwasmer_jni.dylib test -h target/current || ln -s x86_64-apple-darwin/release target/current +build-rust-arm64-darwin: + rustup target add aarch64-apple-darwin + cargo build --release --target=aarch64-apple-darwin + mkdir -p artifacts/darwin-arm64 + cp target/aarch64-apple-darwin/release/libwasmer_jni.dylib artifacts/darwin-arm64 + install_name_tool -id "@rpath/libwasmer_jni.dylib" ./artifacts/darwin-arm64/libwasmer_jni.dylib + test -h target/current || ln -s aarch64-apple-darwin/release target/current + build-rust-amd64-linux: rustup target add x86_64-unknown-linux-gnu cargo build --release --target=x86_64-unknown-linux-gnu @@ -72,6 +81,9 @@ test-rust: test-rust-$(build_arch)-$(build_os) test-rust-amd64-darwin: cargo test --lib --release --target=x86_64-apple-darwin +test-rust-arm64-darwin: + cargo test --lib --release --target=aarch64-apple-darwin + test-rust-amd64-linux: cargo test --lib --release --target=x86_64-unknown-linux-gnu @@ -85,7 +97,7 @@ test-java: # Test the examples. test-examples: @for example in $(shell find examples -name "*Example.java") ; do \ - example=$${example#examples/}; \ + example=$${example%examples/}; \ example=$${example%Example.java}; \ echo "Testing $${example}"; \ make run-example EXAMPLE=$${example}; \ diff --git a/build.gradle b/build.gradle index b3903ae..a701697 100644 --- a/build.gradle +++ b/build.gradle @@ -83,7 +83,9 @@ String inferWasmerJarAppendix() { case ["x86_64", "x64", "x86-64"]: arch_name = "amd64" break; - + case ["arm64", "aarch64"]: + arch_name = "arm64" + break; default: throw new RuntimeException("Architecture " + arch.getName() + " is not supported.") } diff --git a/examples/ImportExample.java b/examples/ImportExample.java index abbd2b0..a8345cf 100644 --- a/examples/ImportExample.java +++ b/examples/ImportExample.java @@ -3,6 +3,7 @@ import org.wasmer.Instance; import org.wasmer.Module; import org.wasmer.Type; +import org.wasmer.Memory; import java.io.IOException; import java.nio.file.Files; @@ -33,6 +34,7 @@ public static void main(String[] args) throws IOException { System.out.println("Calling exported function 'Core_initialize_block' as it calls both of the imported functions"); instance.exports.getFunction("Core_initialize_block").apply(1,2); + Memory memory = instance.exports.getMemory("memory"); instance.close(); } } diff --git a/examples/polkadot_runtime-v9360.compact.compressed.wasm b/examples/polkadot_runtime-v9360.compact.compressed.wasm deleted file mode 100644 index 7925d43..0000000 Binary files a/examples/polkadot_runtime-v9360.compact.compressed.wasm and /dev/null differ diff --git a/src/imports.rs b/src/imports.rs index f9a24d6..dd8dcc5 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -10,8 +10,10 @@ use jni::{ }; use std::{collections::HashMap, panic}; use std::convert::TryFrom; +use std::sync::Arc; use wasmer::{ImportObject, NamedResolver, ChainableNamedResolver, Exports, Function, FunctionType, Type, Value, Memory, MemoryType}; use wasmer_wasi::WasiState; +use crate::memory::{IMPORTED_MEMORY, Memory as MemoryWrapper}; pub struct Imports { pub(crate) import_object: Box, @@ -59,7 +61,9 @@ pub extern "system" fn Java_org_wasmer_Imports_nativeImportsInstantiate( }; let shared = env.get_field(import, "shared", "Z")?.z()?; let memory_type = MemoryType::new(u32::try_from(min_pages)?, max_pages, shared); - namespaces.entry(namespace).or_insert_with(|| Exports::new()).insert(name, Memory::new(&store, memory_type)?) + let memory = Memory::new(&store, memory_type)?; + IMPORTED_MEMORY.lock().unwrap().replace(MemoryWrapper::new(Arc::new(memory.clone()))); + namespaces.entry(namespace).or_insert_with(|| Exports::new()).insert(name, memory) } else { let function = env.get_field(import, "function", "Ljava/util/function/Function;")?.l()?; let params = env.get_field(import, "argTypesInt", "[I")?.l()?; diff --git a/src/instance.rs b/src/instance.rs index 43cb5f7..9bb8b2b 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -11,6 +11,7 @@ use jni::{ JNIEnv, }; use std::{collections::HashMap, convert::TryFrom, panic, rc::Rc}; +use std::sync::Arc; use wasmer::{imports, Extern, Value as WasmValue, Function}; use wasmer as core; use wasmer_compiler_cranelift::Cranelift; @@ -43,7 +44,7 @@ impl Instance { .exports .iter() .filter_map(|(export_name, export)| match export { - Extern::Memory(memory) => Some((export_name.to_string(), Memory::new(Rc::new(memory.clone())))), + Extern::Memory(memory) => Some((export_name.to_string(), Memory::new(Arc::new(memory.clone())))), _ => None, }) .collect(); diff --git a/src/java/org/wasmer/Exports.java b/src/java/org/wasmer/Exports.java index 5e456d9..64d55cd 100644 --- a/src/java/org/wasmer/Exports.java +++ b/src/java/org/wasmer/Exports.java @@ -23,7 +23,7 @@ * } */ public class Exports { - private Map inner; + private final Map inner; private Instance instance; /** diff --git a/src/java/org/wasmer/Main.java b/src/java/org/wasmer/Main.java index bcbaa4d..a4e4f8c 100644 --- a/src/java/org/wasmer/Main.java +++ b/src/java/org/wasmer/Main.java @@ -3,12 +3,33 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; public class Main { public static void main(String[] args) throws IOException { - //Purely for demonstration purposes, delete all of it when writing actual code - byte[] bytes = Files.readAllBytes(Paths.get(System.getProperty("user.dir"), "../../examples/runtime.wasm")); + System.out.println("Reading wasm bytes"); + byte[] bytes = Files.readAllBytes(Paths.get("../../examples/runtime.wasm")); Module module = new Module(bytes); - System.out.println("Success"); + System.out.println("Creating import object"); + Imports imports = Imports.from(Arrays.asList( + new ImportObject.FuncImport("env", "ext_storage_set_version_1", argv -> { + System.out.println("Message printed in the body of 'ext_storage_set_version_1'"); + return argv; + }, Arrays.asList(Type.I64, Type.I64), Collections.emptyList()), + new ImportObject.FuncImport("env", "ext_storage_get_version_1", argv -> { + System.out.println("Message printed in the body of 'ext_storage_get_version_1'"); + return argv; + }, Collections.singletonList(Type.I64), Collections.singletonList(Type.I64)), + new ImportObject.MemoryImport("env", 20, false)), module); + System.out.println("Instantiating module"); + Instance instance = module.instantiate(imports); + + System.out.println("Calling exported function 'Core_initialize_block' as it calls both of the imported functions"); + instance.exports.getFunction("Core_initialize_block").apply(1,2); + + Memory memory = instance.exports.getMemory("memory"); + System.out.println(memory.buffer()); + instance.close(); } } diff --git a/src/java/org/wasmer/Module.java b/src/java/org/wasmer/Module.java index 9595bad..178f8b8 100644 --- a/src/java/org/wasmer/Module.java +++ b/src/java/org/wasmer/Module.java @@ -1,6 +1,9 @@ package org.wasmer; +import org.wasmer.exports.Export; + import java.util.Collections; +import java.util.Map; /** * `Module` is a Java class that represents a WebAssembly module. diff --git a/src/java/org/wasmer/Native.java b/src/java/org/wasmer/Native.java index 749c396..9969073 100644 --- a/src/java/org/wasmer/Native.java +++ b/src/java/org/wasmer/Native.java @@ -7,15 +7,13 @@ import java.io.*; import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import java.nio.file.Files; +import java.util.logging.Level; +import java.util.logging.Logger; public class Native { public static final boolean LOADED_EMBEDDED_LIBRARY; + private static final Logger logger = Logger.getLogger(Native.class.getName()); static { LOADED_EMBEDDED_LIBRARY = loadEmbeddedLibrary(); @@ -30,10 +28,19 @@ public static String getCurrentPlatformIdentifier() { osName = "windows"; } else if (osName.contains("mac os x")) { osName = "darwin"; + String[] args = new String[] {"/bin/bash", "-c", "uname", "-p"}; + try { + Process proc = new ProcessBuilder(args).start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); + if (reader.readLine().equals("Darwin")) { + return osName + "-arm64"; + } + } catch (IOException e) { + throw new RuntimeException(e); + } } else { osName = osName.replaceAll("\\s+", "_"); } - return osName + "-" + System.getProperty("os.arch"); } @@ -56,16 +63,15 @@ private static boolean loadEmbeddedLibrary() { url.append(getCurrentPlatformIdentifier()).append("/"); URL nativeLibraryUrl = null; - // loop through extensions, stopping after finding first one for (String lib: libs) { - nativeLibraryUrl = Module.class.getResource(url.toString() + lib); + nativeLibraryUrl = Module.class.getResource(url + lib); if (nativeLibraryUrl != null) { break; } } - + if (nativeLibraryUrl != null) { // native library found within JAR, extract and load try { @@ -73,9 +79,9 @@ private static boolean loadEmbeddedLibrary() { libfile.deleteOnExit(); // just in case final InputStream in = nativeLibraryUrl.openStream(); - final OutputStream out = new BufferedOutputStream(new FileOutputStream(libfile)); + final OutputStream out = new BufferedOutputStream(Files.newOutputStream(libfile.toPath())); - int len = 0; + int len; byte[] buffer = new byte[8192]; while ((len = in.read(buffer)) > -1) { @@ -88,7 +94,7 @@ private static boolean loadEmbeddedLibrary() { usingEmbedded = true; } catch (IOException x) { - // mission failed, do nothing + logger.log(Level.SEVERE, "Failed to load native library", x); } } diff --git a/src/memory.rs b/src/memory.rs index 4ffc07e..f7bf11c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -7,18 +7,24 @@ use jni::{ sys::jint, JNIEnv, }; -use std::{cell::Cell, panic, rc::Rc, slice}; +use std::{cell::Cell, panic, slice}; +use std::sync::{Arc, Mutex}; use wasmer::MemoryView; use wasmer::Pages; use wasmer::Memory as WasmMemory; +/// A static variable holding the imported memory if there's such +/// +/// Note that `Memory` here refers to the local struct and not `wasmer::Memory` +pub static IMPORTED_MEMORY: Mutex> = Mutex::new(None); + #[derive(Clone)] pub struct Memory { - pub memory: Rc, + pub memory: Arc, } impl Memory { - pub fn new(memory: Rc) -> Self { + pub fn new(memory: Arc) -> Self { Self { memory } } @@ -108,6 +114,7 @@ pub mod java { types::{jptr, Pointer}, }; use jni::{objects::JObject, JNIEnv}; + use crate::memory::IMPORTED_MEMORY; pub fn initialize_memories(env: &JNIEnv, instance: &Instance) -> Result<(), Error> { let exports_object: JObject = env @@ -120,6 +127,27 @@ pub mod java { // Get the `org.wasmer.Memory` class. let memory_class = env.find_class("org/wasmer/Memory")?; + let memory_lock = IMPORTED_MEMORY.lock().unwrap(); + if memory_lock.is_some() { + let memory_object = env.new_object(memory_class, "()V", &[])?; + + // Try to set the memory pointer to the field `org.wasmer.Memory.memoryPointer`. + let memory_pointer: jptr = Pointer::new(memory_lock.clone().unwrap()).into(); + env.set_field(memory_object, "memoryPointer", "J", memory_pointer.into())?; + + // Add the newly created `org.wasmer.Memory` in the + // `org.wasmer.Exports` collection. + env.call_method( + exports_object, + "addMemory", + "(Ljava/lang/String;Lorg/wasmer/Memory;)V", + &[ + JObject::from(env.new_string("memory")?).into(), + memory_object.into(), + ], + )?; + } + for (memory_name, memory) in &instance.memories { // Instantiate the `Memory` class. diff --git a/src/module.rs b/src/module.rs index 64d8456..25042d0 100644 --- a/src/module.rs +++ b/src/module.rs @@ -11,6 +11,7 @@ use jni::{ JNIEnv, }; use std::{collections::HashMap, panic, rc::Rc}; +use std::sync::Arc; use wasmer::{self as runtime, Extern, Engine as _}; use wasmer_compiler_cranelift::Cranelift; use wasmer_engine_universal::Universal as UniversalEngine; @@ -106,12 +107,11 @@ pub extern "system" fn Java_org_wasmer_Module_nativeInstantiate( let instance = runtime::Instance::new(&module.module, &imports.import_object).map_err(|e| { runtime_error(format!("Failed to instantiate a WebAssembly module: {}", e)) })?; - let memories: HashMap = instance .exports .iter() .filter_map(|(export_name, export)| match export { - Extern::Memory(memory) => Some((export_name.to_string(), Memory::new(Rc::new(memory.clone())))), + Extern::Memory(memory) => Some((export_name.to_string(), Memory::new(Arc::new(memory.clone())))), _ => None, }) .collect();