bool BaseProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const { assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); MOZ_ASSERT(props.length() == 0); if (!ownPropertyKeys(cx, proxy, props)) return false; /* Select only the enumerable properties through in-place iteration. */ RootedId id(cx); size_t i = 0; for (size_t j = 0, len = props.length(); j < len; j++) { MOZ_ASSERT(i <= j); id = props[j]; if (JSID_IS_SYMBOL(id)) continue; AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET); Rooted<PropertyDescriptor> desc(cx); if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) return false; if (desc.object() && desc.isEnumerable()) props[i++].set(id); } MOZ_ASSERT(i <= props.length()); props.resize(i); return true; }
bool JSProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props) { JS_ASSERT(OperationInProgress(cx, proxy)); JS_ASSERT(props.length() == 0); if (!getOwnPropertyNames(cx, proxy, props)) return false; /* Select only the enumerable properties through in-place iteration. */ AutoPropertyDescriptorRooter desc(cx); size_t i = 0; for (size_t j = 0, len = props.length(); j < len; j++) { JS_ASSERT(i <= j); jsid id = props[j]; if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc)) return false; if (desc.obj && (desc.attrs & JSPROP_ENUMERATE)) props[i++] = id; } JS_ASSERT(i <= props.length()); props.resize(i); return true; }
bool JSCompartment::wrap(JSContext *cx, AutoIdVector &props) { jsid *vector = props.begin(); int length = props.length(); for (size_t n = 0; n < size_t(length); ++n) { if (!wrapId(cx, &vector[n])) return false; } return true; }
static bool Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { size_t w = 0; for (size_t n = 0; n < props.length(); ++n) { jsid id = props[n]; if (Policy::check(cx, wrapper, id, Wrapper::GET)) props[w++] = id; else if (JS_IsExceptionPending(cx)) return false; } props.resize(w); return true; }
static bool Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { size_t w = 0; for (size_t n = 0; n < props.length(); ++n) { jsid id = props[n]; Permission perm; if (!Policy::check(cx, wrapper, id, Wrapper::GET, perm)) return false; // Error if (perm != DenyAccess) props[w++] = id; } props.resize(w); return true; }
static bool Filter(JSContext* cx, HandleObject wrapper, AutoIdVector& props) { size_t w = 0; RootedId id(cx); for (size_t n = 0; n < props.length(); ++n) { id = props[n]; if (Policy::check(cx, wrapper, id, Wrapper::GET) || Policy::check(cx, wrapper, id, Wrapper::SET)) props[w++].set(id); else if (JS_IsExceptionPending(cx)) return false; } props.resize(w); return true; }
static bool MarkAtoms(JSContext* cx, const AutoIdVector& ids) { for (size_t i = 0; i < ids.length(); i++) cx->markId(ids[i]); return true; }
bool WrapperOwner::getPropertyKeys(JSContext* cx, HandleObject proxy, uint32_t flags, AutoIdVector& props) { ObjectId objId = idOf(proxy); ReturnStatus status; InfallibleTArray<JSIDVariant> ids; if (!SendGetPropertyKeys(objId, flags, &status, &ids)) return ipcfail(cx); LOG_STACK(); if (!ok(cx, status)) return false; for (size_t i = 0; i < ids.Length(); i++) { RootedId id(cx); if (!fromJSIDVariant(cx, ids[i], &id)) return false; if (!props.append(id)) return false; } return true; }
bool StructTypeRepresentation::init(JSContext *cx, AutoIdVector &ids, AutoObjectVector &typeReprOwners) { JS_ASSERT(ids.length() == typeReprOwners.length()); fieldCount_ = ids.length(); // We compute alignment into the field `align_` directly in the // loop below, but not `size_` because we have to very careful // about overflow. For now, we always use a uint32_t for // consistency across build environments. uint32_t totalSize = 0; for (size_t i = 0; i < ids.length(); i++) { TypeRepresentation *fieldTypeRepr = fromOwnerObject(typeReprOwners[i]); uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment()); if (alignedSize < totalSize) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG); return false; } new(fields() + i) StructField(i, ids[i], fieldTypeRepr, alignedSize); alignment_ = js::Max(alignment_, fieldTypeRepr->alignment()); uint32_t incrementedSize = alignedSize + fieldTypeRepr->size(); if (incrementedSize < alignedSize) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG); return false; } totalSize = incrementedSize; } uint32_t alignedSize = alignTo(totalSize, alignment_); if (alignedSize < totalSize) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG); return false; } size_ = alignedSize; return true; }
bool ModuleNamespaceObject::ProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>()); RootedArrayObject exports(cx, &ns->exports()); uint32_t count = exports->length(); if (!props.reserve(props.length() + count)) return false; for (uint32_t i = 0; i < count; i++) { Value value = exports->getDenseElement(i); props.infallibleAppend(AtomToId(&value.toString()->asAtom())); } return true; }
bool js::VectorToIdArray(JSContext* cx, AutoIdVector& props, JSIdArray** idap) { JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid)); size_t len = props.length(); size_t idsz = len * sizeof(jsid); size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz; JSIdArray* ida = reinterpret_cast<JSIdArray*>(cx->zone()->pod_malloc<uint8_t>(sz)); if (!ida) return false; ida->length = static_cast<int>(len); jsid* v = props.begin(); for (int i = 0; i < ida->length; i++) ida->vector[i].init(v[i]); *idap = ida; return true; }
bool ModuleNamespaceObject::ProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>()); RootedObject exports(cx, &ns->exports()); uint32_t count; if (!GetLengthProperty(cx, exports, &count) || !props.reserve(props.length() + count)) return false; Rooted<ValueVector> names(cx, ValueVector(cx)); if (!names.resize(count) || !GetElements(cx, exports, count, names.begin())) return false; for (uint32_t i = 0; i < count; i++) props.infallibleAppend(AtomToId(&names[i].toString()->asAtom())); return true; }
js::AppendUnique(JSContext* cx, AutoIdVector& base, AutoIdVector& others) { AutoIdVector uniqueOthers(cx); if (!uniqueOthers.reserve(others.length())) return false; for (size_t i = 0; i < others.length(); ++i) { bool unique = true; for (size_t j = 0; j < base.length(); ++j) { if (others[i].get() == base[j]) { unique = false; break; } } if (unique) { if (!uniqueOthers.append(others[i])) return false; } } return base.appendAll(uniqueOthers); }
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 // 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string. static bool CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& props) { // Step 2. RootedObject obj(cx, NonNullObjectWithName(cx, "return value of the ownKeys trap", v)); if (!obj) return false; // Step 3. uint32_t len; if (!GetLengthProperty(cx, obj, &len)) return false; // Steps 4-6. RootedValue next(cx); RootedId id(cx); uint32_t index = 0; while (index < len) { // Steps 6a-b. if (!GetElement(cx, obj, obj, index, &next)) return false; // Step 6c. if (!next.isString() && !next.isSymbol()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OWNKEYS_STR_SYM); return false; } if (!ValueToId<CanGC>(cx, next, &id)) return false; // Step 6d. if (!props.append(id)) return false; // Step 6e. index++; } // Step 7. return true; }
bool NativeIterator::initProperties(JSContext* cx, Handle<PropertyIteratorObject*> obj, const AutoIdVector& props) { // The obj parameter is just so that we can ensure that this object will get // traced if we GC. MOZ_ASSERT(this == obj->getNativeIterator()); size_t plength = props.length(); MOZ_ASSERT(plength == size_t(end() - begin())); for (size_t i = 0; i < plength; i++) { JSFlatString* str = IdToString(cx, props[i]); if (!str) return false; props_array[i].init(str); } return true; }
// ES6 7.3.17 But elementTypes is is fixed to symbol/string. static bool CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& props) { // Step 3. RootedObject obj(cx, NonNullObject(cx, v)); if (!obj) return false; // Steps 4-5. uint32_t len; if (!GetLengthProperty(cx, obj, &len)) return false; // Steps 6-8. RootedValue next(cx); RootedId id(cx); for (uint32_t index = 0; index < len; index++) { if (!GetElement(cx, obj, obj, index, &next)) return false; if (!next.isString() && !next.isSymbol()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ONWKEYS_STR_SYM); return false; } // Unobservable for strings/symbols. if (!ValueToId<CanGC>(cx, next, &id)) return false; if (!props.append(id)) return false; } // Step 9. return true; }
// ES2018 draft rev aab1ea3bd4d03c85d6f4a91503b4169346ab7271 // 9.5.11 Proxy.[[OwnPropertyKeys]]() bool ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { // Steps 1-3. RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); if (!handler) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } // Step 4. RootedObject target(cx, proxy->as<ProxyObject>().target()); MOZ_ASSERT(target); // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap)) return false; // Step 6. if (trap.isUndefined()) return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props); // Step 7. RootedValue trapResultArray(cx); RootedValue targetVal(cx, ObjectValue(*target)); if (!Call(cx, trap, handler, targetVal, &trapResultArray)) return false; // Step 8. AutoIdVector trapResult(cx); if (!CreateFilteredListFromArrayLike(cx, trapResultArray, trapResult)) return false; // Steps 9, 18. Rooted<GCHashSet<jsid>> uncheckedResultKeys(cx, GCHashSet<jsid>(cx)); if (!uncheckedResultKeys.init(trapResult.length())) return false; for (size_t i = 0, len = trapResult.length(); i < len; i++) { MOZ_ASSERT(!JSID_IS_VOID(trapResult[i])); auto ptr = uncheckedResultKeys.lookupForAdd(trapResult[i]); if (ptr) return js::Throw(cx, trapResult[i], JSMSG_OWNKEYS_DUPLICATE); if (!uncheckedResultKeys.add(ptr, trapResult[i])) return false; } // Step 10. bool extensibleTarget; if (!IsExtensible(cx, target, &extensibleTarget)) return false; // Steps 11-13. AutoIdVector targetKeys(cx); if (!GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &targetKeys)) return false; // Steps 14-15. AutoIdVector targetConfigurableKeys(cx); AutoIdVector targetNonconfigurableKeys(cx); // Step 16. Rooted<PropertyDescriptor> desc(cx); for (size_t i = 0; i < targetKeys.length(); ++i) { // Step 16.a. if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc)) return false; // Steps 16.b-c. if (desc.object() && !desc.configurable()) { if (!targetNonconfigurableKeys.append(targetKeys[i])) return false; } else { if (!targetConfigurableKeys.append(targetKeys[i])) return false; } } // Step 17. if (extensibleTarget && targetNonconfigurableKeys.empty()) return props.appendAll(trapResult); // Step 19. for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) { MOZ_ASSERT(!JSID_IS_VOID(targetNonconfigurableKeys[i])); auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]); // Step 19.a. if (!ptr) return js::Throw(cx, targetNonconfigurableKeys[i], JSMSG_CANT_SKIP_NC); // Step 19.b. uncheckedResultKeys.remove(ptr); } // Step 20. if (extensibleTarget) return props.appendAll(trapResult); // Step 21. for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) { MOZ_ASSERT(!JSID_IS_VOID(targetConfigurableKeys[i])); auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]); // Step 21.a. if (!ptr) return js::Throw(cx, targetConfigurableKeys[i], JSMSG_CANT_REPORT_E_AS_NE); // Step 21.b. uncheckedResultKeys.remove(ptr); } // Step 22. if (!uncheckedResultKeys.empty()) return js::Throw(cx, uncheckedResultKeys.all().front(), JSMSG_CANT_REPORT_NEW); // Step 23. return props.appendAll(trapResult); }
// This function is shared between ownPropertyKeys, enumerate, and // getOwnEnumerablePropertyKeys. static bool ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v, AutoIdVector &props, unsigned flags, JSAtom *trapName_) { MOZ_ASSERT(v.isObject()); RootedObject array(cx, &v.toObject()); RootedAtom trapName(cx, trapName_); // steps g-h uint32_t n; if (!GetLengthProperty(cx, array, &n)) return false; // steps i-k for (uint32_t i = 0; i < n; ++i) { // step i RootedValue v(cx); if (!GetElement(cx, array, array, i, &v)) return false; // step ii RootedId id(cx); if (!ValueToId<CanGC>(cx, v, &id)) return false; // step iii for (uint32_t j = 0; j < i; ++j) { if (props[j].get() == id) { ReportInvalidTrapResult(cx, proxy, trapName); return false; } } // step iv bool isFixed; if (!HasOwnProperty(cx, target, id, &isFixed)) return false; // step v bool extensible; if (!IsExtensible(cx, target, &extensible)) return false; if (!extensible && !isFixed) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW); return false; } // step vi if (!props.append(id)) return false; } // step l AutoIdVector ownProps(cx); if (!GetPropertyKeys(cx, target, flags, &ownProps)) return false; // step m for (size_t i = 0; i < ownProps.length(); ++i) { RootedId id(cx, ownProps[i]); bool found = false; for (size_t j = 0; j < props.length(); ++j) { if (props[j].get() == id) { found = true; break; } } if (found) continue; // step i bool sealed; if (!IsSealed(cx, target, id, &sealed)) return false; if (sealed) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC); return false; } // step ii bool isFixed; if (!HasOwnProperty(cx, target, id, &isFixed)) return false; // step iii bool extensible; if (!IsExtensible(cx, target, &extensible)) return false; if (!extensible && isFixed) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); return false; } } // step n return true; }
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.11 Proxy.[[OwnPropertyKeys]]() bool ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { // Steps 1-3. RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } // Step 4. RootedObject target(cx, proxy->as<ProxyObject>().target()); MOZ_ASSERT(target); // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap)) return false; // Step 6. if (trap.isUndefined()) return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props); // Step 7. RootedValue trapResultArray(cx); RootedValue targetVal(cx, ObjectValue(*target)); if (!Call(cx, trap, handler, targetVal, &trapResultArray)) return false; // Step 8. AutoIdVector trapResult(cx); if (!CreateFilteredListFromArrayLike(cx, trapResultArray, trapResult)) return false; // Step 9. bool extensibleTarget; if (!IsExtensible(cx, target, &extensibleTarget)) return false; // Steps 10-11. AutoIdVector targetKeys(cx); if (!GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &targetKeys)) return false; // Steps 12-13. AutoIdVector targetConfigurableKeys(cx); AutoIdVector targetNonconfigurableKeys(cx); // Step 14. Rooted<PropertyDescriptor> desc(cx); for (size_t i = 0; i < targetKeys.length(); ++i) { // Step 14a. if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc)) return false; // Steps 14b-c. if (desc.object() && !desc.configurable()) { if (!targetNonconfigurableKeys.append(targetKeys[i])) return false; } else { if (!targetConfigurableKeys.append(targetKeys[i])) return false; } } // Step 15. if (extensibleTarget && targetNonconfigurableKeys.empty()) return props.appendAll(trapResult); // Step 16. // The algorithm below always removes all occurences of the same key // at once, so we can use a set here. Rooted<GCHashSet<jsid>> uncheckedResultKeys(cx, GCHashSet<jsid>(cx)); if (!uncheckedResultKeys.init(trapResult.length())) return false; for (size_t i = 0, len = trapResult.length(); i < len; i++) { MOZ_ASSERT(!JSID_IS_VOID(trapResult[i])); if (!uncheckedResultKeys.put(trapResult[i])) return false; } // Step 17. for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) { MOZ_ASSERT(!JSID_IS_VOID(targetNonconfigurableKeys[i])); auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]); // Step 17a. if (!ptr) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC); return false; } // Step 17b. uncheckedResultKeys.remove(ptr); } // Step 18. if (extensibleTarget) return props.appendAll(trapResult); // Step 19. for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) { MOZ_ASSERT(!JSID_IS_VOID(targetConfigurableKeys[i])); auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]); // Step 19a. if (!ptr) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); return false; } // Step 19b. uncheckedResultKeys.remove(ptr); } // Step 20. if (!uncheckedResultKeys.empty()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW); return false; } // Step 21. return props.appendAll(trapResult); }
// ES6 9.5.12 Proxy.[[OwnPropertyKeys]]() bool ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { // Step 1. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); // Step 2. if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } // Step 3. Superfluous assertion. // Step 4. RootedObject target(cx, proxy->as<ProxyObject>().target()); // Steps 5-6. RootedValue trap(cx); if (!GetProperty(cx, handler, handler, cx->names().ownKeys, &trap)) return false; // Step 7. if (trap.isUndefined()) return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props); // Step 8. Value argv[] = { ObjectValue(*target) }; RootedValue trapResultArray(cx); if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResultArray)) return false; // Steps 9-10. AutoIdVector trapResult(cx); if (!CreateFilteredListFromArrayLike(cx, trapResultArray, trapResult)) return false; // Steps 11-12. bool extensibleTarget; if (!IsExtensible(cx, target, &extensibleTarget)) return false; // Steps 13-14. AutoIdVector targetKeys(cx); if (!GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &targetKeys)) return false; // Step 15. Superfluous assertion. // Steps 16-17. AutoIdVector targetConfigurableKeys(cx); AutoIdVector targetNonconfigurableKeys(cx); // Step 18. Rooted<PropertyDescriptor> desc(cx); for (size_t i = 0; i < targetKeys.length(); ++i) { // Steps a-b. if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc)) return false; // Steps c-d. if (desc.object() && !desc.configurable()) { if (!targetNonconfigurableKeys.append(targetKeys[i])) return false; } else { if (!targetConfigurableKeys.append(targetKeys[i])) return false; } } // Step 19. if (extensibleTarget && targetNonconfigurableKeys.empty()) return props.appendAll(trapResult); // Step 20. AutoIdVector uncheckedResultKeys(cx); if (!uncheckedResultKeys.appendAll(trapResult)) return false; // Step 21. for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) { RootedId key(cx, targetNonconfigurableKeys[i]); MOZ_ASSERT(key != JSID_VOID); bool found = false; for (size_t j = 0; j < uncheckedResultKeys.length(); ++j) { if (key == uncheckedResultKeys[j]) { uncheckedResultKeys[j].set(JSID_VOID); found = true; break; } } if (!found) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC); return false; } } // Step 22. if (extensibleTarget) return props.appendAll(trapResult); // Step 23. for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) { RootedId key(cx, targetConfigurableKeys[i]); MOZ_ASSERT(key != JSID_VOID); bool found = false; for (size_t j = 0; j < uncheckedResultKeys.length(); ++j) { if (key == uncheckedResultKeys[j]) { uncheckedResultKeys[j].set(JSID_VOID); found = true; break; } } if (!found) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); return false; } } // Step 24. for (size_t i = 0; i < uncheckedResultKeys.length(); ++i) { if (uncheckedResultKeys[i].get() != JSID_VOID) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW); return false; } } // Step 25. return props.appendAll(trapResult); }