Skip to content

Commit

Permalink
Move Fabric JSC bindings to oss
Browse files Browse the repository at this point in the history
Reviewed By: mdvacca

Differential Revision: D7205065

fbshipit-source-id: 5876cb3e08ee96e39b80e6b377c60600f404ca21
  • Loading branch information
ayc1 authored and facebook-github-bot committed Mar 14, 2018
1 parent bec97dc commit 3a2bdf5
Show file tree
Hide file tree
Showing 6 changed files with 416 additions and 0 deletions.
24 changes: 24 additions & 0 deletions ReactAndroid/src/main/java/com/facebook/react/fabric/jsc/BUCK
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
load("@xplat//ReactNative:DEFS.bzl", "react_native_dep", "react_native_target", "rn_android_library", "IS_OSS_BUILD")

rn_android_library(
name = "jsc",
srcs = glob(["**/*.java"]),
exported_deps = [
react_native_dep("java/com/facebook/jni:jni"),
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
],
provided_deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
],
required_for_source_only_abi = True,
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/fabric:fabric"),
react_native_target("java/com/facebook/react/fabric/jsc/jni:jni"),
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2004-present Facebook. All Rights Reserved.

package com.facebook.react.fabric.jsc;

import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.fabric.FabricBinding;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.soloader.SoLoader;

@DoNotStrip
public class FabricJSCBinding implements FabricBinding {

static {
SoLoader.loadLibrary("fabricjscjni");
}

// used from native
@SuppressWarnings("unused")
private final HybridData mHybridData;

private static native HybridData initHybrid();

private native void installFabric(long jsContextNativePointer, Object fabricModule);

public FabricJSCBinding() {
mHybridData = initHybrid();
}

@Override
public void installFabric(JavaScriptContextHolder jsContext, FabricUIManager fabricModule) {
installFabric(jsContext.get(), fabricModule);
}
}
20 changes: 20 additions & 0 deletions ReactAndroid/src/main/java/com/facebook/react/fabric/jsc/jni/BUCK
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@xplat//ReactNative:DEFS.bzl", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "FBJNI_TARGET", "ANDROID")

