Add default implementation for Object.create(prototype) (#47946)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47946

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]

Reviewed By: avp

Differential Revision: D66485209

fbshipit-source-id: 32018f847190ac16f695f011a78be0c45c4c4659
This commit is contained in:
Chi Tsai
2024-12-12 01:14:00 -08:00
committed by Facebook GitHub Bot
parent 04f33ecd58
commit e34c1e9bd2
4 changed files with 49 additions and 0 deletions
@@ -248,6 +248,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
plain_.getPropNameIdData(sym, ctx, cb);
}
Object createObjectWithPrototype(const Value& prototype) override {
return plain_.createObjectWithPrototype(prototype);
}
Object createObject() override {
return plain_.createObject();
};
@@ -737,6 +741,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();
@@ -288,6 +288,13 @@ Value Runtime::getPrototypeOf(const Object& object) {
return setPrototypeOfFn.call(*this, object);
}
Object Runtime::createObjectWithPrototype(const Value& prototype) {
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();
@@ -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(
@@ -751,6 +754,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);
@@ -1698,6 +1698,31 @@ TEST_P(JSITest, ObjectSetPrototype) {
EXPECT_EQ(getPrototypeRes.getProperty(rd, "someProperty").getNumber(), 123);
}
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.getPrototype(rd).isNull());
}
INSTANTIATE_TEST_CASE_P(
Runtimes,
JSITest,