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); }
// 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); }
// 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); }
// 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); }