Add default implementation for Object.getPrototypeOf and Object.setPrototypeOf (#47996)

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

Getting and setting an Object's prototype is convoluted. Users have to
call into the global object to get the method, then call it.

This diff adds a JSI API for Object.getPrototype and Object.setPrototype
to make it easy for users.

Changelog: [Internal]

Reviewed By: fbmal7

Differential Revision: D66562549

fbshipit-source-id: 85a2e49deb9d00500544de4cc5ab123c4717398e
This commit is contained in:
Chi Tsai
2024-12-12 01:14:00 -08:00
committed by Facebook GitHub Bot
parent 07d723cb72
commit 04f33ecd58
5 changed files with 83 additions and 0 deletions
@@ -282,6 +282,14 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
plain_.setExternalMemoryPressure(obj, amt);
}
void setPrototypeOf(const Object& object, const Value& prototype) override {
plain_.setPrototypeOf(object, prototype);
}
Value getPrototypeOf(const Object& object) override {
return plain_.getPrototypeOf(object);
}
Value getProperty(const Object& o, const PropNameID& name) override {
return plain_.getProperty(o, name);
};
@@ -760,6 +768,16 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
RD::setNativeState(o, state);
};
void setPrototypeOf(const Object& object, const Value& prototype) override {
Around around{with_};
RD::setPrototypeOf(object, prototype);
}
Value getPrototypeOf(const Object& object) override {
Around around{with_};
return RD::getPrototypeOf(object);
}
Value getProperty(const Object& o, const PropNameID& name) override {
Around around{with_};
return RD::getProperty(o, name);
@@ -84,6 +84,10 @@ inline const Runtime::PointerValue* Runtime::getPointerValue(
return value.data_.pointer.ptr_;
}
Value Object::getPrototype(Runtime& runtime) const {
return runtime.getPrototypeOf(*this);
}
inline Value Object::getProperty(Runtime& runtime, const char* name) const {
return getProperty(runtime, String::createFromAscii(runtime, name));
}
@@ -274,6 +274,20 @@ void Runtime::getPropNameIdData(
cb(ctx, false, utf16Str.data(), utf16Str.size());
}
void Runtime::setPrototypeOf(const Object& object, const Value& prototype) {
auto setPrototypeOfFn = global()
.getPropertyAsObject(*this, "Object")
.getPropertyAsFunction(*this, "setPrototypeOf");
setPrototypeOfFn.call(*this, object, prototype).asObject(*this);
}
Value Runtime::getPrototypeOf(const Object& object) {
auto setPrototypeOfFn = global()
.getPropertyAsObject(*this, "Object")
.getPropertyAsFunction(*this, "getPrototypeOf");
return setPrototypeOfFn.call(*this, object);
}
Pointer& Pointer::operator=(Pointer&& other) noexcept {
if (ptr_) {
ptr_->invalidate();
@@ -339,6 +339,9 @@ class JSI_EXPORT Runtime {
const jsi::Object&,
std::shared_ptr<NativeState> state) = 0;
virtual void setPrototypeOf(const Object& object, const Value& prototype);
virtual Value getPrototypeOf(const Object& object);
virtual Value getProperty(const Object&, const PropNameID& name) = 0;
virtual Value getProperty(const Object&, const String& name) = 0;
virtual bool hasProperty(const Object&, const PropNameID& name) = 0;
@@ -758,6 +761,16 @@ class JSI_EXPORT Object : public Pointer {
return rt.instanceOf(*this, ctor);
}
/// Sets \p prototype as the prototype of the object. The prototype must be
/// either an Object or null. If the prototype was not set successfully, this
/// method will throw.
void setPrototype(Runtime& runtime, const Value& prototype) const {
return runtime.setPrototypeOf(*this, prototype);
}
/// \return the prototype of the object
inline Value getPrototype(Runtime& runtime) const;
/// \return the property of the object with the given ascii name.
/// If the name isn't a property on the object, returns the
/// undefined value.
@@ -1664,6 +1664,40 @@ TEST_P(JSITest, GetStringDataTest) {
EXPECT_EQ(buf, str.utf16(rd));
}
TEST_P(JSITest, ObjectSetPrototype) {
// This Runtime Decorator is used to test the default implementation of
// Object.setPrototypeOf
class RD : public RuntimeDecorator<Runtime, Runtime> {
public:
explicit RD(Runtime& rt) : RuntimeDecorator(rt) {}
void setPrototypeOf(const Object& object, const Value& prototype) override {
return Runtime::setPrototypeOf(object, prototype);
}
Value getPrototypeOf(const Object& object) override {
return Runtime::getPrototypeOf(object);
}
};
RD rd = RD(rt);
Object child(rd);
// Tests null value as prototype
child.setPrototype(rd, Value::null());
EXPECT_TRUE(child.getPrototype(rd).isNull());
Object prototypeObj(rd);
prototypeObj.setProperty(rd, "someProperty", 123);
Value prototype(rd, prototypeObj);
child.setPrototype(rd, prototype);
EXPECT_EQ(child.getProperty(rd, "someProperty").getNumber(), 123);
auto getPrototypeRes = child.getPrototype(rd).asObject(rd);
EXPECT_EQ(getPrototypeRes.getProperty(rd, "someProperty").getNumber(), 123);
}
INSTANTIATE_TEST_CASE_P(
Runtimes,
JSITest,