Skip to content

Commit

Permalink
Open source pure native binary.
Browse files Browse the repository at this point in the history
See #127.

PiperOrigin-RevId: 689950666
  • Loading branch information
jwhpryor authored and copybara-github committed Oct 25, 2024
1 parent 38ef9f9 commit 3c55ec1
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 2 deletions.
24 changes: 24 additions & 0 deletions java/com/google/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
load("@rules_java//java:defs.bzl", "java_library")

licenses(["notice"])

java_library(
name = "random_string_java",
srcs = ["RandomString.java"],
)

cc_library(
name = "java_runtime_environment",
hdrs = ["java_runtime_environment.h"],
deps = ["//:jni_bind"],
)

cc_binary(
name = "main",
srcs = ["main.cc"],
data = [":random_string_java"],
deps = [
":java_runtime_environment",
"//:jni_bind",
],
)
3 changes: 3 additions & 0 deletions java/com/google/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This binary is meant to demonstrate a simple binary that is entirely written in native.

This is experimental.
43 changes: 43 additions & 0 deletions java/com/google/RandomString.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google;

import java.util.Random;

/** Just a toy model to make sure that we have a large data structure in memory. */
class RandomString {
public RandomString() {}

public static String generateRandomString(int length) {
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

Random random = new Random();
StringBuilder sb = new StringBuilder(length);

for (int i = 0; i < length; i++) {
int randomIndex = random.nextInt(characters.length());
char randomChar = characters.charAt(randomIndex);
sb.append(randomChar);
}

return sb.toString();
}

public String format() {
return generateRandomString(50);
}
}
110 changes: 110 additions & 0 deletions java/com/google/java_runtime_environment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#ifndef JNI_BIND_JAVA_COM_GOOGLE_JAVA_RUNTIME_ENVIRONMENT_H_
#define JNI_BIND_JAVA_COM_GOOGLE_JAVA_RUNTIME_ENVIRONMENT_H_

#include <dlfcn.h>
#include <sys/stat.h>

#include <cstdio>
#include <optional>
#include <string_view>

#include "jni_bind.h"

namespace jni_bind_sample_binary {

// True if `path` is present on disk.
inline bool FileExists(std::string_view path) {
struct stat buf;

return stat(path.data(), &buf) == 0 && S_ISREG(buf.st_mode);
}

inline std::optional<void *> LoadLibrary(std::string_view lib_path) {
void *lib_handle = dlopen(lib_path.data(), RTLD_NOW);
if (lib_handle == nullptr) {
printf("Failed to load libjvm so lib: %s\n", dlerror());
return std::nullopt;
}

return lib_handle;
}

// Represents JVM so lib, normally launched by some kind of launcher, but in
// this sample the JVM is driven entirely by native. This class may be suitable
// for use beyond this toy binary, but needs more rigorous review.
class JavaRuntimeEnvironment {
using CreateJvmFunc = jint (*)(JavaVM **, JNIEnv **, JavaVMInitArgs *);
using GetJvmFunc = jint (*)(JavaVM **, jsize, jsize *);

private:
inline JavaRuntimeEnvironment(void *lib_handle,
std::string_view class_collection)
: lib_handle_(lib_handle),
create_function_(reinterpret_cast<CreateJvmFunc>(
dlsym(lib_handle_, "JNI_CreateJavaVM"))),
get_jvm_function_(reinterpret_cast<GetJvmFunc>(
dlsym(lib_handle_, "JNI_GetCreatedJavaVMs"))) {
auto *vm_options = new JavaVMOption[1];
vm_options[0] = {.optionString =
const_cast<char *>(class_collection.data())};

vm_args_ = JavaVMInitArgs{.version = JNI_VERSION_1_6,
.nOptions = 1,
.options = vm_options,
.ignoreUnrecognized = JNI_FALSE};

jint result = create_function_(&jvm_, &jenv_, &vm_args_);
if (result < 0) {
printf("JNI_CreateJavaVM() failed\n");
return;
}

success_loading_jvm_ = true;
printf("JVM created.\n");
}

public:
~JavaRuntimeEnvironment() { dlclose(lib_handle_); }

// Builds a `JavaRuntimeEnvironment`, or nothing if failure.
static inline std::optional<JavaRuntimeEnvironment> Build(
std::string_view jvm_so_lib, std::string_view class_collection) {
if (!FileExists(jvm_so_lib.data())) {
printf("libjvm so solib does not exist.");
return std::nullopt;
}

auto lib_handle = LoadLibrary(jvm_so_lib);
if (lib_handle == std::nullopt) {
printf("Failed to load libjvm so lib.\n");
return std::nullopt;
}

JavaRuntimeEnvironment ret(*lib_handle, class_collection);
if (ret.success_loading_jvm_ == false) {
printf("Initializing JVM failed.\n");
return std::nullopt;
}

printf("Loading succeeded.\n");

return ret;
}

JavaVM *GetJvm() { return jvm_; }

private:
void *lib_handle_;
const CreateJvmFunc create_function_;
const GetJvmFunc get_jvm_function_;
JavaVMInitArgs vm_args_;

JavaVM *jvm_;
JNIEnv *jenv_;

bool success_loading_jvm_ = false;
};

} // namespace jni_bind_sample_binary

