Skip to content

Commit

Permalink
Add default implementation for Object.create(prototype) (facebook#47946)
Browse files Browse the repository at this point in the history
Summary:

Object creation with custom prototype can currently be done, but it is
unnecessarily convoluted. Users have to call into the global object to
get the `Object.create` function, then call it with the custom
prototype.

This diff adds a JSI API for Object.create(prototype) to make it easy
for users.

Changelog: [Internal]

Differential Revision: D66485209
  • Loading branch information
Chi Tsai authored and facebook-github-bot committed Nov 27, 2024
1 parent 1d909ef commit b3ff8ec
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 0 deletions.
9 changes: 9 additions & 0 deletions packages/react-native/ReactCommon/jsi/jsi/decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
return plain_.utf16(sym);
}

Object createObjectWithPrototype(const Value& prototype) override {
return plain_.createObjectWithPrototype(prototype);
}

Object createObject() override {
return plain_.createObject();
};
Expand Down Expand Up @@ -695,6 +699,11 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
return RD::createValueFromJsonUtf8(json, length);
};

Object createObjectWithPrototype(const Value& prototype) override {
Around around{with_};
return RD::createObjectWithPrototype(prototype);
}

Object createObject() override {
Around around{with_};
return RD::createObject();
Expand Down
10 changes: 10 additions & 0 deletions packages/react-native/ReactCommon/jsi/jsi/jsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,16 @@ std::u16string Runtime::utf16(const String& str) {
return convertUTF8ToUTF16(utf8Str);
}

Object Runtime::createObjectWithPrototype(const Value& prototype) {
if (!prototype.isObject() && !prototype.isNull()) {
throw JSError(*this, "Object prototype argument must be an Object or null");
}
auto createFn = global()
.getPropertyAsObject(*this, "Object")
.getPropertyAsFunction(*this, "create");
return createFn.call(*this, prototype).asObject(*this);
}

Pointer& Pointer::operator=(Pointer&& other) noexcept {
if (ptr_) {
ptr_->invalidate();
Expand Down
8 changes: 8 additions & 0 deletions packages/react-native/ReactCommon/jsi/jsi/jsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ class JSI_EXPORT Runtime {
virtual std::shared_ptr<HostObject> getHostObject(const jsi::Object&) = 0;
virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0;

// Creates a new Object with the custom prototype
virtual Object createObjectWithPrototype(const Value& prototype);

virtual bool hasNativeState(const jsi::Object&) = 0;
virtual std::shared_ptr<NativeState> getNativeState(const jsi::Object&) = 0;
virtual void setNativeState(
Expand Down Expand Up @@ -688,6 +691,11 @@ class JSI_EXPORT Object : public Pointer {
return runtime.createObject(ho);
}

/// Creates a new Object with the custom prototype
static Object create(Runtime& runtime, const Value& prototype) {
return runtime.createObjectWithPrototype(prototype);
}

/// \return whether this and \c obj are the same JSObject or not.
static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) {
return runtime.strictEquals(a, b);
Expand Down
28 changes: 28 additions & 0 deletions packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,34 @@ TEST_P(JSITest, UTF16Test) {
EXPECT_EQ(str.utf16(rd), u"\uFFFD\u007A");
}

TEST_P(JSITest, ObjectCreateWithPrototype) {
// This Runtime Decorator is used to test the default implementation of
// Object.create(prototype)
class RD : public RuntimeDecorator<Runtime, Runtime> {
public:
RD(Runtime& rt) : RuntimeDecorator(rt) {}

Object createObjectWithPrototype(const Value& prototype) override {
return Runtime::createObjectWithPrototype(prototype);
}
};

RD rd = RD(rt);
Object prototypeObj(rd);
prototypeObj.setProperty(rd, "someProperty", 123);
Value prototype(rd, prototypeObj);

Object child = Object::create(rd, prototype);
EXPECT_EQ(child.getProperty(rd, "someProperty").getNumber(), 123);

// Tests null value as prototype
child = Object::create(rd, Value::null());
EXPECT_TRUE(child.getProperty(rd, "__proto__").isUndefined());

// Throw when prototype is neither an Object nor null
EXPECT_THROW(Object::create(rd, Value(1)), JSError);
}

INSTANTIATE_TEST_CASE_P(
Runtimes,
JSITest,
Expand Down

0 comments on commit b3ff8ec

Please sign in to comment.