lib/VM/JSLib/Object.cpp (1,240 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ //===----------------------------------------------------------------------===// /// \file /// ES5.1 15.2 Initialize the Object constructor. //===----------------------------------------------------------------------===// #include "Object.h" #include "JSLibInternal.h" #include "hermes/VM/HermesValueTraits.h" #include "hermes/VM/Operations.h" #include "hermes/VM/PrimitiveBox.h" #include "hermes/VM/PropertyAccessor.h" #include "hermes/VM/StringBuilder.h" namespace hermes { namespace vm { /// Initialize a freshly created instance of Object. static inline HermesValue objectInitInstance( Handle<JSObject> thisHandle, Runtime &) { return thisHandle.getHermesValue(); } //===----------------------------------------------------------------------===// /// Object. Handle<JSObject> createObjectConstructor(Runtime &runtime) { auto objectPrototype = Handle<JSObject>::vmcast(&runtime.objectPrototype); auto cons = defineSystemConstructor<JSObject>( runtime, Predefined::getSymbolID(Predefined::Object), objectConstructor, Handle<JSObject>::vmcast(&runtime.objectPrototype), 1, CellKind::JSObjectKind); void *ctx = nullptr; // Object.prototype.xxx methods. defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::toString), ctx, objectPrototypeToString, 0); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::toLocaleString), ctx, objectPrototypeToLocaleString, 0); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::valueOf), ctx, objectPrototypeValueOf, 0); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::hasOwnProperty), ctx, objectPrototypeHasOwnProperty, 1); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::isPrototypeOf), ctx, objectPrototypeIsPrototypeOf, 1); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::propertyIsEnumerable), ctx, objectPrototypePropertyIsEnumerable, 1); defineAccessor( runtime, objectPrototype, Predefined::getSymbolID(Predefined::underscore_proto), ctx, objectPrototypeProto_getter, objectPrototypeProto_setter, false, true); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::__defineGetter__), ctx, objectPrototypeDefineGetter, 2); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::__defineSetter__), ctx, objectPrototypeDefineSetter, 2); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::__lookupGetter__), ctx, objectPrototypeLookupGetter, 1); defineMethod( runtime, objectPrototype, Predefined::getSymbolID(Predefined::__lookupSetter__), ctx, objectPrototypeLookupSetter, 1); // Object.xxx() methods. defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::getPrototypeOf), ctx, objectGetPrototypeOf, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::getOwnPropertyDescriptor), ctx, objectGetOwnPropertyDescriptor, 2); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::getOwnPropertyDescriptors), ctx, objectGetOwnPropertyDescriptors, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::getOwnPropertyNames), ctx, objectGetOwnPropertyNames, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::getOwnPropertySymbols), ctx, objectGetOwnPropertySymbols, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::hasOwn), ctx, objectHasOwn, 2); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::seal), ctx, objectSeal, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::freeze), ctx, objectFreeze, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::fromEntries), ctx, objectFromEntries, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::preventExtensions), ctx, objectPreventExtensions, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::is), ctx, objectIs, 2); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::isSealed), ctx, objectIsSealed, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::isFrozen), ctx, objectIsFrozen, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::isExtensible), ctx, objectIsExtensible, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::keys), ctx, objectKeys, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::values), ctx, objectValues, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::entries), ctx, objectEntries, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::create), ctx, objectCreate, 2); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::defineProperty), ctx, objectDefineProperty, 3); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::defineProperties), ctx, objectDefineProperties, 2); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::assign), ctx, objectAssign, 2); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::setPrototypeOf), ctx, objectSetPrototypeOf, 2); return cons; } /// ES5.1 15.2.1.1 and 15.2.2.1. Object() invoked as a function and as a /// constructor. CallResult<HermesValue> objectConstructor(void *, Runtime &runtime, NativeArgs args) { auto arg0 = args.getArgHandle(0); // If arg0 is supplied and is not null or undefined, call ToObject(). { if (!arg0->isUndefined() && !arg0->isNull()) { return toObject(runtime, arg0); } } // The other cases must have been handled above. assert(arg0->isUndefined() || arg0->isNull()); if (args.isConstructorCall()) { assert( args.getThisArg().isObject() && "'this' must be an object in a constructor call"); return objectInitInstance( Handle<JSObject>::vmcast(&args.getThisArg()), runtime); } // This is a function call that must act as a constructor and create a new // object. auto thisHandle = runtime.makeHandle(JSObject::create(runtime)); return objectInitInstance(thisHandle, runtime); } CallResult<HermesValue> getPrototypeOf(Runtime &runtime, Handle<JSObject> obj) { CallResult<PseudoHandle<JSObject>> protoRes = JSObject::getPrototypeOf(obj, runtime); if (LLVM_UNLIKELY(protoRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } // Note that we must return 'null' if there is no prototype. if (!*protoRes) { return HermesValue::encodeNullValue(); } return protoRes->getHermesValue(); } CallResult<HermesValue> objectGetPrototypeOf(void *, Runtime &runtime, NativeArgs args) { CallResult<HermesValue> res = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return getPrototypeOf(runtime, runtime.makeHandle(vmcast<JSObject>(*res))); } CallResult<HermesValue> getOwnPropertyDescriptor( Runtime &runtime, Handle<JSObject> object, Handle<> key) { ComputedPropertyDescriptor desc; MutableHandle<> valueOrAccessor{runtime}; MutableHandle<SymbolID> tmpPropNameStorage{runtime}; { auto result = JSObject::getOwnComputedDescriptor( object, runtime, key, tmpPropNameStorage, desc, valueOrAccessor); if (result == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } if (!*result) { if (LLVM_LIKELY(!object->isHostObject())) return HermesValue::encodeUndefinedValue(); // For compatibility with polyfills we want to pretend that all HostObject // properties are "own" properties in hasOwnProperty() and in // getOwnPropertyDescriptor(). Since there is no way to check for a // HostObject property, we must always assume the property exists. desc.flags.enumerable = true; desc.flags.writable = true; desc.flags.hostObject = true; } } if (LLVM_UNLIKELY(!desc.flags.accessor && desc.flags.hostObject)) { auto propRes = JSObject::getComputed_RJS(object, runtime, key); if (propRes == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } valueOrAccessor = std::move(*propRes); } return objectFromPropertyDescriptor(runtime, desc, valueOrAccessor); } CallResult<HermesValue> objectGetOwnPropertyDescriptor(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } Handle<JSObject> O = runtime.makeHandle<JSObject>(objRes.getValue()); return getOwnPropertyDescriptor(runtime, O, args.getArgHandle(1)); } /// ES10.0 19.1.2.9 CallResult<HermesValue> objectGetOwnPropertyDescriptors(void *, Runtime &runtime, NativeArgs args) { GCScope gcScope{runtime}; // 1. Let obj be ? ToObject(O). auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } Handle<JSObject> obj = runtime.makeHandle<JSObject>(objRes.getValue()); // 2. Let ownKeys be ? obj.[[OwnPropertyKeys]](). auto ownKeysRes = JSObject::getOwnPropertyKeys( obj, runtime, OwnKeysFlags() .plusIncludeNonSymbols() .plusIncludeSymbols() .plusIncludeNonEnumerable()); if (LLVM_UNLIKELY(ownKeysRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } Handle<JSArray> ownKeys = *ownKeysRes; uint32_t len = JSArray::getLength(*ownKeys, runtime); // 3. Let descriptors be ! ObjectCreate(%ObjectPrototype%). auto descriptors = runtime.makeHandle<JSObject>(JSObject::create(runtime)); MutableHandle<> key{runtime}; MutableHandle<> descriptor{runtime}; DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags(); auto marker = gcScope.createMarker(); // 4. For each element key of ownKeys in List order, do for (uint32_t i = 0; i < len; ++i) { gcScope.flushToMarker(marker); key = ownKeys->at(runtime, i); // a. Let desc be ? obj.[[GetOwnProperty]](key). // b. Let descriptor be ! FromPropertyDescriptor(desc). auto descriptorRes = getOwnPropertyDescriptor(runtime, obj, key); if (LLVM_UNLIKELY(descriptorRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } // c. If descriptor is not undefined, // perform !CreateDataProperty(descriptors, key, descriptor). if (!descriptorRes->isUndefined()) { descriptor = *descriptorRes; auto res = JSObject::defineOwnComputedPrimitive( descriptors, runtime, key, dpf, descriptor); (void)res; assert( res != ExecutionStatus::EXCEPTION && "defining own property on a new object cannot fail"); } } // 5. Return descriptors. return descriptors.getHermesValue(); } /// Return a list of property names belonging to this object. All /// properties are converted into strings. The order of /// properties will remain the same as Object::getOwnPropertyNames. /// \returns a JSArray containing the names, encoded in HermesValue. CallResult<HermesValue> getOwnPropertyKeysAsStrings( Handle<JSObject> selfHandle, Runtime &runtime, OwnKeysFlags okFlags) { auto cr = JSObject::getOwnPropertyKeys(selfHandle, runtime, okFlags); if (cr == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } auto array = *cr; MutableHandle<> prop(runtime); GCScope gcScope(runtime); auto marker = gcScope.createMarker(); for (unsigned i = 0, e = array->getEndIndex(); i < e; ++i) { gcScope.flushToMarker(marker); prop = array->at(runtime, i); if (prop->isString() || prop->isSymbol()) { // Nothing to do if it's already a string or symbol. continue; } assert(prop->isNumber() && "Property name is either string or number"); // Otherwise convert it to a string and replace the element. auto status = toString_RJS(runtime, prop); assert( status != ExecutionStatus::EXCEPTION && "toString() on property name cannot fail"); JSArray::setElementAt( array, runtime, i, runtime.makeHandle(std::move(*status))); } return array.getHermesValue(); } CallResult<HermesValue> objectGetOwnPropertyNames(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto objHandle = runtime.makeHandle<JSObject>(objRes.getValue()); auto cr = getOwnPropertyKeysAsStrings( objHandle, runtime, OwnKeysFlags().plusIncludeNonSymbols().plusIncludeNonEnumerable()); if (LLVM_UNLIKELY(cr == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return *cr; } CallResult<HermesValue> objectGetOwnPropertySymbols(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto objHandle = runtime.makeHandle<JSObject>(objRes.getValue()); auto cr = JSObject::getOwnPropertySymbols(objHandle, runtime); if (LLVM_UNLIKELY(cr == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return cr->getHermesValue(); } CallResult<bool> defineProperty(Runtime &runtime, NativeArgs args, PropOpFlags opFlags) { // ES9 19.1.2.4 (throwOnError == true) or 26.1.3 (throwOnError == false) auto O = args.dyncastArg<JSObject>(0); // 1. If Type(O) is not Object, throw a TypeError exception. if (!O) { return runtime.raiseTypeError( "Object.defineProperty() called on non-object"); } // 2. Let key be ? ToPropertyKey(P). // Convert the property name to string if it's an object. This is // done explicitly instead of calling defineOwnComputed so that // converting the key argument to a primitive happens before // toPropertyDescriptor (which can fail, so the order is // observable). CallResult<Handle<>> keyRes = toPropertyKeyIfObject(runtime, args.getArgHandle(1)); if (LLVM_UNLIKELY(keyRes == ExecutionStatus::EXCEPTION)) return ExecutionStatus::EXCEPTION; // 3. Let desc be ? ToPropertyDescriptor(Attributes). DefinePropertyFlags descFlags; MutableHandle<> descValueOrAccessor{runtime}; if (toPropertyDescriptor( args.getArgHandle(2), runtime, descFlags, descValueOrAccessor) == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } // 4[throwOnError]. Perform ? DefinePropertyOrThrow(O, key, desc). // 4[!throwOnError]. Return ? O.[[DefineOwnProperty]](key, desc). return JSObject::defineOwnComputedPrimitive( O, runtime, *keyRes, descFlags, descValueOrAccessor, opFlags); } CallResult<HermesValue> objectDefineProperty(void *, Runtime &runtime, NativeArgs args) { // ES9 19.1.2.4 CallResult<bool> res = defineProperty(runtime, args, PropOpFlags().plusThrowOnError()); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } assert(*res && "defineProperty with throwOnError == true returned false"); // 5. Return O. return args.getArg(0); } static CallResult<HermesValue> objectDefinePropertiesInternal(Runtime &runtime, Handle<> obj, Handle<> props) { // Verify this method is called on an object. auto *objPtr = dyn_vmcast<JSObject>(obj.get()); if (!objPtr) { return runtime.raiseTypeError( "Object.defineProperties() called on non-object"); } auto objHandle = runtime.makeHandle(objPtr); // Verify that the properties argument is also an object. auto objRes = toObject(runtime, props); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto propsHandle = runtime.makeHandle<JSObject>(objRes.getValue()); // Get the list of identifiers in props. auto cr = JSObject::getOwnPropertyKeys( propsHandle, runtime, OwnKeysFlags() .plusIncludeSymbols() .plusIncludeNonSymbols() // setIncludeNonEnumerable for proxies is necessary to get the right // traps in the right order. The non-enumerable props will be // filtered out below. .setIncludeNonEnumerable(propsHandle->isProxyObject())); if (cr == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } auto propNames = *cr; // This function may create an unbounded number of GC handles. GCScope scope{runtime, "objectDefinePropertiesInternal", UINT_MAX}; // Iterate through every identifier, get the property descriptor // object, and store it in a list, according to Step 5. What the // spec describes is a pair is represented in NewProps, which has // three arguments because the spec descriptor is represented here // by flags and a handle. struct NewProps { unsigned propNameIndex; DefinePropertyFlags flags; MutableHandle<> valueOrAccessor; NewProps(unsigned i, DefinePropertyFlags f, MutableHandle<> voa) : propNameIndex(i), flags(f), valueOrAccessor(std::move(voa)) {} }; llvh::SmallVector<NewProps, 4> newProps; // We store each property name here. This is hoisted out of the loop // to avoid allocating a handle per property. MutableHandle<> propName{runtime}; MutableHandle<SymbolID> tmpPropNameStorage{runtime}; for (unsigned i = 0, e = propNames->getEndIndex(); i < e; ++i) { propName = propNames->at(runtime, i); ComputedPropertyDescriptor desc; CallResult<bool> descRes = JSObject::getOwnComputedDescriptor( propsHandle, runtime, propName, tmpPropNameStorage, desc); if (LLVM_UNLIKELY(descRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (LLVM_UNLIKELY(!*descRes || !desc.flags.enumerable)) { continue; } CallResult<PseudoHandle<>> propRes = propsHandle->isProxyObject() ? JSObject::getComputed_RJS(propsHandle, runtime, propName) : JSObject::getComputedPropertyValueInternal_RJS( propsHandle, runtime, propsHandle, desc); if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } DefinePropertyFlags flags; MutableHandle<> valueOrAccessor{runtime}; if (LLVM_UNLIKELY( toPropertyDescriptor( runtime.makeHandle(std::move(*propRes)), runtime, flags, valueOrAccessor) == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } newProps.emplace_back(i, flags, std::move(valueOrAccessor)); } // For each descriptor in the list, add it to the object. for (const auto &newProp : newProps) { propName = propNames->at(runtime, newProp.propNameIndex); auto result = JSObject::defineOwnComputedPrimitive( objHandle, runtime, propName, newProp.flags, newProp.valueOrAccessor, PropOpFlags().plusThrowOnError()); if (result == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } } return objHandle.getHermesValue(); } CallResult<HermesValue> objectCreate(void *, Runtime &runtime, NativeArgs args) { // Verify this method is called with an object or with 'null'. auto obj = args.dyncastArg<JSObject>(0); if (!obj && !args.getArg(0).isNull()) { return runtime.raiseTypeError( "Object prototype argument must be an Object or null"); } auto newObj = objectInitInstance( runtime.makeHandle(JSObject::create(runtime, obj)), runtime); auto arg1 = args.getArgHandle(1); if (arg1->isUndefined()) { return newObj; } // Properties argument is present and not undefined. auto cr = objectDefinePropertiesInternal(runtime, runtime.makeHandle(newObj), arg1); if (LLVM_UNLIKELY(cr == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return *cr; } CallResult<HermesValue> objectDefineProperties(void *, Runtime &runtime, NativeArgs args) { auto cr = objectDefinePropertiesInternal( runtime, args.getArgHandle(0), args.getArgHandle(1)); if (cr == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } return *cr; } CallResult<HermesValue> objectSeal(void *, Runtime &runtime, NativeArgs args) { auto objHandle = args.dyncastArg<JSObject>(0); if (!objHandle) { return args.getArg(0); } if (LLVM_UNLIKELY( JSObject::seal(objHandle, runtime) == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return objHandle.getHermesValue(); } CallResult<HermesValue> objectFreeze(void *, Runtime &runtime, NativeArgs args) { auto objHandle = args.dyncastArg<JSObject>(0); if (!objHandle) { return args.getArg(0); } if (LLVM_UNLIKELY( JSObject::freeze(objHandle, runtime) == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return objHandle.getHermesValue(); } CallResult<HermesValue> objectPreventExtensions(void *, Runtime &runtime, NativeArgs args) { Handle<JSObject> obj = args.dyncastArg<JSObject>(0); if (!obj) { return args.getArg(0); } CallResult<bool> statusRes = JSObject::preventExtensions( obj, runtime, PropOpFlags().plusThrowOnError()); if (LLVM_UNLIKELY(statusRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } assert( *statusRes && "Object.preventExtensions with ThrowOnError returned false"); return args.getArg(0); } CallResult<HermesValue> objectIs(void *, Runtime &runtime, NativeArgs args) { return HermesValue::encodeBoolValue( isSameValue(args.getArg(0), args.getArg(1))); } CallResult<HermesValue> objectIsSealed(void *, Runtime &runtime, NativeArgs args) { auto objHandle = args.dyncastArg<JSObject>(0); if (!objHandle) { // ES6.0 19.1.2.13: If Type(O) is not Object, return true. return HermesValue::encodeBoolValue(true); } return HermesValue::encodeBoolValue(JSObject::isSealed(objHandle, runtime)); } CallResult<HermesValue> objectIsFrozen(void *, Runtime &runtime, NativeArgs args) { auto objHandle = args.dyncastArg<JSObject>(0); if (!objHandle) { // ES6.0 19.1.2.12: If Type(O) is not Object, return true. return HermesValue::encodeBoolValue(true); } return HermesValue::encodeBoolValue(JSObject::isFrozen(objHandle, runtime)); } CallResult<HermesValue> objectIsExtensible(void *, Runtime &runtime, NativeArgs args) { PseudoHandle<JSObject> obj = createPseudoHandle(dyn_vmcast<JSObject>(args.getArg(0))); if (!obj) { // ES6.0 19.1.2.11: If Type(O) is not Object, return false. return HermesValue::encodeBoolValue(false); } CallResult<bool> extRes = JSObject::isExtensible(std::move(obj), runtime); if (LLVM_UNLIKELY(extRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return HermesValue::encodeBoolValue(*extRes); } /// ES8.0 7.3.21. /// EnumerableOwnProperties gets the requested properties based on \p kind. CallResult<HermesValue> enumerableOwnProperties_RJS( Runtime &runtime, Handle<JSObject> objHandle, EnumerableOwnPropertiesKind kind) { GCScope gcScope{runtime}; auto namesRes = getOwnPropertyKeysAsStrings( objHandle, runtime, OwnKeysFlags().plusIncludeNonSymbols().setIncludeNonEnumerable( objHandle->isProxyObject())); if (namesRes == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } if (kind == EnumerableOwnPropertiesKind::Key && !objHandle->isProxyObject()) { return *namesRes; } auto names = runtime.makeHandle<JSArray>(*namesRes); uint32_t len = JSArray::getLength(*names, runtime); auto propertiesRes = JSArray::create(runtime, len, len); if (LLVM_UNLIKELY(propertiesRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto properties = *propertiesRes; MutableHandle<StringPrimitive> name{runtime}; MutableHandle<> value{runtime}; MutableHandle<> entry{runtime}; MutableHandle<SymbolID> tmpPropNameStorage{runtime}; uint32_t targetIdx = 0; // Add the requested elements to properties. // We must keep track of the targetIdx because elements' enumerability may be // modified by a getter at any point in the loop, so `i` will not necessarily // correspond to `targetIdx`. auto marker = gcScope.createMarker(); for (uint32_t i = 0, len = JSArray::getLength(*names, runtime); i < len; ++i) { gcScope.flushToMarker(marker); name = names->at(runtime, i).getString(); // By calling getString, name is guaranteed to be primitive. ComputedPropertyDescriptor desc; CallResult<bool> descRes = JSObject::getOwnComputedPrimitiveDescriptor( objHandle, runtime, name, JSObject::IgnoreProxy::Yes, tmpPropNameStorage, desc); if (LLVM_UNLIKELY(descRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (LLVM_LIKELY(*descRes && desc.flags.enumerable)) { // Ensure that the property is still there and that it is enumerable, // as descriptors can be modified by a getter at any point. // Safe to call the Internal version here because we ignored Proxy above. auto valueRes = JSObject::getComputedPropertyValueInternal_RJS( objHandle, runtime, objHandle, desc); if (LLVM_UNLIKELY(valueRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } value = std::move(*valueRes); } else if (!objHandle->isProxyObject()) { continue; } else { // This is a proxy, so we need to call getOwnProperty() to see // if the value exists and is enumerable on the proxy. descRes = JSProxy::getOwnProperty(objHandle, runtime, name, desc, nullptr); if (LLVM_UNLIKELY(descRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (!*descRes || !desc.flags.enumerable) { // And skip the property if not. continue; } // And if the caller needs the value, we need to fetch that, // too. if (kind != EnumerableOwnPropertiesKind::Key) { auto valueRes = JSProxy::getComputed(objHandle, runtime, name, objHandle); if (LLVM_UNLIKELY(valueRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } value = std::move(*valueRes); } } if (kind == EnumerableOwnPropertiesKind::KeyValue) { auto entryRes = JSArray::create(runtime, 2, 2); if (LLVM_UNLIKELY(entryRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } entry = entryRes->getHermesValue(); JSArray::setElementAt(Handle<JSArray>::vmcast(entry), runtime, 0, name); JSArray::setElementAt(Handle<JSArray>::vmcast(entry), runtime, 1, value); } else if (kind == EnumerableOwnPropertiesKind::Value) { entry = value.getHermesValue(); } else { assert( objHandle->isProxyObject() && "Key kind did not return early but not proxy"); entry = names->at(runtime, i); } // The element must exist because we just read it. JSArray::setElementAt(properties, runtime, targetIdx++, entry); } // Set length at the end only, because properties may be shorter than // names.size() - some properties may have been made non-enumerable by getters // in the loop. if (LLVM_UNLIKELY( JSArray::setLengthProperty(properties, runtime, targetIdx) == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return properties.getHermesValue(); } CallResult<HermesValue> objectKeys(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return enumerableOwnProperties_RJS( runtime, runtime.makeHandle<JSObject>(*objRes), EnumerableOwnPropertiesKind::Key); } CallResult<HermesValue> objectValues(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return enumerableOwnProperties_RJS( runtime, runtime.makeHandle<JSObject>(*objRes), EnumerableOwnPropertiesKind::Value); } CallResult<HermesValue> objectEntries(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return enumerableOwnProperties_RJS( runtime, runtime.makeHandle<JSObject>(*objRes), EnumerableOwnPropertiesKind::KeyValue); } /// ES10 19.1.2.7 Object.fromEntries(iterable) /// Creates an object from an iterable of [key, value] pairs. CallResult<HermesValue> objectFromEntries(void *, Runtime &runtime, NativeArgs args) { // 1. Perform ? RequireObjectCoercible(iterable). if (args.getArg(0).isNull() || args.getArg(0).isUndefined()) { return runtime.raiseTypeError( "fromEntries argument is not coercible to Object"); } GCScopeMarkerRAII marker{runtime}; // 2. Let obj be ObjectCreate(%ObjectPrototype%). Handle<JSObject> obj = runtime.makeHandle(JSObject::create(runtime)); // 3. Assert: obj is an extensible ordinary object with no own properties. // 4. Let stepsDefine be the algorithm steps defined in // CreateDataPropertyOnObject Functions. // 5. Let adder be CreateBuiltinFunction(stepsDefine, « »). // NOTE: We avoid actually creating the NativeFunction here by simply putting // the DefineProperty code in the callback. // 6. Return ? AddEntriesFromIterable(obj, iterable, adder). return addEntriesFromIterable( runtime, obj, args.getArgHandle(0), [obj, &runtime](Runtime &, Handle<> key, Handle<> value) { const DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags(); return JSObject::defineOwnComputed( obj, runtime, key, dpf, value, PropOpFlags().plusThrowOnError()); }); } CallResult<HermesValue> objectAssign(void *, Runtime &runtime, NativeArgs args) { vm::GCScope gcScope(runtime); // 1. Let to be ToObject(target). auto objRes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { // 2. ReturnIfAbrupt(to). return ExecutionStatus::EXCEPTION; } auto toHandle = runtime.makeHandle<JSObject>(objRes.getValue()); // 3. If only one argument was passed, return to. if (LLVM_UNLIKELY(args.getArgCount() == 1)) { return toHandle.getHermesValue(); } // 4. Let sources be the List of argument values starting with the second // argument. // 5. For each element nextSource of sources, in ascending index order, // Handle for the current object being copied from. MutableHandle<JSObject> fromHandle{runtime}; // Handle for the next key to be processed when copying properties. MutableHandle<> nextKeyHandle{runtime}; // Handle for the property value being copied. MutableHandle<> propValueHandle{runtime}; // Handle for the property value being copied. MutableHandle<SymbolID> tmpPropNameStorage{runtime}; for (uint32_t argIdx = 1; argIdx < args.getArgCount(); argIdx++) { GCScopeMarkerRAII markerOuter(gcScope); auto nextSource = args.getArgHandle(argIdx); // 5.a. If nextSource is undefined or null, let keys be an empty List. if (nextSource->isNull() || nextSource->isUndefined()) { continue; } // 5.b.i. Let from be ToObject(nextSource). if (LLVM_UNLIKELY( (objRes = toObject(runtime, nextSource)) == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } fromHandle = vmcast<JSObject>(objRes.getValue()); // 5.b.ii. Let keys be from.[[OwnPropertyKeys]](). auto cr = JSObject::getOwnPropertyKeys( fromHandle, runtime, OwnKeysFlags() .plusIncludeSymbols() .plusIncludeNonSymbols() .setIncludeNonEnumerable(fromHandle->isProxyObject())); if (LLVM_UNLIKELY(cr == ExecutionStatus::EXCEPTION)) { // 5.c.ii. ReturnIfAbrupt(keys). return ExecutionStatus::EXCEPTION; } auto keys = *cr; ComputedPropertyDescriptor desc; // 5.c. Repeat for each element nextKey of keys in List order, for (uint32_t nextKeyIdx = 0, endIdx = keys->getEndIndex(); nextKeyIdx < endIdx; ++nextKeyIdx) { GCScopeMarkerRAII markerInner(gcScope); nextKeyHandle = keys->at(runtime, nextKeyIdx); // 5.c.i. Let desc be from.[[GetOwnProperty]](nextKey). auto descCr = JSObject::getOwnComputedDescriptor( fromHandle, runtime, nextKeyHandle, tmpPropNameStorage, desc); if (LLVM_UNLIKELY(descCr == ExecutionStatus::EXCEPTION)) { // 5.c.ii. ReturnIfAbrupt(desc). return ExecutionStatus::EXCEPTION; } // 5.c.iii. if desc is not undefined and desc.[[Enumerable]] is true, then if (LLVM_UNLIKELY(!*descCr) || LLVM_UNLIKELY(!desc.flags.enumerable)) { continue; } // 5.c.iii.1. Let propValue be Get(from, nextKey). // getComputed_RJS would work here in all cases. But, just // changing it to make proxy work is is a surprisingly large // regression if used always, even with no Proxy objects. So we // check if we can use getComputedPropertyValue_RJS and do so. CallResult<PseudoHandle<>> propRes = fromHandle->isProxyObject() ? JSObject::getComputed_RJS(fromHandle, runtime, nextKeyHandle) : JSObject::getComputedPropertyValue_RJS( fromHandle, runtime, fromHandle, tmpPropNameStorage, desc, nextKeyHandle); if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) { // 5.c.iii.2. ReturnIfAbrupt(propValue). return ExecutionStatus::EXCEPTION; } propValueHandle = std::move(*propRes); // 5.c.iii.3. Let status be Set(to, nextKey, propValue, true). auto statusCr = JSObject::putComputed_RJS( toHandle, runtime, nextKeyHandle, propValueHandle, PropOpFlags().plusThrowOnError()); if (LLVM_UNLIKELY(statusCr == ExecutionStatus::EXCEPTION)) { // 5.c.ii.4. ReturnIfAbrupt(status). return ExecutionStatus::EXCEPTION; } } } // 6 Return to. return toHandle.getHermesValue(); } CallResult<HermesValue> objectSetPrototypeOf(void *, Runtime &runtime, NativeArgs args) { Handle<> O = args.getArgHandle(0); Handle<> proto = args.getArgHandle(1); // 1. Let O be RequireObjectCoercible(O). if (O->isNull() || O->isUndefined()) { return runtime.raiseTypeError( "setPrototypeOf argument is not coercible to Object"); } // 3. If Type(proto) is neither Object nor Null, throw a TypeError exception. if (!(proto->isObject() || proto->isNull())) { return runtime.raiseTypeError( "setPrototypeOf new prototype must be object or null"); } // 4. If Type(O) is not Object, return O. if (!vmisa<JSObject>(*O)) { return *O; } // 5. Let status be O.[[SetPrototypeOf]](proto). auto status = JSObject::setParent( vmcast<JSObject>(*O), runtime, dyn_vmcast<JSObject>(*proto), PropOpFlags().plusThrowOnError()); // 7. If status is false, throw a TypeError exception. // Note that JSObject::setParent throws instead of returning false. if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } // 8. Return O. return *O; } //===----------------------------------------------------------------------===// /// Object.prototype. CallResult<HermesValue> directObjectPrototypeToString( Runtime &runtime, Handle<> arg) { StringPrimitive *str; if (arg->isUndefined()) { str = runtime.getPredefinedString(Predefined::squareObjectUndefined); } else if (arg->isNull()) { str = runtime.getPredefinedString(Predefined::squareObjectNull); } else if (arg->getRaw() == runtime.getGlobal().getHermesValue().getRaw()) { str = runtime.getPredefinedString(Predefined::squareObjectGlobal); } else { auto res = toObject(runtime, arg); if (res == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } auto O = runtime.makeHandle<JSObject>(res.getValue()); // 16. Let tag be Get (O, @@toStringTag). auto tagRes = JSObject::getNamed_RJS( O, runtime, Predefined::getSymbolID(Predefined::SymbolToStringTag)); if (LLVM_UNLIKELY(tagRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if ((*tagRes)->isString()) { auto tag = runtime.makeHandle( PseudoHandle<StringPrimitive>::vmcast(std::move(*tagRes))); SafeUInt32 tagLen(tag->getStringLength()); tagLen.add(9); CallResult<StringBuilder> builder = StringBuilder::createStringBuilder(runtime, tagLen); // 19. Return the String that is the result of concatenating // "[object ", tag, and "]". builder->appendASCIIRef(ASCIIRef{"[object ", 8}); builder->appendStringPrim(tag); builder->appendCharacter(']'); return builder->getStringPrimitive().getHermesValue(); } // 18. If Type(tag) is not String, let tag be builtinTag. CallResult<bool> isArrayRes = isArray(runtime, *O); if (LLVM_UNLIKELY(isArrayRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (*isArrayRes) { // 6. If isArray is true, let builtinTag be "Array". str = runtime.getPredefinedString(Predefined::squareObject_JSArray); } else if (vmisa<JSString>(O.getHermesValue())) { // 7. Else, if O is an exotic String object, let builtinTag be "String". str = runtime.getPredefinedString(Predefined::squareObject_JSString); } else if (vmisa<Arguments>(O.getHermesValue())) { // 8. Else, if O has an [[ParameterMap]] internal slot, let builtinTag be // "Arguments". str = runtime.getPredefinedString(Predefined::squareObject_Arguments); } else if (vmisa<Callable>(O.getHermesValue())) { // 9. Else, if O has a [[Call]] internal method, let builtinTag be // "Function". str = runtime.getPredefinedString(Predefined::squareObject_JSFunction); } else if (vmisa<JSError>(O.getHermesValue())) { // 10. Else, if O has an [[ErrorData]] internal slot, let builtinTag be // "Error". str = runtime.getPredefinedString(Predefined::squareObject_JSError); } else if (vmisa<JSBoolean>(O.getHermesValue())) { // 11. Else, if O has a [[BooleanData]] internal slot, let builtinTag be // "Boolean". str = runtime.getPredefinedString(Predefined::squareObject_JSBoolean); } else if (vmisa<JSNumber>(O.getHermesValue())) { // 12. Else, if O has a [[NumberData]] internal slot, let builtinTag be // "Number". str = runtime.getPredefinedString(Predefined::squareObject_JSNumber); } else if (vmisa<JSDate>(O.getHermesValue())) { // 13. Else, if O has a [[DateValue]] internal slot, let builtinTag be // "Date". str = runtime.getPredefinedString(Predefined::squareObject_JSDate); } else if (vmisa<JSRegExp>(O.getHermesValue())) { // 14. Else, if O has a [[RegExpMatcher]] internal slot, let builtinTag be // "RegExp". str = runtime.getPredefinedString(Predefined::squareObject_JSRegExp); } else { str = runtime.getPredefinedString(Predefined::squareObject_JSObject); } } return HermesValue::encodeStringValue(str); } CallResult<HermesValue> objectPrototypeToString(void *, Runtime &runtime, NativeArgs args) { return directObjectPrototypeToString(runtime, args.getThisHandle()); } CallResult<HermesValue> objectPrototypeToLocaleString(void *, Runtime &runtime, NativeArgs args) { GCScope gcScope(runtime); auto objRes = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto selfHandle = runtime.makeHandle<JSObject>(objRes.getValue()); auto propRes = JSObject::getNamed_RJS( selfHandle, runtime, Predefined::getSymbolID(Predefined::toString)); if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (auto func = Handle<Callable>::dyn_vmcast( runtime.makeHandle(std::move(*propRes)))) { return Callable::executeCall0(func, runtime, selfHandle) .toCallResultHermesValue(); } return runtime.raiseTypeError("toString must be callable"); } CallResult<HermesValue> objectPrototypeValueOf(void *, Runtime &runtime, NativeArgs args) { auto res = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return res; } static CallResult<HermesValue> objectHasOwnHelper(Runtime &runtime, Handle<JSObject> O, Handle<> P) { ComputedPropertyDescriptor desc; MutableHandle<SymbolID> tmpPropNameStorage{runtime}; CallResult<bool> hasProp = JSObject::getOwnComputedDescriptor( O, runtime, P, tmpPropNameStorage, desc); if (LLVM_UNLIKELY(hasProp == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (*hasProp) { return HermesValue::encodeBoolValue(true); } // For compatibility with polyfills we want to pretend that all HostObject // properties are "own" properties in hasOwnProperty() and in // getOwnPropertyDescriptor(). Since there is no way to check for a // HostObject property, we must always assume success. In practice the // property name would have been obtained from enumerating the properties in // JS code that looks something like this: // for(key in hostObj) { // if (Object.hasOwnProperty(hostObj, key)) // ... // } if (LLVM_UNLIKELY(O->isHostObject())) { return HermesValue::encodeBoolValue(true); } return HermesValue::encodeBoolValue(false); } /// ES11.0 19.1.3.2 CallResult<HermesValue> objectPrototypeHasOwnProperty(void *, Runtime &runtime, NativeArgs args) { /// 1. Let P be ? ToPropertyKey(V). auto PRes = toPropertyKey(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(PRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } /// 2. Let O be ? ToObject(this value). auto ORes = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(ORes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } /// 3. Return ? HasOwnProperty(O, P). Handle<JSObject> O = runtime.makeHandle<JSObject>(ORes.getValue()); return objectHasOwnHelper(runtime, O, *PRes); } CallResult<HermesValue> objectHasOwn(void *, Runtime &runtime, NativeArgs args) { /// 1. Let O be ? ToObject(O). auto ORes = toObject(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(ORes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } Handle<JSObject> O = runtime.makeHandle<JSObject>(ORes.getValue()); /// 2. Let P be ? ToPropertyKey(P). auto PRes = toPropertyKey(runtime, args.getArgHandle(1)); if (LLVM_UNLIKELY(PRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } /// 3. Return ? HasOwnProperty(O, P). return objectHasOwnHelper(runtime, O, *PRes); } CallResult<HermesValue> objectPrototypeIsPrototypeOf(void *, Runtime &runtime, NativeArgs args) { if (LLVM_UNLIKELY(!args.getArg(0).isObject())) { // If arg[0] is not an object, return false. return HermesValue::encodeBoolValue(false); } auto res = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } Handle<JSObject> objHandle = runtime.makeHandle<JSObject>(*res); PseudoHandle<JSObject> parent = createPseudoHandle(vmcast<JSObject>(args.getArg(0))); while (true) { CallResult<PseudoHandle<JSObject>> protoRes = JSObject::getPrototypeOf(std::move(parent), runtime); if (LLVM_UNLIKELY(protoRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (!*protoRes) { break; } parent = std::move(*protoRes); if (parent.get() == objHandle.get()) { return HermesValue::encodeBoolValue(true); } } return HermesValue::encodeBoolValue(false); } CallResult<HermesValue> objectPrototypePropertyIsEnumerable(void *, Runtime &runtime, NativeArgs args) { MutableHandle<SymbolID> tmpPropNameStorage{runtime}; auto res = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } ComputedPropertyDescriptor desc; auto status = JSObject::getOwnComputedDescriptor( runtime.makeHandle<JSObject>(res.getValue()), runtime, args.getArgHandle(0), tmpPropNameStorage, desc); if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return HermesValue::encodeBoolValue( status.getValue() && desc.flags.enumerable); } CallResult<HermesValue> objectPrototypeProto_getter(void *, Runtime &runtime, NativeArgs args) { CallResult<HermesValue> res = toObject(runtime, args.getThisHandle()); if (res == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } return getPrototypeOf(runtime, runtime.makeHandle(vmcast<JSObject>(*res))); } CallResult<HermesValue> objectPrototypeProto_setter(void *, Runtime &runtime, NativeArgs args) { // thisArg must be coercible to Object. if (args.getThisArg().isNull() || args.getThisArg().isUndefined()) { return runtime.raiseTypeError("'this' is not coercible to JSObject"); } // But if it isn't an actual object, do nothing. if (!args.getThisArg().isObject()) { return HermesValue::encodeUndefinedValue(); } HermesValue proto = args.getArg(0); JSObject *protoPtr; if (proto.isObject()) protoPtr = vmcast<JSObject>(proto); else if (proto.isNull()) protoPtr = nullptr; else return HermesValue::encodeUndefinedValue(); if (LLVM_UNLIKELY( JSObject::setParent( vmcast<JSObject>(args.getThisArg()), runtime, protoPtr, PropOpFlags().plusThrowOnError()) == ExecutionStatus::EXCEPTION)) return ExecutionStatus::EXCEPTION; return HermesValue::encodeUndefinedValue(); } CallResult<HermesValue> objectPrototypeDefineGetter(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto O = runtime.makeHandle<JSObject>(objRes.getValue()); auto getter = args.dyncastArg<Callable>(1); if (!getter) { return runtime.raiseTypeError("__defineGetter__ getter not callable"); } auto crtRes = PropertyAccessor::create( runtime, getter, Runtime::makeNullHandle<Callable>()); if (crtRes == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } auto accessor = runtime.makeHandle<PropertyAccessor>(*crtRes); DefinePropertyFlags dpf; dpf.setEnumerable = 1; dpf.enumerable = 1; dpf.setConfigurable = 1; dpf.configurable = 1; dpf.setGetter = 1; auto res = JSObject::defineOwnComputed( O, runtime, args.getArgHandle(0), dpf, accessor, PropOpFlags().plusThrowOnError()); if (res == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } return HermesValue::encodeUndefinedValue(); } CallResult<HermesValue> objectPrototypeDefineSetter(void *, Runtime &runtime, NativeArgs args) { auto objRes = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto O = runtime.makeHandle<JSObject>(objRes.getValue()); auto setter = args.dyncastArg<Callable>(1); if (!setter) { return runtime.raiseTypeError("__defineSetter__ setter not callable"); } auto crtRes = PropertyAccessor::create( runtime, Runtime::makeNullHandle<Callable>(), setter); if (crtRes == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } auto accessor = runtime.makeHandle<PropertyAccessor>(*crtRes); DefinePropertyFlags dpf; dpf.setEnumerable = 1; dpf.enumerable = 1; dpf.setConfigurable = 1; dpf.configurable = 1; dpf.setSetter = 1; auto res = JSObject::defineOwnComputed( O, runtime, args.getArgHandle(0), dpf, accessor, PropOpFlags().plusThrowOnError()); if (res == ExecutionStatus::EXCEPTION) { return ExecutionStatus::EXCEPTION; } return HermesValue::encodeUndefinedValue(); } namespace { /// Helper function for objectPrototypeLookup{Get,Set}ter. Returns a /// pointer to the cell which contains the actual accessors, or /// nullptr if there aren't any. This iterates internally because the /// functions in JSObject are structured in such a way that for /// complex proxy/target/prototype chains, the accessor is called in a /// deeply nested place. To expose it, we need to do the chaining /// explicitly. CallResult<PropertyAccessor *> lookupAccessor( Runtime &runtime, NativeArgs args) { CallResult<HermesValue> res = toObject(runtime, args.getThisHandle()); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } MutableHandle<JSObject> O = runtime.makeMutableHandle(vmcast<JSObject>(*res)); Handle<> key = args.getArgHandle(0); MutableHandle<> valueOrAccessor{runtime}; MutableHandle<SymbolID> tmpPropNameStorage{runtime}; do { ComputedPropertyDescriptor desc; CallResult<bool> definedRes = JSObject::getOwnComputedDescriptor( O, runtime, key, tmpPropNameStorage, desc, valueOrAccessor); if (LLVM_UNLIKELY(definedRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } if (*definedRes) { if (!desc.flags.accessor) { break; } return vmcast<PropertyAccessor>(valueOrAccessor.get()); } CallResult<PseudoHandle<JSObject>> protoRes = JSObject::getPrototypeOf(O, runtime); if (LLVM_UNLIKELY(protoRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } O = protoRes->get(); } while (O); return nullptr; } } // namespace CallResult<HermesValue> objectPrototypeLookupGetter(void *, Runtime &runtime, NativeArgs args) { CallResult<PropertyAccessor *> accessorRes = lookupAccessor(runtime, args); if (LLVM_UNLIKELY(accessorRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return (*accessorRes && (*accessorRes)->getter) ? HermesValue::encodeObjectValue( (*accessorRes)->getter.getNonNull(runtime)) : HermesValue::encodeUndefinedValue(); } CallResult<HermesValue> objectPrototypeLookupSetter(void *, Runtime &runtime, NativeArgs args) { CallResult<PropertyAccessor *> accessorRes = lookupAccessor(runtime, args); if (LLVM_UNLIKELY(accessorRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return (*accessorRes && (*accessorRes)->setter) ? HermesValue::encodeObjectValue( (*accessorRes)->setter.getNonNull(runtime)) : HermesValue::encodeUndefinedValue(); } } // namespace vm } // namespace hermes