From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- js/src/jscompartment.h | 1110 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1110 insertions(+) create mode 100644 js/src/jscompartment.h (limited to 'js/src/jscompartment.h') diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h new file mode 100644 index 000000000..7bfeee1f6 --- /dev/null +++ b/js/src/jscompartment.h @@ -0,0 +1,1110 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jscompartment_h +#define jscompartment_h + +#include "mozilla/LinkedList.h" +#include "mozilla/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Tuple.h" +#include "mozilla/Variant.h" +#include "mozilla/XorShift128PlusRNG.h" + +#include "builtin/RegExp.h" +#include "gc/Barrier.h" +#include "gc/NurseryAwareHashMap.h" +#include "gc/Zone.h" +#include "vm/GlobalObject.h" +#include "vm/PIC.h" +#include "vm/SavedStacks.h" +#include "vm/Time.h" +#include "wasm/WasmCompartment.h" + +namespace js { + +namespace jit { +class JitCompartment; +} // namespace jit + +namespace gc { +template class ComponentFinder; +} // namespace gc + +class LexicalEnvironmentObject; +class ScriptSourceObject; +struct NativeIterator; + +/* + * A single-entry cache for some base-10 double-to-string conversions. This + * helps date-format-xparb.js. It also avoids skewing the results for + * v8-splay.js when measured by the SunSpider harness, where the splay tree + * initialization (which includes many repeated double-to-string conversions) + * is erroneously included in the measurement; see bug 562553. + */ +class DtoaCache { + double d; + int base; + JSFlatString* s; // if s==nullptr, d and base are not valid + + public: + DtoaCache() : s(nullptr) {} + void purge() { s = nullptr; } + + JSFlatString* lookup(int base, double d) { + return this->s && base == this->base && d == this->d ? this->s : nullptr; + } + + void cache(int base, double d, JSFlatString* s) { + this->base = base; + this->d = d; + this->s = s; + } + +#ifdef JSGC_HASH_TABLE_CHECKS + void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); } +#endif +}; + +class CrossCompartmentKey +{ + public: + enum DebuggerObjectKind : uint8_t { DebuggerSource, DebuggerEnvironment, DebuggerObject, + DebuggerWasmScript, DebuggerWasmSource }; + using DebuggerAndObject = mozilla::Tuple; + using DebuggerAndScript = mozilla::Tuple; + using WrappedType = mozilla::Variant< + JSObject*, + JSString*, + DebuggerAndScript, + DebuggerAndObject>; + + explicit CrossCompartmentKey(JSObject* obj) : wrapped(obj) { MOZ_RELEASE_ASSERT(obj); } + explicit CrossCompartmentKey(JSString* str) : wrapped(str) { MOZ_RELEASE_ASSERT(str); } + explicit CrossCompartmentKey(const JS::Value& v) + : wrapped(v.isString() ? WrappedType(v.toString()) : WrappedType(&v.toObject())) + {} + explicit CrossCompartmentKey(NativeObject* debugger, JSObject* obj, DebuggerObjectKind kind) + : wrapped(DebuggerAndObject(debugger, obj, kind)) + { + MOZ_RELEASE_ASSERT(debugger); + MOZ_RELEASE_ASSERT(obj); + } + explicit CrossCompartmentKey(NativeObject* debugger, JSScript* script) + : wrapped(DebuggerAndScript(debugger, script)) + { + MOZ_RELEASE_ASSERT(debugger); + MOZ_RELEASE_ASSERT(script); + } + + bool operator==(const CrossCompartmentKey& other) const { return wrapped == other.wrapped; } + bool operator!=(const CrossCompartmentKey& other) const { return wrapped != other.wrapped; } + + template bool is() const { return wrapped.is(); } + template const T& as() const { return wrapped.as(); } + + template + auto applyToWrapped(F f) -> decltype(f(static_cast(nullptr))) { + using ReturnType = decltype(f(static_cast(nullptr))); + struct WrappedMatcher { + F f_; + explicit WrappedMatcher(F f) : f_(f) {} + ReturnType match(JSObject*& obj) { return f_(&obj); } + ReturnType match(JSString*& str) { return f_(&str); } + ReturnType match(DebuggerAndScript& tpl) { return f_(&mozilla::Get<1>(tpl)); } + ReturnType match(DebuggerAndObject& tpl) { return f_(&mozilla::Get<1>(tpl)); } + } matcher(f); + return wrapped.match(matcher); + } + + template + auto applyToDebugger(F f) -> decltype(f(static_cast(nullptr))) { + using ReturnType = decltype(f(static_cast(nullptr))); + struct DebuggerMatcher { + F f_; + explicit DebuggerMatcher(F f) : f_(f) {} + ReturnType match(JSObject*& obj) { return ReturnType(); } + ReturnType match(JSString*& str) { return ReturnType(); } + ReturnType match(DebuggerAndScript& tpl) { return f_(&mozilla::Get<0>(tpl)); } + ReturnType match(DebuggerAndObject& tpl) { return f_(&mozilla::Get<0>(tpl)); } + } matcher(f); + return wrapped.match(matcher); + } + + // Valid for JSObject* and Debugger keys. Crashes immediately if used on a + // JSString* key. + JSCompartment* compartment() { + struct GetCompartmentFunctor { + JSCompartment* operator()(JSObject** tp) const { return (*tp)->compartment(); } + JSCompartment* operator()(JSScript** tp) const { return (*tp)->compartment(); } + JSCompartment* operator()(JSString** tp) const { + MOZ_CRASH("invalid ccw key"); return nullptr; + } + }; + return applyToWrapped(GetCompartmentFunctor()); + } + + struct Hasher : public DefaultHasher + { + struct HashFunctor { + HashNumber match(JSObject* obj) { return DefaultHasher::hash(obj); } + HashNumber match(JSString* str) { return DefaultHasher::hash(str); } + HashNumber match(const DebuggerAndScript& tpl) { + return DefaultHasher::hash(mozilla::Get<0>(tpl)) ^ + DefaultHasher::hash(mozilla::Get<1>(tpl)); + } + HashNumber match(const DebuggerAndObject& tpl) { + return DefaultHasher::hash(mozilla::Get<0>(tpl)) ^ + DefaultHasher::hash(mozilla::Get<1>(tpl)) ^ + (mozilla::Get<2>(tpl) << 5); + } + }; + static HashNumber hash(const CrossCompartmentKey& key) { + return key.wrapped.match(HashFunctor()); + } + + static bool match(const CrossCompartmentKey& l, const CrossCompartmentKey& k) { + return l.wrapped == k.wrapped; + } + }; + + bool isTenured() const { + struct IsTenuredFunctor { + using ReturnType = bool; + ReturnType operator()(JSObject** tp) { return !IsInsideNursery(*tp); } + ReturnType operator()(JSScript** tp) { return true; } + ReturnType operator()(JSString** tp) { return true; } + }; + return const_cast(this)->applyToWrapped(IsTenuredFunctor()); + } + + void trace(JSTracer* trc); + bool needsSweep(); + + private: + CrossCompartmentKey() = delete; + WrappedType wrapped; +}; + + +using WrapperMap = NurseryAwareHashMap; + +// We must ensure that all newly allocated JSObjects get their metadata +// set. However, metadata builders may require the new object be in a sane +// state (eg, have its reserved slots initialized so they can get the +// sizeOfExcludingThis of the object). Therefore, for objects of certain +// JSClasses (those marked with JSCLASS_DELAY_METADATA_BUILDER), it is not safe +// for the allocation paths to call the object metadata builder +// immediately. Instead, the JSClass-specific "constructor" C++ function up the +// stack makes a promise that it will ensure that the new object has its +// metadata set after the object is initialized. +// +// To help those constructor functions keep their promise of setting metadata, +// each compartment is in one of three states at any given time: +// +// * ImmediateMetadata: Allocators should set new object metadata immediately, +// as usual. +// +// * DelayMetadata: Allocators should *not* set new object metadata, it will be +// handled after reserved slots are initialized by custom code +// for the object's JSClass. The newly allocated object's +// JSClass *must* have the JSCLASS_DELAY_METADATA_BUILDER flag +// set. +// +// * PendingMetadata: This object has been allocated and is still pending its +// metadata. This should never be the case when we begin an +// allocation, as a constructor function was supposed to have +// set the metadata of the previous object *before* +// allocating another object. +// +// The js::AutoSetNewObjectMetadata RAII class provides an ergonomic way for +// constructor functions to navigate state transitions, and its instances +// collectively maintain a stack of previous states. The stack is required to +// support the lazy resolution and allocation of global builtin constructors and +// prototype objects. The initial (and intuitively most common) state is +// ImmediateMetadata. +// +// Without the presence of internal errors (such as OOM), transitions between +// the states are as follows: +// +// ImmediateMetadata .----- previous state on stack +// | | ^ +// | via constructor | | +// | | | via setting the new +// | via constructor | | object's metadata +// | .-----------------------' | +// | | | +// V V | +// DelayMetadata -------------------------> PendingMetadata +// via allocation +// +// In the presence of internal errors, we do not set the new object's metadata +// (if it was even allocated) and reset to the previous state on the stack. + +struct ImmediateMetadata { }; +struct DelayMetadata { }; +using PendingMetadata = JSObject*; + +using NewObjectMetadataState = mozilla::Variant; + +class MOZ_RAII AutoSetNewObjectMetadata : private JS::CustomAutoRooter +{ + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; + + JSContext* cx_; + NewObjectMetadataState prevState_; + + AutoSetNewObjectMetadata(const AutoSetNewObjectMetadata& aOther) = delete; + void operator=(const AutoSetNewObjectMetadata& aOther) = delete; + + protected: + virtual void trace(JSTracer* trc) override { + if (prevState_.is()) { + TraceRoot(trc, + &prevState_.as(), + "Object pending metadata"); + } + } + + public: + explicit AutoSetNewObjectMetadata(ExclusiveContext* ecx MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoSetNewObjectMetadata(); +}; + +} /* namespace js */ + +namespace js { +class DebugEnvironments; +class ObjectWeakMap; +class WatchpointMap; +class WeakMapBase; +} // namespace js + +struct JSCompartment +{ + const JS::CompartmentCreationOptions creationOptions_; + JS::CompartmentBehaviors behaviors_; + + private: + JS::Zone* zone_; + JSRuntime* runtime_; + + public: + /* + * The principals associated with this compartment. Note that the + * same several compartments may share the same principals and + * that a compartment may change principals during its lifetime + * (e.g. in case of lazy parsing). + */ + inline JSPrincipals* principals() { + return principals_; + } + inline void setPrincipals(JSPrincipals* principals) { + if (principals_ == principals) + return; + + // If we change principals, we need to unlink immediately this + // compartment from its PerformanceGroup. For one thing, the + // performance data we collect should not be improperly associated + // with a group to which we do not belong anymore. For another thing, + // we use `principals()` as part of the key to map compartments + // to a `PerformanceGroup`, so if we do not unlink now, this will + // be too late once we have updated `principals_`. + performanceMonitoring.unlink(); + principals_ = principals; + } + inline bool isSystem() const { + return isSystem_; + } + inline void setIsSystem(bool isSystem) { + if (isSystem_ == isSystem) + return; + + // If we change `isSystem*(`, we need to unlink immediately this + // compartment from its PerformanceGroup. For one thing, the + // performance data we collect should not be improperly associated + // to a group to which we do not belong anymore. For another thing, + // we use `isSystem()` as part of the key to map compartments + // to a `PerformanceGroup`, so if we do not unlink now, this will + // be too late once we have updated `isSystem_`. + performanceMonitoring.unlink(); + isSystem_ = isSystem; + } + + bool isAtomsCompartment() const { + return isAtomsCompartment_; + } + void setIsAtomsCompartment() { + isAtomsCompartment_ = true; + } + + // Used to approximate non-content code when reporting telemetry. + inline bool isProbablySystemOrAddonCode() const { + if (creationOptions_.addonIdOrNull()) + return true; + + return isSystem_; + } + private: + JSPrincipals* principals_; + bool isSystem_; + bool isAtomsCompartment_; + + public: + bool isSelfHosting; + bool marked; + bool warnedAboutExprClosure; + bool warnedAboutForEach; + +#ifdef DEBUG + bool firedOnNewGlobalObject; +#endif + + void mark() { marked = true; } + + private: + friend struct JSRuntime; + friend struct JSContext; + friend class js::ExclusiveContext; + js::ReadBarrieredGlobalObject global_; + + unsigned enterCompartmentDepth; + int64_t startInterval; + + public: + js::PerformanceGroupHolder performanceMonitoring; + + void enter() { + enterCompartmentDepth++; + } + void leave() { + enterCompartmentDepth--; + } + bool hasBeenEntered() { return !!enterCompartmentDepth; } + + JS::Zone* zone() { return zone_; } + const JS::Zone* zone() const { return zone_; } + + const JS::CompartmentCreationOptions& creationOptions() const { return creationOptions_; } + JS::CompartmentBehaviors& behaviors() { return behaviors_; } + const JS::CompartmentBehaviors& behaviors() const { return behaviors_; } + + JSRuntime* runtimeFromMainThread() const { + MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); + return runtime_; + } + + // Note: Unrestricted access to the zone's runtime from an arbitrary + // thread can easily lead to races. Use this method very carefully. + JSRuntime* runtimeFromAnyThread() const { + return runtime_; + } + + JSContext* contextFromMainThread() const { + return runtime_->contextFromMainThread(); + } + + /* + * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or + * (b) the compartment's global has been collected. The latter can happen + * if e.g. a string in a compartment is rooted but no object is, and thus + * the global isn't rooted, and thus the global can be finalized while the + * compartment lives on. + * + * In contrast, JSObject::global() is infallible because marking a JSObject + * always marks its global as well. + * TODO: add infallible JSScript::global() + */ + inline js::GlobalObject* maybeGlobal() const; + + /* An unbarriered getter for use while tracing. */ + inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const; + + inline void initGlobal(js::GlobalObject& global); + + public: + void* data; + + private: + const js::AllocationMetadataBuilder *allocationMetadataBuilder; + + js::SavedStacks savedStacks_; + + js::WrapperMap crossCompartmentWrappers; + + using CCKeyVector = mozilla::Vector; + CCKeyVector nurseryCCKeys; + + // The global environment record's [[VarNames]] list that contains all + // names declared using FunctionDeclaration, GeneratorDeclaration, and + // VariableDeclaration declarations in global code in this compartment. + // Names are only removed from this list by a |delete IdentifierReference| + // that successfully removes that global property. + JS::GCHashSet, + js::SystemAllocPolicy> varNames_; + + public: + /* Last time at which an animation was played for a global in this compartment. */ + int64_t lastAnimationTime; + + js::RegExpCompartment regExps; + + /* + * For generational GC, record whether a write barrier has added this + * compartment's global to the store buffer since the last minor GC. + * + * This is used to avoid calling into the VM every time a nursery object is + * written to a property of the global. + */ + uint32_t globalWriteBarriered; + + // Non-zero if the storage underlying any typed object in this compartment + // might be detached. + int32_t detachedTypedObjects; + + private: + friend class js::AutoSetNewObjectMetadata; + js::NewObjectMetadataState objectMetadataState; + + public: + // Recompute the probability with which this compartment should record + // profiling data (stack traces, allocations log, etc.) about each + // allocation. We consult the probabilities requested by the Debugger + // instances observing us, if any. + void chooseAllocationSamplingProbability() { savedStacks_.chooseSamplingProbability(this); } + + bool hasObjectPendingMetadata() const { return objectMetadataState.is(); } + + void setObjectPendingMetadata(JSContext* cx, JSObject* obj) { + MOZ_ASSERT(objectMetadataState.is()); + objectMetadataState = js::NewObjectMetadataState(js::PendingMetadata(obj)); + } + + void setObjectPendingMetadata(js::ExclusiveContext* ecx, JSObject* obj) { + if (JSContext* cx = ecx->maybeJSContext()) + setObjectPendingMetadata(cx, obj); + } + + public: + void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t* tiAllocationSiteTables, + size_t* tiArrayTypeTables, + size_t* tiObjectTypeTables, + size_t* compartmentObject, + size_t* compartmentTables, + size_t* innerViews, + size_t* lazyArrayBuffers, + size_t* objectMetadataTables, + size_t* crossCompartmentWrappers, + size_t* regexpCompartment, + size_t* savedStacksSet, + size_t* varNamesSet, + size_t* nonSyntacticLexicalScopes, + size_t* jitCompartment, + size_t* privateData); + + // Object group tables and other state in the compartment. + js::ObjectGroupCompartment objectGroups; + +#ifdef JSGC_HASH_TABLE_CHECKS + void checkWrapperMapAfterMovingGC(); + void checkScriptMapsAfterMovingGC(); +#endif + + /* + * Lazily initialized script source object to use for scripts cloned + * from the self-hosting global. + */ + js::ReadBarrieredScriptSourceObject selfHostingScriptSource; + + // Keep track of the metadata objects which can be associated with each JS + // object. Both keys and values are in this compartment. + js::ObjectWeakMap* objectMetadataTable; + + // Map from array buffers to views sharing that storage. + JS::WeakCache innerViews; + + // Inline transparent typed objects do not initially have an array buffer, + // but can have that buffer created lazily if it is accessed later. This + // table manages references from such typed objects to their buffers. + js::ObjectWeakMap* lazyArrayBuffers; + + // All unboxed layouts in the compartment. + mozilla::LinkedList unboxedLayouts; + + // WebAssembly state for the compartment. + js::wasm::Compartment wasm; + + private: + // All non-syntactic lexical environments in the compartment. These are kept in + // a map because when loading scripts into a non-syntactic environment, we need + // to use the same lexical environment to persist lexical bindings. + js::ObjectWeakMap* nonSyntacticLexicalEnvironments_; + + public: + /* During GC, stores the index of this compartment in rt->compartments. */ + unsigned gcIndex; + + /* + * During GC, stores the head of a list of incoming pointers from gray cells. + * + * The objects in the list are either cross-compartment wrappers, or + * debugger wrapper objects. The list link is either in the second extra + * slot for the former, or a special slot for the latter. + */ + JSObject* gcIncomingGrayPointers; + + private: + enum { + IsDebuggee = 1 << 0, + DebuggerObservesAllExecution = 1 << 1, + DebuggerObservesAsmJS = 1 << 2, + DebuggerObservesCoverage = 1 << 3, + DebuggerNeedsDelazification = 1 << 4 + }; + + unsigned debugModeBits; + friend class AutoRestoreCompartmentDebugMode; + + static const unsigned DebuggerObservesMask = IsDebuggee | + DebuggerObservesAllExecution | + DebuggerObservesCoverage | + DebuggerObservesAsmJS; + + void updateDebuggerObservesFlag(unsigned flag); + + bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj); + bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj); + + public: + JSCompartment(JS::Zone* zone, const JS::CompartmentOptions& options); + ~JSCompartment(); + + MOZ_MUST_USE bool init(JSContext* maybecx); + + MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp); + + MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandleString strp); + MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandleObject obj); + MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandle desc); + MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandle> vec); + MOZ_MUST_USE bool rewrap(JSContext* cx, JS::MutableHandleObject obj, JS::HandleObject existing); + + MOZ_MUST_USE bool putWrapper(JSContext* cx, const js::CrossCompartmentKey& wrapped, + const js::Value& wrapper); + + js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) const { + return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(wrapped)); + } + + void removeWrapper(js::WrapperMap::Ptr p) { + crossCompartmentWrappers.remove(p); + } + + struct WrapperEnum : public js::WrapperMap::Enum { + explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {} + }; + + js::LexicalEnvironmentObject* + getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, js::HandleObject enclosing); + js::LexicalEnvironmentObject* getNonSyntacticLexicalEnvironment(JSObject* enclosing) const; + + /* + * This method traces data that is live iff we know that this compartment's + * global is still live. + */ + void trace(JSTracer* trc); + /* + * This method traces JSCompartment-owned GC roots that are considered live + * regardless of whether the JSCompartment itself is still live. + */ + void traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark); + /* + * This method clears out tables of roots in preparation for the final GC. + */ + void finishRoots(); + /* + * These methods mark pointers that cross compartment boundaries. They are + * called in per-zone GCs to prevent the wrappers' outgoing edges from + * dangling (full GCs naturally follow pointers across compartments) and + * when compacting to update cross-compartment pointers. + */ + void traceOutgoingCrossCompartmentWrappers(JSTracer* trc); + static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc); + + /* Whether to preserve JIT code on non-shrinking GCs. */ + bool preserveJitCode() { return creationOptions_.preserveJitCode(); } + + void sweepAfterMinorGC(JSTracer* trc); + + void sweepCrossCompartmentWrappers(); + void sweepSavedStacks(); + void sweepGlobalObject(js::FreeOp* fop); + void sweepSelfHostingScriptSource(); + void sweepJitCompartment(js::FreeOp* fop); + void sweepRegExps(); + void sweepDebugEnvironments(); + void sweepNativeIterators(); + void sweepTemplateObjects(); + void sweepVarNames(); + + void purge(); + void clearTables(); + + static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc); + void fixupAfterMovingGC(); + void fixupGlobal(); + void fixupScriptMapsAfterMovingGC(); + + bool hasAllocationMetadataBuilder() const { return allocationMetadataBuilder; } + const js::AllocationMetadataBuilder* getAllocationMetadataBuilder() const { + return allocationMetadataBuilder; + } + void setAllocationMetadataBuilder(const js::AllocationMetadataBuilder* builder); + void forgetAllocationMetadataBuilder() { + allocationMetadataBuilder = nullptr; + } + void setNewObjectMetadata(JSContext* cx, JS::HandleObject obj); + void clearObjectMetadata(); + const void* addressOfMetadataBuilder() const { + return &allocationMetadataBuilder; + } + + js::SavedStacks& savedStacks() { return savedStacks_; } + + // Add a name to [[VarNames]]. Reports OOM on failure. + MOZ_MUST_USE bool addToVarNames(JSContext* cx, JS::Handle name); + + void removeFromVarNames(JS::Handle name) { + varNames_.remove(name); + } + + // Whether the given name is in [[VarNames]]. + bool isInVarNames(JS::Handle name) { + return varNames_.has(name); + } + + void findOutgoingEdges(js::gc::ZoneComponentFinder& finder); + + js::DtoaCache dtoaCache; + + // Random number generator for Math.random(). + mozilla::Maybe randomNumberGenerator; + + // Initialize randomNumberGenerator if needed. + void ensureRandomNumberGenerator(); + + private: + mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_; + + public: + js::HashNumber randomHashCode(); + + mozilla::HashCodeScrambler randomHashCodeScrambler(); + + static size_t offsetOfRegExps() { + return offsetof(JSCompartment, regExps); + } + + private: + JSCompartment* thisForCtor() { return this; } + + public: + // + // The Debugger observes execution on a frame-by-frame basis. The + // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee, + // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are + // enumerated below. + // + // 1. When a compartment's isDebuggee() == true, relazification and lazy + // parsing are disabled. + // + // Whether AOT wasm is disabled is togglable by the Debugger API. By + // default it is disabled. See debuggerObservesAsmJS below. + // + // 2. When a compartment's debuggerObservesAllExecution() == true, all of + // the compartment's scripts are considered debuggee scripts. + // + // 3. A script is considered a debuggee script either when, per above, its + // compartment is observing all execution, or if it has breakpoints set. + // + // 4. A debuggee script always pushes a debuggee frame. + // + // 5. A debuggee frame calls all slow path Debugger hooks in the + // Interpreter and Baseline. A debuggee frame implies that its script's + // BaselineScript, if extant, has been compiled with debug hook calls. + // + // 6. A debuggee script or a debuggee frame (i.e., during OSR) ensures + // that the compiled BaselineScript is compiled with debug hook calls + // when attempting to enter Baseline. + // + // 7. A debuggee script or a debuggee frame (i.e., during OSR) does not + // attempt to enter Ion. + // + // Note that a debuggee frame may exist without its script being a + // debuggee script. e.g., Debugger.Frame.prototype.eval only marks the + // frame in which it is evaluating as a debuggee frame. + // + + // True if this compartment's global is a debuggee of some Debugger + // object. + bool isDebuggee() const { return !!(debugModeBits & IsDebuggee); } + void setIsDebuggee() { debugModeBits |= IsDebuggee; } + void unsetIsDebuggee(); + + // True if this compartment's global is a debuggee of some Debugger + // object with a live hook that observes all execution; e.g., + // onEnterFrame. + bool debuggerObservesAllExecution() const { + static const unsigned Mask = IsDebuggee | DebuggerObservesAllExecution; + return (debugModeBits & Mask) == Mask; + } + void updateDebuggerObservesAllExecution() { + updateDebuggerObservesFlag(DebuggerObservesAllExecution); + } + + // True if this compartment's global is a debuggee of some Debugger object + // whose allowUnobservedAsmJS flag is false. + // + // Note that since AOT wasm functions cannot bail out, this flag really + // means "observe wasm from this point forward". We cannot make + // already-compiled wasm code observable to Debugger. + bool debuggerObservesAsmJS() const { + static const unsigned Mask = IsDebuggee | DebuggerObservesAsmJS; + return (debugModeBits & Mask) == Mask; + } + void updateDebuggerObservesAsmJS() { + updateDebuggerObservesFlag(DebuggerObservesAsmJS); + } + + // True if this compartment's global is a debuggee of some Debugger object + // whose collectCoverageInfo flag is true. + bool debuggerObservesCoverage() const { + static const unsigned Mask = DebuggerObservesCoverage; + return (debugModeBits & Mask) == Mask; + } + void updateDebuggerObservesCoverage(); + + // The code coverage can be enabled either for each compartment, with the + // Debugger API, or for the entire runtime. + bool collectCoverage() const; + bool collectCoverageForDebug() const; + bool collectCoverageForPGO() const; + void clearScriptCounts(); + + bool needsDelazificationForDebugger() const { + return debugModeBits & DebuggerNeedsDelazification; + } + + /* + * Schedule the compartment to be delazified. Called from + * LazyScript::Create. + */ + void scheduleDelazificationForDebugger() { debugModeBits |= DebuggerNeedsDelazification; } + + /* + * If we scheduled delazification for turning on debug mode, delazify all + * scripts. + */ + bool ensureDelazifyScriptsForDebugger(JSContext* cx); + + void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JS::HandleObject handler); + + private: + void sweepBreakpoints(js::FreeOp* fop); + + public: + js::WatchpointMap* watchpointMap; + + js::ScriptCountsMap* scriptCountsMap; + + js::DebugScriptMap* debugScriptMap; + + /* Bookkeeping information for debug scope objects. */ + js::DebugEnvironments* debugEnvs; + + /* + * List of potentially active iterators that may need deleted property + * suppression. + */ + js::NativeIterator* enumerators; + + /* Native iterator most recently started. */ + js::PropertyIteratorObject* lastCachedNativeIterator; + + private: + /* Used by memory reporters and invalid otherwise. */ + JS::CompartmentStats* compartmentStats_; + + public: + // This should only be called when it is non-null, i.e. during memory + // reporting. + JS::CompartmentStats& compartmentStats() { + // We use MOZ_RELEASE_ASSERT here because in bug 1132502 there was some + // (inconclusive) evidence that compartmentStats_ can be nullptr + // unexpectedly. + MOZ_RELEASE_ASSERT(compartmentStats_); + return *compartmentStats_; + } + void nullCompartmentStats() { + MOZ_ASSERT(compartmentStats_); + compartmentStats_ = nullptr; + } + void setCompartmentStats(JS::CompartmentStats* newStats) { + MOZ_ASSERT(!compartmentStats_ && newStats); + compartmentStats_ = newStats; + } + + // These flags help us to discover if a compartment that shouldn't be alive + // manages to outlive a GC. + bool scheduledForDestruction; + bool maybeAlive; + + private: + js::jit::JitCompartment* jitCompartment_; + + js::ReadBarriered mappedArgumentsTemplate_; + js::ReadBarriered unmappedArgumentsTemplate_; + + public: + bool ensureJitCompartmentExists(JSContext* cx); + js::jit::JitCompartment* jitCompartment() { + return jitCompartment_; + } + + enum DeprecatedLanguageExtension { + DeprecatedForEach = 0, // JS 1.6+ + // NO LONGER USING 1 + DeprecatedLegacyGenerator = 2, // JS 1.7+ + DeprecatedExpressionClosure = 3, // Added in JS 1.8 + // NO LONGER USING 4 + // NO LONGER USING 5 + // NO LONGER USING 6 + // NO LONGER USING 7 + // NO LONGER USING 8 + // NO LONGER USING 9 + DeprecatedBlockScopeFunRedecl = 10, + DeprecatedLanguageExtensionCount + }; + + js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped); + + js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const; + + private: + // Used for collecting telemetry on SpiderMonkey's deprecated language extensions. + bool sawDeprecatedLanguageExtension[DeprecatedLanguageExtensionCount]; + + void reportTelemetry(); + + public: + void addTelemetry(const char* filename, DeprecatedLanguageExtension e); + + public: + // Aggregated output used to collect JSScript hit counts when code coverage + // is enabled. + js::coverage::LCovCompartment lcovOutput; +}; + +inline bool +JSRuntime::isAtomsZone(const JS::Zone* zone) const +{ + return zone == atomsCompartment_->zone(); +} + +namespace js { + +// We only set the maybeAlive flag for objects and scripts. It's assumed that, +// if a compartment is alive, then it will have at least some live object or +// script it in. Even if we get this wrong, the worst that will happen is that +// scheduledForDestruction will be set on the compartment, which will cause +// some extra GC activity to try to free the compartment. +template inline void SetMaybeAliveFlag(T* thing) {} +template<> inline void SetMaybeAliveFlag(JSObject* thing) {thing->compartment()->maybeAlive = true;} +template<> inline void SetMaybeAliveFlag(JSScript* thing) {thing->compartment()->maybeAlive = true;} + +inline js::Handle +ExclusiveContext::global() const +{ + /* + * It's safe to use |unsafeGet()| here because any compartment that is + * on-stack will be marked automatically, so there's no need for a read + * barrier on it. Once the compartment is popped, the handle is no longer + * safe to use. + */ + MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first"); + return Handle::fromMarkedLocation(compartment_->global_.unsafeGet()); +} + +class MOZ_RAII AssertCompartmentUnchanged +{ + public: + explicit AssertCompartmentUnchanged(JSContext* cx + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : cx(cx), oldCompartment(cx->compartment()) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + ~AssertCompartmentUnchanged() { + MOZ_ASSERT(cx->compartment() == oldCompartment); + } + + protected: + JSContext * const cx; + JSCompartment * const oldCompartment; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class AutoCompartment +{ + ExclusiveContext * const cx_; + JSCompartment * const origin_; + const js::AutoLockForExclusiveAccess* maybeLock_; + + public: + inline AutoCompartment(ExclusiveContext* cx, JSObject* target, + js::AutoLockForExclusiveAccess* maybeLock = nullptr); + inline AutoCompartment(ExclusiveContext* cx, JSCompartment* target, + js::AutoLockForExclusiveAccess* maybeLock = nullptr); + inline ~AutoCompartment(); + + ExclusiveContext* context() const { return cx_; } + JSCompartment* origin() const { return origin_; } + + private: + AutoCompartment(const AutoCompartment&) = delete; + AutoCompartment & operator=(const AutoCompartment&) = delete; +}; + +/* + * Use this to change the behavior of an AutoCompartment slightly on error. If + * the exception happens to be an Error object, copy it to the origin compartment + * instead of wrapping it. + */ +class ErrorCopier +{ + mozilla::Maybe& ac; + + public: + explicit ErrorCopier(mozilla::Maybe& ac) + : ac(ac) {} + ~ErrorCopier(); +}; + +/* + * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that + * are obtained from the cross-compartment map. However, these classes should + * not be used if the wrapper will escape. For example, it should not be stored + * in the heap. + * + * The AutoWrapper rooters are different from other autorooters because their + * wrappers are marked on every GC slice rather than just the first one. If + * there's some wrapper that we want to use temporarily without causing it to be + * marked, we can use these AutoWrapper classes. If we get unlucky and a GC + * slice runs during the code using the wrapper, the GC will mark the wrapper so + * that it doesn't get swept out from under us. Otherwise, the wrapper needn't + * be marked. This is useful in functions like JS_TransplantObject that + * manipulate wrappers in compartments that may no longer be alive. + */ + +/* + * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It + * should not be used in any other situations. + */ +struct WrapperValue +{ + /* + * We use unsafeGet() in the constructors to avoid invoking a read barrier + * on the wrapper, which may be dead (see the comment about bug 803376 in + * jsgc.cpp regarding this). If there is an incremental GC while the wrapper + * is in use, the AutoWrapper rooter will ensure the wrapper gets marked. + */ + explicit WrapperValue(const WrapperMap::Ptr& ptr) + : value(*ptr->value().unsafeGet()) + {} + + explicit WrapperValue(const WrapperMap::Enum& e) + : value(*e.front().value().unsafeGet()) + {} + + Value& get() { return value; } + Value get() const { return value; } + operator const Value&() const { return value; } + JSObject& toObject() const { return value.toObject(); } + + private: + Value value; +}; + +class MOZ_RAII AutoWrapperVector : public JS::AutoVectorRooterBase +{ + public: + explicit AutoWrapperVector(JSContext* cx + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoVectorRooterBase(cx, WRAPVECTOR) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class MOZ_RAII AutoWrapperRooter : private JS::AutoGCRooter { + public: + AutoWrapperRooter(JSContext* cx, const WrapperValue& v + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : JS::AutoGCRooter(cx, WRAPPER), value(v) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + operator JSObject*() const { + return value.get().toObjectOrNull(); + } + + friend void JS::AutoGCRooter::trace(JSTracer* trc); + + private: + WrapperValue value; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class MOZ_RAII AutoSuppressAllocationMetadataBuilder { + JS::Zone* zone; + bool saved; + + public: + explicit AutoSuppressAllocationMetadataBuilder(ExclusiveContext* cx) + : AutoSuppressAllocationMetadataBuilder(cx->compartment()->zone()) + { } + + explicit AutoSuppressAllocationMetadataBuilder(JS::Zone* zone) + : zone(zone), + saved(zone->suppressAllocationMetadataBuilder) + { + zone->suppressAllocationMetadataBuilder = true; + } + + ~AutoSuppressAllocationMetadataBuilder() { + zone->suppressAllocationMetadataBuilder = saved; + } +}; + +} /* namespace js */ + +namespace JS { +template <> +struct GCPolicy : public StructGCPolicy { + static bool isTenured(const js::CrossCompartmentKey& key) { return key.isTenured(); } +}; +} // namespace JS + +#endif /* jscompartment_h */ -- cgit v1.2.3