summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/WeakMapObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/WeakMapObject.cpp')
-rw-r--r--js/src/builtin/WeakMapObject.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp
new file mode 100644
index 0000000000..680f49bc3e
--- /dev/null
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "builtin/WeakMapObject-inl.h"
+
+#include "builtin/WeakSetObject.h"
+#include "gc/GC.h"
+#include "gc/GCContext.h"
+#include "js/friend/ErrorMessages.h" // JSMSG_*
+#include "js/PropertySpec.h"
+#include "js/WeakMap.h"
+#include "vm/Compartment.h"
+#include "vm/JSContext.h"
+#include "vm/SelfHosting.h"
+
+#include "gc/GCContext-inl.h"
+#include "gc/WeakMap-inl.h"
+#include "vm/NativeObject-inl.h"
+
+using namespace js;
+
+/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::is(HandleValue v) {
+ return v.isObject() && v.toObject().is<WeakMapObject>();
+}
+
+/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::has_impl(
+ JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(is(args.thisv()));
+
+ if (!CanBeHeldWeakly(cx, args.get(0))) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ if (ValueValueWeakMap* map =
+ args.thisv().toObject().as<WeakMapObject>().getMap()) {
+ Value key = args[0];
+ if (map->has(key)) {
+ args.rval().setBoolean(true);
+ return true;
+ }
+ }
+
+ args.rval().setBoolean(false);
+ return true;
+}
+
+/* static */
+bool WeakMapObject::has(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::has_impl>(cx,
+ args);
+}
+
+/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::get_impl(
+ JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(WeakMapObject::is(args.thisv()));
+
+ if (!CanBeHeldWeakly(cx, args.get(0))) {
+ args.rval().setUndefined();
+ return true;
+ }
+
+ if (ValueValueWeakMap* map =
+ args.thisv().toObject().as<WeakMapObject>().getMap()) {
+ Value key = args[0];
+ if (ValueValueWeakMap::Ptr ptr = map->lookup(key)) {
+ args.rval().set(ptr->value());
+ return true;
+ }
+ }
+
+ args.rval().setUndefined();
+ return true;
+}
+
+/* static */
+bool WeakMapObject::get(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::get_impl>(cx,
+ args);
+}
+
+/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::delete_impl(
+ JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(WeakMapObject::is(args.thisv()));
+
+ if (!CanBeHeldWeakly(cx, args.get(0))) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ if (ValueValueWeakMap* map =
+ args.thisv().toObject().as<WeakMapObject>().getMap()) {
+ Value key = args[0];
+ // The lookup here is only used for the removal, so we can skip the read
+ // barrier. This is not very important for performance, but makes it easier
+ // to test nonbarriered removal from internal weakmaps (eg Debugger maps.)
+ if (ValueValueWeakMap::Ptr ptr = map->lookupUnbarriered(key)) {
+ map->remove(ptr);
+ args.rval().setBoolean(true);
+ return true;
+ }
+ }
+
+ args.rval().setBoolean(false);
+ return true;
+}
+
+/* static */
+bool WeakMapObject::delete_(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::delete_impl>(
+ cx, args);
+}
+
+/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::set_impl(
+ JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(WeakMapObject::is(args.thisv()));
+
+ if (!CanBeHeldWeakly(cx, args.get(0))) {
+ ReportValueError(cx, JSMSG_WEAKMAP_KEY_CANT_BE_HELD_WEAKLY,
+ JSDVG_IGNORE_STACK, args.get(0), nullptr);
+ return false;
+ }
+
+ Rooted<WeakMapObject*> map(cx, &args.thisv().toObject().as<WeakMapObject>());
+
+ if (!WeakCollectionPutEntryInternal(cx, map, args[0], args.get(1))) {
+ return false;
+ }
+ args.rval().set(args.thisv());
+ return true;
+}
+
+/* static */
+bool WeakMapObject::set(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::set_impl>(cx,
+ args);
+}
+
+size_t WeakCollectionObject::sizeOfExcludingThis(
+ mozilla::MallocSizeOf aMallocSizeOf) {
+ ValueValueWeakMap* map = getMap();
+ return map ? map->sizeOfIncludingThis(aMallocSizeOf) : 0;
+}
+
+bool WeakCollectionObject::nondeterministicGetKeys(
+ JSContext* cx, Handle<WeakCollectionObject*> obj, MutableHandleObject ret) {
+ RootedObject arr(cx, NewDenseEmptyArray(cx));
+ if (!arr) {
+ return false;
+ }
+ if (ValueValueWeakMap* map = obj->getMap()) {
+ // Prevent GC from mutating the weakmap while iterating.
+ gc::AutoSuppressGC suppress(cx);
+ for (ValueValueWeakMap::Base::Range r = map->all(); !r.empty();
+ r.popFront()) {
+ const auto& key = r.front().key();
+ MOZ_ASSERT(key.isObject() || key.isSymbol());
+ JS::ExposeValueToActiveJS(key);
+ RootedValue keyVal(cx, key);
+ if (!cx->compartment()->wrap(cx, &keyVal)) {
+ return false;
+ }
+ if (!NewbornArrayPush(cx, arr, keyVal)) {
+ return false;
+ }
+ }
+ }
+ ret.set(arr);
+ return true;
+}
+
+JS_PUBLIC_API bool JS_NondeterministicGetWeakMapKeys(JSContext* cx,
+ HandleObject objArg,
+ MutableHandleObject ret) {
+ RootedObject obj(cx, UncheckedUnwrap(objArg));
+ if (!obj || !obj->is<WeakMapObject>()) {
+ ret.set(nullptr);
+ return true;
+ }
+ return WeakCollectionObject::nondeterministicGetKeys(
+ cx, obj.as<WeakCollectionObject>(), ret);
+}
+
+static void WeakCollection_trace(JSTracer* trc, JSObject* obj) {
+ if (ValueValueWeakMap* map = obj->as<WeakCollectionObject>().getMap()) {
+ map->trace(trc);
+ }
+}
+
+static void WeakCollection_finalize(JS::GCContext* gcx, JSObject* obj) {
+ if (ValueValueWeakMap* map = obj->as<WeakCollectionObject>().getMap()) {
+ gcx->delete_(obj, map, MemoryUse::WeakMapObject);
+ }
+}
+
+JS_PUBLIC_API JSObject* JS::NewWeakMapObject(JSContext* cx) {
+ return NewBuiltinClassInstance<WeakMapObject>(cx);
+}
+
+JS_PUBLIC_API bool JS::IsWeakMapObject(JSObject* obj) {
+ return obj->is<WeakMapObject>();
+}
+
+JS_PUBLIC_API bool JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj,
+ HandleValue key,
+ MutableHandleValue rval) {
+ CHECK_THREAD(cx);
+ cx->check(key);
+ rval.setUndefined();
+
+ if (!CanBeHeldWeakly(cx, key)) {
+ return true;
+ }
+
+ ValueValueWeakMap* map = mapObj->as<WeakMapObject>().getMap();
+ if (!map) {
+ return true;
+ }
+
+ if (ValueValueWeakMap::Ptr ptr = map->lookup(key)) {
+ // Read barrier to prevent an incorrectly gray value from escaping the
+ // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
+ ExposeValueToActiveJS(ptr->value().get());
+ rval.set(ptr->value());
+ }
+ return true;
+}
+
+JS_PUBLIC_API bool JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj,
+ HandleValue key, HandleValue val) {
+ CHECK_THREAD(cx);
+ cx->check(key, val);
+ if (!CanBeHeldWeakly(cx, key)) {
+ ReportValueError(cx, JSMSG_WEAKMAP_KEY_CANT_BE_HELD_WEAKLY,
+ JSDVG_IGNORE_STACK, key, nullptr);
+ return false;
+ }
+
+ Handle<WeakMapObject*> rootedMap = mapObj.as<WeakMapObject>();
+
+ return WeakCollectionPutEntryInternal(cx, rootedMap, key, val);
+}
+
+/* static */
+bool WeakMapObject::construct(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1.
+ if (!ThrowIfNotConstructing(cx, args, "WeakMap")) {
+ return false;
+ }
+
+ RootedObject proto(cx);
+ if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WeakMap, &proto)) {
+ return false;
+ }
+
+ RootedObject obj(cx, NewObjectWithClassProto<WeakMapObject>(cx, proto));
+ if (!obj) {
+ return false;
+ }
+
+ // Steps 5-6, 11.
+ if (!args.get(0).isNullOrUndefined()) {
+ FixedInvokeArgs<1> args2(cx);
+ args2[0].set(args[0]);
+
+ RootedValue thisv(cx, ObjectValue(*obj));
+ if (!CallSelfHostedFunction(cx, cx->names().WeakMapConstructorInit, thisv,
+ args2, args2.rval())) {
+ return false;
+ }
+ }
+
+ args.rval().setObject(*obj);
+ return true;
+}
+
+const JSClassOps WeakCollectionObject::classOps_ = {
+ nullptr, // addProperty
+ nullptr, // delProperty
+ nullptr, // enumerate
+ nullptr, // newEnumerate
+ nullptr, // resolve
+ nullptr, // mayResolve
+ WeakCollection_finalize, // finalize
+ nullptr, // call
+ nullptr, // construct
+ WeakCollection_trace, // trace
+};
+
+const ClassSpec WeakMapObject::classSpec_ = {
+ GenericCreateConstructor<WeakMapObject::construct, 0,
+ gc::AllocKind::FUNCTION>,
+ GenericCreatePrototype<WeakMapObject>,
+ nullptr,
+ nullptr,
+ WeakMapObject::methods,
+ WeakMapObject::properties,
+};
+
+const JSClass WeakMapObject::class_ = {
+ "WeakMap",
+ JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
+ JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap) | JSCLASS_BACKGROUND_FINALIZE,
+ &WeakCollectionObject::classOps_, &WeakMapObject::classSpec_};
+
+const JSClass WeakMapObject::protoClass_ = {
+ "WeakMap.prototype", JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
+ JS_NULL_CLASS_OPS, &WeakMapObject::classSpec_};
+
+const JSPropertySpec WeakMapObject::properties[] = {
+ JS_STRING_SYM_PS(toStringTag, "WeakMap", JSPROP_READONLY), JS_PS_END};
+
+const JSFunctionSpec WeakMapObject::methods[] = {
+ JS_FN("has", has, 1, 0), JS_FN("get", get, 1, 0),
+ JS_FN("delete", delete_, 1, 0), JS_FN("set", set, 2, 0), JS_FS_END};