Skip to content

Commit b2d25c8

Browse files
tsaichienfacebook-github-bot
authored andcommitted
Add Value override for has/get/setProperty (#52910)
Summary: Pull Request resolved: #52910 For `get/has/setProperty`, we should also be able to take in a generic JS Value as the property key. This change adds the Value overload for these APIs. The default implementation will use `Reflect.get`, `Reflect.has`, and `Reflect.set`. Changelog: [Internal] Reviewed By: lavenzg Differential Revision: D79120823 fbshipit-source-id: 7e2e5ff1ca93397c549e7dd922797fe77aa97940
1 parent f06f9c9 commit b2d25c8

File tree

5 files changed

+143
-0
lines changed

5 files changed

+143
-0
lines changed

packages/react-native/ReactCommon/jsi/jsi/decorator.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,12 +311,18 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
311311
Value getProperty(const Object& o, const String& name) override {
312312
return plain_.getProperty(o, name);
313313
};
314+
Value getProperty(const Object& o, const Value& name) override {
315+
return plain_.getProperty(o, name);
316+
}
314317
bool hasProperty(const Object& o, const PropNameID& name) override {
315318
return plain_.hasProperty(o, name);
316319
};
317320
bool hasProperty(const Object& o, const String& name) override {
318321
return plain_.hasProperty(o, name);
319322
};
323+
bool hasProperty(const Object& o, const Value& name) override {
324+
return plain_.hasProperty(o, name);
325+
}
320326
void setPropertyValue(
321327
const Object& o,
322328
const PropNameID& name,
@@ -327,6 +333,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
327333
override {
328334
plain_.setPropertyValue(o, name, value);
329335
};
336+
void setPropertyValue(const Object& o, const Value& name, const Value& value)
337+
override {
338+
plain_.setPropertyValue(o, name, value);
339+
}
330340

331341
void deleteProperty(const Object& object, const PropNameID& name) override {
332342
plain_.deleteProperty(object, name);
@@ -843,6 +853,10 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
843853
Around around{with_};
844854
return RD::getProperty(o, name);
845855
};
856+
Value getProperty(const Object& o, const Value& name) override {
857+
Around around{with_};
858+
return RD::getProperty(o, name);
859+
}
846860
bool hasProperty(const Object& o, const PropNameID& name) override {
847861
Around around{with_};
848862
return RD::hasProperty(o, name);
@@ -851,6 +865,10 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
851865
Around around{with_};
852866
return RD::hasProperty(o, name);
853867
};
868+
bool hasProperty(const Object& o, const Value& name) override {
869+
Around around{with_};
870+
return RD::hasProperty(o, name);
871+
}
854872
void setPropertyValue(
855873
const Object& o,
856874
const PropNameID& name,
@@ -863,6 +881,11 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
863881
Around around{with_};
864882
RD::setPropertyValue(o, name, value);
865883
};
884+
void setPropertyValue(const Object& o, const Value& name, const Value& value)
885+
override {
886+
Around around{with_};
887+
RD::setPropertyValue(o, name, value);
888+
}
866889

867890
void deleteProperty(const Object& object, const PropNameID& name) override {
868891
Around around{with_};

packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ inline Value Object::getProperty(Runtime& runtime, const PropNameID& name)
115115
return runtime.getProperty(*this, name);
116116
}
117117

118+
inline Value Object::getProperty(Runtime& runtime, const Value& name) const {
119+
return runtime.getProperty(*this, name);
120+
}
121+
118122
inline bool Object::hasProperty(Runtime& runtime, const char* name) const {
119123
return hasProperty(runtime, String::createFromAscii(runtime, name));
120124
}
@@ -128,6 +132,10 @@ inline bool Object::hasProperty(Runtime& runtime, const PropNameID& name)
128132
return runtime.hasProperty(*this, name);
129133
}
130134

135+
inline bool Object::hasProperty(Runtime& runtime, const Value& name) const {
136+
return runtime.hasProperty(*this, name);
137+
}
138+
131139
template <typename T>
132140
void Object::setProperty(Runtime& runtime, const char* name, T&& value) const {
133141
setProperty(
@@ -148,6 +156,12 @@ void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value)
148156
runtime, name, detail::toValue(runtime, std::forward<T>(value)));
149157
}
150158

