|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
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 "vm/GlobalObject.h" |
|
8 |
|
9 #include "jscntxt.h" |
|
10 #include "jsdate.h" |
|
11 #include "jsexn.h" |
|
12 #include "jsfriendapi.h" |
|
13 #include "jsmath.h" |
|
14 #include "json.h" |
|
15 #include "jsprototypes.h" |
|
16 #include "jsweakmap.h" |
|
17 #include "jsworkers.h" |
|
18 |
|
19 #include "builtin/Eval.h" |
|
20 #if EXPOSE_INTL_API |
|
21 # include "builtin/Intl.h" |
|
22 #endif |
|
23 #include "builtin/MapObject.h" |
|
24 #include "builtin/Object.h" |
|
25 #include "builtin/RegExp.h" |
|
26 #include "builtin/SIMD.h" |
|
27 #include "builtin/TypedObject.h" |
|
28 #include "vm/PIC.h" |
|
29 #include "vm/RegExpStatics.h" |
|
30 #include "vm/StopIterationObject.h" |
|
31 #include "vm/WeakMapObject.h" |
|
32 |
|
33 #include "jscompartmentinlines.h" |
|
34 #include "jsobjinlines.h" |
|
35 #include "jsscriptinlines.h" |
|
36 |
|
37 #include "vm/ObjectImpl-inl.h" |
|
38 |
|
39 using namespace js; |
|
40 |
|
41 struct ProtoTableEntry { |
|
42 const Class *clasp; |
|
43 ClassInitializerOp init; |
|
44 }; |
|
45 |
|
46 #define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init,clasp) \ |
|
47 extern JSObject *init(JSContext *cx, Handle<JSObject*> obj); |
|
48 JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT) |
|
49 #undef DECLARE_PROTOTYPE_CLASS_INIT |
|
50 |
|
51 JSObject * |
|
52 js_InitViaClassSpec(JSContext *cx, Handle<JSObject*> obj) |
|
53 { |
|
54 MOZ_ASSUME_UNREACHABLE(); |
|
55 } |
|
56 |
|
57 static const ProtoTableEntry protoTable[JSProto_LIMIT] = { |
|
58 #define INIT_FUNC(name,code,init,clasp) { clasp, init }, |
|
59 #define INIT_FUNC_DUMMY(name,code,init,clasp) { nullptr, nullptr }, |
|
60 JS_FOR_PROTOTYPES(INIT_FUNC, INIT_FUNC_DUMMY) |
|
61 #undef INIT_FUNC_DUMMY |
|
62 #undef INIT_FUNC |
|
63 }; |
|
64 |
|
65 const js::Class * |
|
66 js::ProtoKeyToClass(JSProtoKey key) |
|
67 { |
|
68 MOZ_ASSERT(key < JSProto_LIMIT); |
|
69 return protoTable[key].clasp; |
|
70 } |
|
71 |
|
72 // This method is not in the header file to avoid having to include |
|
73 // TypedObject.h from GlobalObject.h. It is not generally perf |
|
74 // sensitive. |
|
75 TypedObjectModuleObject& |
|
76 js::GlobalObject::getTypedObjectModule() const { |
|
77 Value v = getConstructor(JSProto_TypedObject); |
|
78 // only gets called from contexts where TypedObject must be initialized |
|
79 JS_ASSERT(v.isObject()); |
|
80 return v.toObject().as<TypedObjectModuleObject>(); |
|
81 } |
|
82 |
|
83 JSObject * |
|
84 js_InitObjectClass(JSContext *cx, HandleObject obj) |
|
85 { |
|
86 JS_ASSERT(obj->isNative()); |
|
87 |
|
88 return obj->as<GlobalObject>().getOrCreateObjectPrototype(cx); |
|
89 } |
|
90 |
|
91 JSObject * |
|
92 js_InitFunctionClass(JSContext *cx, HandleObject obj) |
|
93 { |
|
94 JS_ASSERT(obj->isNative()); |
|
95 |
|
96 return obj->as<GlobalObject>().getOrCreateFunctionPrototype(cx); |
|
97 } |
|
98 |
|
99 static bool |
|
100 ThrowTypeError(JSContext *cx, unsigned argc, Value *vp) |
|
101 { |
|
102 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr, |
|
103 JSMSG_THROW_TYPE_ERROR); |
|
104 return false; |
|
105 } |
|
106 |
|
107 static bool |
|
108 TestProtoThis(HandleValue v) |
|
109 { |
|
110 return !v.isNullOrUndefined(); |
|
111 } |
|
112 |
|
113 static bool |
|
114 ProtoGetterImpl(JSContext *cx, CallArgs args) |
|
115 { |
|
116 JS_ASSERT(TestProtoThis(args.thisv())); |
|
117 |
|
118 HandleValue thisv = args.thisv(); |
|
119 if (thisv.isPrimitive() && !BoxNonStrictThis(cx, args)) |
|
120 return false; |
|
121 |
|
122 RootedObject obj(cx, &args.thisv().toObject()); |
|
123 RootedObject proto(cx); |
|
124 if (!JSObject::getProto(cx, obj, &proto)) |
|
125 return false; |
|
126 args.rval().setObjectOrNull(proto); |
|
127 return true; |
|
128 } |
|
129 |
|
130 static bool |
|
131 ProtoGetter(JSContext *cx, unsigned argc, Value *vp) |
|
132 { |
|
133 CallArgs args = CallArgsFromVp(argc, vp); |
|
134 return CallNonGenericMethod(cx, TestProtoThis, ProtoGetterImpl, args); |
|
135 } |
|
136 |
|
137 namespace js { |
|
138 size_t sSetProtoCalled = 0; |
|
139 } // namespace js |
|
140 |
|
141 static bool |
|
142 ProtoSetterImpl(JSContext *cx, CallArgs args) |
|
143 { |
|
144 JS_ASSERT(TestProtoThis(args.thisv())); |
|
145 |
|
146 HandleValue thisv = args.thisv(); |
|
147 if (thisv.isPrimitive()) { |
|
148 JS_ASSERT(!thisv.isNullOrUndefined()); |
|
149 |
|
150 // Mutating a boxed primitive's [[Prototype]] has no side effects. |
|
151 args.rval().setUndefined(); |
|
152 return true; |
|
153 } |
|
154 |
|
155 if (!cx->runningWithTrustedPrincipals()) |
|
156 ++sSetProtoCalled; |
|
157 |
|
158 Rooted<JSObject*> obj(cx, &args.thisv().toObject()); |
|
159 |
|
160 /* Do nothing if __proto__ isn't being set to an object or null. */ |
|
161 if (args.length() == 0 || !args[0].isObjectOrNull()) { |
|
162 args.rval().setUndefined(); |
|
163 return true; |
|
164 } |
|
165 |
|
166 Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull()); |
|
167 |
|
168 bool success; |
|
169 if (!JSObject::setProto(cx, obj, newProto, &success)) |
|
170 return false; |
|
171 |
|
172 if (!success) { |
|
173 js_ReportValueError(cx, JSMSG_SETPROTOTYPEOF_FAIL, JSDVG_IGNORE_STACK, thisv, js::NullPtr()); |
|
174 return false; |
|
175 } |
|
176 |
|
177 args.rval().setUndefined(); |
|
178 return true; |
|
179 } |
|
180 |
|
181 static bool |
|
182 ProtoSetter(JSContext *cx, unsigned argc, Value *vp) |
|
183 { |
|
184 CallArgs args = CallArgsFromVp(argc, vp); |
|
185 |
|
186 // Do this here, rather than in |ProtoSetterImpl|, so even likely-buggy |
|
187 // use of the __proto__ setter on unacceptable values, where no subsequent |
|
188 // use occurs on an acceptable value, will trigger a warning. |
|
189 RootedObject callee(cx, &args.callee()); |
|
190 if (!GlobalObject::warnOnceAboutPrototypeMutation(cx, callee)) |
|
191 return false; |
|
192 |
|
193 return CallNonGenericMethod(cx, TestProtoThis, ProtoSetterImpl, args); |
|
194 } |
|
195 |
|
196 JSObject * |
|
197 GlobalObject::initFunctionAndObjectClasses(JSContext *cx) |
|
198 { |
|
199 Rooted<GlobalObject*> self(cx, this); |
|
200 |
|
201 JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); |
|
202 JS_ASSERT(isNative()); |
|
203 |
|
204 cx->setDefaultCompartmentObjectIfUnset(self); |
|
205 |
|
206 RootedObject objectProto(cx); |
|
207 |
|
208 /* |
|
209 * Create |Object.prototype| first, mirroring CreateBlankProto but for the |
|
210 * prototype of the created object. |
|
211 */ |
|
212 objectProto = NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, self, SingletonObject); |
|
213 if (!objectProto) |
|
214 return nullptr; |
|
215 |
|
216 /* |
|
217 * The default 'new' type of Object.prototype is required by type inference |
|
218 * to have unknown properties, to simplify handling of e.g. heterogenous |
|
219 * objects in JSON and script literals. |
|
220 */ |
|
221 if (!setNewTypeUnknown(cx, &JSObject::class_, objectProto)) |
|
222 return nullptr; |
|
223 |
|
224 /* Create |Function.prototype| next so we can create other functions. */ |
|
225 RootedFunction functionProto(cx); |
|
226 { |
|
227 JSObject *functionProto_ = NewObjectWithGivenProto(cx, &JSFunction::class_, |
|
228 objectProto, self, SingletonObject); |
|
229 if (!functionProto_) |
|
230 return nullptr; |
|
231 functionProto = &functionProto_->as<JSFunction>(); |
|
232 |
|
233 /* |
|
234 * Bizarrely, |Function.prototype| must be an interpreted function, so |
|
235 * give it the guts to be one. |
|
236 */ |
|
237 { |
|
238 JSObject *proto = NewFunction(cx, functionProto, nullptr, 0, JSFunction::INTERPRETED, |
|
239 self, NullPtr()); |
|
240 if (!proto) |
|
241 return nullptr; |
|
242 JS_ASSERT(proto == functionProto); |
|
243 functionProto->setIsFunctionPrototype(); |
|
244 } |
|
245 |
|
246 const char *rawSource = "() {\n}"; |
|
247 size_t sourceLen = strlen(rawSource); |
|
248 jschar *source = InflateString(cx, rawSource, &sourceLen); |
|
249 if (!source) |
|
250 return nullptr; |
|
251 ScriptSource *ss = |
|
252 cx->new_<ScriptSource>(); |
|
253 if (!ss) { |
|
254 js_free(source); |
|
255 return nullptr; |
|
256 } |
|
257 ScriptSourceHolder ssHolder(ss); |
|
258 ss->setSource(source, sourceLen); |
|
259 CompileOptions options(cx); |
|
260 options.setNoScriptRval(true) |
|
261 .setVersion(JSVERSION_DEFAULT); |
|
262 RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss, options)); |
|
263 if (!sourceObject) |
|
264 return nullptr; |
|
265 |
|
266 RootedScript script(cx, JSScript::Create(cx, |
|
267 /* enclosingScope = */ NullPtr(), |
|
268 /* savedCallerFun = */ false, |
|
269 options, |
|
270 /* staticLevel = */ 0, |
|
271 sourceObject, |
|
272 0, |
|
273 ss->length())); |
|
274 if (!script || !JSScript::fullyInitTrivial(cx, script)) |
|
275 return nullptr; |
|
276 |
|
277 functionProto->initScript(script); |
|
278 types::TypeObject* protoType = functionProto->getType(cx); |
|
279 if (!protoType) |
|
280 return nullptr; |
|
281 protoType->interpretedFunction = functionProto; |
|
282 script->setFunction(functionProto); |
|
283 |
|
284 /* |
|
285 * The default 'new' type of Function.prototype is required by type |
|
286 * inference to have unknown properties, to simplify handling of e.g. |
|
287 * CloneFunctionObject. |
|
288 */ |
|
289 if (!setNewTypeUnknown(cx, &JSFunction::class_, functionProto)) |
|
290 return nullptr; |
|
291 } |
|
292 |
|
293 /* Create the Object function now that we have a [[Prototype]] for it. */ |
|
294 RootedFunction objectCtor(cx); |
|
295 { |
|
296 RootedObject ctor(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto, |
|
297 self, SingletonObject)); |
|
298 if (!ctor) |
|
299 return nullptr; |
|
300 RootedAtom objectAtom(cx, cx->names().Object); |
|
301 objectCtor = NewFunction(cx, ctor, obj_construct, 1, JSFunction::NATIVE_CTOR, self, |
|
302 objectAtom); |
|
303 if (!objectCtor) |
|
304 return nullptr; |
|
305 } |
|
306 |
|
307 /* |
|
308 * Install |Object| and |Object.prototype| for the benefit of subsequent |
|
309 * code that looks for them. |
|
310 */ |
|
311 self->setObjectClassDetails(objectCtor, objectProto); |
|
312 |
|
313 /* Create |Function| so it and |Function.prototype| can be installed. */ |
|
314 RootedFunction functionCtor(cx); |
|
315 { |
|
316 // Note that ctor is rooted purely for the JS_ASSERT at the end |
|
317 RootedObject ctor(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto, |
|
318 self, SingletonObject)); |
|
319 if (!ctor) |
|
320 return nullptr; |
|
321 RootedAtom functionAtom(cx, cx->names().Function); |
|
322 functionCtor = NewFunction(cx, ctor, Function, 1, JSFunction::NATIVE_CTOR, self, |
|
323 functionAtom); |
|
324 if (!functionCtor) |
|
325 return nullptr; |
|
326 JS_ASSERT(ctor == functionCtor); |
|
327 } |
|
328 |
|
329 /* |
|
330 * Install |Function| and |Function.prototype| so that we can freely create |
|
331 * functions and objects without special effort. |
|
332 */ |
|
333 self->setFunctionClassDetails(functionCtor, functionProto); |
|
334 |
|
335 /* |
|
336 * The hard part's done: now go back and add all the properties these |
|
337 * primordial values have. |
|
338 */ |
|
339 if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) || |
|
340 !DefinePropertiesAndBrand(cx, objectProto, nullptr, object_methods)) |
|
341 { |
|
342 return nullptr; |
|
343 } |
|
344 |
|
345 /* |
|
346 * Add an Object.prototype.__proto__ accessor property to implement that |
|
347 * extension (if it's actually enabled). Cache the getter for this |
|
348 * function so that cross-compartment [[Prototype]]-getting is implemented |
|
349 * in one place. |
|
350 */ |
|
351 RootedFunction getter(cx, NewFunction(cx, NullPtr(), ProtoGetter, 0, JSFunction::NATIVE_FUN, |
|
352 self, NullPtr())); |
|
353 if (!getter) |
|
354 return nullptr; |
|
355 #if JS_HAS_OBJ_PROTO_PROP |
|
356 RootedFunction setter(cx, NewFunction(cx, NullPtr(), ProtoSetter, 0, JSFunction::NATIVE_FUN, |
|
357 self, NullPtr())); |
|
358 if (!setter) |
|
359 return nullptr; |
|
360 if (!JSObject::defineProperty(cx, objectProto, |
|
361 cx->names().proto, UndefinedHandleValue, |
|
362 JS_DATA_TO_FUNC_PTR(PropertyOp, getter.get()), |
|
363 JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setter.get()), |
|
364 JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED)) |
|
365 { |
|
366 return nullptr; |
|
367 } |
|
368 #endif /* JS_HAS_OBJ_PROTO_PROP */ |
|
369 self->setProtoGetter(getter); |
|
370 |
|
371 |
|
372 if (!DefinePropertiesAndBrand(cx, objectCtor, nullptr, object_static_methods) || |
|
373 !LinkConstructorAndPrototype(cx, functionCtor, functionProto) || |
|
374 !DefinePropertiesAndBrand(cx, functionProto, nullptr, function_methods) || |
|
375 !DefinePropertiesAndBrand(cx, functionCtor, nullptr, nullptr)) |
|
376 { |
|
377 return nullptr; |
|
378 } |
|
379 |
|
380 /* Add the global Function and Object properties now. */ |
|
381 if (!self->addDataProperty(cx, cx->names().Object, constructorPropertySlot(JSProto_Object), 0)) |
|
382 return nullptr; |
|
383 if (!self->addDataProperty(cx, cx->names().Function, constructorPropertySlot(JSProto_Function), 0)) |
|
384 return nullptr; |
|
385 |
|
386 /* Heavy lifting done, but lingering tasks remain. */ |
|
387 |
|
388 /* ES5 15.1.2.1. */ |
|
389 RootedId evalId(cx, NameToId(cx->names().eval)); |
|
390 JSObject *evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS); |
|
391 if (!evalobj) |
|
392 return nullptr; |
|
393 self->setOriginalEval(evalobj); |
|
394 |
|
395 /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */ |
|
396 RootedFunction throwTypeError(cx, NewFunction(cx, NullPtr(), ThrowTypeError, 0, |
|
397 JSFunction::NATIVE_FUN, self, NullPtr())); |
|
398 if (!throwTypeError) |
|
399 return nullptr; |
|
400 if (!JSObject::preventExtensions(cx, throwTypeError)) |
|
401 return nullptr; |
|
402 self->setThrowTypeError(throwTypeError); |
|
403 |
|
404 RootedObject intrinsicsHolder(cx); |
|
405 if (cx->runtime()->isSelfHostingGlobal(self)) { |
|
406 intrinsicsHolder = self; |
|
407 } else { |
|
408 RootedObject proto(cx, self->getOrCreateObjectPrototype(cx)); |
|
409 if (!proto) |
|
410 return nullptr; |
|
411 intrinsicsHolder = NewObjectWithGivenProto(cx, &JSObject::class_, proto, self, |
|
412 TenuredObject); |
|
413 if (!intrinsicsHolder) |
|
414 return nullptr; |
|
415 } |
|
416 self->setIntrinsicsHolder(intrinsicsHolder); |
|
417 /* Define a property 'global' with the current global as its value. */ |
|
418 RootedValue global(cx, ObjectValue(*self)); |
|
419 if (!JSObject::defineProperty(cx, intrinsicsHolder, cx->names().global, |
|
420 global, JS_PropertyStub, JS_StrictPropertyStub, |
|
421 JSPROP_PERMANENT | JSPROP_READONLY)) |
|
422 { |
|
423 return nullptr; |
|
424 } |
|
425 |
|
426 /* |
|
427 * The global object should have |Object.prototype| as its [[Prototype]]. |
|
428 * Eventually we'd like to have standard classes be there from the start, |
|
429 * and thus we would know we were always setting what had previously been a |
|
430 * null [[Prototype]], but right now some code assumes it can set the |
|
431 * [[Prototype]] before standard classes have been initialized. For now, |
|
432 * only set the [[Prototype]] if it hasn't already been set. |
|
433 */ |
|
434 Rooted<TaggedProto> tagged(cx, TaggedProto(objectProto)); |
|
435 if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, self->getClass(), tagged)) |
|
436 return nullptr; |
|
437 |
|
438 /* |
|
439 * Notify any debuggers about the creation of the script for |
|
440 * |Function.prototype| -- after all initialization, for simplicity. |
|
441 */ |
|
442 RootedScript functionProtoScript(cx, functionProto->nonLazyScript()); |
|
443 CallNewScriptHook(cx, functionProtoScript, functionProto); |
|
444 return functionProto; |
|
445 } |
|
446 |
|
447 /* static */ bool |
|
448 GlobalObject::ensureConstructor(JSContext *cx, Handle<GlobalObject*> global, JSProtoKey key) |
|
449 { |
|
450 if (global->isStandardClassResolved(key)) |
|
451 return true; |
|
452 return resolveConstructor(cx, global, key); |
|
453 } |
|
454 |
|
455 /* static*/ bool |
|
456 GlobalObject::resolveConstructor(JSContext *cx, Handle<GlobalObject*> global, JSProtoKey key) |
|
457 { |
|
458 MOZ_ASSERT(!global->isStandardClassResolved(key)); |
|
459 |
|
460 // There are two different kinds of initialization hooks. One of them is |
|
461 // the class js_InitFoo hook, defined in a JSProtoKey-keyed table at the |
|
462 // top of this file. The other lives in the ClassSpec for classes that |
|
463 // define it. Classes may use one or the other, but not both. |
|
464 ClassInitializerOp init = protoTable[key].init; |
|
465 if (init == js_InitViaClassSpec) |
|
466 init = nullptr; |
|
467 |
|
468 const Class *clasp = ProtoKeyToClass(key); |
|
469 |
|
470 // Some classes have no init routine, which means that they're disabled at |
|
471 // compile-time. We could try to enforce that callers never pass such keys |
|
472 // to resolveConstructor, but that would cramp the style of consumers like |
|
473 // GlobalObject::initStandardClasses that want to just carpet-bomb-call |
|
474 // ensureConstructor with every JSProtoKey. So it's easier to just handle |
|
475 // it here. |
|
476 bool haveSpec = clasp && clasp->spec.defined(); |
|
477 if (!init && !haveSpec) |
|
478 return true; |
|
479 |
|
480 // See if there's an old-style initialization hook. |
|
481 if (init) { |
|
482 MOZ_ASSERT(!haveSpec); |
|
483 return init(cx, global); |
|
484 } |
|
485 |
|
486 // |
|
487 // Ok, we're doing it with a class spec. |
|
488 // |
|
489 |
|
490 // Create the constructor. |
|
491 RootedObject ctor(cx, clasp->spec.createConstructor(cx, key)); |
|
492 if (!ctor) |
|
493 return false; |
|
494 |
|
495 // Define any specified functions. |
|
496 if (const JSFunctionSpec *funs = clasp->spec.constructorFunctions) { |
|
497 if (!JS_DefineFunctions(cx, ctor, funs)) |
|
498 return false; |
|
499 } |
|
500 |
|
501 // We don't always have a prototype (i.e. Math and JSON). If we don't, |
|
502 // |createPrototype| and |prototypeFunctions| should both be null. |
|
503 RootedObject proto(cx); |
|
504 if (clasp->spec.createPrototype) { |
|
505 proto = clasp->spec.createPrototype(cx, key); |
|
506 if (!proto) |
|
507 return false; |
|
508 } |
|
509 if (const JSFunctionSpec *funs = clasp->spec.prototypeFunctions) { |
|
510 if (!JS_DefineFunctions(cx, proto, funs)) |
|
511 return false; |
|
512 } |
|
513 |
|
514 // If the prototype exists, link it with the constructor. |
|
515 if (proto && !LinkConstructorAndPrototype(cx, ctor, proto)) |
|
516 return false; |
|
517 |
|
518 // Call the post-initialization hook, if provided. |
|
519 if (clasp->spec.finishInit && !clasp->spec.finishInit(cx, ctor, proto)) |
|
520 return false; |
|
521 |
|
522 // Stash things in the right slots and define the constructor on the global. |
|
523 return initBuiltinConstructor(cx, global, key, ctor, proto); |
|
524 } |
|
525 |
|
526 /* static */ bool |
|
527 GlobalObject::initBuiltinConstructor(JSContext *cx, Handle<GlobalObject*> global, |
|
528 JSProtoKey key, HandleObject ctor, HandleObject proto) |
|
529 { |
|
530 JS_ASSERT(!global->nativeEmpty()); // reserved slots already allocated |
|
531 JS_ASSERT(key != JSProto_Null); |
|
532 JS_ASSERT(ctor); |
|
533 JS_ASSERT(proto); |
|
534 |
|
535 RootedId id(cx, NameToId(ClassName(key, cx))); |
|
536 JS_ASSERT(!global->nativeLookup(cx, id)); |
|
537 |
|
538 if (!global->addDataProperty(cx, id, constructorPropertySlot(key), 0)) |
|
539 return false; |
|
540 |
|
541 global->setConstructor(key, ObjectValue(*ctor)); |
|
542 global->setPrototype(key, ObjectValue(*proto)); |
|
543 global->setConstructorPropertySlot(key, ObjectValue(*ctor)); |
|
544 |
|
545 types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor)); |
|
546 return true; |
|
547 } |
|
548 |
|
549 GlobalObject * |
|
550 GlobalObject::create(JSContext *cx, const Class *clasp) |
|
551 { |
|
552 JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL); |
|
553 JS_ASSERT(clasp->trace == JS_GlobalObjectTraceHook); |
|
554 |
|
555 JSObject *obj = NewObjectWithGivenProto(cx, clasp, nullptr, nullptr, SingletonObject); |
|
556 if (!obj) |
|
557 return nullptr; |
|
558 |
|
559 Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>()); |
|
560 |
|
561 cx->compartment()->initGlobal(*global); |
|
562 |
|
563 if (!global->setVarObj(cx)) |
|
564 return nullptr; |
|
565 if (!global->setDelegate(cx)) |
|
566 return nullptr; |
|
567 |
|
568 /* Construct a regexp statics object for this global object. */ |
|
569 JSObject *res = RegExpStatics::create(cx, global); |
|
570 if (!res) |
|
571 return nullptr; |
|
572 |
|
573 global->initSlot(REGEXP_STATICS, ObjectValue(*res)); |
|
574 return global; |
|
575 } |
|
576 |
|
577 /* static */ bool |
|
578 GlobalObject::getOrCreateEval(JSContext *cx, Handle<GlobalObject*> global, |
|
579 MutableHandleObject eval) |
|
580 { |
|
581 if (!global->getOrCreateObjectPrototype(cx)) |
|
582 return false; |
|
583 eval.set(&global->getSlot(EVAL).toObject()); |
|
584 return true; |
|
585 } |
|
586 |
|
587 bool |
|
588 GlobalObject::valueIsEval(Value val) |
|
589 { |
|
590 Value eval = getSlot(EVAL); |
|
591 return eval.isObject() && eval == val; |
|
592 } |
|
593 |
|
594 /* static */ bool |
|
595 GlobalObject::initStandardClasses(JSContext *cx, Handle<GlobalObject*> global) |
|
596 { |
|
597 /* Define a top-level property 'undefined' with the undefined value. */ |
|
598 if (!JSObject::defineProperty(cx, global, cx->names().undefined, UndefinedHandleValue, |
|
599 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) |
|
600 { |
|
601 return false; |
|
602 } |
|
603 |
|
604 for (size_t k = 0; k < JSProto_LIMIT; ++k) { |
|
605 if (!ensureConstructor(cx, global, static_cast<JSProtoKey>(k))) |
|
606 return false; |
|
607 } |
|
608 return true; |
|
609 } |
|
610 |
|
611 /* static */ bool |
|
612 GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx, Handle<GlobalObject*> global) |
|
613 { |
|
614 HeapSlot &v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED); |
|
615 if (v.isUndefined()) { |
|
616 /* |
|
617 * If there are callbacks, make sure that the CSP callback is installed |
|
618 * and that it permits runtime code generation, then cache the result. |
|
619 */ |
|
620 JSCSPEvalChecker allows = cx->runtime()->securityCallbacks->contentSecurityPolicyAllows; |
|
621 Value boolValue = BooleanValue(!allows || allows(cx)); |
|
622 v.set(global, HeapSlot::Slot, RUNTIME_CODEGEN_ENABLED, boolValue); |
|
623 } |
|
624 return !v.isFalse(); |
|
625 } |
|
626 |
|
627 /* static */ bool |
|
628 GlobalObject::warnOnceAbout(JSContext *cx, HandleObject obj, uint32_t slot, unsigned errorNumber) |
|
629 { |
|
630 Rooted<GlobalObject*> global(cx, &obj->global()); |
|
631 HeapSlot &v = global->getSlotRef(slot); |
|
632 if (v.isUndefined()) { |
|
633 if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, |
|
634 errorNumber)) |
|
635 { |
|
636 return false; |
|
637 } |
|
638 v.init(global, HeapSlot::Slot, slot, BooleanValue(true)); |
|
639 } |
|
640 return true; |
|
641 } |
|
642 |
|
643 JSFunction * |
|
644 GlobalObject::createConstructor(JSContext *cx, Native ctor, JSAtom *nameArg, unsigned length, |
|
645 gc::AllocKind kind) |
|
646 { |
|
647 RootedAtom name(cx, nameArg); |
|
648 RootedObject self(cx, this); |
|
649 return NewFunction(cx, NullPtr(), ctor, length, JSFunction::NATIVE_CTOR, self, name, kind); |
|
650 } |
|
651 |
|
652 static JSObject * |
|
653 CreateBlankProto(JSContext *cx, const Class *clasp, JSObject &proto, GlobalObject &global) |
|
654 { |
|
655 JS_ASSERT(clasp != &JSObject::class_); |
|
656 JS_ASSERT(clasp != &JSFunction::class_); |
|
657 |
|
658 RootedObject blankProto(cx, NewObjectWithGivenProto(cx, clasp, &proto, &global, SingletonObject)); |
|
659 if (!blankProto || !blankProto->setDelegate(cx)) |
|
660 return nullptr; |
|
661 |
|
662 return blankProto; |
|
663 } |
|
664 |
|
665 JSObject * |
|
666 GlobalObject::createBlankPrototype(JSContext *cx, const Class *clasp) |
|
667 { |
|
668 Rooted<GlobalObject*> self(cx, this); |
|
669 JSObject *objectProto = getOrCreateObjectPrototype(cx); |
|
670 if (!objectProto) |
|
671 return nullptr; |
|
672 |
|
673 return CreateBlankProto(cx, clasp, *objectProto, *self.get()); |
|
674 } |
|
675 |
|
676 JSObject * |
|
677 GlobalObject::createBlankPrototypeInheriting(JSContext *cx, const Class *clasp, JSObject &proto) |
|
678 { |
|
679 return CreateBlankProto(cx, clasp, proto, *this); |
|
680 } |
|
681 |
|
682 bool |
|
683 js::LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor_, JSObject *proto_) |
|
684 { |
|
685 RootedObject ctor(cx, ctor_), proto(cx, proto_); |
|
686 |
|
687 RootedValue protoVal(cx, ObjectValue(*proto)); |
|
688 RootedValue ctorVal(cx, ObjectValue(*ctor)); |
|
689 |
|
690 return JSObject::defineProperty(cx, ctor, cx->names().prototype, |
|
691 protoVal, JS_PropertyStub, JS_StrictPropertyStub, |
|
692 JSPROP_PERMANENT | JSPROP_READONLY) && |
|
693 JSObject::defineProperty(cx, proto, cx->names().constructor, |
|
694 ctorVal, JS_PropertyStub, JS_StrictPropertyStub, 0); |
|
695 } |
|
696 |
|
697 bool |
|
698 js::DefinePropertiesAndBrand(JSContext *cx, JSObject *obj_, |
|
699 const JSPropertySpec *ps, const JSFunctionSpec *fs) |
|
700 { |
|
701 RootedObject obj(cx, obj_); |
|
702 |
|
703 if (ps && !JS_DefineProperties(cx, obj, ps)) |
|
704 return false; |
|
705 if (fs && !JS_DefineFunctions(cx, obj, fs)) |
|
706 return false; |
|
707 return true; |
|
708 } |
|
709 |
|
710 static void |
|
711 GlobalDebuggees_finalize(FreeOp *fop, JSObject *obj) |
|
712 { |
|
713 fop->delete_((GlobalObject::DebuggerVector *) obj->getPrivate()); |
|
714 } |
|
715 |
|
716 static const Class |
|
717 GlobalDebuggees_class = { |
|
718 "GlobalDebuggee", JSCLASS_HAS_PRIVATE, |
|
719 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
720 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, GlobalDebuggees_finalize |
|
721 }; |
|
722 |
|
723 GlobalObject::DebuggerVector * |
|
724 GlobalObject::getDebuggers() |
|
725 { |
|
726 Value debuggers = getReservedSlot(DEBUGGERS); |
|
727 if (debuggers.isUndefined()) |
|
728 return nullptr; |
|
729 JS_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class); |
|
730 return (DebuggerVector *) debuggers.toObject().getPrivate(); |
|
731 } |
|
732 |
|
733 /* static */ GlobalObject::DebuggerVector * |
|
734 GlobalObject::getOrCreateDebuggers(JSContext *cx, Handle<GlobalObject*> global) |
|
735 { |
|
736 assertSameCompartment(cx, global); |
|
737 DebuggerVector *debuggers = global->getDebuggers(); |
|
738 if (debuggers) |
|
739 return debuggers; |
|
740 |
|
741 JSObject *obj = NewObjectWithGivenProto(cx, &GlobalDebuggees_class, nullptr, global); |
|
742 if (!obj) |
|
743 return nullptr; |
|
744 debuggers = cx->new_<DebuggerVector>(); |
|
745 if (!debuggers) |
|
746 return nullptr; |
|
747 obj->setPrivate(debuggers); |
|
748 global->setReservedSlot(DEBUGGERS, ObjectValue(*obj)); |
|
749 return debuggers; |
|
750 } |
|
751 |
|
752 /* static */ bool |
|
753 GlobalObject::addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger *dbg) |
|
754 { |
|
755 DebuggerVector *debuggers = getOrCreateDebuggers(cx, global); |
|
756 if (!debuggers) |
|
757 return false; |
|
758 #ifdef DEBUG |
|
759 for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) |
|
760 JS_ASSERT(*p != dbg); |
|
761 #endif |
|
762 if (debuggers->empty() && !global->compartment()->addDebuggee(cx, global)) |
|
763 return false; |
|
764 if (!debuggers->append(dbg)) { |
|
765 (void) global->compartment()->removeDebuggee(cx, global); |
|
766 return false; |
|
767 } |
|
768 return true; |
|
769 } |
|
770 |
|
771 /* static */ JSObject * |
|
772 GlobalObject::getOrCreateForOfPICObject(JSContext *cx, Handle<GlobalObject *> global) |
|
773 { |
|
774 assertSameCompartment(cx, global); |
|
775 JSObject *forOfPIC = global->getForOfPICObject(); |
|
776 if (forOfPIC) |
|
777 return forOfPIC; |
|
778 |
|
779 forOfPIC = ForOfPIC::createForOfPICObject(cx, global); |
|
780 if (!forOfPIC) |
|
781 return nullptr; |
|
782 global->setReservedSlot(FOR_OF_PIC_CHAIN, ObjectValue(*forOfPIC)); |
|
783 return forOfPIC; |
|
784 } |
|
785 |
|
786 bool |
|
787 GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name, |
|
788 unsigned nargs, MutableHandleValue funVal) |
|
789 { |
|
790 RootedId shId(cx, AtomToId(selfHostedName)); |
|
791 RootedObject holder(cx, cx->global()->intrinsicsHolder()); |
|
792 |
|
793 if (cx->global()->maybeGetIntrinsicValue(shId, funVal.address())) |
|
794 return true; |
|
795 |
|
796 JSFunction *fun = NewFunction(cx, NullPtr(), nullptr, nargs, JSFunction::INTERPRETED_LAZY, |
|
797 holder, name, JSFunction::ExtendedFinalizeKind, SingletonObject); |
|
798 if (!fun) |
|
799 return false; |
|
800 fun->setIsSelfHostedBuiltin(); |
|
801 fun->setExtendedSlot(0, StringValue(selfHostedName)); |
|
802 funVal.setObject(*fun); |
|
803 |
|
804 return cx->global()->addIntrinsicValue(cx, shId, funVal); |
|
805 } |
|
806 |
|
807 bool |
|
808 GlobalObject::addIntrinsicValue(JSContext *cx, HandleId id, HandleValue value) |
|
809 { |
|
810 RootedObject holder(cx, intrinsicsHolder()); |
|
811 |
|
812 uint32_t slot = holder->slotSpan(); |
|
813 RootedShape last(cx, holder->lastProperty()); |
|
814 Rooted<UnownedBaseShape*> base(cx, last->base()->unowned()); |
|
815 |
|
816 StackShape child(base, id, slot, 0, 0); |
|
817 RootedShape shape(cx, cx->compartment()->propertyTree.getChild(cx, last, child)); |
|
818 if (!shape) |
|
819 return false; |
|
820 |
|
821 if (!JSObject::setLastProperty(cx, holder, shape)) |
|
822 return false; |
|
823 |
|
824 holder->setSlot(shape->slot(), value); |
|
825 return true; |
|
826 } |