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 "vm/GlobalObject.h"
8
9 #include "jsdate.h"
10 #include "jsexn.h"
11 #include "jsfriendapi.h"
12
13 #include "builtin/AtomicsObject.h"
14 #include "builtin/BigInt.h"
15 #include "builtin/DataViewObject.h"
16 #include "builtin/Eval.h"
17 #ifdef JS_HAS_INTL_API
18 # include "builtin/intl/Collator.h"
19 # include "builtin/intl/DateTimeFormat.h"
20 # include "builtin/intl/DisplayNames.h"
21 # include "builtin/intl/ListFormat.h"
22 # include "builtin/intl/Locale.h"
23 # include "builtin/intl/NumberFormat.h"
24 # include "builtin/intl/PluralRules.h"
25 # include "builtin/intl/RelativeTimeFormat.h"
26 #endif
27 #include "builtin/FinalizationRegistryObject.h"
28 #include "builtin/MapObject.h"
29 #include "builtin/ModuleObject.h"
30 #include "builtin/Object.h"
31 #include "builtin/RegExp.h"
32 #include "builtin/SelfHostingDefines.h"
33 #include "builtin/Stream.h"
34 #include "builtin/streams/QueueingStrategies.h" // js::{ByteLength,Count}QueueingStrategy
35 #include "builtin/streams/ReadableStream.h" // js::ReadableStream
36 #include "builtin/streams/ReadableStreamController.h" // js::Readable{StreamDefault,ByteStream}Controller
37 #include "builtin/streams/ReadableStreamReader.h" // js::ReadableStreamDefaultReader
38 #include "builtin/streams/WritableStream.h" // js::WritableStream
39 #include "builtin/streams/WritableStreamDefaultController.h" // js::WritableStreamDefaultController
40 #include "builtin/streams/WritableStreamDefaultWriter.h" // js::WritableStreamDefaultWriter
41 #include "builtin/Symbol.h"
42 #include "builtin/WeakMapObject.h"
43 #include "builtin/WeakRefObject.h"
44 #include "builtin/WeakSetObject.h"
45 #include "debugger/DebugAPI.h"
46 #include "gc/FreeOp.h"
47 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
48 #include "js/friend/WindowProxy.h" // js::ToWindowProxyIfWindow
49 #include "js/OffThreadScriptCompilation.h" // js::UseOffThreadParseGlobal
50 #include "js/ProtoKey.h"
51 #include "vm/AsyncFunction.h"
52 #include "vm/AsyncIteration.h"
53 #include "vm/BooleanObject.h"
54 #include "vm/DateObject.h"
55 #include "vm/EnvironmentObject.h"
56 #include "vm/ErrorObject.h"
57 #include "vm/GeneratorObject.h"
58 #include "vm/HelperThreads.h"
59 #include "vm/JSContext.h"
60 #include "vm/NumberObject.h"
61 #include "vm/PIC.h"
62 #include "vm/RegExpStatics.h"
63 #include "vm/RegExpStaticsObject.h"
64 #include "vm/StringObject.h"
65
66 #include "gc/FreeOp-inl.h"
67 #include "vm/JSObject-inl.h"
68 #include "vm/JSScript-inl.h"
69 #include "vm/NativeObject-inl.h"
70 #include "vm/Realm-inl.h"
71
72 using namespace js;
73
74 namespace js {
75
76 extern const JSClass IntlClass;
77 extern const JSClass JSONClass;
78 extern const JSClass MathClass;
79 extern const JSClass ReflectClass;
80
81 } // namespace js
82
83 static const JSClass* const protoTable[JSProto_LIMIT] = {
84 #define INIT_FUNC(name, clasp) clasp,
85 #define INIT_FUNC_DUMMY(name, clasp) nullptr,
86 JS_FOR_PROTOTYPES(INIT_FUNC, INIT_FUNC_DUMMY)
87 #undef INIT_FUNC_DUMMY
88 #undef INIT_FUNC
89 };
90
ProtoKeyToClass(JSProtoKey key)91 JS_PUBLIC_API const JSClass* js::ProtoKeyToClass(JSProtoKey key) {
92 MOZ_ASSERT(key < JSProto_LIMIT);
93 return protoTable[key];
94 }
95
96 /* static */
skipDeselectedConstructor(JSContext * cx,JSProtoKey key)97 bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) {
98 switch (key) {
99 case JSProto_Null:
100 case JSProto_Object:
101 case JSProto_Function:
102 case JSProto_Array:
103 case JSProto_Boolean:
104 case JSProto_JSON:
105 case JSProto_Date:
106 case JSProto_Math:
107 case JSProto_Number:
108 case JSProto_String:
109 case JSProto_RegExp:
110 case JSProto_Error:
111 case JSProto_InternalError:
112 case JSProto_AggregateError:
113 case JSProto_EvalError:
114 case JSProto_RangeError:
115 case JSProto_ReferenceError:
116 case JSProto_SyntaxError:
117 case JSProto_TypeError:
118 case JSProto_URIError:
119 case JSProto_DebuggeeWouldRun:
120 case JSProto_CompileError:
121 case JSProto_LinkError:
122 case JSProto_RuntimeError:
123 case JSProto_ArrayBuffer:
124 case JSProto_Int8Array:
125 case JSProto_Uint8Array:
126 case JSProto_Int16Array:
127 case JSProto_Uint16Array:
128 case JSProto_Int32Array:
129 case JSProto_Uint32Array:
130 case JSProto_Float32Array:
131 case JSProto_Float64Array:
132 case JSProto_Uint8ClampedArray:
133 case JSProto_BigInt64Array:
134 case JSProto_BigUint64Array:
135 case JSProto_BigInt:
136 case JSProto_Proxy:
137 case JSProto_WeakMap:
138 case JSProto_Map:
139 case JSProto_Set:
140 case JSProto_DataView:
141 case JSProto_Symbol:
142 case JSProto_Reflect:
143 case JSProto_WeakSet:
144 case JSProto_TypedArray:
145 case JSProto_SavedFrame:
146 case JSProto_Promise:
147 case JSProto_AsyncFunction:
148 case JSProto_GeneratorFunction:
149 case JSProto_AsyncGeneratorFunction:
150 return false;
151
152 case JSProto_WebAssembly:
153 return !wasm::HasSupport(cx);
154
155 case JSProto_WasmModule:
156 case JSProto_WasmInstance:
157 case JSProto_WasmMemory:
158 case JSProto_WasmTable:
159 case JSProto_WasmGlobal:
160 case JSProto_WasmException:
161 case JSProto_WasmRuntimeException:
162 return false;
163
164 #ifdef JS_HAS_INTL_API
165 case JSProto_Intl:
166 case JSProto_Collator:
167 case JSProto_DateTimeFormat:
168 case JSProto_DisplayNames:
169 case JSProto_Locale:
170 case JSProto_ListFormat:
171 case JSProto_NumberFormat:
172 case JSProto_PluralRules:
173 case JSProto_RelativeTimeFormat:
174 return false;
175 #endif
176
177 case JSProto_ReadableStream:
178 case JSProto_ReadableStreamDefaultReader:
179 case JSProto_ReadableStreamDefaultController:
180 case JSProto_ReadableByteStreamController:
181 case JSProto_ByteLengthQueuingStrategy:
182 case JSProto_CountQueuingStrategy:
183 return !cx->realm()->creationOptions().getStreamsEnabled();
184
185 case JSProto_WritableStream:
186 case JSProto_WritableStreamDefaultController:
187 case JSProto_WritableStreamDefaultWriter: {
188 const auto& realmOptions = cx->realm()->creationOptions();
189 return !realmOptions.getStreamsEnabled() ||
190 !realmOptions.getWritableStreamsEnabled();
191 }
192
193 // Return true if the given constructor has been disabled at run-time.
194 case JSProto_Atomics:
195 case JSProto_SharedArrayBuffer:
196 return !cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
197
198 case JSProto_WeakRef:
199 case JSProto_FinalizationRegistry:
200 return cx->realm()->creationOptions().getWeakRefsEnabled() ==
201 JS::WeakRefSpecifier::Disabled;
202
203 case JSProto_Iterator:
204 case JSProto_AsyncIterator:
205 return !cx->realm()->creationOptions().getIteratorHelpersEnabled();
206
207 default:
208 MOZ_CRASH("unexpected JSProtoKey");
209 }
210 }
211
212 /* static*/
resolveConstructor(JSContext * cx,Handle<GlobalObject * > global,JSProtoKey key,IfClassIsDisabled mode)213 bool GlobalObject::resolveConstructor(JSContext* cx,
214 Handle<GlobalObject*> global,
215 JSProtoKey key, IfClassIsDisabled mode) {
216 MOZ_ASSERT(key != JSProto_Null);
217 MOZ_ASSERT(!global->isStandardClassResolved(key));
218 MOZ_ASSERT(cx->compartment() == global->compartment());
219
220 // |global| must be same-compartment but make sure we're in its realm: the
221 // code below relies on this.
222 AutoRealm ar(cx, global);
223
224 if (global->zone()->createdForHelperThread()) {
225 return resolveOffThreadConstructor(cx, global, key);
226 }
227
228 MOZ_ASSERT(!cx->isHelperThreadContext());
229
230 // Prohibit collection of allocation metadata. Metadata builders shouldn't
231 // need to observe lazily-constructed prototype objects coming into
232 // existence. And assertions start to fail when the builder itself attempts
233 // an allocation that re-entrantly tries to create the same prototype.
234 AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
235
236 // Constructor resolution may execute self-hosted scripts. These
237 // self-hosted scripts do not call out to user code by construction. Allow
238 // all scripts to execute, even in debuggee compartments that are paused.
239 AutoSuppressDebuggeeNoExecuteChecks suppressNX(cx);
240
241 // Some classes can be disabled at compile time, others at run time;
242 // if a feature is compile-time disabled, clasp is null.
243 const JSClass* clasp = ProtoKeyToClass(key);
244 if (!clasp || skipDeselectedConstructor(cx, key)) {
245 if (mode == IfClassIsDisabled::Throw) {
246 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
247 JSMSG_CONSTRUCTOR_DISABLED,
248 clasp ? clasp->name : "constructor");
249 return false;
250 }
251 return true;
252 }
253
254 // Class spec must have a constructor defined.
255 if (!clasp->specDefined()) {
256 return true;
257 }
258
259 bool isObjectOrFunction = key == JSProto_Function || key == JSProto_Object;
260
261 // We need to create the prototype first, and immediately stash it in the
262 // slot. This is so the following bootstrap ordering is possible:
263 // * Object.prototype
264 // * Function.prototype
265 // * Function
266 // * Object
267 //
268 // We get the above when Object is resolved before Function. If Function
269 // is resolved before Object, we'll end up re-entering resolveConstructor
270 // for Function, which is a problem. So if Function is being resolved
271 // before Object.prototype exists, we just resolve Object instead, since we
272 // know that Function will also be resolved before we return.
273 if (key == JSProto_Function &&
274 global->getPrototype(JSProto_Object).isUndefined()) {
275 return resolveConstructor(cx, global, JSProto_Object,
276 IfClassIsDisabled::DoNothing);
277 }
278
279 // %IteratorPrototype%.map.[[Prototype]] is %Generator% and
280 // %Generator%.prototype.[[Prototype]] is %IteratorPrototype%.
281 // A workaround in initIteratorProto prevents runaway mutual recursion while
282 // setting these up. Ensure the workaround is triggered already:
283 if (key == JSProto_GeneratorFunction &&
284 !global->getSlotRef(ITERATOR_PROTO).isObject()) {
285 if (!getOrCreateIteratorPrototype(cx, global)) {
286 return false;
287 }
288
289 // If iterator helpers are enabled, populating %IteratorPrototype% will
290 // have recursively gone through here.
291 if (global->isStandardClassResolved(key)) {
292 return true;
293 }
294 }
295
296 // We don't always have a prototype (i.e. Math and JSON). If we don't,
297 // |createPrototype|, |prototypeFunctions|, and |prototypeProperties|
298 // should all be null.
299 RootedObject proto(cx);
300 if (ClassObjectCreationOp createPrototype =
301 clasp->specCreatePrototypeHook()) {
302 proto = createPrototype(cx, key);
303 if (!proto) {
304 return false;
305 }
306
307 if (isObjectOrFunction) {
308 // Make sure that creating the prototype didn't recursively resolve
309 // our own constructor. We can't just assert that there's no
310 // prototype; OOMs can result in incomplete resolutions in which
311 // the prototype is saved but not the constructor. So use the same
312 // criteria that protects entry into this function.
313 MOZ_ASSERT(!global->isStandardClassResolved(key));
314
315 global->setPrototype(key, ObjectValue(*proto));
316 }
317 }
318
319 // Create the constructor.
320 RootedObject ctor(cx, clasp->specCreateConstructorHook()(cx, key));
321 if (!ctor) {
322 return false;
323 }
324
325 RootedId id(cx, NameToId(ClassName(key, cx)));
326 if (isObjectOrFunction) {
327 if (clasp->specShouldDefineConstructor()) {
328 RootedValue ctorValue(cx, ObjectValue(*ctor));
329 if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) {
330 return false;
331 }
332 }
333
334 global->setConstructor(key, ObjectValue(*ctor));
335 }
336
337 // If we're operating on the self-hosting global, we don't want any
338 // functions and properties on the builtins and their prototypes.
339 if (!cx->runtime()->isSelfHostingGlobal(global)) {
340 if (const JSFunctionSpec* funs = clasp->specPrototypeFunctions()) {
341 if (!JS_DefineFunctions(cx, proto, funs)) {
342 return false;
343 }
344 }
345 if (const JSPropertySpec* props = clasp->specPrototypeProperties()) {
346 if (!JS_DefineProperties(cx, proto, props)) {
347 return false;
348 }
349 }
350 if (const JSFunctionSpec* funs = clasp->specConstructorFunctions()) {
351 if (!JS_DefineFunctions(cx, ctor, funs)) {
352 return false;
353 }
354 }
355 if (const JSPropertySpec* props = clasp->specConstructorProperties()) {
356 if (!JS_DefineProperties(cx, ctor, props)) {
357 return false;
358 }
359 }
360 }
361
362 // If the prototype exists, link it with the constructor.
363 if (proto && !LinkConstructorAndPrototype(cx, ctor, proto)) {
364 return false;
365 }
366
367 // Call the post-initialization hook, if provided.
368 if (FinishClassInitOp finishInit = clasp->specFinishInitHook()) {
369 if (!finishInit(cx, ctor, proto)) {
370 return false;
371 }
372 }
373
374 if (!isObjectOrFunction) {
375 // Any operations that modifies the global object should be placed
376 // after any other fallible operations.
377
378 // Fallible operation that modifies the global object.
379 if (clasp->specShouldDefineConstructor()) {
380 bool shouldReallyDefine = true;
381
382 // On the web, it isn't presently possible to expose the global
383 // "SharedArrayBuffer" property unless the page is cross-site-isolated.
384 // Only define this constructor if an option on the realm indicates that
385 // it should be defined.
386 if (key == JSProto_SharedArrayBuffer) {
387 const JS::RealmCreationOptions& options =
388 global->realm()->creationOptions();
389
390 MOZ_ASSERT(options.getSharedMemoryAndAtomicsEnabled(),
391 "shouldn't be defining SharedArrayBuffer if shared memory "
392 "is disabled");
393
394 shouldReallyDefine = options.defineSharedArrayBufferConstructor();
395 }
396
397 if (shouldReallyDefine) {
398 RootedValue ctorValue(cx, ObjectValue(*ctor));
399 if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) {
400 return false;
401 }
402 }
403 }
404
405 // Infallible operations that modify the global object.
406 global->setConstructor(key, ObjectValue(*ctor));
407 if (proto) {
408 global->setPrototype(key, ObjectValue(*proto));
409 }
410 }
411
412 return true;
413 }
414
415 // Resolve a "globalThis" self-referential property if necessary,
416 // per a stage-3 proposal. https://github.com/tc39/ecma262/pull/702
417 //
418 // We could also do this in |FinishObjectClassInit| to trim the global
419 // resolve hook. Unfortunately, |ToWindowProxyIfWindow| doesn't work then:
420 // the browser's |nsGlobalWindow::SetNewDocument| invokes Object init
421 // *before* it sets the global's WindowProxy using |js::SetWindowProxy|.
422 //
423 // Refactoring global object creation code to support this approach is a
424 // challenge for another day.
425 /* static */
maybeResolveGlobalThis(JSContext * cx,Handle<GlobalObject * > global,bool * resolved)426 bool GlobalObject::maybeResolveGlobalThis(JSContext* cx,
427 Handle<GlobalObject*> global,
428 bool* resolved) {
429 if (global->getSlot(GLOBAL_THIS_RESOLVED).isUndefined()) {
430 RootedValue v(cx, ObjectValue(*ToWindowProxyIfWindow(global)));
431 if (!DefineDataProperty(cx, global, cx->names().globalThis, v,
432 JSPROP_RESOLVING)) {
433 return false;
434 }
435
436 *resolved = true;
437 global->setSlot(GLOBAL_THIS_RESOLVED, BooleanValue(true));
438 }
439
440 return true;
441 }
442
443 /* static */
createObject(JSContext * cx,Handle<GlobalObject * > global,unsigned slot,ObjectInitOp init)444 JSObject* GlobalObject::createObject(JSContext* cx,
445 Handle<GlobalObject*> global,
446 unsigned slot, ObjectInitOp init) {
447 if (global->zone()->createdForHelperThread()) {
448 return createOffThreadObject(cx, global, slot);
449 }
450
451 MOZ_ASSERT(!cx->isHelperThreadContext());
452 if (!init(cx, global)) {
453 return nullptr;
454 }
455
456 return &global->getSlot(slot).toObject();
457 }
458
createObject(JSContext * cx,Handle<GlobalObject * > global,unsigned slot,HandleAtom tag,ObjectInitWithTagOp init)459 JSObject* GlobalObject::createObject(JSContext* cx,
460 Handle<GlobalObject*> global,
461 unsigned slot, HandleAtom tag,
462 ObjectInitWithTagOp init) {
463 if (global->zone()->createdForHelperThread()) {
464 return createOffThreadObject(cx, global, slot);
465 }
466
467 MOZ_ASSERT(!cx->isHelperThreadContext());
468 if (!init(cx, global, tag)) {
469 return nullptr;
470 }
471
472 return &global->getSlot(slot).toObject();
473 }
474
475 const JSClass GlobalObject::OffThreadPlaceholderObject::class_ = {
476 "off-thread-prototype-placeholder", JSCLASS_HAS_RESERVED_SLOTS(1)};
477
478 /* static */ GlobalObject::OffThreadPlaceholderObject*
New(JSContext * cx,unsigned slot)479 GlobalObject::OffThreadPlaceholderObject::New(JSContext* cx, unsigned slot) {
480 Rooted<OffThreadPlaceholderObject*> placeholder(cx);
481 placeholder =
482 NewObjectWithGivenProto<OffThreadPlaceholderObject>(cx, nullptr);
483 if (!placeholder) {
484 return nullptr;
485 }
486
487 placeholder->setReservedSlot(SlotIndexSlot, Int32Value(slot));
488 return placeholder;
489 }
490
getSlotIndex() const491 inline int32_t GlobalObject::OffThreadPlaceholderObject::getSlotIndex() const {
492 return getReservedSlot(SlotIndexSlot).toInt32();
493 }
494
495 /* static */
resolveOffThreadConstructor(JSContext * cx,Handle<GlobalObject * > global,JSProtoKey key)496 bool GlobalObject::resolveOffThreadConstructor(JSContext* cx,
497 Handle<GlobalObject*> global,
498 JSProtoKey key) {
499 // Don't resolve constructors for off-thread parse globals. Instead create a
500 // placeholder object for the prototype which we can use to find the real
501 // prototype when the off-thread compartment is merged back into the target
502 // compartment.
503
504 MOZ_ASSERT(global->zone()->createdForHelperThread());
505 MOZ_ASSERT(key == JSProto_Object || key == JSProto_Function ||
506 key == JSProto_Array || key == JSProto_RegExp ||
507 key == JSProto_AsyncFunction || key == JSProto_GeneratorFunction ||
508 key == JSProto_AsyncGeneratorFunction);
509
510 Rooted<OffThreadPlaceholderObject*> placeholder(cx);
511 placeholder = OffThreadPlaceholderObject::New(cx, prototypeSlot(key));
512 if (!placeholder) {
513 return false;
514 }
515
516 if (key == JSProto_Object &&
517 !JSObject::setFlag(cx, placeholder, ObjectFlag::ImmutablePrototype)) {
518 return false;
519 }
520
521 global->setPrototype(key, ObjectValue(*placeholder));
522 global->setConstructor(key, MagicValue(JS_OFF_THREAD_CONSTRUCTOR));
523 return true;
524 }
525
526 /* static */
createOffThreadObject(JSContext * cx,Handle<GlobalObject * > global,unsigned slot)527 JSObject* GlobalObject::createOffThreadObject(JSContext* cx,
528 Handle<GlobalObject*> global,
529 unsigned slot) {
530 // Don't create prototype objects for off-thread parse globals. Instead
531 // create a placeholder object which we can use to find the real prototype
532 // when the off-thread compartment is merged back into the target
533 // compartment.
534
535 MOZ_ASSERT(global->zone()->createdForHelperThread());
536 MOZ_ASSERT(slot == MODULE_PROTO || slot == IMPORT_ENTRY_PROTO ||
537 slot == EXPORT_ENTRY_PROTO || slot == REQUESTED_MODULE_PROTO);
538
539 auto placeholder = OffThreadPlaceholderObject::New(cx, slot);
540 if (!placeholder) {
541 return nullptr;
542 }
543
544 global->setSlot(slot, ObjectValue(*placeholder));
545 return placeholder;
546 }
547
getPrototypeForOffThreadPlaceholder(JSObject * obj)548 JSObject* GlobalObject::getPrototypeForOffThreadPlaceholder(JSObject* obj) {
549 auto placeholder = &obj->as<OffThreadPlaceholderObject>();
550 return &getSlot(placeholder->getSlotIndex()).toObject();
551 }
552
553 /* static */
initBuiltinConstructor(JSContext * cx,Handle<GlobalObject * > global,JSProtoKey key,HandleObject ctor,HandleObject proto)554 bool GlobalObject::initBuiltinConstructor(JSContext* cx,
555 Handle<GlobalObject*> global,
556 JSProtoKey key, HandleObject ctor,
557 HandleObject proto) {
558 MOZ_ASSERT(!global->empty()); // reserved slots already allocated
559 MOZ_ASSERT(key != JSProto_Null);
560 MOZ_ASSERT(ctor);
561 MOZ_ASSERT(proto);
562
563 RootedId id(cx, NameToId(ClassName(key, cx)));
564 MOZ_ASSERT(!global->lookup(cx, id));
565
566 RootedValue ctorValue(cx, ObjectValue(*ctor));
567 if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) {
568 return false;
569 }
570
571 global->setConstructor(key, ObjectValue(*ctor));
572 global->setPrototype(key, ObjectValue(*proto));
573 return true;
574 }
575
ThrowTypeError(JSContext * cx,unsigned argc,Value * vp)576 static bool ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) {
577 ThrowTypeErrorBehavior(cx);
578 return false;
579 }
580
581 /* static */
getOrCreateThrowTypeError(JSContext * cx,Handle<GlobalObject * > global)582 JSObject* GlobalObject::getOrCreateThrowTypeError(
583 JSContext* cx, Handle<GlobalObject*> global) {
584 Value v = global->getReservedSlot(THROWTYPEERROR);
585 if (v.isObject()) {
586 return &v.toObject();
587 }
588 MOZ_ASSERT(v.isUndefined());
589
590 // Construct the unique [[%ThrowTypeError%]] function object, used only for
591 // "callee" and "caller" accessors on strict mode arguments objects. (The
592 // spec also uses this for "arguments" and "caller" on various functions,
593 // but we're experimenting with implementing them using accessors on
594 // |Function.prototype| right now.)
595
596 RootedFunction throwTypeError(
597 cx, NewNativeFunction(cx, ThrowTypeError, 0, nullptr));
598 if (!throwTypeError || !PreventExtensions(cx, throwTypeError)) {
599 return nullptr;
600 }
601
602 // The "length" property of %ThrowTypeError% is non-configurable.
603 Rooted<PropertyDescriptor> nonConfigurableDesc(cx,
604 PropertyDescriptor::Empty());
605 nonConfigurableDesc.setConfigurable(false);
606
607 RootedId lengthId(cx, NameToId(cx->names().length));
608 ObjectOpResult lengthResult;
609 if (!NativeDefineProperty(cx, throwTypeError, lengthId, nonConfigurableDesc,
610 lengthResult)) {
611 return nullptr;
612 }
613 MOZ_ASSERT(lengthResult);
614
615 // The "name" property of %ThrowTypeError% is non-configurable, adjust
616 // the default property attributes accordingly.
617 RootedId nameId(cx, NameToId(cx->names().name));
618 ObjectOpResult nameResult;
619 if (!NativeDefineProperty(cx, throwTypeError, nameId, nonConfigurableDesc,
620 nameResult)) {
621 return nullptr;
622 }
623 MOZ_ASSERT(nameResult);
624
625 global->setReservedSlot(THROWTYPEERROR, ObjectValue(*throwTypeError));
626 return throwTypeError;
627 }
628
createInternal(JSContext * cx,const JSClass * clasp)629 GlobalObject* GlobalObject::createInternal(JSContext* cx,
630 const JSClass* clasp) {
631 MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
632 MOZ_ASSERT(clasp->isTrace(JS_GlobalObjectTraceHook));
633
634 JSObject* obj = NewTenuredObjectWithGivenProto(cx, clasp, nullptr);
635 if (!obj) {
636 return nullptr;
637 }
638
639 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
640 MOZ_ASSERT(global->isUnqualifiedVarObj());
641
642 // Initialize the private slot to null if present, as GC can call class
643 // hooks before the caller gets to set this to a non-garbage value.
644 if (clasp->flags & JSCLASS_HAS_PRIVATE) {
645 global->setPrivate(nullptr);
646 }
647
648 Rooted<GlobalLexicalEnvironmentObject*> lexical(
649 cx, GlobalLexicalEnvironmentObject::create(cx, global));
650 if (!lexical) {
651 return nullptr;
652 }
653
654 Rooted<GlobalScope*> emptyGlobalScope(
655 cx, GlobalScope::createEmpty(cx, ScopeKind::Global));
656 if (!emptyGlobalScope) {
657 return nullptr;
658 }
659 global->setReservedSlot(EMPTY_GLOBAL_SCOPE,
660 PrivateGCThingValue(emptyGlobalScope));
661
662 cx->realm()->initGlobal(*global, *lexical);
663
664 if (!JSObject::setQualifiedVarObj(cx, global)) {
665 return nullptr;
666 }
667
668 return global;
669 }
670
671 /* static */
new_(JSContext * cx,const JSClass * clasp,JSPrincipals * principals,JS::OnNewGlobalHookOption hookOption,const JS::RealmOptions & options)672 GlobalObject* GlobalObject::new_(JSContext* cx, const JSClass* clasp,
673 JSPrincipals* principals,
674 JS::OnNewGlobalHookOption hookOption,
675 const JS::RealmOptions& options) {
676 MOZ_ASSERT(!cx->isExceptionPending());
677 MOZ_ASSERT_IF(cx->zone(), !cx->zone()->isAtomsZone());
678
679 // If we are creating a new global in an existing compartment, make sure the
680 // compartment has a live global at all times (by rooting it here).
681 // See bug 1530364.
682 Rooted<GlobalObject*> existingGlobal(cx);
683 const JS::RealmCreationOptions& creationOptions = options.creationOptions();
684 if (creationOptions.compartmentSpecifier() ==
685 JS::CompartmentSpecifier::ExistingCompartment) {
686 Compartment* comp = creationOptions.compartment();
687 existingGlobal = &comp->firstGlobal();
688 }
689
690 Realm* realm = NewRealm(cx, principals, options);
691 if (!realm) {
692 return nullptr;
693 }
694
695 Rooted<GlobalObject*> global(cx);
696 {
697 AutoRealmUnchecked ar(cx, realm);
698 global = GlobalObject::createInternal(cx, clasp);
699 if (!global) {
700 return nullptr;
701 }
702
703 if (hookOption == JS::FireOnNewGlobalHook) {
704 JS_FireOnNewGlobalObject(cx, global);
705 }
706 }
707
708 return global;
709 }
710
lexicalEnvironment() const711 GlobalLexicalEnvironmentObject& GlobalObject::lexicalEnvironment() const {
712 // The lexical environment is marked when marking the global, so we don't need
713 // a read barrier here because we know the global is live.
714 return *realm()->unbarrieredLexicalEnvironment();
715 }
716
emptyGlobalScope() const717 GlobalScope& GlobalObject::emptyGlobalScope() const {
718 const Value& v = getReservedSlot(EMPTY_GLOBAL_SCOPE);
719 MOZ_ASSERT(v.isPrivateGCThing() && v.traceKind() == JS::TraceKind::Scope);
720 return static_cast<Scope*>(v.toGCThing())->as<GlobalScope>();
721 }
722
723 /* static */
getOrCreateEval(JSContext * cx,Handle<GlobalObject * > global,MutableHandleObject eval)724 bool GlobalObject::getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
725 MutableHandleObject eval) {
726 if (!getOrCreateObjectPrototype(cx, global)) {
727 return false;
728 }
729 eval.set(&global->getSlot(EVAL).toObject());
730 return true;
731 }
732
valueIsEval(const Value & val)733 bool GlobalObject::valueIsEval(const Value& val) {
734 Value eval = getSlot(EVAL);
735 return eval.isObject() && eval == val;
736 }
737
738 /* static */
initStandardClasses(JSContext * cx,Handle<GlobalObject * > global)739 bool GlobalObject::initStandardClasses(JSContext* cx,
740 Handle<GlobalObject*> global) {
741 /* Define a top-level property 'undefined' with the undefined value. */
742 if (!DefineDataProperty(
743 cx, global, cx->names().undefined, UndefinedHandleValue,
744 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
745 return false;
746 }
747
748 // Resolve a "globalThis" self-referential property if necessary.
749 bool resolved;
750 if (!GlobalObject::maybeResolveGlobalThis(cx, global, &resolved)) {
751 return false;
752 }
753
754 for (size_t k = 0; k < JSProto_LIMIT; ++k) {
755 JSProtoKey key = static_cast<JSProtoKey>(k);
756 if (key != JSProto_Null && !global->isStandardClassResolved(key)) {
757 if (!resolveConstructor(cx, global, static_cast<JSProtoKey>(k),
758 IfClassIsDisabled::DoNothing)) {
759 return false;
760 }
761 }
762 }
763 return true;
764 }
765
766 /* static */
initSelfHostingBuiltins(JSContext * cx,Handle<GlobalObject * > global,const JSFunctionSpec * builtins)767 bool GlobalObject::initSelfHostingBuiltins(JSContext* cx,
768 Handle<GlobalObject*> global,
769 const JSFunctionSpec* builtins) {
770 return DefineFunctions(cx, global, builtins, AsIntrinsic);
771 }
772
773 /* static */
isRuntimeCodeGenEnabled(JSContext * cx,HandleString code,Handle<GlobalObject * > global)774 bool GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, HandleString code,
775 Handle<GlobalObject*> global) {
776 HeapSlot& v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED);
777 if (v.isUndefined()) {
778 /*
779 * If there are callbacks, make sure that the CSP callback is installed
780 * and that it permits runtime code generation.
781 */
782 JSCSPEvalChecker allows =
783 cx->runtime()->securityCallbacks->contentSecurityPolicyAllows;
784 if (allows) {
785 return allows(cx, code);
786 }
787
788 // Let's cache the result only if the contentSecurityPolicyAllows callback
789 // is not set. In this way, contentSecurityPolicyAllows callback is executed
790 // each time, with the current HandleValue code.
791 v.set(global, HeapSlot::Slot, RUNTIME_CODEGEN_ENABLED, JS::TrueValue());
792 }
793 return !v.isFalse();
794 }
795
796 /* static */
createConstructor(JSContext * cx,Native ctor,JSAtom * nameArg,unsigned length,gc::AllocKind kind,const JSJitInfo * jitInfo)797 JSFunction* GlobalObject::createConstructor(JSContext* cx, Native ctor,
798 JSAtom* nameArg, unsigned length,
799 gc::AllocKind kind,
800 const JSJitInfo* jitInfo) {
801 RootedAtom name(cx, nameArg);
802 JSFunction* fun = NewNativeConstructor(cx, ctor, length, name, kind);
803 if (!fun) {
804 return nullptr;
805 }
806
807 if (jitInfo) {
808 fun->setJitInfo(jitInfo);
809 }
810
811 return fun;
812 }
813
CreateBlankProto(JSContext * cx,const JSClass * clasp,HandleObject proto)814 static NativeObject* CreateBlankProto(JSContext* cx, const JSClass* clasp,
815 HandleObject proto) {
816 MOZ_ASSERT(clasp != &JSFunction::class_);
817
818 RootedObject blankProto(cx, NewTenuredObjectWithGivenProto(cx, clasp, proto));
819 if (!blankProto) {
820 return nullptr;
821 }
822
823 return &blankProto->as<NativeObject>();
824 }
825
826 /* static */
createBlankPrototype(JSContext * cx,Handle<GlobalObject * > global,const JSClass * clasp)827 NativeObject* GlobalObject::createBlankPrototype(JSContext* cx,
828 Handle<GlobalObject*> global,
829 const JSClass* clasp) {
830 RootedObject objectProto(cx, getOrCreateObjectPrototype(cx, global));
831 if (!objectProto) {
832 return nullptr;
833 }
834
835 return CreateBlankProto(cx, clasp, objectProto);
836 }
837
838 /* static */
createBlankPrototypeInheriting(JSContext * cx,const JSClass * clasp,HandleObject proto)839 NativeObject* GlobalObject::createBlankPrototypeInheriting(JSContext* cx,
840 const JSClass* clasp,
841 HandleObject proto) {
842 return CreateBlankProto(cx, clasp, proto);
843 }
844
LinkConstructorAndPrototype(JSContext * cx,JSObject * ctor_,JSObject * proto_,unsigned prototypeAttrs,unsigned constructorAttrs)845 bool js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_,
846 JSObject* proto_, unsigned prototypeAttrs,
847 unsigned constructorAttrs) {
848 RootedObject ctor(cx, ctor_), proto(cx, proto_);
849
850 RootedValue protoVal(cx, ObjectValue(*proto));
851 RootedValue ctorVal(cx, ObjectValue(*ctor));
852
853 return DefineDataProperty(cx, ctor, cx->names().prototype, protoVal,
854 prototypeAttrs) &&
855 DefineDataProperty(cx, proto, cx->names().constructor, ctorVal,
856 constructorAttrs);
857 }
858
DefinePropertiesAndFunctions(JSContext * cx,HandleObject obj,const JSPropertySpec * ps,const JSFunctionSpec * fs)859 bool js::DefinePropertiesAndFunctions(JSContext* cx, HandleObject obj,
860 const JSPropertySpec* ps,
861 const JSFunctionSpec* fs) {
862 if (ps && !JS_DefineProperties(cx, obj, ps)) {
863 return false;
864 }
865 if (fs && !JS_DefineFunctions(cx, obj, fs)) {
866 return false;
867 }
868 return true;
869 }
870
DefineToStringTag(JSContext * cx,HandleObject obj,JSAtom * tag)871 bool js::DefineToStringTag(JSContext* cx, HandleObject obj, JSAtom* tag) {
872 RootedId toStringTagId(cx,
873 SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
874 RootedValue tagString(cx, StringValue(tag));
875 return DefineDataProperty(cx, obj, toStringTagId, tagString, JSPROP_READONLY);
876 }
877
878 /* static */
getOrCreateForOfPICObject(JSContext * cx,Handle<GlobalObject * > global)879 NativeObject* GlobalObject::getOrCreateForOfPICObject(
880 JSContext* cx, Handle<GlobalObject*> global) {
881 cx->check(global);
882 NativeObject* forOfPIC = global->getForOfPICObject();
883 if (forOfPIC) {
884 return forOfPIC;
885 }
886
887 forOfPIC = ForOfPIC::createForOfPICObject(cx, global);
888 if (!forOfPIC) {
889 return nullptr;
890 }
891 global->setReservedSlot(FOR_OF_PIC_CHAIN, ObjectValue(*forOfPIC));
892 return forOfPIC;
893 }
894
895 /* static */
getOrCreateRealmKeyObject(JSContext * cx,Handle<GlobalObject * > global)896 JSObject* GlobalObject::getOrCreateRealmKeyObject(
897 JSContext* cx, Handle<GlobalObject*> global) {
898 cx->check(global);
899 Value v = global->getReservedSlot(REALM_KEY_OBJECT);
900 if (v.isObject()) {
901 return &v.toObject();
902 }
903
904 PlainObject* key = NewBuiltinClassInstance<PlainObject>(cx);
905 if (!key) {
906 return nullptr;
907 }
908
909 global->setReservedSlot(REALM_KEY_OBJECT, ObjectValue(*key));
910 return key;
911 }
912
913 /* static */
getRegExpStatics(JSContext * cx,Handle<GlobalObject * > global)914 RegExpStatics* GlobalObject::getRegExpStatics(JSContext* cx,
915 Handle<GlobalObject*> global) {
916 MOZ_ASSERT(cx);
917 RegExpStaticsObject* resObj = nullptr;
918 const Value& val = global->getSlot(REGEXP_STATICS);
919 if (!val.isObject()) {
920 MOZ_ASSERT(val.isUndefined());
921 resObj = RegExpStatics::create(cx);
922 if (!resObj) {
923 return nullptr;
924 }
925
926 global->initSlot(REGEXP_STATICS, ObjectValue(*resObj));
927 } else {
928 resObj = &val.toObject().as<RegExpStaticsObject>();
929 }
930 return static_cast<RegExpStatics*>(resObj->getPrivate(/* nfixed = */ 1));
931 }
932
933 /* static */
getIntrinsicsHolder(JSContext * cx,Handle<GlobalObject * > global)934 NativeObject* GlobalObject::getIntrinsicsHolder(JSContext* cx,
935 Handle<GlobalObject*> global) {
936 Value slot = global->getReservedSlot(INTRINSICS);
937 MOZ_ASSERT(slot.isUndefined() || slot.isObject());
938
939 if (slot.isObject()) {
940 return &slot.toObject().as<NativeObject>();
941 }
942
943 Rooted<NativeObject*> intrinsicsHolder(cx);
944 bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(global);
945 if (isSelfHostingGlobal) {
946 intrinsicsHolder = global;
947 } else {
948 intrinsicsHolder = NewTenuredObjectWithGivenProto<PlainObject>(cx, nullptr);
949 if (!intrinsicsHolder) {
950 return nullptr;
951 }
952 }
953
954 // Define a top-level property 'undefined' with the undefined value.
955 if (!DefineDataProperty(cx, intrinsicsHolder, cx->names().undefined,
956 UndefinedHandleValue,
957 JSPROP_PERMANENT | JSPROP_READONLY)) {
958 return nullptr;
959 }
960
961 // Install the intrinsics holder in the intrinsics.
962 global->setReservedSlot(INTRINSICS, ObjectValue(*intrinsicsHolder));
963 return intrinsicsHolder;
964 }
965
966 /* static */
getSelfHostedFunction(JSContext * cx,Handle<GlobalObject * > global,HandlePropertyName selfHostedName,HandleAtom name,unsigned nargs,MutableHandleValue funVal)967 bool GlobalObject::getSelfHostedFunction(JSContext* cx,
968 Handle<GlobalObject*> global,
969 HandlePropertyName selfHostedName,
970 HandleAtom name, unsigned nargs,
971 MutableHandleValue funVal) {
972 bool exists = false;
973 if (!GlobalObject::maybeGetIntrinsicValue(cx, global, selfHostedName, funVal,
974 &exists)) {
975 return false;
976 }
977 if (exists) {
978 RootedFunction fun(cx, &funVal.toObject().as<JSFunction>());
979 if (fun->explicitName() == name) {
980 return true;
981 }
982
983 if (fun->explicitName() == selfHostedName) {
984 // This function was initially cloned because it was called by
985 // other self-hosted code, so the clone kept its self-hosted name,
986 // instead of getting the name it's intended to have in content
987 // compartments. This can happen when a lazy builtin is initialized
988 // after self-hosted code for another builtin used the same
989 // function. In that case, we need to change the function's name,
990 // which is ok because it can't have been exposed to content
991 // before.
992 fun->initAtom(name);
993 return true;
994 }
995
996 // The function might be installed multiple times on the same or
997 // different builtins, under different property names, so its name
998 // might be neither "selfHostedName" nor "name". In that case, its
999 // canonical name must've been set using the `_SetCanonicalName`
1000 // intrinsic.
1001 cx->runtime()->assertSelfHostedFunctionHasCanonicalName(cx, selfHostedName);
1002 return true;
1003 }
1004
1005 RootedFunction fun(cx);
1006 if (!cx->runtime()->createLazySelfHostedFunctionClone(
1007 cx, selfHostedName, name, nargs, TenuredObject, &fun)) {
1008 return false;
1009 }
1010 funVal.setObject(*fun);
1011
1012 return GlobalObject::addIntrinsicValue(cx, global, selfHostedName, funVal);
1013 }
1014
1015 /* static */
getIntrinsicValueSlow(JSContext * cx,Handle<GlobalObject * > global,HandlePropertyName name,MutableHandleValue value)1016 bool GlobalObject::getIntrinsicValueSlow(JSContext* cx,
1017 Handle<GlobalObject*> global,
1018 HandlePropertyName name,
1019 MutableHandleValue value) {
1020 if (!cx->runtime()->cloneSelfHostedValue(cx, name, value)) {
1021 return false;
1022 }
1023
1024 // It's possible in certain edge cases that cloning the value ended up
1025 // defining the intrinsic. For instance, cloning can call NewArray, which
1026 // resolves Array.prototype, which defines some self-hosted functions. If this
1027 // happens we use the value already defined on the intrinsics holder.
1028 bool exists = false;
1029 if (!GlobalObject::maybeGetIntrinsicValue(cx, global, name, value, &exists)) {
1030 return false;
1031 }
1032 if (exists) {
1033 return true;
1034 }
1035
1036 return GlobalObject::addIntrinsicValue(cx, global, name, value);
1037 }
1038
1039 /* static */
addIntrinsicValue(JSContext * cx,Handle<GlobalObject * > global,HandlePropertyName name,HandleValue value)1040 bool GlobalObject::addIntrinsicValue(JSContext* cx,
1041 Handle<GlobalObject*> global,
1042 HandlePropertyName name,
1043 HandleValue value) {
1044 RootedNativeObject holder(cx, GlobalObject::getIntrinsicsHolder(cx, global));
1045 if (!holder) {
1046 return false;
1047 }
1048
1049 RootedId id(cx, NameToId(name));
1050 MOZ_ASSERT(!holder->containsPure(id));
1051
1052 constexpr PropertyFlags propFlags = {PropertyFlag::Configurable,
1053 PropertyFlag::Writable};
1054 uint32_t slot;
1055 if (!NativeObject::addProperty(cx, holder, id, propFlags, &slot)) {
1056 return false;
1057 }
1058 holder->initSlot(slot, value);
1059 return true;
1060 }
1061
1062 /* static */
ensureModulePrototypesCreated(JSContext * cx,Handle<GlobalObject * > global,bool setUsedAsPrototype)1063 bool GlobalObject::ensureModulePrototypesCreated(JSContext* cx,
1064 Handle<GlobalObject*> global,
1065 bool setUsedAsPrototype) {
1066 // Note: if you arrived here because you're removing UseOffThreadParseGlobal,
1067 // please also remove the setUsedAsPrototype argument and the lambda below.
1068 MOZ_ASSERT_IF(!UseOffThreadParseGlobal(), !setUsedAsPrototype);
1069
1070 auto maybeSetUsedAsPrototype = [cx, setUsedAsPrototype](HandleObject proto) {
1071 if (!setUsedAsPrototype) {
1072 return true;
1073 }
1074 return JSObject::setIsUsedAsPrototype(cx, proto);
1075 };
1076
1077 RootedObject proto(cx);
1078 proto = getOrCreateModulePrototype(cx, global);
1079 if (!proto || !maybeSetUsedAsPrototype(proto)) {
1080 return false;
1081 }
1082
1083 proto = getOrCreateImportEntryPrototype(cx, global);
1084 if (!proto || !maybeSetUsedAsPrototype(proto)) {
1085 return false;
1086 }
1087
1088 proto = getOrCreateExportEntryPrototype(cx, global);
1089 if (!proto || !maybeSetUsedAsPrototype(proto)) {
1090 return false;
1091 }
1092
1093 proto = getOrCreateRequestedModulePrototype(cx, global);
1094 if (!proto || !maybeSetUsedAsPrototype(proto)) {
1095 return false;
1096 }
1097
1098 return true;
1099 }
1100
1101 /* static */
createIteratorPrototype(JSContext * cx,Handle<GlobalObject * > global)1102 JSObject* GlobalObject::createIteratorPrototype(JSContext* cx,
1103 Handle<GlobalObject*> global) {
1104 if (!cx->realm()->creationOptions().getIteratorHelpersEnabled()) {
1105 return getOrCreateObject(cx, global, ITERATOR_PROTO, initIteratorProto);
1106 }
1107
1108 if (!ensureConstructor(cx, global, JSProto_Iterator)) {
1109 return nullptr;
1110 }
1111 JSObject* proto = &global->getPrototype(JSProto_Iterator).toObject();
1112 global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
1113 return proto;
1114 }
1115
1116 /* static */
createAsyncIteratorPrototype(JSContext * cx,Handle<GlobalObject * > global)1117 JSObject* GlobalObject::createAsyncIteratorPrototype(
1118 JSContext* cx, Handle<GlobalObject*> global) {
1119 if (!cx->realm()->creationOptions().getIteratorHelpersEnabled()) {
1120 return getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO,
1121 initAsyncIteratorProto);
1122 }
1123
1124 if (!ensureConstructor(cx, global, JSProto_AsyncIterator)) {
1125 return nullptr;
1126 }
1127 JSObject* proto = &global->getPrototype(JSProto_AsyncIterator).toObject();
1128 global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*proto));
1129 return proto;
1130 }
1131