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

Initial implementation of Java bindings #455

Merged
merged 1 commit into from
Dec 3, 2019
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning].

### Added

- Added Java bindings.
[#455](https://github.com/ethereum/evmc/pull/455)
- Added `MockedHost` C++ class (in form of header-only `evmc::mocked_host` library)
which can be used to emulate Host behavior when testing VM implementations.
[#456](https://github.com/ethereum/evmc/pull/456)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Please visit the [documentation].
| **C++** | C++11, C++14, C++17 | GCC 6+, clang 3.8+, MSVC 2015+
| **Go** _(bindings)_ | 1.9 - 1.12 |
| **Rust** _(bindings)_[¹](#n1) | 2018 edition | 1.37.0 and newer
| **Java** _(bindings)_ | 11 |

<b id="n1">1</b>. Rust support is limited and not complete yet, but it is mostly functional already. Breaking changes are possible at this stage.

Expand Down
23 changes: 23 additions & 0 deletions bindings/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.idea/
.vscode
java/build/*
java/out/*
c/build/*
*.o
*.dylib
.DS_Store
.gradle
build
.classpath
.project
.settings/
java/.classpath
java/.project
java/.settings/
java/bin/
java/hs_err_pid*
gradle/
gradlew
gradlew.bat
c/evmc-vm.h
*.class
46 changes: 46 additions & 0 deletions bindings/java/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
OS:=$(shell uname -s | tr '[:upper:]' '[:lower:]')
ifeq ($(OS), linux)
EXT:=so
OS_LFLAGS:=
JAVA_HOME:=/usr/local/openjdk-11
else ifeq ($(OS), darwin)
EXT:=so
OS_LFLAGS:=-mmacosx-version-min=$(shell defaults read loginwindow SystemVersionStampAsString) -framework CoreFoundation -framework Security
JAVA_HOME:=$(shell java -XshowSettings:properties -version 2>&1 > /dev/null | grep 'java.home' | sed 's/\s*java.home = //' | sed 's/\/jre//')
endif

INCLUDES = -I../../include
JAVA_INCLUDES = -I$(JAVA_HOME)/include/$(OS) -I$(JAVA_HOME)/include
JAVA_LIBS = -L$(JAVA_HOME)/lib/server -ljvm
CFLAGS = -O2 -fPIC
LFLAGS = -shared
OUT_DIR = ./c/build

gradlew:
gradle setup

build: gradlew
mkdir -p $(OUT_DIR)
javac ./java/src/main/java/org/ethereum/evmc/EvmcVm.java -h ./c --class-path ./java/src/main/java -s ./java/build
mv c/org_ethereum_evmc_EvmcVm.h c/evmc-vm.h
gcc $(DEBUG_FLAG) $(CFLAGS) -c $(INCLUDES) -o $(OUT_DIR)/loader.o ../../lib/loader/loader.c
gcc $(DEBUG_FLAG) $(CFLAGS) -c $(INCLUDES) $(JAVA_INCLUDES) -o $(OUT_DIR)/host.o ./c/host.c
gcc $(DEBUG_FLAG) $(CFLAGS) ./c/evmc-vm.c $(INCLUDES) $(JAVA_INCLUDES) $(JAVA_LIBS) $(CFLAGS) $(LFLAGS) -o $(OUT_DIR)/evmc.$(EXT) $(OUT_DIR)/host.o $(OUT_DIR)/loader.o
gcc $(DEBUG_FLAG) -shared ../../examples/example_vm/example_vm.c $(INCLUDES) -o $(OUT_DIR)/example_vm.$(EXT)
jrhea marked this conversation as resolved.
Show resolved Hide resolved
mkdir -p ./java/build
./gradlew --no-daemon clean spotlessApply build

debug: DEBUG_FLAG = -D DEBUG

debug: build

test: build
./gradlew --no-daemon test

format:
clang-format -i c/evmc-vm.c c/host.c c/host.h

clean:
rm -rf build
rm -rf ./java/build/
rm -rf ./c/build/
40 changes: 40 additions & 0 deletions bindings/java/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
plugins {
id 'com.diffplug.gradle.spotless' version '3.16.0'
}
apply from: "${rootDir}/wrapper.gradle"
allprojects {
apply plugin: 'java-library'
repositories {
google()
jcenter()
mavenCentral()
}
sourceCompatibility = '11'
targetCompatibility = '11'
apply plugin: 'com.diffplug.gradle.spotless'
spotless {
java {
// This path needs to be relative to each project
target fileTree('.') {
include '**/*.java'
exclude '**/.gradle/**'
}
importOrder 'org.ethereum', 'java', ''
trimTrailingWhitespace()
endWithNewline()
googleJavaFormat('1.7')
}
}
}

subprojects {
tasks.withType(Test) {
testLogging.showStandardStreams = true
// If GRADLE_MAX_TEST_FORKS is not set, use half the available processors
maxParallelForks = (System.getenv('GRADLE_MAX_TEST_FORKS') ?: (Runtime.runtime.availableProcessors().intdiv(2) ?: 1)).toInteger()
useJUnitPlatform()
reports {
junitXml.enabled = true
}
}
}
163 changes: 163 additions & 0 deletions bindings/java/c/evmc-vm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

#include "evmc-vm.h"
#include "evmc/helpers.h"
#include "evmc/loader.h"
#include "host.h"

