Line data Source code
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 "jscompartmentinlines.h"
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "mozilla/MemoryReporting.h"
11 :
12 : #include <stddef.h>
13 :
14 : #include "jscntxt.h"
15 : #include "jsfriendapi.h"
16 : #include "jsgc.h"
17 : #include "jsiter.h"
18 : #include "jswatchpoint.h"
19 : #include "jswrapper.h"
20 :
21 : #include "gc/Marking.h"
22 : #include "gc/Policy.h"
23 : #include "jit/JitCompartment.h"
24 : #include "jit/JitOptions.h"
25 : #include "js/Date.h"
26 : #include "js/Proxy.h"
27 : #include "js/RootingAPI.h"
28 : #include "proxy/DeadObjectProxy.h"
29 : #include "vm/Debugger.h"
30 : #include "vm/StopIterationObject.h"
31 : #include "vm/WrapperObject.h"
32 :
33 : #include "jsatominlines.h"
34 : #include "jsfuninlines.h"
35 : #include "jsgcinlines.h"
36 : #include "jsobjinlines.h"
37 : #include "jsscriptinlines.h"
38 :
39 : #include "vm/NativeObject-inl.h"
40 : #include "vm/UnboxedObject-inl.h"
41 :
42 : using namespace js;
43 : using namespace js::gc;
44 : using namespace js::jit;
45 :
46 : using mozilla::DebugOnly;
47 : using mozilla::PodArrayZero;
48 :
49 315 : JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions())
50 315 : : creationOptions_(options.creationOptions()),
51 315 : behaviors_(options.behaviors()),
52 : zone_(zone),
53 315 : runtime_(zone->runtimeFromAnyThread()),
54 : principals_(nullptr),
55 : isSystem_(false),
56 : isAtomsCompartment_(false),
57 : isSelfHosting(false),
58 : marked(true),
59 : warnedAboutDateToLocaleFormat(false),
60 : warnedAboutExprClosure(false),
61 : warnedAboutForEach(false),
62 : warnedAboutStringGenericsMethods(0),
63 : #ifdef DEBUG
64 : firedOnNewGlobalObject(false),
65 : #endif
66 : global_(nullptr),
67 : enterCompartmentDepth(0),
68 : performanceMonitoring(runtime_),
69 : data(nullptr),
70 : allocationMetadataBuilder(nullptr),
71 : lastAnimationTime(0),
72 : regExps(zone),
73 : globalWriteBarriered(0),
74 : detachedTypedObjects(0),
75 315 : objectMetadataState(ImmediateMetadata()),
76 : selfHostingScriptSource(nullptr),
77 : objectMetadataTable(nullptr),
78 : innerViews(zone),
79 : lazyArrayBuffers(nullptr),
80 : wasm(zone),
81 : nonSyntacticLexicalEnvironments_(nullptr),
82 : gcIncomingGrayPointers(nullptr),
83 : debugModeBits(0),
84 : validAccessPtr(nullptr),
85 315 : randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
86 : watchpointMap(nullptr),
87 : scriptCountsMap(nullptr),
88 : scriptNameMap(nullptr),
89 : debugScriptMap(nullptr),
90 : debugEnvs(nullptr),
91 : enumerators(nullptr),
92 : lastCachedNativeIterator(nullptr),
93 : compartmentStats_(nullptr),
94 : scheduledForDestruction(false),
95 : maybeAlive(true),
96 : jitCompartment_(nullptr),
97 : mappedArgumentsTemplate_(nullptr),
98 : unmappedArgumentsTemplate_(nullptr),
99 1890 : lcovOutput()
100 : {
101 315 : PodArrayZero(sawDeprecatedLanguageExtension);
102 315 : runtime_->numCompartments++;
103 315 : MOZ_ASSERT_IF(creationOptions_.mergeable(),
104 : creationOptions_.invisibleToDebugger());
105 315 : }
106 :
107 0 : JSCompartment::~JSCompartment()
108 : {
109 0 : reportTelemetry();
110 :
111 : // Write the code coverage information in a file.
112 0 : JSRuntime* rt = runtimeFromActiveCooperatingThread();
113 0 : if (rt->lcovOutput().isEnabled())
114 0 : rt->lcovOutput().writeLCovResult(lcovOutput);
115 :
116 0 : js_delete(jitCompartment_);
117 0 : js_delete(watchpointMap);
118 0 : js_delete(scriptCountsMap);
119 0 : js_delete(scriptNameMap);
120 0 : js_delete(debugScriptMap);
121 0 : js_delete(debugEnvs);
122 0 : js_delete(objectMetadataTable);
123 0 : js_delete(lazyArrayBuffers);
124 0 : js_delete(nonSyntacticLexicalEnvironments_);
125 0 : js_free(enumerators);
126 :
127 : #ifdef DEBUG
128 : // Avoid assertion destroying the unboxed layouts list if the embedding
129 : // leaked GC things.
130 0 : if (!rt->gc.shutdownCollectedEverything())
131 0 : unboxedLayouts.clear();
132 : #endif
133 :
134 0 : runtime_->numCompartments--;
135 0 : }
136 :
137 : bool
138 315 : JSCompartment::init(JSContext* maybecx)
139 : {
140 : /*
141 : * maybecx is null when called to create the atoms compartment from
142 : * JSRuntime::init().
143 : *
144 : * As a hack, we clear our timezone cache every time we create a new
145 : * compartment. This ensures that the cache is always relatively fresh, but
146 : * shouldn't interfere with benchmarks that create tons of date objects
147 : * (unless they also create tons of iframes, which seems unlikely).
148 : */
149 315 : JS::ResetTimeZone();
150 :
151 315 : if (!crossCompartmentWrappers.init(0)) {
152 0 : if (maybecx)
153 0 : ReportOutOfMemory(maybecx);
154 0 : return false;
155 : }
156 :
157 315 : enumerators = NativeIterator::allocateSentinel(maybecx);
158 315 : if (!enumerators)
159 0 : return false;
160 :
161 315 : if (!savedStacks_.init() || !varNames_.init() || !templateLiteralMap_.init()) {
162 0 : if (maybecx)
163 0 : ReportOutOfMemory(maybecx);
164 0 : return false;
165 : }
166 :
167 315 : return true;
168 : }
169 :
170 : jit::JitRuntime*
171 4 : JSRuntime::createJitRuntime(JSContext* cx)
172 : {
173 : // The shared stubs are created in the atoms compartment, which may be
174 : // accessed by other threads with an exclusive context.
175 8 : AutoLockForExclusiveAccess atomsLock(cx);
176 :
177 4 : MOZ_ASSERT(!jitRuntime_);
178 :
179 4 : if (!CanLikelyAllocateMoreExecutableMemory()) {
180 : // Report OOM instead of potentially hitting the MOZ_CRASH below.
181 0 : ReportOutOfMemory(cx);
182 0 : return nullptr;
183 : }
184 :
185 4 : jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>(cx->runtime());
186 4 : if (!jrt)
187 0 : return nullptr;
188 :
189 : // Protect jitRuntime_ from being observed (by InterruptRunningJitCode)
190 : // while it is being initialized. Unfortunately, initialization depends on
191 : // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_.
192 8 : JitRuntime::AutoPreventBackedgePatching apbp(cx->runtime(), jrt);
193 4 : jitRuntime_ = jrt;
194 :
195 8 : AutoEnterOOMUnsafeRegion noOOM;
196 4 : if (!jitRuntime_->initialize(cx, atomsLock)) {
197 : // Handling OOM here is complicated: if we delete jitRuntime_ now, we
198 : // will destroy the ExecutableAllocator, even though there may still be
199 : // JitCode instances holding references to ExecutablePools.
200 0 : noOOM.crash("OOM in createJitRuntime");
201 : }
202 :
203 4 : return jitRuntime_;
204 : }
205 :
206 : bool
207 14882 : JSCompartment::ensureJitCompartmentExists(JSContext* cx)
208 : {
209 : using namespace js::jit;
210 14882 : if (jitCompartment_)
211 14598 : return true;
212 :
213 284 : if (!zone()->getJitZone(cx))
214 0 : return false;
215 :
216 : /* Set the compartment early, so linking works. */
217 284 : jitCompartment_ = cx->new_<JitCompartment>();
218 :
219 284 : if (!jitCompartment_)
220 0 : return false;
221 :
222 284 : if (!jitCompartment_->initialize(cx)) {
223 0 : js_delete(jitCompartment_);
224 0 : jitCompartment_ = nullptr;
225 0 : return false;
226 : }
227 :
228 284 : return true;
229 : }
230 :
231 : #ifdef JSGC_HASH_TABLE_CHECKS
232 :
233 : void
234 0 : js::DtoaCache::checkCacheAfterMovingGC()
235 : {
236 0 : MOZ_ASSERT(!s || !IsForwarded(s));
237 0 : }
238 :
239 : namespace {
240 : struct CheckGCThingAfterMovingGCFunctor {
241 0 : template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
242 : };
243 : } // namespace (anonymous)
244 :
245 : void
246 0 : JSCompartment::checkWrapperMapAfterMovingGC()
247 : {
248 : /*
249 : * Assert that the postbarriers have worked and that nothing is left in
250 : * wrapperMap that points into the nursery, and that the hash table entries
251 : * are discoverable.
252 : */
253 0 : for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
254 0 : e.front().mutableKey().applyToWrapped(CheckGCThingAfterMovingGCFunctor());
255 0 : e.front().mutableKey().applyToDebugger(CheckGCThingAfterMovingGCFunctor());
256 :
257 0 : WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(e.front().key());
258 0 : MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
259 : }
260 0 : }
261 :
262 : #endif // JSGC_HASH_TABLE_CHECKS
263 :
264 : bool
265 9609 : JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
266 : const js::Value& wrapper)
267 : {
268 9609 : MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
269 9609 : MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
270 :
271 9609 : if (!crossCompartmentWrappers.put(wrapped, wrapper)) {
272 0 : ReportOutOfMemory(cx);
273 0 : return false;
274 : }
275 :
276 9609 : return true;
277 : }
278 :
279 : static JSString*
280 330 : CopyStringPure(JSContext* cx, JSString* str)
281 : {
282 : /*
283 : * Directly allocate the copy in the destination compartment, rather than
284 : * first flattening it (and possibly allocating in source compartment),
285 : * because we don't know whether the flattening will pay off later.
286 : */
287 :
288 330 : size_t len = str->length();
289 : JSString* copy;
290 330 : if (str->isLinear()) {
291 : /* Only use AutoStableStringChars if the NoGC allocation fails. */
292 326 : if (str->hasLatin1Chars()) {
293 488 : JS::AutoCheckCannotGC nogc;
294 244 : copy = NewStringCopyN<NoGC>(cx, str->asLinear().latin1Chars(nogc), len);
295 : } else {
296 164 : JS::AutoCheckCannotGC nogc;
297 82 : copy = NewStringCopyNDontDeflate<NoGC>(cx, str->asLinear().twoByteChars(nogc), len);
298 : }
299 326 : if (copy)
300 326 : return copy;
301 :
302 0 : AutoStableStringChars chars(cx);
303 0 : if (!chars.init(cx, str))
304 0 : return nullptr;
305 :
306 0 : return chars.isLatin1()
307 0 : ? NewStringCopyN<CanGC>(cx, chars.latin1Range().begin().get(), len)
308 0 : : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().begin().get(), len);
309 : }
310 :
311 4 : if (str->hasLatin1Chars()) {
312 8 : ScopedJSFreePtr<Latin1Char> copiedChars;
313 4 : if (!str->asRope().copyLatin1CharsZ(cx, copiedChars))
314 0 : return nullptr;
315 :
316 4 : return NewString<CanGC>(cx, copiedChars.forget(), len);
317 : }
318 :
319 0 : ScopedJSFreePtr<char16_t> copiedChars;
320 0 : if (!str->asRope().copyTwoByteCharsZ(cx, copiedChars))
321 0 : return nullptr;
322 :
323 0 : return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len);
324 : }
325 :
326 : bool
327 5447 : JSCompartment::wrap(JSContext* cx, MutableHandleString strp)
328 : {
329 5447 : MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
330 5447 : MOZ_ASSERT(cx->compartment() == this);
331 :
332 : /* If the string is already in this compartment, we are done. */
333 5447 : JSString* str = strp;
334 5447 : if (str->zoneFromAnyThread() == zone())
335 603 : return true;
336 :
337 : /*
338 : * If the string is an atom, we don't have to copy, but we do need to mark
339 : * the atom as being in use by the new zone.
340 : */
341 4844 : if (str->isAtom()) {
342 4505 : cx->markAtom(&str->asAtom());
343 4505 : return true;
344 : }
345 :
346 : /* Check the cache. */
347 678 : RootedValue key(cx, StringValue(str));
348 339 : if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
349 9 : strp.set(p->value().get().toString());
350 9 : return true;
351 : }
352 :
353 : /* No dice. Make a copy, and cache it. */
354 330 : JSString* copy = CopyStringPure(cx, str);
355 330 : if (!copy)
356 0 : return false;
357 330 : if (!putWrapper(cx, CrossCompartmentKey(key), StringValue(copy)))
358 0 : return false;
359 :
360 330 : strp.set(copy);
361 330 : return true;
362 : }
363 :
364 : bool
365 44301 : JSCompartment::getNonWrapperObjectForCurrentCompartment(JSContext* cx, MutableHandleObject obj)
366 : {
367 : // Ensure that we have entered a compartment.
368 44301 : MOZ_ASSERT(cx->global());
369 :
370 : // If we have a cross-compartment wrapper, make sure that the cx isn't
371 : // associated with the self-hosting global. We don't want to create
372 : // wrappers for objects in other runtimes, which may be the case for the
373 : // self-hosting global.
374 44301 : MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(cx->global()));
375 44301 : MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(&obj->global()));
376 :
377 : // The object is already in the right compartment. Normally same-
378 : // compartment returns the object itself, however, windows are always
379 : // wrapped by a proxy, so we have to check for that case here manually.
380 44301 : if (obj->compartment() == this) {
381 13087 : obj.set(ToWindowProxyIfWindow(obj));
382 13087 : return true;
383 : }
384 :
385 : // Note that if the object is same-compartment, but has been wrapped into a
386 : // different compartment, we need to unwrap it and return the bare same-
387 : // compartment object. Note again that windows are always wrapped by a
388 : // WindowProxy even when same-compartment so take care not to strip this
389 : // particular wrapper.
390 62428 : RootedObject objectPassedToWrap(cx, obj);
391 31214 : obj.set(UncheckedUnwrap(obj, /* stopAtWindowProxy = */ true));
392 31214 : if (obj->compartment() == this) {
393 5131 : MOZ_ASSERT(!IsWindow(obj));
394 5131 : return true;
395 : }
396 :
397 : // Translate StopIteration singleton.
398 26083 : if (obj->is<StopIterationObject>()) {
399 : // StopIteration isn't a constructor, but it's stored in GlobalObject
400 : // as one, out of laziness. Hence the GetBuiltinConstructor call here.
401 10 : RootedObject stopIteration(cx);
402 5 : if (!GetBuiltinConstructor(cx, JSProto_StopIteration, &stopIteration))
403 0 : return false;
404 5 : obj.set(stopIteration);
405 5 : return true;
406 : }
407 :
408 : // Invoke the prewrap callback. The prewrap callback is responsible for
409 : // doing similar reification as above, but can account for any additional
410 : // embedder requirements.
411 : //
412 : // We're a bit worried about infinite recursion here, so we do a check -
413 : // see bug 809295.
414 26078 : auto preWrap = cx->runtime()->wrapObjectCallbacks->preWrap;
415 26078 : if (!CheckSystemRecursionLimit(cx))
416 0 : return false;
417 26078 : if (preWrap) {
418 26078 : preWrap(cx, cx->global(), obj, objectPassedToWrap, obj);
419 26078 : if (!obj)
420 0 : return false;
421 : }
422 26078 : MOZ_ASSERT(!IsWindow(obj));
423 :
424 26078 : return true;
425 : }
426 :
427 : bool
428 23354 : JSCompartment::getOrCreateWrapper(JSContext* cx, HandleObject existing, MutableHandleObject obj)
429 : {
430 : // If we already have a wrapper for this value, use it.
431 46708 : RootedValue key(cx, ObjectValue(*obj));
432 23354 : if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
433 14087 : obj.set(&p->value().get().toObject());
434 14087 : MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>());
435 14087 : return true;
436 : }
437 :
438 : // Create a new wrapper for the object.
439 9267 : auto wrap = cx->runtime()->wrapObjectCallbacks->wrap;
440 18534 : RootedObject wrapper(cx, wrap(cx, existing, obj));
441 9267 : if (!wrapper)
442 0 : return false;
443 :
444 : // We maintain the invariant that the key in the cross-compartment wrapper
445 : // map is always directly wrapped by the value.
446 9267 : MOZ_ASSERT(Wrapper::wrappedObject(wrapper) == &key.get().toObject());
447 :
448 9267 : if (!putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*wrapper))) {
449 : // Enforce the invariant that all cross-compartment wrapper object are
450 : // in the map by nuking the wrapper if we couldn't add it.
451 : // Unfortunately it's possible for the wrapper to still be marked if we
452 : // took this path, for example if the object metadata callback stashes a
453 : // reference to it.
454 0 : if (wrapper->is<CrossCompartmentWrapperObject>())
455 0 : NukeCrossCompartmentWrapper(cx, wrapper);
456 0 : return false;
457 : }
458 :
459 9267 : obj.set(wrapper);
460 9267 : return true;
461 : }
462 :
463 : bool
464 46167 : JSCompartment::wrap(JSContext* cx, MutableHandleObject obj)
465 : {
466 46167 : MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
467 46167 : MOZ_ASSERT(cx->compartment() == this);
468 :
469 46167 : if (!obj)
470 1876 : return true;
471 :
472 88582 : AutoDisableProxyCheck adpc;
473 :
474 : // Anything we're wrapping has already escaped into script, so must have
475 : // been unmarked-gray at some point in the past.
476 44291 : MOZ_ASSERT(JS::ObjectIsNotGray(obj));
477 :
478 : // The passed object may already be wrapped, or may fit a number of special
479 : // cases that we need to check for and manually correct.
480 44291 : if (!getNonWrapperObjectForCurrentCompartment(cx, obj))
481 0 : return false;
482 :
483 : // If the reification above did not result in a same-compartment object,
484 : // get or create a new wrapper object in this compartment for it.
485 44291 : if (obj->compartment() != this) {
486 23344 : if (!getOrCreateWrapper(cx, nullptr, obj))
487 0 : return false;
488 : }
489 :
490 : // Ensure that the wrapper is also exposed.
491 44291 : ExposeObjectToActiveJS(obj);
492 44291 : return true;
493 : }
494 :
495 : bool
496 10 : JSCompartment::rewrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg)
497 : {
498 10 : MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
499 10 : MOZ_ASSERT(cx->compartment() == this);
500 10 : MOZ_ASSERT(obj);
501 10 : MOZ_ASSERT(existingArg);
502 10 : MOZ_ASSERT(existingArg->compartment() == cx->compartment());
503 10 : MOZ_ASSERT(IsDeadProxyObject(existingArg));
504 :
505 20 : AutoDisableProxyCheck adpc;
506 :
507 : // It may not be possible to re-use existing; if so, clear it so that we
508 : // are forced to create a new wrapper. Note that this cannot call out to
509 : // |wrap| because of the different gray unmarking semantics.
510 20 : RootedObject existing(cx, existingArg);
511 30 : if (existing->hasStaticPrototype() ||
512 : // Note: Class asserted above, so all that's left to check is callability
513 20 : existing->isCallable() ||
514 10 : obj->isCallable())
515 : {
516 0 : existing.set(nullptr);
517 : }
518 :
519 : // The passed object may already be wrapped, or may fit a number of special
520 : // cases that we need to check for and manually correct.
521 10 : if (!getNonWrapperObjectForCurrentCompartment(cx, obj))
522 0 : return false;
523 :
524 : // If the reification above resulted in a same-compartment object, we do
525 : // not need to create or return an existing wrapper.
526 10 : if (obj->compartment() == this)
527 0 : return true;
528 :
529 10 : return getOrCreateWrapper(cx, existing, obj);
530 : }
531 :
532 : bool
533 1920 : JSCompartment::wrap(JSContext* cx, MutableHandle<PropertyDescriptor> desc)
534 : {
535 1920 : if (!wrap(cx, desc.object()))
536 0 : return false;
537 :
538 1920 : if (desc.hasGetterObject()) {
539 1301 : if (!wrap(cx, desc.getterObject()))
540 0 : return false;
541 : }
542 1920 : if (desc.hasSetterObject()) {
543 53 : if (!wrap(cx, desc.setterObject()))
544 0 : return false;
545 : }
546 :
547 1920 : return wrap(cx, desc.value());
548 : }
549 :
550 : bool
551 0 : JSCompartment::wrap(JSContext* cx, MutableHandle<GCVector<Value>> vec)
552 : {
553 0 : for (size_t i = 0; i < vec.length(); ++i) {
554 0 : if (!wrap(cx, vec[i]))
555 0 : return false;
556 : }
557 0 : return true;
558 : }
559 :
560 : LexicalEnvironmentObject*
561 188 : JSCompartment::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, HandleObject enclosing)
562 : {
563 188 : if (!nonSyntacticLexicalEnvironments_) {
564 3 : nonSyntacticLexicalEnvironments_ = cx->new_<ObjectWeakMap>(cx);
565 3 : if (!nonSyntacticLexicalEnvironments_ || !nonSyntacticLexicalEnvironments_->init())
566 0 : return nullptr;
567 : }
568 :
569 : // The key is the unwrapped dynamic scope, as we may be creating different
570 : // WithEnvironmentObject wrappers each time.
571 188 : MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
572 376 : RootedObject key(cx, &enclosing->as<WithEnvironmentObject>().object());
573 376 : RootedObject lexicalEnv(cx, nonSyntacticLexicalEnvironments_->lookup(key));
574 :
575 188 : if (!lexicalEnv) {
576 65 : lexicalEnv = LexicalEnvironmentObject::createNonSyntactic(cx, enclosing);
577 65 : if (!lexicalEnv)
578 0 : return nullptr;
579 65 : if (!nonSyntacticLexicalEnvironments_->add(cx, key, lexicalEnv))
580 0 : return nullptr;
581 : }
582 :
583 188 : return &lexicalEnv->as<LexicalEnvironmentObject>();
584 : }
585 :
586 : LexicalEnvironmentObject*
587 145 : JSCompartment::getNonSyntacticLexicalEnvironment(JSObject* enclosing) const
588 : {
589 145 : if (!nonSyntacticLexicalEnvironments_)
590 105 : return nullptr;
591 40 : if (!enclosing->is<WithEnvironmentObject>())
592 0 : return nullptr;
593 40 : JSObject* key = &enclosing->as<WithEnvironmentObject>().object();
594 40 : JSObject* lexicalEnv = nonSyntacticLexicalEnvironments_->lookup(key);
595 40 : if (!lexicalEnv)
596 0 : return nullptr;
597 40 : return &lexicalEnv->as<LexicalEnvironmentObject>();
598 : }
599 :
600 : bool
601 2944 : JSCompartment::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
602 : {
603 2944 : MOZ_ASSERT(name);
604 2944 : MOZ_ASSERT(!isAtomsCompartment());
605 :
606 2944 : if (varNames_.put(name))
607 2944 : return true;
608 :
609 0 : ReportOutOfMemory(cx);
610 0 : return false;
611 : }
612 :
613 : /* static */ HashNumber
614 12 : TemplateRegistryHashPolicy::hash(const Lookup& lookup)
615 : {
616 12 : size_t length = GetAnyBoxedOrUnboxedInitializedLength(lookup);
617 12 : HashNumber hash = 0;
618 48 : for (uint32_t i = 0; i < length; i++) {
619 36 : JSAtom& lookupAtom = GetAnyBoxedOrUnboxedDenseElement(lookup, i).toString()->asAtom();
620 36 : hash = mozilla::AddToHash(hash, lookupAtom.hash());
621 : }
622 12 : return hash;
623 : }
624 :
625 : /* static */ bool
626 10 : TemplateRegistryHashPolicy::match(const Key& key, const Lookup& lookup)
627 : {
628 10 : size_t length = GetAnyBoxedOrUnboxedInitializedLength(lookup);
629 10 : if (GetAnyBoxedOrUnboxedInitializedLength(key) != length)
630 0 : return false;
631 :
632 40 : for (uint32_t i = 0; i < length; i++) {
633 30 : JSAtom* a = &GetAnyBoxedOrUnboxedDenseElement(key, i).toString()->asAtom();
634 30 : JSAtom* b = &GetAnyBoxedOrUnboxedDenseElement(lookup, i).toString()->asAtom();
635 30 : if (a != b)
636 0 : return false;
637 : }
638 :
639 10 : return true;
640 : }
641 :
642 : bool
643 11 : JSCompartment::getTemplateLiteralObject(JSContext* cx, HandleObject rawStrings,
644 : MutableHandleObject templateObj)
645 : {
646 11 : if (TemplateRegistry::AddPtr p = templateLiteralMap_.lookupForAdd(rawStrings)) {
647 10 : templateObj.set(p->value());
648 :
649 : // The template object must have been frozen when it was added to the
650 : // registry.
651 10 : MOZ_ASSERT(!templateObj->nonProxyIsExtensible());
652 : } else {
653 1 : MOZ_ASSERT(templateObj->nonProxyIsExtensible());
654 2 : RootedValue rawValue(cx, ObjectValue(*rawStrings));
655 1 : if (!DefineProperty(cx, templateObj, cx->names().raw, rawValue, nullptr, nullptr, 0))
656 0 : return false;
657 1 : if (!FreezeObject(cx, rawStrings))
658 0 : return false;
659 1 : if (!FreezeObject(cx, templateObj))
660 0 : return false;
661 :
662 1 : if (!templateLiteralMap_.relookupOrAdd(p, rawStrings, templateObj))
663 0 : return false;
664 : }
665 :
666 11 : return true;
667 : }
668 :
669 : JSObject*
670 0 : JSCompartment::getExistingTemplateLiteralObject(JSObject* rawStrings)
671 : {
672 0 : TemplateRegistry::Ptr p = templateLiteralMap_.lookup(rawStrings);
673 0 : MOZ_ASSERT(p);
674 0 : return p->value();
675 : }
676 :
677 : void
678 0 : JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
679 : {
680 0 : MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
681 0 : MOZ_ASSERT(!zone()->isCollectingFromAnyThread() || trc->runtime()->gc.isHeapCompacting());
682 :
683 0 : for (NonStringWrapperEnum e(this); !e.empty(); e.popFront()) {
684 0 : if (e.front().key().is<JSObject*>()) {
685 0 : Value v = e.front().value().unbarrieredGet();
686 0 : ProxyObject* wrapper = &v.toObject().as<ProxyObject>();
687 :
688 : /*
689 : * We have a cross-compartment wrapper. Its private pointer may
690 : * point into the compartment being collected, so we should mark it.
691 : */
692 0 : TraceEdge(trc, wrapper->slotOfPrivate(), "cross-compartment wrapper");
693 : }
694 : }
695 0 : }
696 :
697 : /* static */ void
698 1 : JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc)
699 : {
700 2 : gcstats::AutoPhase ap(trc->runtime()->gc.stats(), gcstats::PhaseKind::MARK_CCWS);
701 1 : MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
702 223 : for (CompartmentsIter c(trc->runtime(), SkipAtoms); !c.done(); c.next()) {
703 222 : if (!c->zone()->isCollecting())
704 0 : c->traceOutgoingCrossCompartmentWrappers(trc);
705 : }
706 1 : Debugger::traceIncomingCrossCompartmentEdges(trc);
707 1 : }
708 :
709 : void
710 26 : JSCompartment::traceGlobal(JSTracer* trc)
711 : {
712 : // Trace things reachable from the compartment's global. Note that these
713 : // edges must be swept too in case the compartment is live but the global is
714 : // not.
715 :
716 26 : savedStacks_.trace(trc);
717 :
718 : // The template registry strongly holds everything in it by design and
719 : // spec.
720 26 : templateLiteralMap_.trace(trc);
721 :
722 : // Atoms are always tenured.
723 26 : if (!JS::CurrentThreadIsHeapMinorCollecting())
724 26 : varNames_.trace(trc);
725 26 : }
726 :
727 : void
728 2637 : JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
729 : {
730 2637 : if (objectMetadataState.is<PendingMetadata>()) {
731 : TraceRoot(trc,
732 0 : &objectMetadataState.as<PendingMetadata>(),
733 0 : "on-stack object pending metadata");
734 : }
735 :
736 2637 : if (!JS::CurrentThreadIsHeapMinorCollecting()) {
737 : // The global is never nursery allocated, so we don't need to
738 : // trace it when doing a minor collection.
739 : //
740 : // If a compartment is on-stack, we mark its global so that
741 : // JSContext::global() remains valid.
742 222 : if (enterCompartmentDepth && global_.unbarrieredGet())
743 0 : TraceRoot(trc, global_.unsafeUnbarrieredForTracing(), "on-stack compartment global");
744 : }
745 :
746 : // Nothing below here needs to be treated as a root if we aren't marking
747 : // this zone for a collection.
748 2637 : if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollectingFromAnyThread())
749 0 : return;
750 :
751 : // During a GC, these are treated as weak pointers.
752 2637 : if (traceOrMark == js::gc::GCRuntime::TraceRuntime) {
753 2415 : if (watchpointMap)
754 0 : watchpointMap->trace(trc);
755 : }
756 :
757 : /* Mark debug scopes, if present */
758 2637 : if (debugEnvs)
759 0 : debugEnvs->trace(trc);
760 :
761 2637 : if (lazyArrayBuffers)
762 0 : lazyArrayBuffers->trace(trc);
763 :
764 2637 : if (objectMetadataTable)
765 0 : objectMetadataTable->trace(trc);
766 :
767 : // If code coverage is only enabled with the Debugger or the LCovOutput,
768 : // then the following comment holds.
769 : //
770 : // The scriptCountsMap maps JSScript weak-pointers to ScriptCounts
771 : // structures. It uses a HashMap instead of a WeakMap, so that we can keep
772 : // the data alive for the JSScript::finalize call. Thus, we do not trace the
773 : // keys of the HashMap to avoid adding a strong reference to the JSScript
774 : // pointers.
775 : //
776 : // If the code coverage is either enabled with the --dump-bytecode command
777 : // line option, or with the PCCount JSFriend API functions, then we mark the
778 : // keys of the map to hold the JSScript alive.
779 5890 : if (scriptCountsMap &&
780 2637 : trc->runtime()->profilingScripts &&
781 0 : !JS::CurrentThreadIsHeapMinorCollecting())
782 : {
783 0 : MOZ_ASSERT_IF(!trc->runtime()->isBeingDestroyed(), collectCoverage());
784 0 : for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
785 0 : JSScript* script = const_cast<JSScript*>(r.front().key());
786 0 : MOZ_ASSERT(script->hasScriptCounts());
787 0 : TraceRoot(trc, &script, "profilingScripts");
788 0 : MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
789 : }
790 : }
791 :
792 2637 : if (nonSyntacticLexicalEnvironments_)
793 15 : nonSyntacticLexicalEnvironments_->trace(trc);
794 : }
795 :
796 : void
797 0 : JSCompartment::finishRoots()
798 : {
799 0 : if (watchpointMap)
800 0 : watchpointMap->clear();
801 :
802 0 : if (debugEnvs)
803 0 : debugEnvs->finish();
804 :
805 0 : if (lazyArrayBuffers)
806 0 : lazyArrayBuffers->clear();
807 :
808 0 : if (objectMetadataTable)
809 0 : objectMetadataTable->clear();
810 :
811 0 : clearScriptCounts();
812 0 : clearScriptNames();
813 :
814 0 : if (nonSyntacticLexicalEnvironments_)
815 0 : nonSyntacticLexicalEnvironments_->clear();
816 0 : }
817 :
818 : void
819 2415 : JSCompartment::sweepAfterMinorGC(JSTracer* trc)
820 : {
821 2415 : globalWriteBarriered = 0;
822 :
823 2415 : InnerViewTable& table = innerViews.get();
824 2415 : if (table.needsSweepAfterMinorGC())
825 0 : table.sweepAfterMinorGC();
826 :
827 2415 : crossCompartmentWrappers.sweepAfterMinorGC(trc);
828 2415 : }
829 :
830 : void
831 0 : JSCompartment::sweepSavedStacks()
832 : {
833 0 : savedStacks_.sweep();
834 0 : }
835 :
836 : void
837 0 : JSCompartment::sweepTemplateLiteralMap()
838 : {
839 0 : templateLiteralMap_.sweep();
840 0 : }
841 :
842 : void
843 0 : JSCompartment::sweepGlobalObject()
844 : {
845 0 : if (global_ && IsAboutToBeFinalized(&global_))
846 0 : global_.set(nullptr);
847 0 : }
848 :
849 : void
850 0 : JSCompartment::sweepSelfHostingScriptSource()
851 : {
852 0 : if (selfHostingScriptSource.unbarrieredGet() &&
853 0 : IsAboutToBeFinalized(&selfHostingScriptSource))
854 : {
855 0 : selfHostingScriptSource.set(nullptr);
856 : }
857 0 : }
858 :
859 : void
860 0 : JSCompartment::sweepJitCompartment(FreeOp* fop)
861 : {
862 0 : if (jitCompartment_)
863 0 : jitCompartment_->sweep(fop, this);
864 0 : }
865 :
866 : void
867 0 : JSCompartment::sweepRegExps()
868 : {
869 : /*
870 : * JIT code increments activeWarmUpCounter for any RegExpShared used by jit
871 : * code for the lifetime of the JIT script. Thus, we must perform
872 : * sweeping after clearing jit code.
873 : */
874 0 : regExps.sweep(runtimeFromAnyThread());
875 0 : }
876 :
877 : void
878 0 : JSCompartment::sweepDebugEnvironments()
879 : {
880 0 : JSRuntime* rt = runtimeFromAnyThread();
881 0 : if (debugEnvs)
882 0 : debugEnvs->sweep(rt);
883 0 : }
884 :
885 : void
886 0 : JSCompartment::sweepNativeIterators()
887 : {
888 : /* Sweep list of native iterators. */
889 0 : NativeIterator* ni = enumerators->next();
890 0 : while (ni != enumerators) {
891 0 : JSObject* iterObj = ni->iterObj();
892 0 : NativeIterator* next = ni->next();
893 0 : if (gc::IsAboutToBeFinalizedUnbarriered(&iterObj))
894 0 : ni->unlink();
895 0 : ni = next;
896 : }
897 0 : }
898 :
899 : /*
900 : * Remove dead wrappers from the table. We must sweep all compartments, since
901 : * string entries in the crossCompartmentWrappers table are not marked during
902 : * markCrossCompartmentWrappers.
903 : */
904 : void
905 0 : JSCompartment::sweepCrossCompartmentWrappers()
906 : {
907 0 : crossCompartmentWrappers.sweep();
908 0 : }
909 :
910 : void
911 0 : JSCompartment::sweepVarNames()
912 : {
913 0 : varNames_.sweep();
914 0 : }
915 :
916 : void
917 0 : JSCompartment::sweepWatchpoints()
918 : {
919 0 : if (watchpointMap)
920 0 : watchpointMap->sweep();
921 0 : }
922 :
923 : namespace {
924 : struct TraceRootFunctor {
925 : JSTracer* trc;
926 : const char* name;
927 0 : TraceRootFunctor(JSTracer* trc, const char* name) : trc(trc), name(name) {}
928 0 : template <class T> void operator()(T* t) { return TraceRoot(trc, t, name); }
929 : };
930 : struct NeedsSweepUnbarrieredFunctor {
931 2101 : template <class T> bool operator()(T* t) const { return IsAboutToBeFinalizedUnbarriered(t); }
932 : };
933 : } // namespace (anonymous)
934 :
935 : void
936 0 : CrossCompartmentKey::trace(JSTracer* trc)
937 : {
938 0 : applyToWrapped(TraceRootFunctor(trc, "CrossCompartmentKey::wrapped"));
939 0 : applyToDebugger(TraceRootFunctor(trc, "CrossCompartmentKey::debugger"));
940 0 : }
941 :
942 : bool
943 2101 : CrossCompartmentKey::needsSweep()
944 : {
945 8404 : return applyToWrapped(NeedsSweepUnbarrieredFunctor()) ||
946 6303 : applyToDebugger(NeedsSweepUnbarrieredFunctor());
947 : }
948 :
949 : void
950 0 : JSCompartment::sweepTemplateObjects()
951 : {
952 0 : if (mappedArgumentsTemplate_ && IsAboutToBeFinalized(&mappedArgumentsTemplate_))
953 0 : mappedArgumentsTemplate_.set(nullptr);
954 :
955 0 : if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
956 0 : unmappedArgumentsTemplate_.set(nullptr);
957 0 : }
958 :
959 : /* static */ void
960 0 : JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
961 : {
962 0 : MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());
963 :
964 0 : for (CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
965 : // Sweep the wrapper map to update keys (wrapped values) in other
966 : // compartments that may have been moved.
967 0 : comp->sweepCrossCompartmentWrappers();
968 : // Trace the wrappers in the map to update their cross-compartment edges
969 : // to wrapped values in other compartments that may have been moved.
970 0 : comp->traceOutgoingCrossCompartmentWrappers(trc);
971 : }
972 0 : }
973 :
974 : void
975 0 : JSCompartment::fixupAfterMovingGC()
976 : {
977 0 : MOZ_ASSERT(zone()->isGCCompacting());
978 :
979 0 : purge();
980 0 : fixupGlobal();
981 0 : objectGroups.fixupTablesAfterMovingGC();
982 0 : fixupScriptMapsAfterMovingGC();
983 :
984 : // Sweep the wrapper map to update values (wrapper objects) in this
985 : // compartment that may have been moved.
986 0 : sweepCrossCompartmentWrappers();
987 0 : }
988 :
989 : void
990 0 : JSCompartment::fixupGlobal()
991 : {
992 0 : GlobalObject* global = *global_.unsafeGet();
993 0 : if (global)
994 0 : global_.set(MaybeForwarded(global));
995 0 : }
996 :
997 : void
998 0 : JSCompartment::fixupScriptMapsAfterMovingGC()
999 : {
1000 : // Map entries are removed by JSScript::finalize, but we need to update the
1001 : // script pointers here in case they are moved by the GC.
1002 :
1003 0 : if (scriptCountsMap) {
1004 0 : for (ScriptCountsMap::Enum e(*scriptCountsMap); !e.empty(); e.popFront()) {
1005 0 : JSScript* script = e.front().key();
1006 0 : if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
1007 0 : e.rekeyFront(script);
1008 : }
1009 : }
1010 :
1011 0 : if (scriptNameMap) {
1012 0 : for (ScriptNameMap::Enum e(*scriptNameMap); !e.empty(); e.popFront()) {
1013 0 : JSScript* script = e.front().key();
1014 0 : if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
1015 0 : e.rekeyFront(script);
1016 : }
1017 : }
1018 :
1019 0 : if (debugScriptMap) {
1020 0 : for (DebugScriptMap::Enum e(*debugScriptMap); !e.empty(); e.popFront()) {
1021 0 : JSScript* script = e.front().key();
1022 0 : if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
1023 0 : e.rekeyFront(script);
1024 : }
1025 : }
1026 0 : }
1027 :
1028 : #ifdef JSGC_HASH_TABLE_CHECKS
1029 : void
1030 0 : JSCompartment::checkScriptMapsAfterMovingGC()
1031 : {
1032 0 : if (scriptCountsMap) {
1033 0 : for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
1034 0 : JSScript* script = r.front().key();
1035 0 : CheckGCThingAfterMovingGC(script);
1036 0 : auto ptr = scriptCountsMap->lookup(script);
1037 0 : MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
1038 : }
1039 : }
1040 :
1041 0 : if (scriptNameMap) {
1042 0 : for (auto r = scriptNameMap->all(); !r.empty(); r.popFront()) {
1043 0 : JSScript* script = r.front().key();
1044 0 : CheckGCThingAfterMovingGC(script);
1045 0 : auto ptr = scriptNameMap->lookup(script);
1046 0 : MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
1047 : }
1048 : }
1049 :
1050 0 : if (debugScriptMap) {
1051 0 : for (auto r = debugScriptMap->all(); !r.empty(); r.popFront()) {
1052 0 : JSScript* script = r.front().key();
1053 0 : CheckGCThingAfterMovingGC(script);
1054 0 : DebugScript* ds = r.front().value();
1055 0 : for (uint32_t i = 0; i < ds->numSites; i++) {
1056 0 : BreakpointSite* site = ds->breakpoints[i];
1057 0 : if (site && site->type() == BreakpointSite::Type::JS)
1058 0 : CheckGCThingAfterMovingGC(site->asJS()->script);
1059 : }
1060 0 : auto ptr = debugScriptMap->lookup(script);
1061 0 : MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
1062 : }
1063 : }
1064 0 : }
1065 : #endif
1066 :
1067 : void
1068 223 : JSCompartment::purge()
1069 : {
1070 223 : dtoaCache.purge();
1071 223 : newProxyCache.purge();
1072 223 : lastCachedNativeIterator = nullptr;
1073 223 : objectGroups.purge();
1074 223 : }
1075 :
1076 : void
1077 15 : JSCompartment::clearTables()
1078 : {
1079 15 : global_.set(nullptr);
1080 :
1081 : // No scripts should have run in this compartment. This is used when
1082 : // merging a compartment that has been used off thread into another
1083 : // compartment and zone.
1084 15 : MOZ_ASSERT(crossCompartmentWrappers.empty());
1085 15 : MOZ_ASSERT(!jitCompartment_);
1086 15 : MOZ_ASSERT(!debugEnvs);
1087 15 : MOZ_ASSERT(enumerators->next() == enumerators);
1088 15 : MOZ_ASSERT(templateLiteralMap_.empty());
1089 :
1090 15 : objectGroups.clearTables();
1091 15 : if (savedStacks_.initialized())
1092 15 : savedStacks_.clear();
1093 15 : if (varNames_.initialized())
1094 15 : varNames_.clear();
1095 15 : }
1096 :
1097 : void
1098 0 : JSCompartment::setAllocationMetadataBuilder(const js::AllocationMetadataBuilder *builder)
1099 : {
1100 : // Clear any jitcode in the runtime, which behaves differently depending on
1101 : // whether there is a creation callback.
1102 0 : ReleaseAllJITCode(runtime_->defaultFreeOp());
1103 :
1104 0 : allocationMetadataBuilder = builder;
1105 0 : }
1106 :
1107 : void
1108 0 : JSCompartment::clearObjectMetadata()
1109 : {
1110 0 : js_delete(objectMetadataTable);
1111 0 : objectMetadataTable = nullptr;
1112 0 : }
1113 :
1114 : void
1115 0 : JSCompartment::setNewObjectMetadata(JSContext* cx, HandleObject obj)
1116 : {
1117 0 : assertSameCompartment(cx, this, obj);
1118 :
1119 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
1120 0 : if (JSObject* metadata = allocationMetadataBuilder->build(cx, obj, oomUnsafe)) {
1121 0 : assertSameCompartment(cx, metadata);
1122 0 : if (!objectMetadataTable) {
1123 0 : objectMetadataTable = cx->new_<ObjectWeakMap>(cx);
1124 0 : if (!objectMetadataTable || !objectMetadataTable->init())
1125 0 : oomUnsafe.crash("setNewObjectMetadata");
1126 : }
1127 0 : if (!objectMetadataTable->add(cx, obj, metadata))
1128 0 : oomUnsafe.crash("setNewObjectMetadata");
1129 : }
1130 0 : }
1131 :
1132 : static bool
1133 0 : AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions)
1134 : {
1135 0 : if (!script->hasObjects())
1136 0 : return true;
1137 0 : ObjectArray* objects = script->objects();
1138 0 : for (size_t i = 0; i < objects->length; i++) {
1139 0 : JSObject* obj = objects->vector[i];
1140 0 : if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
1141 0 : if (!lazyFunctions.append(obj))
1142 0 : return false;
1143 : }
1144 : }
1145 0 : return true;
1146 : }
1147 :
1148 : static bool
1149 0 : AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
1150 : {
1151 : // Find all live root lazy functions in the compartment: those which have a
1152 : // source object, indicating that they have a parent, and which do not have
1153 : // an uncompiled enclosing script. The last condition is so that we don't
1154 : // compile lazy scripts whose enclosing scripts failed to compile,
1155 : // indicating that the lazy script did not escape the script.
1156 : //
1157 : // Some LazyScripts have a non-null |JSScript* script| pointer. We still
1158 : // want to delazify in that case: this pointer is weak so the JSScript
1159 : // could be destroyed at the next GC.
1160 :
1161 0 : for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
1162 0 : JSFunction* fun = &i->as<JSFunction>();
1163 :
1164 : // Sweeping is incremental; take care to not delazify functions that
1165 : // are about to be finalized. GC things referenced by objects that are
1166 : // about to be finalized (e.g., in slots) may already be freed.
1167 0 : if (gc::IsAboutToBeFinalizedUnbarriered(&fun) ||
1168 0 : fun->compartment() != cx->compartment())
1169 : {
1170 0 : continue;
1171 : }
1172 :
1173 0 : if (fun->isInterpretedLazy()) {
1174 0 : LazyScript* lazy = fun->lazyScriptOrNull();
1175 0 : if (lazy && lazy->sourceObject() && !lazy->hasUncompiledEnclosingScript()) {
1176 0 : if (!lazyFunctions.append(fun))
1177 0 : return false;
1178 : }
1179 : }
1180 : }
1181 :
1182 0 : return true;
1183 : }
1184 :
1185 : static bool
1186 0 : CreateLazyScriptsForCompartment(JSContext* cx)
1187 : {
1188 0 : AutoObjectVector lazyFunctions(cx);
1189 :
1190 0 : if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION))
1191 0 : return false;
1192 :
1193 : // Methods, for instance {get method() {}}, are extended functions that can
1194 : // be relazified, so we need to handle those as well.
1195 0 : if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION_EXTENDED))
1196 0 : return false;
1197 :
1198 : // Create scripts for each lazy function, updating the list of functions to
1199 : // process with any newly exposed inner functions in created scripts.
1200 : // A function cannot be delazified until its outer script exists.
1201 0 : RootedFunction fun(cx);
1202 0 : for (size_t i = 0; i < lazyFunctions.length(); i++) {
1203 0 : fun = &lazyFunctions[i]->as<JSFunction>();
1204 :
1205 : // lazyFunctions may have been populated with multiple functions for
1206 : // a lazy script.
1207 0 : if (!fun->isInterpretedLazy())
1208 0 : continue;
1209 :
1210 0 : bool lazyScriptHadNoScript = !fun->lazyScript()->maybeScript();
1211 :
1212 0 : JSScript* script = JSFunction::getOrCreateScript(cx, fun);
1213 0 : if (!script)
1214 0 : return false;
1215 0 : if (lazyScriptHadNoScript && !AddInnerLazyFunctionsFromScript(script, lazyFunctions))
1216 0 : return false;
1217 : }
1218 :
1219 0 : return true;
1220 : }
1221 :
1222 : bool
1223 0 : JSCompartment::ensureDelazifyScriptsForDebugger(JSContext* cx)
1224 : {
1225 0 : AutoCompartmentUnchecked ac(cx, this);
1226 0 : if (needsDelazificationForDebugger() && !CreateLazyScriptsForCompartment(cx))
1227 0 : return false;
1228 0 : debugModeBits &= ~DebuggerNeedsDelazification;
1229 0 : return true;
1230 : }
1231 :
1232 : void
1233 0 : JSCompartment::updateDebuggerObservesFlag(unsigned flag)
1234 : {
1235 0 : MOZ_ASSERT(isDebuggee());
1236 0 : MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
1237 : flag == DebuggerObservesCoverage ||
1238 : flag == DebuggerObservesAsmJS ||
1239 : flag == DebuggerObservesBinarySource);
1240 :
1241 0 : GlobalObject* global = zone()->runtimeFromActiveCooperatingThread()->gc.isForegroundSweeping()
1242 0 : ? unsafeUnbarrieredMaybeGlobal()
1243 0 : : maybeGlobal();
1244 0 : const GlobalObject::DebuggerVector* v = global->getDebuggers();
1245 0 : for (auto p = v->begin(); p != v->end(); p++) {
1246 0 : Debugger* dbg = *p;
1247 0 : if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
1248 0 : flag == DebuggerObservesCoverage ? dbg->observesCoverage() :
1249 0 : flag == DebuggerObservesAsmJS ? dbg->observesAsmJS() :
1250 0 : dbg->observesBinarySource())
1251 : {
1252 0 : debugModeBits |= flag;
1253 0 : return;
1254 : }
1255 : }
1256 :
1257 0 : debugModeBits &= ~flag;
1258 : }
1259 :
1260 : void
1261 15 : JSCompartment::unsetIsDebuggee()
1262 : {
1263 15 : if (isDebuggee()) {
1264 0 : debugModeBits &= ~DebuggerObservesMask;
1265 0 : DebugEnvironments::onCompartmentUnsetIsDebuggee(this);
1266 : }
1267 15 : }
1268 :
1269 : void
1270 0 : JSCompartment::updateDebuggerObservesCoverage()
1271 : {
1272 0 : bool previousState = debuggerObservesCoverage();
1273 0 : updateDebuggerObservesFlag(DebuggerObservesCoverage);
1274 0 : if (previousState == debuggerObservesCoverage())
1275 0 : return;
1276 :
1277 0 : if (debuggerObservesCoverage()) {
1278 : // Interrupt any running interpreter frame. The scriptCounts are
1279 : // allocated on demand when a script resumes its execution.
1280 0 : JSContext* cx = TlsContext.get();
1281 0 : for (ActivationIterator iter(cx); !iter.done(); ++iter) {
1282 0 : if (iter->isInterpreter())
1283 0 : iter->asInterpreter()->enableInterruptsUnconditionally();
1284 : }
1285 0 : return;
1286 : }
1287 :
1288 : // If code coverage is enabled by any other means, keep it.
1289 0 : if (collectCoverage())
1290 0 : return;
1291 :
1292 0 : clearScriptCounts();
1293 0 : clearScriptNames();
1294 : }
1295 :
1296 : bool
1297 613 : JSCompartment::collectCoverage() const
1298 : {
1299 613 : return collectCoverageForPGO() ||
1300 613 : collectCoverageForDebug();
1301 : }
1302 :
1303 : bool
1304 613 : JSCompartment::collectCoverageForPGO() const
1305 : {
1306 613 : return !JitOptions.disablePgo;
1307 : }
1308 :
1309 : bool
1310 11796 : JSCompartment::collectCoverageForDebug() const
1311 : {
1312 23592 : return debuggerObservesCoverage() ||
1313 23592 : runtimeFromAnyThread()->profilingScripts ||
1314 23592 : runtimeFromAnyThread()->lcovOutput().isEnabled();
1315 : }
1316 :
1317 : void
1318 0 : JSCompartment::clearScriptCounts()
1319 : {
1320 0 : if (!scriptCountsMap)
1321 0 : return;
1322 :
1323 : // Clear all hasScriptCounts_ flags of JSScript, in order to release all
1324 : // ScriptCounts entry of the current compartment.
1325 0 : for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
1326 0 : ScriptCounts* value = r.front().value();
1327 0 : r.front().key()->takeOverScriptCountsMapEntry(value);
1328 0 : js_delete(value);
1329 : }
1330 :
1331 0 : js_delete(scriptCountsMap);
1332 0 : scriptCountsMap = nullptr;
1333 : }
1334 :
1335 : void
1336 0 : JSCompartment::clearScriptNames()
1337 : {
1338 0 : if (!scriptNameMap)
1339 0 : return;
1340 :
1341 0 : for (ScriptNameMap::Range r = scriptNameMap->all(); !r.empty(); r.popFront())
1342 0 : js_delete(r.front().value());
1343 :
1344 0 : js_delete(scriptNameMap);
1345 0 : scriptNameMap = nullptr;
1346 : }
1347 :
1348 : void
1349 0 : JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
1350 : {
1351 0 : for (auto script = zone()->cellIter<JSScript>(); !script.done(); script.next()) {
1352 0 : if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
1353 0 : script->clearBreakpointsIn(fop, dbg, handler);
1354 : }
1355 0 : }
1356 :
1357 : void
1358 0 : JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
1359 : size_t* tiAllocationSiteTables,
1360 : size_t* tiArrayTypeTables,
1361 : size_t* tiObjectTypeTables,
1362 : size_t* compartmentObject,
1363 : size_t* compartmentTables,
1364 : size_t* innerViewsArg,
1365 : size_t* lazyArrayBuffersArg,
1366 : size_t* objectMetadataTablesArg,
1367 : size_t* crossCompartmentWrappersArg,
1368 : size_t* savedStacksSet,
1369 : size_t* varNamesSet,
1370 : size_t* nonSyntacticLexicalEnvironmentsArg,
1371 : size_t* templateLiteralMap,
1372 : size_t* jitCompartment,
1373 : size_t* privateData)
1374 : {
1375 0 : *compartmentObject += mallocSizeOf(this);
1376 0 : objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
1377 : tiArrayTypeTables, tiObjectTypeTables,
1378 0 : compartmentTables);
1379 0 : wasm.addSizeOfExcludingThis(mallocSizeOf, compartmentTables);
1380 0 : *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
1381 0 : if (lazyArrayBuffers)
1382 0 : *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf);
1383 0 : if (objectMetadataTable)
1384 0 : *objectMetadataTablesArg += objectMetadataTable->sizeOfIncludingThis(mallocSizeOf);
1385 0 : *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
1386 0 : *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
1387 0 : *varNamesSet += varNames_.sizeOfExcludingThis(mallocSizeOf);
1388 0 : if (nonSyntacticLexicalEnvironments_)
1389 0 : *nonSyntacticLexicalEnvironmentsArg +=
1390 0 : nonSyntacticLexicalEnvironments_->sizeOfIncludingThis(mallocSizeOf);
1391 0 : *templateLiteralMap += templateLiteralMap_.sizeOfExcludingThis(mallocSizeOf);
1392 0 : if (jitCompartment_)
1393 0 : *jitCompartment += jitCompartment_->sizeOfIncludingThis(mallocSizeOf);
1394 :
1395 0 : auto callback = runtime_->sizeOfIncludingThisCompartmentCallback;
1396 0 : if (callback)
1397 0 : *privateData += callback(mallocSizeOf, this);
1398 0 : }
1399 :
1400 : void
1401 0 : JSCompartment::reportTelemetry()
1402 : {
1403 : // Only report telemetry for web content and add-ons, not chrome JS.
1404 0 : if (isSystem_)
1405 0 : return;
1406 :
1407 : // Hazard analysis can't tell that the telemetry callbacks don't GC.
1408 0 : JS::AutoSuppressGCAnalysis nogc;
1409 :
1410 0 : int id = creationOptions_.addonIdOrNull()
1411 0 : ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS
1412 0 : : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
1413 :
1414 : // Call back into Firefox's Telemetry reporter.
1415 0 : for (size_t i = 0; i < size_t(DeprecatedLanguageExtension::Count); i++) {
1416 0 : if (sawDeprecatedLanguageExtension[i])
1417 0 : runtime_->addTelemetry(id, i);
1418 : }
1419 : }
1420 :
1421 : void
1422 0 : JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
1423 : {
1424 : // Only report telemetry for web content and add-ons, not chrome JS.
1425 0 : if (isSystem_)
1426 0 : return;
1427 0 : if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0))
1428 0 : return;
1429 :
1430 0 : sawDeprecatedLanguageExtension[size_t(e)] = true;
1431 : }
1432 :
1433 : HashNumber
1434 56 : JSCompartment::randomHashCode()
1435 : {
1436 56 : ensureRandomNumberGenerator();
1437 56 : return HashNumber(randomNumberGenerator.ref().next());
1438 : }
1439 :
1440 : mozilla::HashCodeScrambler
1441 408 : JSCompartment::randomHashCodeScrambler()
1442 : {
1443 816 : return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
1444 816 : randomKeyGenerator_.next());
1445 : }
1446 :
1447 16469 : AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx
1448 16469 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
1449 : : CustomAutoRooter(cx)
1450 16469 : , cx_(cx->helperThread() ? nullptr : cx)
1451 32938 : , prevState_(cx->compartment()->objectMetadataState)
1452 : {
1453 16469 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1454 16469 : if (cx_)
1455 16168 : cx_->compartment()->objectMetadataState = NewObjectMetadataState(DelayMetadata());
1456 16469 : }
1457 :
1458 32938 : AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata()
1459 : {
1460 : // If we don't have a cx, we didn't change the metadata state, so no need to
1461 : // reset it here.
1462 16469 : if (!cx_)
1463 301 : return;
1464 :
1465 16168 : if (!cx_->isExceptionPending() && cx_->compartment()->hasObjectPendingMetadata()) {
1466 : // This destructor often runs upon exit from a function that is
1467 : // returning an unrooted pointer to a Cell. The allocation metadata
1468 : // callback often allocates; if it causes a GC, then the Cell pointer
1469 : // being returned won't be traced or relocated.
1470 : //
1471 : // The only extant callbacks are those internal to SpiderMonkey that
1472 : // capture the JS stack. In fact, we're considering removing general
1473 : // callbacks altogther in bug 1236748. Since it's not running arbitrary
1474 : // code, it's adequate to simply suppress GC while we run the callback.
1475 32336 : AutoSuppressGC autoSuppressGC(cx_);
1476 :
1477 16168 : JSObject* obj = cx_->compartment()->objectMetadataState.as<PendingMetadata>();
1478 :
1479 : // Make sure to restore the previous state before setting the object's
1480 : // metadata. SetNewObjectMetadata asserts that the state is not
1481 : // PendingMetadata in order to ensure that metadata callbacks are called
1482 : // in order.
1483 16168 : cx_->compartment()->objectMetadataState = prevState_;
1484 :
1485 16168 : obj = SetNewObjectMetadata(cx_, obj);
1486 : } else {
1487 0 : cx_->compartment()->objectMetadataState = prevState_;
1488 : }
1489 16469 : }
|