Skip to content
Draft

HBC #808

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
16 changes: 15 additions & 1 deletion core/player/BUILD
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
load("@rules_player//javascript:defs.bzl", "js_pipeline")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("@rules_player//javascript:defs.bzl", "js_pipeline")
load("//jvm/hermes:hermesc.bzl", "hermes_compile")
load("//tools:defs.bzl", "NATIVE_BUILD_DEPS", "tsup_config", "vitest_config")

npm_link_all_packages(name = "node_modules")
Expand Down Expand Up @@ -49,3 +50,16 @@ filegroup(
),
visibility = ["//visibility:public"],
)

genrule(
name = "native",
srcs = [":player_native_bundle"],
outs = ["Player.native.js"],
cmd = "echo $(SRCS) | tr ' ' '\\n' | grep %s$$ | xargs -I {} cp {} $(OUTS)" % "Player.native.js",
)

hermes_compile(
name = "hbc",
js = ":native",
visibility = ["//visibility:public"],
)
1 change: 1 addition & 0 deletions jvm/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ main_deps = main_exports + [

main_resources = [
"//core/player:player_native_bundle",
"//core/player:hbc",
]

# Test dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ public data class ScriptContext(
val script: String,
val id: String,
val sourceMap: String? = null,
val preCompiledScript: ByteArray = byteArrayOf()
)
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ public class HeadlessPlayer @ExperimentalPlayerApi @JvmOverloads public construc
if (runtime.config.debuggable) debugSource.readText() else source.readText(),
BUNDLED_SOURCE_PATH,
sourceMap.readText(),
),
precompiledSource.readBytes()
)
)

/** 2. merge explicit [LoggerPlugin]s with ones created by service loader */
Expand Down Expand Up @@ -196,6 +197,8 @@ public class HeadlessPlayer @ExperimentalPlayerApi @JvmOverloads public construc

private const val DEBUG_SOURCE_PATH = "core/player/dist/Player.native.js"

private const val hbcSourcePath = "core/player/Player.native.js.hbc"

/** Gets [URL] of the bundled source */
private val bundledSource get() = this::class.java
.classLoader
Expand All @@ -208,5 +211,8 @@ public class HeadlessPlayer @ExperimentalPlayerApi @JvmOverloads public construc
private val sourceMap get() = this::class.java
.classLoader
.getResource("$BUNDLED_SOURCE_PATH.map")

private val precompiledSource get() = this::class.java
.classLoader.getResource(hbcSourcePath)
}
}
33 changes: 33 additions & 0 deletions jvm/hermes/hermesc.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
def _hermes_compile_impl(context):
input = context.file.js
hbc = context.actions.declare_file("%s.hbc" % input.basename)

args = context.actions.args()
args.add("-emit-binary")
args.add("-out", hbc)
args.add(input)

context.actions.run(
mnemonic = "HermesC",
executable = context.executable._hermesc,
arguments = [args],
inputs = depset([input]),
outputs = [hbc],
)

return [DefaultInfo(files = depset([hbc]))]

hermes_compile = rule(
implementation = _hermes_compile_impl,
attrs = {
"js": attr.label(
allow_single_file = True,
),
"_hermesc": attr.label(
default = Label("@hermes//:hermesc"),
allow_single_file = True,
executable = True,
cfg = "exec",
),
},
)
18 changes: 18 additions & 0 deletions jvm/hermes/src/main/jni/JJSIValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@
#include "JJSIValue.h"

#include <iostream>
#include <hermes/hermes.h>
#include <hermes/CompileJS.h>

