Skip to content

Commit

Permalink
java: statically load JNI library
Browse files Browse the repository at this point in the history
Also package both the JNI binding and the example VM in the JAR.

Co-authored-by: Antoine Toulme <[email protected]>
  • Loading branch information
axic and atoulme committed Mar 23, 2021
1 parent c39de40 commit 78e2679
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 22 deletions.
26 changes: 24 additions & 2 deletions bindings/java/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
# Licensed under the Apache License, Version 2.0.

JAVA_HOME:=$(shell java -XshowSettings:properties -version 2>&1 > /dev/null | grep 'java.home' | sed 's/\s*java.home = //' | sed 's/\/jre//')
EXTENSION:="so"
ifeq ($(OS),Windows_NT)
EXTENSION:=dll
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
EXTENSION:=so
endif
ifeq ($(UNAME_S),Darwin)
EXTENSION:=dylib
endif
endif

SOURCE_DIR = $(realpath $(CURDIR)/../..)
OUT_DIR = $(CURDIR)/c/build
Expand All @@ -11,7 +23,7 @@ BUILD_DIR = $(OUT_DIR)/_cmake_build
lint: gradlew
./gradlew --no-daemon clean spotlessCheck

build: gradlew $(OUT_DIR)/lib/libevmc-java.so
build: gradlew java/src/test/resources/libexample-vm.$(EXTENSION) java/src/main/resources/libevmc-java.$(EXTENSION)
mkdir -p ./java/build
./gradlew --no-daemon clean spotlessApply build

Expand All @@ -21,11 +33,21 @@ test: build
gradlew:
gradle --no-daemon setup

$(OUT_DIR)/lib/libevmc-java.so:
$(OUT_DIR)/lib/libexample-vm.$(EXTENSION): $(OUT_DIR)/lib/libevmc-java.$(EXTENSION)

$(OUT_DIR)/lib/libevmc-java.$(EXTENSION):
mkdir -p $(BUILD_DIR)
(cd $(BUILD_DIR) && cmake $(SOURCE_DIR) -DCMAKE_INSTALL_PREFIX=$(OUT_DIR) -DEVMC_JAVA=ON -DJAVA_HOME=$(JAVA_HOME) -DEVMC_EXAMPLES=ON)
cmake --build $(OUT_DIR)/_cmake_build --target install

java/src/main/resources/libevmc-java.$(EXTENSION): $(OUT_DIR)/lib/libevmc-java.$(EXTENSION)
mkdir -p java/src/main/resources
cp $(OUT_DIR)/lib/libevmc-java.$(EXTENSION) java/src/main/resources/libevmc-java.$(EXTENSION)

java/src/test/resources/libexample-vm.$(EXTENSION): $(OUT_DIR)/lib/libexample-vm.$(EXTENSION)
mkdir -p java/src/test/resources
cp $(OUT_DIR)/lib/libexample-vm.$(EXTENSION) java/src/test/resources/libexample-vm.$(EXTENSION)

clean:
rm -rf $(OUT_DIR)
rm -rf build
Expand Down
81 changes: 63 additions & 18 deletions bindings/java/java/src/main/java/org/ethereum/evmc/EvmcVm.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,85 @@
// Licensed under the Apache License, Version 2.0.
package org.ethereum.evmc;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

/**
* The Java interface to the evm instance.
*
* <p>Defines the Java methods capable of accessing the evm implementation.
*/
public final class EvmcVm implements AutoCloseable {
private static EvmcVm evmcVm;
private static boolean isEvmcLibraryLoaded = false;
private static final Throwable evmcLoadingError;
private ByteBuffer nativeVm;

// Load the dynamic library containing the JNI bindings to EVMC.
static {
Throwable error = null;

// First try loading from global path.
try {
System.loadLibrary("libevmc-java");
} catch (UnsatisfiedLinkError globalLoadingError) {
String extension = null;
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
extension = "dll";
} else if (os.contains("nix") || os.contains("nux") || os.contains("aix")) {
extension = "so";
} else if (os.contains("mac") || os.contains("darwin")) {
extension = "dylib";
} else {
// Give up, because we are unsure what system we are running on.
error = globalLoadingError;
}

// Try loading the binding from the package.
if (extension != null) {
try {
Path evmcLib = Files.createTempFile("libevmc-java", extension);
Files.copy(
EvmcVm.class.getResourceAsStream("/libevmc-java." + extension),
evmcLib,
StandardCopyOption.REPLACE_EXISTING);
evmcLib.toFile().deleteOnExit();
// We are somewhat certain about the file, try loading it.
try {
System.load(evmcLib.toAbsolutePath().toString());
} catch (UnsatisfiedLinkError packageLoadingError) {
error = packageLoadingError;
}
} catch (IOException packageCreationError) {
error = packageCreationError;
}
}
}
evmcLoadingError = error;
}

/**
* Returns true if the native library was loaded successfully and EVMC capabilities are available.
*
* @return true if the library is available
*/
public static boolean isAvailable() {
return evmcLoadingError == null;
}

/**
* This method loads the specified evm shared library and loads/initializes the jni bindings.
*
* @param filename /path/filename of the evm shared object
* @throws org.ethereum.evmc.EvmcLoaderException
*/
public static EvmcVm create(String filename) throws EvmcLoaderException {
if (!EvmcVm.isEvmcLibraryLoaded) {
try {
// load so containing the jni bindings to evmc
System.load(System.getProperty("user.dir") + "/../c/build/lib/libevmc-java.so");
EvmcVm.isEvmcLibraryLoaded = true;
} catch (UnsatisfiedLinkError e) {
throw new EvmcLoaderException("EVMC JNI binding library failed to load", e);
}
}
if (Objects.isNull(evmcVm)) {
evmcVm = new EvmcVm(filename);
if (!isAvailable()) {
throw new EvmcLoaderException("EVMC JNI binding library failed to load", evmcLoadingError);
}
return evmcVm;
return new EvmcVm(filename);
}

private EvmcVm(String filename) throws EvmcLoaderException {
Expand All @@ -44,7 +91,7 @@ private EvmcVm(String filename) throws EvmcLoaderException {
/**
* This method loads the specified EVM implementation and returns its pointer.
*
* @param Path to the dynamic object representing the EVM implementation.
* @param filename Path to the dynamic object representing the EVM implementation
* @return Internal object pointer.
* @throws org.ethereum.evmc.EvmcLoaderException
*/
Expand Down Expand Up @@ -153,7 +200,5 @@ public int set_option(String name, String value) {
@Override
public void close() {
destroy(nativeVm);
isEvmcLibraryLoaded = false;
evmcVm = null;
}
}
19 changes: 17 additions & 2 deletions bindings/java/java/src/test/java/org/ethereum/evmc/EvmcTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,29 @@
// Licensed under the Apache License, Version 2.0.
package org.ethereum.evmc;

import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

final class EvmcTest {
private static final String exampleVmPath =
System.getProperty("user.dir") + "/../c/build/lib/libexample-vm.so";
private static final String exampleVmPath;

static {
String extension = null;
String os = System.getProperty("os.name", "generic").toLowerCase();
if (os.contains("mac") || os.contains("darwin")) {
extension = "dylib";
} else if (os.contains("win")) {
extension = "dll";
} else {
extension = "so";
}

URL exampleVM = EvmcTest.class.getClassLoader().getResource("libexample-vm." + extension);
exampleVmPath = exampleVM.getFile();
}

@Test
void testInitCloseDestroy() throws Exception {
Expand Down

0 comments on commit 78e2679

Please sign in to comment.