Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java: statically load JNI library #557

Merged
merged 1 commit into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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