159+
template <typename T>
160+
void Object::setProperty(Runtime& runtime, const Value& name, T&& value) const {
161+
setPropertyValue(
162+
runtime, name, detail::toValue(runtime, std::forward<T>(value)));
163+
}
164+
151165
inline void Object::deleteProperty(Runtime& runtime, const char* name) const {
152166
deleteProperty(runtime, String::createFromAscii(runtime, name));
153167
}

packages/react-native/ReactCommon/jsi/jsi/jsi.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,33 @@ const void* Runtime::getRuntimeDataImpl(const UUID& uuid) {
503503
return nullptr;
504504
}
505505

506+
Value Runtime::getProperty(const Object& object, const Value& name) {
507+
auto getFn = global()
508+
.getPropertyAsObject(*this, "Reflect")
509+
.getPropertyAsFunction(*this, "get");
510+
return getFn.call(*this, object, name);
511+
}
512+
513+
bool Runtime::hasProperty(const Object& object, const Value& name) {
514+
auto hasFn = global()
515+
.getPropertyAsObject(*this, "Reflect")
516+
.getPropertyAsFunction(*this, "has");
517+
return hasFn.call(*this, object, name).getBool();
518+
}
519+
520+
void Runtime::setPropertyValue(
521+
const Object& object,
522+
const Value& name,
523+
const Value& value) {
524+
auto setFn = global()
525+
.getPropertyAsObject(*this, "Reflect")
526+
.getPropertyAsFunction(*this, "set");
527+
auto setResult = setFn.call(*this, object, name, value).getBool();
528+
if (!setResult) {
529+
throw JSError(*this, "Failed to set the property");
530+
}
531+
}
532+
506533
Pointer& Pointer::operator=(Pointer&& other) noexcept {
507534
if (ptr_) {
508535
ptr_->invalidate();

packages/react-native/ReactCommon/jsi/jsi/jsi.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,14 +477,18 @@ class JSI_EXPORT Runtime : public ICast {
477477

478478
virtual Value getProperty(const Object&, const PropNameID& name) = 0;
479479
virtual Value getProperty(const Object&, const String& name) = 0;
480+
virtual Value getProperty(const Object&, const Value& name);
480481
virtual bool hasProperty(const Object&, const PropNameID& name) = 0;
481482
virtual bool hasProperty(const Object&, const String& name) = 0;
483+
virtual bool hasProperty(const Object&, const Value& name);
482484
virtual void setPropertyValue(
483485
const Object&,
484486
const PropNameID& name,
485487
const Value& value) = 0;
486488
virtual void
487489
setPropertyValue(const Object&, const String& name, const Value& value) = 0;
490+
virtual void
491+
setPropertyValue(const Object&, const Value& name, const Value& value);
488492

489493
virtual void deleteProperty(const Object&, const PropNameID& name);
490494
virtual void deleteProperty(const Object&, const String& name);
@@ -958,6 +962,12 @@ class JSI_EXPORT Object : public Pointer {
958962
/// undefined value.
959963
Value getProperty(Runtime& runtime, const PropNameID& name) const;
960964

965+
/// \return the Property of the object with the given JS Value name. If the
966+
/// name isn't a property on the object, returns the undefined value.This
967+
/// attempts to convert the JS Value to convert to a property key. If the
968+
/// conversion fails, this method may throw.
969+
Value getProperty(Runtime& runtime, const Value& name) const;
970+
961971
/// \return true if and only if the object has a property with the
962972
/// given ascii name.
963973
bool hasProperty(Runtime& runtime, const char* name) const;
@@ -970,6 +980,11 @@ class JSI_EXPORT Object : public Pointer {
970980
/// given PropNameID name.
971981
bool hasProperty(Runtime& runtime, const PropNameID& name) const;
972982

983+
/// \return true if and only if the object has a property with the given
984+
/// JS Value name. This attempts to convert the JS Value to convert to a
985+
/// property key. If the conversion fails, this method may throw.
986+
bool hasProperty(Runtime& runtime, const Value& name) const;
987+
973988
/// Sets the property value from a Value or anything which can be
974989
/// used to make one: nullptr_t, bool, double, int, const char*,
975990
/// String, or Object.
@@ -988,6 +1003,14 @@ class JSI_EXPORT Object : public Pointer {
9881003
template <typename T>
9891004
void setProperty(Runtime& runtime, const PropNameID& name, T&& value) const;
9901005

1006+
/// Sets the property value from a Value or anything which can be
1007+
/// used to make one: nullptr_t, bool, double, int, const char*,
1008+
/// String, or Object. This takes a JS Value as the property name, and
1009+
/// attempts to convert to a property key. If the conversion fails, this
1010+
/// method may throw.
1011+
template <typename T>
1012+
void setProperty(Runtime& runtime, const Value& name, T&& value) const;
1013+
9911014
/// Delete the property with the given ascii name. Throws if the deletion
9921015
/// failed.
9931016
void deleteProperty(Runtime& runtime, const char* name) const;
@@ -1145,6 +1168,11 @@ class JSI_EXPORT Object : public Pointer {
11451168
return runtime.setPropertyValue(*this, name, value);
11461169
}
11471170

1171+
void setPropertyValue(Runtime& runtime, const Value& name, const Value& value)
1172+
const {
1173+
return runtime.setPropertyValue(*this, name, value);
1174+
}
1175+
11481176
friend class Runtime;
11491177
friend class Value;
11501178
};

packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,57 @@ TEST_P(JSITest, ObjectTest) {
176176
Array names = obj.getPropertyNames(rt);
177177
EXPECT_EQ(names.size(rt), 1);
178178
EXPECT_EQ(names.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "a");
179+
180+
// This Runtime Decorator is used to test the default implementation of
181+
// Runtime::has/get/setProperty with Value overload
182+
class RD : public RuntimeDecorator<Runtime, Runtime> {
183+
public:
184+
explicit RD(Runtime& rt) : RuntimeDecorator(rt) {}
185+
186+
Value getProperty(const Object& object, const Value& name) override {
187+
return Runtime::getProperty(object, name);
188+
}
189+
190+
bool hasProperty(const Object& object, const Value& name) override {
191+
return Runtime::hasProperty(object, name);
192+
}
193+
194+
void setPropertyValue(
195+
const Object& object,
196+
const Value& name,
197+
const Value& value) override {
198+
Runtime::setPropertyValue(object, name, value);
199+
}
200+
};
201+
202+
RD rd = RD(rt);
203+
204+
obj = eval("const obj = {}; obj;").getObject(rd);
205+
auto propVal = Value(123);
206+
obj.setProperty(rd, propVal, 456);
207+
EXPECT_TRUE(obj.hasProperty(rd, propVal));
208+
auto getRes = obj.getProperty(rd, propVal);
209+
EXPECT_EQ(getRes.getNumber(), 456);
210+
211+
/// The property is non-writable so it should fail
212+
obj = eval(
213+
"Object.defineProperty(obj, '456', {"
214+
" value: 10,"
215+
" writable: false,});")
216+
.getObject(rd);
217+
auto unwritableProp = Value(456);
218+
EXPECT_THROW(obj.setProperty(rd, unwritableProp, 1), JSError);
219+
220+
auto badObjKey = eval(
221+
"var badObj = {"
222+
" toString: function() {"
223+
" throw new Error('something went wrong');"
224+
" }"
225+
"};"
226+
"badObj;");
227+
EXPECT_THROW(obj.setProperty(rd, badObjKey, 123), JSError);
228+
EXPECT_THROW(obj.hasProperty(rd, badObjKey), JSError);
229+
EXPECT_THROW(obj.getProperty(rd, badObjKey), JSError);
179230
}
180231

181232
TEST_P(JSITest, HostObjectTest) {

0 commit comments

Comments
 (0)