namespace intuit::playerui {

using facebook::jsi::Runtime;
using facebook::jsi::Value;
using facebook::jsi::Object;
using facebook::jsi::Array;
using facebook::jsi::Function;
using facebook::jsi::String;
using facebook::jsi::Symbol;
using facebook::jsi::BigInt;

[[noreturn]] void throwNativeHandleReleasedException(std::string nativeHandle) {
// TODO: create a new exception type for this to hook into PlayerRuntimeException
auto throwableClass = findClassLocal("com/intuit/playerui/core/player/PlayerException");
Expand Down Expand Up @@ -32,6 +43,12 @@ local_ref<JJSIValue::jhybridobject> JJSIRuntime::evaluateJavaScript(alias_ref<JR
return JJSIValue::newObjectCxxArgs(this->get_scope(), get_runtime().evaluateJavaScript(std::make_shared<StringBuffer>(script), sourceURL));;
}

local_ref<JJSIValue::jhybridobject> JJSIRuntime::evaluateHermesBytecode(alias_ref<JRuntimeThreadContext>, alias_ref<jbyteArray> byteArray, std::string sourceURL) {
auto size = byteArray->size();
auto region = byteArray->getRegion(0, size);
return JJSIValue::newObjectCxxArgs(this->get_scope(), get_runtime().evaluateJavaScript(std::make_shared<ByteArrayBuffer>(region.get(), size), sourceURL));
}

local_ref<JJSIPreparedJavaScript::jhybridobject> JJSIRuntime::prepareJavaScript(alias_ref<JRuntimeThreadContext>, std::string script, std::string sourceURL) {
return JJSIPreparedJavaScript::newObjectCxxArgs(get_runtime().prepareJavaScript(std::make_shared<StringBuffer>(script), sourceURL));
}
Expand Down Expand Up @@ -61,6 +78,7 @@ std::string JJSIRuntime::description(alias_ref<JRuntimeThreadContext>) {
void JJSIRuntime::registerNatives() {
registerHybrid({
makeNativeMethod("evaluateJavaScript", JJSIRuntime::evaluateJavaScript),
makeNativeMethod("evaluateHermesBytecode", JJSIRuntime::evaluateHermesBytecode),
makeNativeMethod("prepareJavaScript", JJSIRuntime::prepareJavaScript),
makeNativeMethod("evaluatePreparedJavaScript", JJSIRuntime::evaluatePreparedJavaScript),
#ifdef JSI_MICROTASK
Expand Down
19 changes: 19 additions & 0 deletions jvm/hermes/src/main/jni/JJSIValue.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <iostream>
#include <vector>
#include <cstring>
#include <jsi/jsi.h>
#include <fbjni/fbjni.h>
#include <fbjni/ByteBuffer.h>
Expand All @@ -14,6 +16,22 @@ namespace intuit::playerui {

[[noreturn]] void throwNativeHandleReleasedException(std::string nativeHandle);

class ByteArrayBuffer : public Buffer {
public:
ByteArrayBuffer(const void* data, size_t size) : size_(size) {
// Copy the data to ensure ownership, otherwise leads to segfault
buffer_.resize(size);
std::memcpy(buffer_.data(), data, size);
}

size_t size() const override { return size_; }
const uint8_t* data() const override { return buffer_.data(); }

private:
std::vector<uint8_t> buffer_;
size_t size_;
};

class JHybridClass : public HybridClass<JHybridClass> {
public:
static constexpr auto kJavaDescriptor = "Lcom/intuit/playerui/jsi/HybridClass;";
Expand Down Expand Up @@ -68,6 +86,7 @@ class JJSIRuntime : public HybridClass<JJSIRuntime> {
static void registerNatives();

local_ref<JJSIValue_jhybridobject> evaluateJavaScript(alias_ref<JRuntimeThreadContext>, std::string script, std::string sourceURL);
local_ref<JJSIValue_jhybridobject> evaluateHermesBytecode(alias_ref<JRuntimeThreadContext>, alias_ref<jbyteArray> byteArray,std::string sourceURL);
local_ref<JJSIPreparedJavaScript::jhybridobject> prepareJavaScript(alias_ref<JRuntimeThreadContext>, std::string script, std::string sourceURL);
local_ref<JJSIValue_jhybridobject> evaluatePreparedJavaScript(alias_ref<JRuntimeThreadContext>, alias_ref<JJSIPreparedJavaScript::jhybridobject> js);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ public class HermesRuntime private constructor(

override fun load(scriptContext: ScriptContext): Any? = evaluateInJSThreadBlocking {
val sourceMap = scriptContext.sourceMap
if (sourceMap != null) {
val hbc = scriptContext.preCompiledScript
if (hbc.isNotEmpty()) {
println("+++ loading precompiled hbc (${hbc.size} bytes)++")
evaluateHermesBytecode(hbc, scriptContext.id)
} else if (sourceMap != null) {
evaluateJavaScriptWithSourceMap(scriptContext.script, sourceMap, scriptContext.id)
} else {
evaluateJavaScript(scriptContext.script, scriptContext.id)
Expand Down
3 changes: 3 additions & 0 deletions jvm/hermes/src/main/kotlin/com/intuit/playerui/jsi/Value.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public open class Runtime(
context(RuntimeThreadContext)
public external fun evaluateJavaScript(script: String, sourceURL: String = "unknown"): Value

context(RuntimeThreadContext)
public external fun evaluateHermesBytecode(bytecode: ByteArray, sourceURL: String = "unknown"): Value

context(RuntimeThreadContext)
public external fun prepareJavaScript(script: String, sourceURL: String = "unknown"): PreparedJavaScript

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ public open class MakeFlowModule internal constructor(
) : JSScriptPluginWrapper("MakeFlow", sourcePath = "core/make-flow/dist/MakeFlow.native.js") {
override fun apply(runtime: Runtime<*>) {
runtime.execute(script)
instance = runtime.buildInstance(name)
instance = runtime.execute("""
({
makeFlow: typeof makeFlow !== 'undefined' ? makeFlow : MakeFlow.makeFlow
})
""") as Node
}

public fun makeFlow(flow: Node): JsonElement {
Expand Down
13 changes: 11 additions & 2 deletions third_party/hermes/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,33 @@ cmake(
## Hermes X-compile requires two steps:
## 1. Compile hermesc against host
cmake(
name = "hermesc",
name = "hermesc_",
env = {
"CMAKE_BUILD_PARALLEL_LEVEL": "$(CMAKE_BUILD_PARALLEL_LEVEL)",
},
generate_args = ["-G Ninja"],
install = False,
lib_source = "working_directory",
out_binaries = ["hermesc"],
out_include_dir = None,
postfix_script = "cp -L $$BUILD_TMPDIR/bin/hermesc $$INSTALLDIR/bin",
tags = ["no-sandbox"],
targets = ["hermesc"],
# Specifically build for host, always
toolchains = [
"@bazel_tools//tools/cpp:toolchain",
"@player//:cmake_build_parallel_level",
],
)

genrule(
name = "hermesc",
srcs = [":hermesc_"],
outs = ["hermesc_bin"],
cmd = "echo $(SRCS) | tr ' ' '\\n' | grep hermesc$$ | xargs -I {} cp {} $(OUTS)",
executable = True,
visibility = ["//visibility:public"],
)

## 2. X-compile hermes for Android referencing host built hermesc
cmake(
name = "android",
Expand Down
Binary file modified third_party/rn/arm64-v8a/libhermes.so
Binary file not shown.
Binary file modified third_party/rn/armeabi-v7a/libhermes.so
Binary file not shown.
Binary file modified third_party/rn/x86/libhermes.so
Binary file not shown.
Binary file modified third_party/rn/x86_64/libhermes.so
Binary file not shown.