JNIEXPORT jobject JNICALL Java_org_ethereum_evmc_EvmcVm_init(JNIEnv* jenv,
jclass jcls,
jstring jfilename)
{
struct evmc_vm* evm;
jint rs = set_jvm(jenv);
assert(rs == JNI_OK);
// load the EVM
const char* filename = (*jenv)->GetStringUTFChars(jenv, jfilename, 0);
if (filename != NULL)
{
enum evmc_loader_error_code loader_error;
evm = evmc_load_and_create(filename, &loader_error);
if (evm == NULL || loader_error != EVMC_LOADER_SUCCESS)
{
const char* error_msg = evmc_last_error_msg();
jclass jclazz = (*jenv)->FindClass(jenv, "java/lang/AssertionError");
(*jenv)->ThrowNew(jenv, jclazz, error_msg);
}
(*jenv)->ReleaseStringUTFChars(jenv, jfilename, filename);
}
else
{
jclass jclazz = (*jenv)->FindClass(jenv, "java/lang/AssertionError");
(*jenv)->ThrowNew(jenv, jclazz, "JNI Error: filename cannot be NULL. \n");
}
jobject jresult = (*jenv)->NewDirectByteBuffer(jenv, (void*)evm, sizeof(struct evmc_vm));
assert(jresult != NULL);
return jresult;
}

JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_abi_1version(JNIEnv* jenv, jclass jcls)
{
return EVMC_ABI_VERSION;
}

JNIEXPORT jstring JNICALL Java_org_ethereum_evmc_EvmcVm_name(JNIEnv* jenv,
jclass jcls,
jobject jevm)
{
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
assert(evm != NULL);
const char* evm_name = evmc_vm_name(evm);
return (*jenv)->NewStringUTF(jenv, evm_name);
}

JNIEXPORT jstring JNICALL Java_org_ethereum_evmc_EvmcVm_version(JNIEnv* jenv,
jclass jcls,
jobject jevm)
{
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
assert(evm != NULL);
const char* evm_version = evmc_vm_version(evm);
return (*jenv)->NewStringUTF(jenv, evm_version);
}

JNIEXPORT void JNICALL Java_org_ethereum_evmc_EvmcVm_destroy(JNIEnv* jenv,
jclass jcls,
jobject jevm)
{
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
assert(evm != NULL);
evmc_destroy(evm);
}

JNIEXPORT void JNICALL Java_org_ethereum_evmc_EvmcVm_execute(JNIEnv* jenv,
jclass jcls,
jobject jevm,
jint jcontext_index,
jint jrev,
jobject jmsg,
jobject jcode,
jint jsize,
jobject jresult)
{
struct evmc_message* cmsg = (struct evmc_message*)(*jenv)->GetDirectBufferAddress(jenv, jmsg);
assert(cmsg != NULL);
const uint8_t* ccode = (uint8_t*)(*jenv)->GetDirectBufferAddress(jenv, jcode);
assert(ccode != NULL);
struct evmc_host_context context = {jcontext_index};
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
assert(evm != NULL);
#ifdef DEBUG
printf("********************before execute*******************\n");

printf("struct: evmc_message=%p\n", cmsg);
printf("sizeof(evmc_message): %lu\n", sizeof(struct evmc_message));
printf("kind=%p\n", &cmsg->kind);
printf("flags=%p\n", &cmsg->flags);
printf("depth=%p\n", &cmsg->depth);
printf("gas=%p\n", &cmsg->gas);
printf("destination=%p\n", &cmsg->destination.bytes);
printf("sender=%p\n", &cmsg->sender.bytes);
printf("input_data=%p\n", &cmsg->input_data);
printf("input_size=%p\n", &cmsg->input_size);
printf("value=%p\n", &cmsg->value.bytes);
printf("create2_salt=%p\n\n", &cmsg->create2_salt.bytes);

printf("kind=%d\n", cmsg->kind);
printf("flags=%d\n", cmsg->flags);
printf("depth=%d\n", cmsg->depth);
printf("gas=%lld\n", cmsg->gas);
printf("destination=%s\n", cmsg->destination.bytes);
printf("sender=%s\n", cmsg->sender.bytes);
printf("input_size=%zu\n", cmsg->input_size);
printf("value=%s\n\n", cmsg->value.bytes);
#endif
const struct evmc_host_interface* host = get_host_interface();
struct evmc_result* result =
(struct evmc_result*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
assert(result != NULL);
*result = evmc_execute(evm, host, &context, jrev, cmsg, ccode, jsize);
#ifdef DEBUG
printf("********************after execute*******************\n");
printf("sizeof(evmc_result): %lu\n", sizeof(struct evmc_result));
printf("status_code=%p\n", &result->status_code);
printf("gas_left=%p\n", &result->gas_left);
printf("output_data=%p\n\n", &result->output_data);

printf("status_code=%d\n", result->status_code);
printf("gas_left=%llu\n", result->gas_left);
printf("output_data=%s\n\n", result->output_data);
#endif
}

JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_get_1capabilities(JNIEnv* jenv,
jclass jcls,
jobject jevm)
{
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
assert(evm != NULL);
return evm->get_capabilities(evm);
}

JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_set_1option(JNIEnv* jenv,
jclass jcls,
jobject jevm,
jstring jname,
jstring jvalue)
{
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
assert(evm != NULL);
const char* name = (*jenv)->GetStringUTFChars(jenv, jname, 0);
const char* value = (*jenv)->GetStringUTFChars(jenv, jvalue, 0);
enum evmc_set_option_result option_result = evmc_set_option(evm, name, value);
(*jenv)->ReleaseStringUTFChars(jenv, jname, name);
(*jenv)->ReleaseStringUTFChars(jenv, jvalue, value);
return option_result;
}

JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_get_1result_1size(JNIEnv* jenv, jclass jcls)
{
return sizeof(struct evmc_result);
}
Loading