Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "builtin/WeakMapObject-inl.h"
8
9
#include "jsapi.h"
10
11
#include "builtin/WeakSetObject.h"
12
#include "gc/FreeOp.h"
13
#include "js/PropertySpec.h"
14
#include "vm/JSContext.h"
15
#include "vm/SelfHosting.h"
16
17
#include "vm/Interpreter-inl.h"
18
19
using namespace js;
20
21
/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::is(HandleValue v) {
22
return v.isObject() && v.toObject().is<WeakMapObject>();
23
}
24
25
/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::has_impl(
26
JSContext* cx, const CallArgs& args) {
27
MOZ_ASSERT(is(args.thisv()));
28
29
if (!args.get(0).isObject()) {
30
args.rval().setBoolean(false);
31
return true;
32
}
33
34
if (ObjectValueWeakMap* map =
35
args.thisv().toObject().as<WeakMapObject>().getMap()) {
36
JSObject* key = &args[0].toObject();
37
if (map->has(key)) {
38
args.rval().setBoolean(true);
39
return true;
40
}
41
}
42
43
args.rval().setBoolean(false);
44
return true;
45
}
46
47
/* static */
48
bool WeakMapObject::has(JSContext* cx, unsigned argc, Value* vp) {
49
CallArgs args = CallArgsFromVp(argc, vp);
50
return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::has_impl>(cx,
51
args);
52
}
53
54
/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::get_impl(
55
JSContext* cx, const CallArgs& args) {
56
MOZ_ASSERT(WeakMapObject::is(args.thisv()));
57
58
if (!args.get(0).isObject()) {
59
args.rval().setUndefined();
60
return true;
61
}
62
63
if (ObjectValueWeakMap* map =
64
args.thisv().toObject().as<WeakMapObject>().getMap()) {
65
JSObject* key = &args[0].toObject();
66
if (ObjectValueWeakMap::Ptr ptr = map->lookup(key)) {
67
args.rval().set(ptr->value());
68
return true;
69
}
70
}
71
72
args.rval().setUndefined();
73
return true;
74
}
75
76
/* static */
77
bool WeakMapObject::get(JSContext* cx, unsigned argc, Value* vp) {
78
CallArgs args = CallArgsFromVp(argc, vp);
79
return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::get_impl>(cx,
80
args);
81
}
82
83
/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::delete_impl(
84
JSContext* cx, const CallArgs& args) {
85
MOZ_ASSERT(WeakMapObject::is(args.thisv()));
86
87
if (!args.get(0).isObject()) {
88
args.rval().setBoolean(false);
89
return true;
90
}
91
92
if (ObjectValueWeakMap* map =
93
args.thisv().toObject().as<WeakMapObject>().getMap()) {
94
JSObject* key = &args[0].toObject();
95
if (ObjectValueWeakMap::Ptr ptr = map->lookup(key)) {
96
map->remove(ptr);
97
args.rval().setBoolean(true);
98
return true;
99
}
100
}
101
102
args.rval().setBoolean(false);
103
return true;
104
}
105
106
/* static */
107
bool WeakMapObject::delete_(JSContext* cx, unsigned argc, Value* vp) {
108
CallArgs args = CallArgsFromVp(argc, vp);
109
return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::delete_impl>(
110
cx, args);
111
}
112
113
/* static */ MOZ_ALWAYS_INLINE bool WeakMapObject::set_impl(
114
JSContext* cx, const CallArgs& args) {
115
MOZ_ASSERT(WeakMapObject::is(args.thisv()));
116
117
if (!args.get(0).isObject()) {
118
ReportNotObject(cx, JSMSG_OBJECT_REQUIRED_WEAKMAP_KEY, args.get(0));
119
return false;
120
}
121
122
RootedObject key(cx, &args[0].toObject());
123
Rooted<WeakMapObject*> map(cx, &args.thisv().toObject().as<WeakMapObject>());
124
125
if (!WeakCollectionPutEntryInternal(cx, map, key, args.get(1))) {
126
return false;
127
}
128
args.rval().set(args.thisv());
129
return true;
130
}
131
132
/* static */
133
bool WeakMapObject::set(JSContext* cx, unsigned argc, Value* vp) {
134
CallArgs args = CallArgsFromVp(argc, vp);
135
return CallNonGenericMethod<WeakMapObject::is, WeakMapObject::set_impl>(cx,
136
args);
137
}
138
139
bool WeakCollectionObject::nondeterministicGetKeys(
140
JSContext* cx, Handle<WeakCollectionObject*> obj, MutableHandleObject ret) {
141
RootedObject arr(cx, NewDenseEmptyArray(cx));
142
if (!arr) {
143
return false;
144
}
145
if (ObjectValueWeakMap* map = obj->getMap()) {
146
// Prevent GC from mutating the weakmap while iterating.
147
gc::AutoSuppressGC suppress(cx);
148
for (ObjectValueWeakMap::Base::Range r = map->all(); !r.empty();
149
r.popFront()) {
150
JS::ExposeObjectToActiveJS(r.front().key());
151
RootedObject key(cx, r.front().key());
152
if (!cx->compartment()->wrap(cx, &key)) {
153
return false;
154
}
155
if (!NewbornArrayPush(cx, arr, ObjectValue(*key))) {
156
return false;
157
}
158
}
159
}
160
ret.set(arr);
161
return true;
162
}
163
164
JS_FRIEND_API bool JS_NondeterministicGetWeakMapKeys(JSContext* cx,
165
HandleObject objArg,
166
MutableHandleObject ret) {
167
RootedObject obj(cx, UncheckedUnwrap(objArg));
168
if (!obj || !obj->is<WeakMapObject>()) {
169
ret.set(nullptr);
170
return true;
171
}
172
return WeakCollectionObject::nondeterministicGetKeys(
173
cx, obj.as<WeakCollectionObject>(), ret);
174
}
175
176
static void WeakCollection_trace(JSTracer* trc, JSObject* obj) {
177
if (ObjectValueWeakMap* map = obj->as<WeakCollectionObject>().getMap()) {
178
map->trace(trc);
179
}
180
}
181
182
static void WeakCollection_finalize(JSFreeOp* fop, JSObject* obj) {
183
MOZ_ASSERT(fop->maybeOnHelperThread());
184
if (ObjectValueWeakMap* map = obj->as<WeakCollectionObject>().getMap()) {
185
fop->delete_(obj, map, MemoryUse::WeakMapObject);
186
}
187
}
188
189
JS_PUBLIC_API JSObject* JS::NewWeakMapObject(JSContext* cx) {
190
return NewBuiltinClassInstance<WeakMapObject>(cx);
191
}
192
193
JS_PUBLIC_API bool JS::IsWeakMapObject(JSObject* obj) {
194
return obj->is<WeakMapObject>();
195
}
196
197
JS_PUBLIC_API bool JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj,
198
HandleObject key,
199
MutableHandleValue rval) {
200
CHECK_THREAD(cx);
201
cx->check(key);
202
rval.setUndefined();
203
ObjectValueWeakMap* map = mapObj->as<WeakMapObject>().getMap();
204
if (!map) {
205
return true;
206
}
207
if (ObjectValueWeakMap::Ptr ptr = map->lookup(key)) {
208
// Read barrier to prevent an incorrectly gray value from escaping the
209
// weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
210
ExposeValueToActiveJS(ptr->value().get());
211
rval.set(ptr->value());
212
}
213
return true;
214
}
215
216
JS_PUBLIC_API bool JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj,
217
HandleObject key, HandleValue val) {
218
CHECK_THREAD(cx);
219
cx->check(key, val);
220
Handle<WeakMapObject*> rootedMap = mapObj.as<WeakMapObject>();
221
return WeakCollectionPutEntryInternal(cx, rootedMap, key, val);
222
}
223
224
/* static */
225
bool WeakMapObject::construct(JSContext* cx, unsigned argc, Value* vp) {
226
CallArgs args = CallArgsFromVp(argc, vp);
227
228
// ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1.
229
if (!ThrowIfNotConstructing(cx, args, "WeakMap")) {
230
return false;
231
}
232
233
RootedObject proto(cx);
234
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WeakMap, &proto)) {
235
return false;
236
}
237
238
RootedObject obj(cx, NewObjectWithClassProto<WeakMapObject>(cx, proto));
239
if (!obj) {
240
return false;
241
}
242
243
// Steps 5-6, 11.
244
if (!args.get(0).isNullOrUndefined()) {
245
FixedInvokeArgs<1> args2(cx);
246
args2[0].set(args[0]);
247
248
RootedValue thisv(cx, ObjectValue(*obj));
249
if (!CallSelfHostedFunction(cx, cx->names().WeakMapConstructorInit, thisv,
250
args2, args2.rval())) {
251
return false;
252
}
253
}
254
255
args.rval().setObject(*obj);
256
return true;
257
}
258
259
const JSClassOps WeakCollectionObject::classOps_ = {nullptr, /* addProperty */
260
nullptr, /* delProperty */
261
nullptr, /* enumerate */
262
nullptr, /* newEnumerate */
263
nullptr, /* resolve */
264
nullptr, /* mayResolve */
265
WeakCollection_finalize,
266
nullptr, /* call */
267
nullptr, /* hasInstance */
268
nullptr, /* construct */
269
WeakCollection_trace};
270
271
const ClassSpec WeakMapObject::classSpec_ = {
272
GenericCreateConstructor<WeakMapObject::construct, 0,
273
gc::AllocKind::FUNCTION>,
274
GenericCreatePrototype<WeakMapObject>,
275
nullptr,
276
nullptr,
277
WeakMapObject::methods,
278
WeakMapObject::properties,
279
};
280
281
const JSClass WeakMapObject::class_ = {
282
"WeakMap",
283
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap) |
284
JSCLASS_BACKGROUND_FINALIZE,
285
&WeakCollectionObject::classOps_, &WeakMapObject::classSpec_};
286
287
const JSClass WeakMapObject::protoClass_ = {
288
js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap), JS_NULL_CLASS_OPS,
289
&WeakMapObject::classSpec_};
290
291
const JSPropertySpec WeakMapObject::properties[] = {
292
JS_STRING_SYM_PS(toStringTag, "WeakMap", JSPROP_READONLY), JS_PS_END};
293
294
const JSFunctionSpec WeakMapObject::methods[] = {
295
JS_FN("has", has, 1, 0), JS_FN("get", get, 1, 0),
296
JS_FN("delete", delete_, 1, 0), JS_FN("set", set, 2, 0), JS_FS_END};