rn_xplat_cxx_library(
name = "jni",
srcs = glob(["*.cpp"]),
headers = glob(["*.h"]),
compiler_flags = [
"-Wall",
"-fexceptions",
"-std=gnu++1y",
],
platforms = ANDROID,
soname = "libfabricjscjni.$(ext)",
visibility = ["PUBLIC"],
deps = [
FBJNI_TARGET,
react_native_xplat_target("jschelpers:jschelpers"),
react_native_target("jni/react/jni:jni"),
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
// Copyright 2004-present Facebook. All Rights Reserved.

#include "FabricJSCBinding.h"
#include <fb/fbjni.h>
#include <react/jni/ReadableNativeMap.h>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Unicode.h>

using namespace facebook::jni;

namespace facebook {
namespace react {

namespace {

bool useCustomJSC = false;

struct JList : public JavaClass<JList> {
static constexpr auto kJavaDescriptor = "Ljava/util/List;";
};

struct JShadowNode : public JavaClass<JShadowNode> {
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/uimanager/ReactShadowNode;";
};

typedef struct FabricJSCUIManager {
FabricJSCUIManager(alias_ref<jobject> module, JSClassRef classRef, bool customJSC)
: wrapperObjectClassRef(classRef)
, useCustomJSC(customJSC) {
fabricUiManager = make_global(module);
}
global_ref<jobject> fabricUiManager;
JSClassRef wrapperObjectClassRef;
bool useCustomJSC;
} FabricJSCUIManager;

jobject makePlainGlobalRef(jobject object) {
// When storing the global reference we need it to be a plain
// pointer. That's why we use plain jni instead of fbjni here.
return Environment::current()->NewGlobalRef(object);
}

local_ref<JString> JSValueToJString(JSContextRef ctx, JSValueRef value) {
JSStringRef strRef = JSC_JSValueToStringCopy(ctx, value, NULL);
const size_t size = JSStringGetMaximumUTF8CStringSize(strRef);
char buffer[size];
JSStringGetUTF8CString(strRef, buffer, size);
JSC_JSStringRelease(ctx, strRef);
return make_jstring(buffer);
}

local_ref<JShadowNode> JSValueToJShadowNode(JSContextRef ctx, JSValueRef value) {
JSObjectRef obj = JSC_JSValueToObject(ctx, value, NULL);
auto node = static_cast<JShadowNode::javaobject>(JSC_JSObjectGetPrivate(useCustomJSC, obj));
return make_local(node);
}

local_ref<JList> JSValueToJList(JSContextRef ctx, JSValueRef value) {
JSObjectRef obj = JSC_JSValueToObject(ctx, value, NULL);
auto node = static_cast<JList::javaobject>(JSC_JSObjectGetPrivate(useCustomJSC, obj));
return make_local(node);
}

local_ref<ReadableNativeMap::jhybridobject> JSValueToReadableMapViaJSON(JSContextRef ctx, JSValueRef value) {
JSStringRef jsonRef = JSC_JSValueCreateJSONString(ctx, value, 0, NULL);
size_t size = JSC_JSStringGetLength(ctx, jsonRef);
const JSChar* utf16 = JSC_JSStringGetCharactersPtr(ctx, jsonRef);
std::string json = unicode::utf16toUTF8(utf16, size);
JSC_JSStringRelease(ctx, jsonRef);
folly::dynamic dynamicValue = folly::parseJson(json);
return ReadableNativeMap::newObjectCxxArgs(std::move(dynamicValue));
}

JSValueRef createNode(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;

static auto createNode =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(jint, jstring, jint, ReadableNativeMap::javaobject)>("createNode");

int reactTag = (int)JSC_JSValueToNumber(ctx, arguments[0], NULL);
auto viewName = JSValueToJString(ctx, arguments[1]);
int rootTag = (int)JSC_JSValueToNumber(ctx, arguments[2], NULL);
auto props = JSC_JSValueIsNull(ctx, arguments[3]) ? local_ref<ReadableNativeMap::jhybridobject>(nullptr) :
JSValueToReadableMapViaJSON(ctx, arguments[3]);;

// TODO: Retain object in arguments[4] using a weak ref.

auto node = createNode(manager, reactTag, viewName.get(), rootTag, props.get());

return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(node.get()));
}

JSValueRef cloneNode(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;

static auto cloneNode =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject)>("cloneNode");

auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto newNode = cloneNode(manager, previousNode.get());

return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}

JSValueRef cloneNodeWithNewChildren(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;

static auto cloneNodeWithNewChildren =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject)>("cloneNodeWithNewChildren");

auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto newNode = cloneNodeWithNewChildren(manager, previousNode.get());

return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}

JSValueRef cloneNodeWithNewProps(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;

static auto cloneNodeWithNewProps =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject, ReadableNativeMap::javaobject)>("cloneNodeWithNewProps");

auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto props = JSValueToReadableMapViaJSON(ctx, arguments[1]);
auto newNode = cloneNodeWithNewProps(manager, previousNode.get(), props.get());

return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}

JSValueRef cloneNodeWithNewChildrenAndProps(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;

static auto cloneNodeWithNewChildrenAndProps =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject, ReadableNativeMap::javaobject)>("cloneNodeWithNewChildrenAndProps");

auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto props = JSValueToReadableMapViaJSON(ctx, arguments[1]);
auto newNode = cloneNodeWithNewChildrenAndProps(manager, previousNode.get(), props.get());

return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}

JSValueRef appendChild(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;

static auto appendChild =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void(JShadowNode::javaobject, JShadowNode::javaobject)>("appendChild");

auto parentNode = JSValueToJShadowNode(ctx, arguments[0]);
auto childNode = JSValueToJShadowNode(ctx, arguments[1]);

appendChild(manager, parentNode.get(), childNode.get());

return JSC_JSValueMakeUndefined(ctx);
}

JSValueRef createChildSet(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;

static auto createChildSet =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JList>(jint)>("createChildSet");

int rootTag = (int)JSC_JSValueToNumber(ctx, arguments[0], NULL);
auto childSet = createChildSet(manager, rootTag);

return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(childSet.get()));
}

JSValueRef appendChildToSet(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;

static auto appendChildToSet =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void(JList::javaobject, JShadowNode::javaobject)>("appendChildToSet");

auto childSet = JSValueToJList(ctx, arguments[0]);
auto childNode = JSValueToJShadowNode(ctx, arguments[1]);

appendChildToSet(manager, childSet.get(), childNode.get());

return JSC_JSValueMakeUndefined(ctx);
}