#endif // JNI_BIND_JAVA_COM_GOOGLE_JAVA_RUNTIME_ENVIRONMENT_H_
100 changes: 100 additions & 0 deletions java/com/google/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <dlfcn.h>
#include <sys/stat.h>

#include <cstdint>
#include <cstdio>
#include <iostream>
#include <optional>
#include <string>
#include <string_view>

#include "java_runtime_environment.h" // NOLINT
#include "jni_bind.h"

// This binary is meant as a test to exercise a JVM loaded entirely from
// native. For now the path to the JVM so lib is hard coded, but it should be
// an argument to the binary.

namespace {

using ::jni_bind_sample_binary::JavaRuntimeEnvironment;

static constexpr int32_t kNumIterations = 1000;

void RunIterationsToCompletion(jni::JvmRef<jni::kDefaultJvm> *jvm_ref) {
static constexpr jni::Class kRandomString{
"com/google/RandomString", jni::Constructor{},
jni::Method{"format", jni::Return<jstring>{}, jni::Params{}}};

jni::GlobalObject<kRandomString> random_string;

for (int i = 0; i < kNumIterations; ++i) {
printf("Iteration %i: %s\n", i,
random_string("format").Pin().ToString().data());
}
}

std::string LibNameToFullPath(std::string runfiles_path, std::string lib_name) {
return runfiles_path + lib_name;
}

} // namespace

int main(int argc, char *argv[]) {
std::string execution_path = argv[0];
std::string runfiles_path = execution_path + ".runfiles";

std::cout
<< "##############################################################"
<< '\n'
<< "Application Run Directory: " << execution_path << "\n"
<< "Runfiles Directory: " << runfiles_path << "\n"
<< "Number of iterations: " << kNumIterations
<< "\n##############################################################"
<< std::endl;
;

std::string full_class_collection =
"-Djava.class.path=" +
LibNameToFullPath(runfiles_path,
"/google3/third_party/jni_wrapper/java/com/google/"
"librandom_string_java.jar");

auto java_runtime_environment = JavaRuntimeEnvironment::Build(
"/usr/local/buildtools/java/jdk/lib/server/libjvm.so",
full_class_collection);

if (java_runtime_environment == std::nullopt) {
return -1;
}

{
jni::JvmRef<jni::kDefaultJvm> jvm_ref{(*java_runtime_environment).GetJvm()};
RunIterationsToCompletion(&jvm_ref);
}

std::cout << '\n'
<< "##############################################################"
<< '\n'
<< "DONE." << '\n'
<< "##############################################################"
<< std::endl;

return 0;
}
2 changes: 0 additions & 2 deletions metaprogramming/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1179,8 +1179,6 @@ cc_test(
cc_library(
name = "unfurl",
hdrs = ["unfurl.h"],
deps = [
],
)

cc_test(
Expand Down

0 comments on commit 3c55ec1

Please sign in to comment.