JSValueRef completeRoot(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;

static auto completeRoot =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void(jint, JList::javaobject)>("completeRoot");

int rootTag = (int)JSC_JSValueToNumber(ctx, arguments[0], NULL);
auto childSet = JSValueToJList(ctx, arguments[1]);

completeRoot(manager, rootTag, childSet.get());

return JSC_JSValueMakeUndefined(ctx);
}

void finalizeJNIObject(JSObjectRef object) {
// Release whatever global ref object we're storing here.
jobject globalRef = (jobject)JSC_JSObjectGetPrivate(useCustomJSC, object);
Environment::current()->DeleteGlobalRef(globalRef);
}

void finalizeWrapper(JSObjectRef object) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, object);
delete managerWrapper;
}

void addFabricMethod(
JSContextRef context,
jni::alias_ref<jobject> fabricModule,
JSClassRef nodeClassRef,
JSObjectRef module,
const char *name,
JSObjectCallAsFunctionCallback callback
) {
JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.callAsFunction = callback;
definition.finalize = finalizeWrapper;
JSClassRef classRef = JSC_JSClassCreate(useCustomJSC, &definition);
FabricJSCUIManager *managerWrapper = new FabricJSCUIManager(fabricModule, nodeClassRef, useCustomJSC);
JSObjectRef functionRef = JSC_JSObjectMake(context, classRef, managerWrapper);
JSC_JSClassRelease(useCustomJSC, classRef);

JSStringRef nameStr = JSC_JSStringCreateWithUTF8CString(context, name);
JSC_JSObjectSetProperty(context, module, nameStr, functionRef, kJSPropertyAttributeNone, NULL);
JSC_JSStringRelease(context, nameStr);
}

}

jni::local_ref<FabricJSCBinding::jhybriddata> FabricJSCBinding::initHybrid(
jni::alias_ref<jclass>) {
return makeCxxInstance();
}

void FabricJSCBinding::installFabric(jlong jsContextNativePointer,
jni::alias_ref<jobject> fabricModule) {
JSContextRef context = (JSContextRef)jsContextNativePointer;
useCustomJSC = facebook::react::isCustomJSCPtr(context);

JSObjectRef module = JSC_JSObjectMake(context, NULL, NULL);

// Class definition for wrapper objects around nodes and sets
JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.finalize = finalizeJNIObject;
JSClassRef classRef = JSC_JSClassCreate(useCustomJSC, &definition);

addFabricMethod(context, fabricModule, classRef, module, "createNode", createNode);
addFabricMethod(context, fabricModule, classRef, module, "cloneNode", cloneNode);
addFabricMethod(context, fabricModule, classRef, module, "cloneNodeWithNewChildren", cloneNodeWithNewChildren);
addFabricMethod(context, fabricModule, classRef, module, "cloneNodeWithNewProps", cloneNodeWithNewProps);
addFabricMethod(context, fabricModule, classRef, module, "cloneNodeWithNewChildrenAndProps", cloneNodeWithNewChildrenAndProps);
addFabricMethod(context, fabricModule, classRef, module, "appendChild", appendChild);
addFabricMethod(context, fabricModule, classRef, module, "createChildSet", createChildSet);
addFabricMethod(context, fabricModule, classRef, module, "appendChildToSet", appendChildToSet);
addFabricMethod(context, fabricModule, classRef, module, "completeRoot", completeRoot);

JSObjectRef globalObject = JSC_JSContextGetGlobalObject(context);
JSStringRef globalName = JSC_JSStringCreateWithUTF8CString(context, "nativeFabricUIManager");
JSC_JSObjectSetProperty(context, globalObject, globalName, module, kJSPropertyAttributeNone, NULL);
JSC_JSStringRelease(context, globalName);
}

void FabricJSCBinding::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", FabricJSCBinding::initHybrid),
makeNativeMethod("installFabric", FabricJSCBinding::installFabric),
});
}

}
}
Loading

2 comments on commit 3a2bdf5

@gengjiawen
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ayc1 This module seems forget add to Android.mk support.

@hey99xx
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ayc1 @gengjiawen @hramos
Will this break OSS builds? If so should we create a GitHub issue for it? I can create if you want.

Please sign in to comment.