summaryrefslogtreecommitdiffstats
path: root/js/src/jsscript.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/jsscript.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/jsscript.h')
-rw-r--r--js/src/jsscript.h2276
1 files changed, 2276 insertions, 0 deletions
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
new file mode 100644
index 000000000..bc8bda83d
--- /dev/null
+++ b/js/src/jsscript.h
@@ -0,0 +1,2276 @@
+/* -*- 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/. */
+
+/* JS script descriptor. */
+
+#ifndef jsscript_h
+#define jsscript_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Variant.h"
+
+#include "jsatom.h"
+#include "jsopcode.h"
+#include "jstypes.h"
+
+#include "frontend/NameAnalysisTypes.h"
+#include "gc/Barrier.h"
+#include "gc/Rooting.h"
+#include "jit/IonCode.h"
+#include "js/UbiNode.h"
+#include "js/UniquePtr.h"
+#include "vm/NativeObject.h"
+#include "vm/Scope.h"
+#include "vm/Shape.h"
+#include "vm/SharedImmutableStringsCache.h"
+
+namespace JS {
+struct ScriptSourceInfo;
+} // namespace JS
+
+namespace js {
+
+namespace jit {
+ struct BaselineScript;
+ struct IonScriptCounts;
+} // namespace jit
+
+# define ION_DISABLED_SCRIPT ((js::jit::IonScript*)0x1)
+# define ION_COMPILING_SCRIPT ((js::jit::IonScript*)0x2)
+# define ION_PENDING_SCRIPT ((js::jit::IonScript*)0x3)
+
+# define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript*)0x1)
+
+class BreakpointSite;
+class Debugger;
+class LazyScript;
+class ModuleObject;
+class RegExpObject;
+struct SourceCompressionTask;
+class Shape;
+
+namespace frontend {
+ struct BytecodeEmitter;
+ class FunctionBox;
+ class ModuleSharedContext;
+} // namespace frontend
+
+namespace detail {
+
+// Do not call this directly! It is exposed for the friend declarations in
+// this file.
+bool
+CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
+ MutableHandle<GCVector<Scope*>> scopes);
+
+} // namespace detail
+
+} // namespace js
+
+/*
+ * Type of try note associated with each catch or finally block, and also with
+ * for-in and other kinds of loops. Non-for-in loops do not need these notes
+ * for exception unwinding, but storing their boundaries here is helpful for
+ * heuristics that need to know whether a given op is inside a loop.
+ */
+enum JSTryNoteKind {
+ JSTRY_CATCH,
+ JSTRY_FINALLY,
+ JSTRY_FOR_IN,
+ JSTRY_FOR_OF,
+ JSTRY_LOOP
+};
+
+/*
+ * Exception handling record.
+ */
+struct JSTryNote {
+ uint8_t kind; /* one of JSTryNoteKind */
+ uint32_t stackDepth; /* stack depth upon exception handler entry */
+ uint32_t start; /* start of the try statement or loop
+ relative to script->main */
+ uint32_t length; /* length of the try statement or loop */
+};
+
+namespace js {
+
+// A block scope has a range in bytecode: it is entered at some offset, and left
+// at some later offset. Scopes can be nested. Given an offset, the
+// ScopeNote containing that offset whose with the highest start value
+// indicates the block scope. The block scope list is sorted by increasing
+// start value.
+//
+// It is possible to leave a scope nonlocally, for example via a "break"
+// statement, so there may be short bytecode ranges in a block scope in which we
+// are popping the block chain in preparation for a goto. These exits are also
+// nested with respect to outer scopes. The scopes in these exits are indicated
+// by the "index" field, just like any other block. If a nonlocal exit pops the
+// last block scope, the index will be NoScopeIndex.
+//
+struct ScopeNote {
+ // Sentinel index for no Scope.
+ static const uint32_t NoScopeIndex = UINT32_MAX;
+
+ // Sentinel index for no ScopeNote.
+ static const uint32_t NoScopeNoteIndex = UINT32_MAX;
+
+ uint32_t index; // Index of Scope in the scopes array, or
+ // NoScopeIndex if there is no block scope in
+ // this range.
+ uint32_t start; // Bytecode offset at which this scope starts,
+ // from script->main().
+ uint32_t length; // Bytecode length of scope.
+ uint32_t parent; // Index of parent block scope in notes, or NoScopeNote.
+};
+
+struct ConstArray {
+ js::GCPtrValue* vector; // array of indexed constant values
+ uint32_t length;
+};
+
+struct ObjectArray {
+ js::GCPtrObject* vector; // Array of indexed objects.
+ uint32_t length; // Count of indexed objects.
+};
+
+struct ScopeArray {
+ js::GCPtrScope* vector; // Array of indexed scopes.
+ uint32_t length; // Count of indexed scopes.
+};
+
+struct TryNoteArray {
+ JSTryNote* vector; // Array of indexed try notes.
+ uint32_t length; // Count of indexed try notes.
+};
+
+struct ScopeNoteArray {
+ ScopeNote* vector; // Array of indexed ScopeNote records.
+ uint32_t length; // Count of indexed try notes.
+};
+
+class YieldOffsetArray {
+ friend bool
+ detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
+ MutableHandle<GCVector<Scope*>> scopes);
+
+ uint32_t* vector_; // Array of bytecode offsets.
+ uint32_t length_; // Count of bytecode offsets.
+
+ public:
+ void init(uint32_t* vector, uint32_t length) {
+ vector_ = vector;
+ length_ = length;
+ }
+ uint32_t& operator[](uint32_t index) {
+ MOZ_ASSERT(index < length_);
+ return vector_[index];
+ }
+ uint32_t length() const {
+ return length_;
+ }
+};
+
+class ScriptCounts
+{
+ public:
+ typedef mozilla::Vector<PCCounts, 0, SystemAllocPolicy> PCCountsVector;
+
+ inline ScriptCounts();
+ inline explicit ScriptCounts(PCCountsVector&& jumpTargets);
+ inline ScriptCounts(ScriptCounts&& src);
+ inline ~ScriptCounts();
+
+ inline ScriptCounts& operator=(ScriptCounts&& src);
+
+ // Return the counter used to count the number of visits. Returns null if
+ // the element is not found.
+ PCCounts* maybeGetPCCounts(size_t offset);
+ const PCCounts* maybeGetPCCounts(size_t offset) const;
+
+ // PCCounts are stored at jump-target offsets. This function looks for the
+ // previous PCCount which is in the same basic block as the current offset.
+ PCCounts* getImmediatePrecedingPCCounts(size_t offset);
+
+ // Return the counter used to count the number of throws. Returns null if
+ // the element is not found.
+ const PCCounts* maybeGetThrowCounts(size_t offset) const;
+
+ // Throw counts are stored at the location of each throwing
+ // instruction. This function looks for the previous throw count.
+ //
+ // Note: if the offset of the returned count is higher than the offset of
+ // the immediate preceding PCCount, then this throw happened in the same
+ // basic block.
+ const PCCounts* getImmediatePrecedingThrowCounts(size_t offset) const;
+
+ // Return the counter used to count the number of throws. Allocate it if
+ // none exists yet. Returns null if the allocation failed.
+ PCCounts* getThrowCounts(size_t offset);
+
+ private:
+ friend class ::JSScript;
+ friend struct ScriptAndCounts;
+
+ // This sorted array is used to map an offset to the number of times a
+ // branch got visited.
+ PCCountsVector pcCounts_;
+
+ // This sorted vector is used to map an offset to the number of times an
+ // instruction throw.
+ PCCountsVector throwCounts_;
+
+ // Information about any Ion compilations for the script.
+ jit::IonScriptCounts* ionCounts_;
+};
+
+// Note: The key of this hash map is a weak reference to a JSScript. We do not
+// use the WeakMap implementation provided in jsweakmap.h because it would be
+// collected at the beginning of the sweeping of the compartment, thus before
+// the calls to the JSScript::finalize function which are used to aggregate code
+// coverage results on the compartment.
+typedef HashMap<JSScript*,
+ ScriptCounts*,
+ DefaultHasher<JSScript*>,
+ SystemAllocPolicy> ScriptCountsMap;
+
+class DebugScript
+{
+ friend class ::JSScript;
+ friend struct ::JSCompartment;
+
+ /*
+ * When non-zero, compile script in single-step mode. The top bit is set and
+ * cleared by setStepMode, as used by JSD. The lower bits are a count,
+ * adjusted by changeStepModeCount, used by the Debugger object. Only
+ * when the bit is clear and the count is zero may we compile the script
+ * without single-step support.
+ */
+ uint32_t stepMode;
+
+ /*
+ * Number of breakpoint sites at opcodes in the script. This is the number
+ * of populated entries in DebugScript::breakpoints, below.
+ */
+ uint32_t numSites;
+
+ /*
+ * Breakpoints set in our script. For speed and simplicity, this array is
+ * parallel to script->code(): the BreakpointSite for the opcode at
+ * script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
+ * this array's true length is script->length().
+ */
+ BreakpointSite* breakpoints[1];
+};
+
+typedef HashMap<JSScript*,
+ DebugScript*,
+ DefaultHasher<JSScript*>,
+ SystemAllocPolicy> DebugScriptMap;
+
+class ScriptSource;
+
+struct ScriptSourceChunk
+{
+ ScriptSource* ss;
+ uint32_t chunk;
+
+ ScriptSourceChunk()
+ : ss(nullptr), chunk(0)
+ {}
+ ScriptSourceChunk(ScriptSource* ss, uint32_t chunk)
+ : ss(ss), chunk(chunk)
+ {
+ MOZ_ASSERT(valid());;
+ }
+ bool valid() const { return ss != nullptr; }
+
+ bool operator==(const ScriptSourceChunk& other) const {
+ return ss == other.ss && chunk == other.chunk;
+ }
+};
+
+struct ScriptSourceChunkHasher
+{
+ using Lookup = ScriptSourceChunk;
+
+ static HashNumber hash(const ScriptSourceChunk& ssc) {
+ return mozilla::AddToHash(DefaultHasher<ScriptSource*>::hash(ssc.ss), ssc.chunk);
+ }
+ static bool match(const ScriptSourceChunk& c1, const ScriptSourceChunk& c2) {
+ return c1 == c2;
+ }
+};
+
+class UncompressedSourceCache
+{
+ typedef HashMap<ScriptSourceChunk,
+ UniqueTwoByteChars,
+ ScriptSourceChunkHasher,
+ SystemAllocPolicy> Map;
+
+ public:
+ // Hold an entry in the source data cache and prevent it from being purged on GC.
+ class AutoHoldEntry
+ {
+ UncompressedSourceCache* cache_;
+ ScriptSourceChunk sourceChunk_;
+ UniqueTwoByteChars charsToFree_;
+ public:
+ explicit AutoHoldEntry();
+ ~AutoHoldEntry();
+ void holdChars(UniqueTwoByteChars chars);
+ private:
+ void holdEntry(UncompressedSourceCache* cache, const ScriptSourceChunk& sourceChunk);
+ void deferDelete(UniqueTwoByteChars chars);
+ const ScriptSourceChunk& sourceChunk() const { return sourceChunk_; }
+ friend class UncompressedSourceCache;
+ };
+
+ private:
+ UniquePtr<Map> map_;
+ AutoHoldEntry* holder_;
+
+ public:
+ UncompressedSourceCache() : holder_(nullptr) {}
+
+ const char16_t* lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& asp);
+ bool put(const ScriptSourceChunk& ssc, UniqueTwoByteChars chars, AutoHoldEntry& asp);
+
+ void purge();
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ private:
+ void holdEntry(AutoHoldEntry& holder, const ScriptSourceChunk& ssc);
+ void releaseEntry(AutoHoldEntry& holder);
+};
+
+class ScriptSource
+{
+ friend struct SourceCompressionTask;
+
+ uint32_t refs;
+
+ // Note: while ScriptSources may be compressed off thread, they are only
+ // modified by the main thread, and all members are always safe to access
+ // on the main thread.
+
+ // Indicate which field in the |data| union is active.
+
+ struct Missing { };
+
+ struct Uncompressed
+ {
+ SharedImmutableTwoByteString string;
+
+ explicit Uncompressed(SharedImmutableTwoByteString&& str)
+ : string(mozilla::Move(str))
+ { }
+ };
+
+ struct Compressed
+ {
+ SharedImmutableString raw;
+ size_t uncompressedLength;
+
+ Compressed(SharedImmutableString&& raw, size_t uncompressedLength)
+ : raw(mozilla::Move(raw))
+ , uncompressedLength(uncompressedLength)
+ { }
+ };
+
+ using SourceType = mozilla::Variant<Missing, Uncompressed, Compressed>;
+ SourceType data;
+
+ // The filename of this script.
+ UniqueChars filename_;
+
+ UniqueTwoByteChars displayURL_;
+ UniqueTwoByteChars sourceMapURL_;
+ bool mutedErrors_;
+
+ // bytecode offset in caller script that generated this code.
+ // This is present for eval-ed code, as well as "new Function(...)"-introduced
+ // scripts.
+ uint32_t introductionOffset_;
+
+ // If this ScriptSource was generated by a code-introduction mechanism such
+ // as |eval| or |new Function|, the debugger needs access to the "raw"
+ // filename of the top-level script that contains the eval-ing code. To
+ // keep track of this, we must preserve the original outermost filename (of
+ // the original introducer script), so that instead of a filename of
+ // "foo.js line 30 > eval line 10 > Function", we can obtain the original
+ // raw filename of "foo.js".
+ //
+ // In the case described above, this field will be non-null and will be the
+ // original raw filename from above. Otherwise this field will be null.
+ UniqueChars introducerFilename_;
+
+ // A string indicating how this source code was introduced into the system.
+ // This accessor returns one of the following values:
+ // "eval" for code passed to |eval|.
+ // "Function" for code passed to the |Function| constructor.
+ // "Worker" for code loaded by calling the Web worker constructor&mdash;the worker's main script.
+ // "importScripts" for code by calling |importScripts| in a web worker.
+ // "handler" for code assigned to DOM elements' event handler IDL attributes.
+ // "scriptElement" for code belonging to <script> elements.
+ // undefined if the implementation doesn't know how the code was introduced.
+ // This is a constant, statically allocated C string, so does not need
+ // memory management.
+ const char* introductionType_;
+
+ // True if we can call JSRuntime::sourceHook to load the source on
+ // demand. If sourceRetrievable_ and hasSourceData() are false, it is not
+ // possible to get source at all.
+ bool sourceRetrievable_:1;
+ bool argumentsNotIncluded_:1;
+ bool hasIntroductionOffset_:1;
+
+ const char16_t* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
+ size_t chunk);
+
+ public:
+ explicit ScriptSource()
+ : refs(0),
+ data(SourceType(Missing())),
+ filename_(nullptr),
+ displayURL_(nullptr),
+ sourceMapURL_(nullptr),
+ mutedErrors_(false),
+ introductionOffset_(0),
+ introducerFilename_(nullptr),
+ introductionType_(nullptr),
+ sourceRetrievable_(false),
+ argumentsNotIncluded_(false),
+ hasIntroductionOffset_(false)
+ {
+ }
+
+ ~ScriptSource() {
+ MOZ_ASSERT(refs == 0);
+ }
+
+ void incref() { refs++; }
+ void decref() {
+ MOZ_ASSERT(refs != 0);
+ if (--refs == 0)
+ js_delete(this);
+ }
+ bool initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options);
+ bool setSourceCopy(ExclusiveContext* cx,
+ JS::SourceBufferHolder& srcBuf,
+ bool argumentsNotIncluded,
+ SourceCompressionTask* tok);
+ void setSourceRetrievable() { sourceRetrievable_ = true; }
+ bool sourceRetrievable() const { return sourceRetrievable_; }
+ bool hasSourceData() const { return !data.is<Missing>(); }
+ bool hasCompressedSource() const { return data.is<Compressed>(); }
+
+ size_t length() const {
+ struct LengthMatcher
+ {
+ size_t match(const Uncompressed& u) {
+ return u.string.length();
+ }
+
+ size_t match(const Compressed& c) {
+ return c.uncompressedLength;
+ }
+
+ size_t match(const Missing& m) {
+ MOZ_CRASH("ScriptSource::length on a missing source");
+ return 0;
+ }
+ };
+
+ MOZ_ASSERT(hasSourceData());
+ return data.match(LengthMatcher());
+ }
+
+ bool argumentsNotIncluded() const {
+ MOZ_ASSERT(hasSourceData());
+ return argumentsNotIncluded_;
+ }
+
+ // Return a string containing the chars starting at |begin| and ending at
+ // |begin + len|.
+ const char16_t* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp,
+ size_t begin, size_t len);
+
+ JSFlatString* substring(JSContext* cx, size_t start, size_t stop);
+ JSFlatString* substringDontDeflate(JSContext* cx, size_t start, size_t stop);
+ void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+ JS::ScriptSourceInfo* info) const;
+
+ MOZ_MUST_USE bool setSource(ExclusiveContext* cx,
+ mozilla::UniquePtr<char16_t[], JS::FreePolicy>&& source,
+ size_t length);
+ void setSource(SharedImmutableTwoByteString&& string);
+
+ MOZ_MUST_USE bool setCompressedSource(
+ ExclusiveContext* cx,
+ mozilla::UniquePtr<char[], JS::FreePolicy>&& raw,
+ size_t rawLength,
+ size_t sourceLength);
+ void setCompressedSource(SharedImmutableString&& raw, size_t sourceLength);
+
+ // XDR handling
+ template <XDRMode mode>
+ bool performXDR(XDRState<mode>* xdr);
+
+ bool setFilename(ExclusiveContext* cx, const char* filename);
+ const char* introducerFilename() const {
+ return introducerFilename_ ? introducerFilename_.get() : filename_.get();
+ }
+ bool hasIntroductionType() const {
+ return introductionType_;
+ }
+ const char* introductionType() const {
+ MOZ_ASSERT(hasIntroductionType());
+ return introductionType_;
+ }
+ const char* filename() const {
+ return filename_.get();
+ }
+
+ // Display URLs
+ bool setDisplayURL(ExclusiveContext* cx, const char16_t* displayURL);
+ bool hasDisplayURL() const { return displayURL_ != nullptr; }
+ const char16_t * displayURL() {
+ MOZ_ASSERT(hasDisplayURL());
+ return displayURL_.get();
+ }
+
+ // Source maps
+ bool setSourceMapURL(ExclusiveContext* cx, const char16_t* sourceMapURL);
+ bool hasSourceMapURL() const { return sourceMapURL_ != nullptr; }
+ const char16_t * sourceMapURL() {
+ MOZ_ASSERT(hasSourceMapURL());
+ return sourceMapURL_.get();
+ }
+
+ bool mutedErrors() const { return mutedErrors_; }
+
+ bool hasIntroductionOffset() const { return hasIntroductionOffset_; }
+ uint32_t introductionOffset() const {
+ MOZ_ASSERT(hasIntroductionOffset());
+ return introductionOffset_;
+ }
+ void setIntroductionOffset(uint32_t offset) {
+ MOZ_ASSERT(!hasIntroductionOffset());
+ MOZ_ASSERT(offset <= (uint32_t)INT32_MAX);
+ introductionOffset_ = offset;
+ hasIntroductionOffset_ = true;
+ }
+};
+
+class ScriptSourceHolder
+{
+ ScriptSource* ss;
+ public:
+ ScriptSourceHolder()
+ : ss(nullptr)
+ {}
+ explicit ScriptSourceHolder(ScriptSource* ss)
+ : ss(ss)
+ {
+ ss->incref();
+ }
+ ~ScriptSourceHolder()
+ {
+ if (ss)
+ ss->decref();
+ }
+ void reset(ScriptSource* newss) {
+ if (ss)
+ ss->decref();
+ ss = newss;
+ ss->incref();
+ }
+ ScriptSource* get() const {
+ return ss;
+ }
+};
+
+class ScriptSourceObject : public NativeObject
+{
+ static const ClassOps classOps_;
+
+ public:
+ static const Class class_;
+
+ static void trace(JSTracer* trc, JSObject* obj);
+ static void finalize(FreeOp* fop, JSObject* obj);
+ static ScriptSourceObject* create(ExclusiveContext* cx, ScriptSource* source);
+
+ // Initialize those properties of this ScriptSourceObject whose values
+ // are provided by |options|, re-wrapping as necessary.
+ static bool initFromOptions(JSContext* cx, HandleScriptSource source,
+ const ReadOnlyCompileOptions& options);
+
+ ScriptSource* source() const {
+ return static_cast<ScriptSource*>(getReservedSlot(SOURCE_SLOT).toPrivate());
+ }
+ JSObject* element() const {
+ return getReservedSlot(ELEMENT_SLOT).toObjectOrNull();
+ }
+ const Value& elementAttributeName() const {
+ MOZ_ASSERT(!getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic());
+ return getReservedSlot(ELEMENT_PROPERTY_SLOT);
+ }
+ JSScript* introductionScript() const {
+ if (getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isUndefined())
+ return nullptr;
+ void* untyped = getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate();
+ MOZ_ASSERT(untyped);
+ return static_cast<JSScript*>(untyped);
+ }
+
+ private:
+ static const uint32_t SOURCE_SLOT = 0;
+ static const uint32_t ELEMENT_SLOT = 1;
+ static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
+ static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
+ static const uint32_t RESERVED_SLOTS = 4;
+};
+
+enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
+enum FunctionAsyncKind { SyncFunction, AsyncFunction };
+
+static inline unsigned
+GeneratorKindAsBits(GeneratorKind generatorKind) {
+ return static_cast<unsigned>(generatorKind);
+}
+
+static inline GeneratorKind
+GeneratorKindFromBits(unsigned val) {
+ MOZ_ASSERT(val <= StarGenerator);
+ return static_cast<GeneratorKind>(val);
+}
+
+static inline unsigned
+AsyncKindAsBits(FunctionAsyncKind asyncKind) {
+ return static_cast<unsigned>(asyncKind);
+}
+
+static inline FunctionAsyncKind
+AsyncKindFromBits(unsigned val) {
+ MOZ_ASSERT(val <= AsyncFunction);
+ return static_cast<FunctionAsyncKind>(val);
+}
+
+/*
+ * NB: after a successful XDR_DECODE, XDRScript callers must do any required
+ * subsequent set-up of owning function or script object and then call
+ * CallNewScriptHook.
+ */
+template<XDRMode mode>
+bool
+XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
+ HandleFunction fun, MutableHandleScript scriptp);
+
+template<XDRMode mode>
+bool
+XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
+ HandleFunction fun, MutableHandle<LazyScript*> lazy);
+
+/*
+ * Code any constant value.
+ */
+template<XDRMode mode>
+bool
+XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp);
+
+/*
+ * Common data that can be shared between many scripts in a single runtime.
+ */
+class SharedScriptData
+{
+ // This class is reference counted as follows: each pointer from a JSScript
+ // counts as one reference plus there may be one reference from the shared
+ // script data table.
+ mozilla::Atomic<uint32_t> refCount_;
+
+ uint32_t dataLength_;
+ uint32_t natoms_;
+ uint32_t codeLength_;
+ uintptr_t data_[1];
+
+ public:
+ static SharedScriptData* new_(ExclusiveContext* cx, uint32_t codeLength,
+ uint32_t srcnotesLength, uint32_t natoms);
+
+ uint32_t refCount() const {
+ return refCount_;
+ }
+ void incRefCount() {
+ refCount_++;
+ }
+ void decRefCount() {
+ MOZ_ASSERT(refCount_ != 0);
+ refCount_--;
+ if (refCount_ == 0)
+ js_free(this);
+ }
+
+ uint32_t dataLength() const {
+ return dataLength_;
+ }
+ uint8_t* data() {
+ return reinterpret_cast<uint8_t*>(data_);
+ }
+
+ uint32_t natoms() const {
+ return natoms_;
+ }
+ GCPtrAtom* atoms() {
+ if (!natoms_)
+ return nullptr;
+ return reinterpret_cast<GCPtrAtom*>(data());
+ }
+
+ uint32_t codeLength() const {
+ return codeLength_;
+ }
+ jsbytecode* code() {
+ return reinterpret_cast<jsbytecode*>(data() + natoms_ * sizeof(GCPtrAtom));
+ }
+
+ void traceChildren(JSTracer* trc);
+
+ private:
+ SharedScriptData() = delete;
+ SharedScriptData(const SharedScriptData&) = delete;
+ SharedScriptData& operator=(const SharedScriptData&) = delete;
+};
+
+struct ScriptBytecodeHasher
+{
+ struct Lookup
+ {
+ const uint8_t* data;
+ uint32_t length;
+
+ explicit Lookup(SharedScriptData* ssd) : data(ssd->data()), length(ssd->dataLength()) {}
+ };
+ static HashNumber hash(const Lookup& l) { return mozilla::HashBytes(l.data, l.length); }
+ static bool match(SharedScriptData* entry, const Lookup& lookup) {
+ if (entry->dataLength() != lookup.length)
+ return false;
+ return mozilla::PodEqual<uint8_t>(entry->data(), lookup.data, lookup.length);
+ }
+};
+
+typedef HashSet<SharedScriptData*,
+ ScriptBytecodeHasher,
+ SystemAllocPolicy> ScriptDataTable;
+
+extern void
+SweepScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
+
+extern void
+FreeScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
+
+} /* namespace js */
+
+class JSScript : public js::gc::TenuredCell
+{
+ template <js::XDRMode mode>
+ friend
+ bool
+ js::XDRScript(js::XDRState<mode>* xdr, js::HandleScope enclosingScope,
+ js::HandleScript enclosingScript, js::HandleFunction fun,
+ js::MutableHandleScript scriptp);
+
+ friend bool
+ js::detail::CopyScript(JSContext* cx, js::HandleScript src, js::HandleScript dst,
+ js::MutableHandle<JS::GCVector<js::Scope*>> scopes);
+
+ private:
+ js::SharedScriptData* scriptData_;
+ public:
+ uint8_t* data; /* pointer to variable-length data array (see
+ comment above Create() for details) */
+
+ JSCompartment* compartment_;
+
+ private:
+ /* Persistent type information retained across GCs. */
+ js::TypeScript* types_;
+
+ // This script's ScriptSourceObject, or a CCW thereof.
+ //
+ // (When we clone a JSScript into a new compartment, we don't clone its
+ // source object. Instead, the clone refers to a wrapper.)
+ js::GCPtrObject sourceObject_;
+
+ /*
+ * Information attached by Ion. Nexto a valid IonScript this could be
+ * ION_DISABLED_SCRIPT, ION_COMPILING_SCRIPT or ION_PENDING_SCRIPT.
+ * The later is a ion compilation that is ready, but hasn't been linked
+ * yet.
+ */
+ js::jit::IonScript* ion;
+
+ /* Information attached by Baseline. */
+ js::jit::BaselineScript* baseline;
+
+ /* Information used to re-lazify a lazily-parsed interpreted function. */
+ js::LazyScript* lazyScript;
+
+ /*
+ * Pointer to either baseline->method()->raw() or ion->method()->raw(), or
+ * nullptr if there's no Baseline or Ion script.
+ */
+ uint8_t* baselineOrIonRaw;
+ uint8_t* baselineOrIonSkipArgCheck;
+
+ // 32-bit fields.
+
+ uint32_t dataSize_; /* size of the used part of the data array */
+
+ uint32_t lineno_; /* base line number of script */
+ uint32_t column_; /* base column of script, optionally set */
+
+ uint32_t mainOffset_;/* offset of main entry point from code, after
+ predef'ing prologue */
+
+ uint32_t nfixed_; /* fixed frame slots */
+ uint32_t nslots_; /* slots plus maximum stack depth */
+
+ uint32_t bodyScopeIndex_; /* index into the scopes array of the body scope */
+
+ /* Range of characters in scriptSource which contains this script's source. */
+ uint32_t sourceStart_;
+ uint32_t sourceEnd_;
+
+ // Number of times the script has been called or has had backedges taken.
+ // When running in ion, also increased for any inlined scripts. Reset if
+ // the script's JIT code is forcibly discarded.
+ mozilla::Atomic<uint32_t, mozilla::Relaxed> warmUpCount;
+
+ // 16-bit fields.
+
+ uint16_t warmUpResetCount; /* Number of times the |warmUpCount| was
+ * forcibly discarded. The counter is reset when
+ * a script is successfully jit-compiled. */
+
+ uint16_t version; /* JS version under which script was compiled */
+
+ uint16_t funLength_; /* ES6 function length */
+
+ uint16_t nTypeSets_; /* number of type sets used in this script for
+ dynamic type monitoring */
+
+ // Bit fields.
+
+ public:
+ // The kinds of the optional arrays.
+ enum ArrayKind {
+ CONSTS,
+ OBJECTS,
+ TRYNOTES,
+ SCOPENOTES,
+ ARRAY_KIND_BITS
+ };
+
+ private:
+ // The bits in this field indicate the presence/non-presence of several
+ // optional arrays in |data|. See the comments above Create() for details.
+ uint8_t hasArrayBits:ARRAY_KIND_BITS;
+
+ // The GeneratorKind of the script.
+ uint8_t generatorKindBits_:2;
+
+ // 1-bit fields.
+
+ // No need for result value of last expression statement.
+ bool noScriptRval_:1;
+
+ // Code is in strict mode.
+ bool strict_:1;
+
+ // Code has "use strict"; explicitly.
+ bool explicitUseStrict_:1;
+
+ // True if the script has a non-syntactic scope on its dynamic scope chain.
+ // That is, there are objects about which we know nothing between the
+ // outermost syntactic scope and the global.
+ bool hasNonSyntacticScope_:1;
+
+ // see Parser::selfHostingMode.
+ bool selfHosted_:1;
+
+ // See FunctionContextFlags.
+ bool bindingsAccessedDynamically_:1;
+ bool funHasExtensibleScope_:1;
+
+ // True if any formalIsAliased(i).
+ bool funHasAnyAliasedFormal_:1;
+
+ // Have warned about uses of undefined properties in this script.
+ bool warnedAboutUndefinedProp_:1;
+
+ // Script has singleton objects.
+ bool hasSingletons_:1;
+
+ // Script is a lambda to treat as running once or a global or eval script
+ // that will only run once. Which one it is can be disambiguated by
+ // checking whether function() is null.
+ bool treatAsRunOnce_:1;
+
+ // If treatAsRunOnce, whether script has executed.
+ bool hasRunOnce_:1;
+
+ // Script has been reused for a clone.
+ bool hasBeenCloned_:1;
+
+ // Script came from eval(), and is still active.
+ bool isActiveEval_:1;
+
+ // Script came from eval(), and is in eval cache.
+ bool isCachedEval_:1;
+
+ // 'this', 'arguments' and f.apply() are used. This is likely to be a wrapper.
+ bool isLikelyConstructorWrapper_:1;
+
+ // IonMonkey compilation hints.
+ bool failedBoundsCheck_:1; /* script has had hoisted bounds checks fail */
+ bool failedShapeGuard_:1; /* script has had hoisted shape guard fail */
+ bool hadFrequentBailouts_:1;
+ bool hadOverflowBailout_:1;
+ bool uninlineable_:1; /* explicitly marked as uninlineable */
+
+ // Idempotent cache has triggered invalidation.
+ bool invalidatedIdempotentCache_:1;
+
+ // Lexical check did fail and bail out.
+ bool failedLexicalCheck_:1;
+
+ // If the generator was created implicitly via a generator expression,
+ // isGeneratorExp will be true.
+ bool isGeneratorExp_:1;
+
+ // Script has an entry in JSCompartment::scriptCountsMap.
+ bool hasScriptCounts_:1;
+
+ // Script has an entry in JSCompartment::debugScriptMap.
+ bool hasDebugScript_:1;
+
+ // Freeze constraints for stack type sets have been generated.
+ bool hasFreezeConstraints_:1;
+
+ /* See comments below. */
+ bool argsHasVarBinding_:1;
+ bool needsArgsAnalysis_:1;
+ bool needsArgsObj_:1;
+ bool functionHasThisBinding_:1;
+ bool functionHasExtraBodyVarScope_:1;
+
+ // Whether the arguments object for this script, if it needs one, should be
+ // mapped (alias formal parameters).
+ bool hasMappedArgsObj_:1;
+
+ // Generation for this script's TypeScript. If out of sync with the
+ // TypeZone's generation, the TypeScript needs to be swept.
+ //
+ // This should be a uint32 but is instead a bool so that MSVC packs it
+ // correctly.
+ bool typesGeneration_:1;
+
+ // Do not relazify this script. This is used by the relazify() testing
+ // function for scripts that are on the stack and also by the AutoDelazify
+ // RAII class. Usually we don't relazify functions in compartments with
+ // scripts on the stack, but the relazify() testing function overrides that,
+ // and sometimes we're working with a cross-compartment function and need to
+ // keep it from relazifying.
+ bool doNotRelazify_:1;
+
+ // Script contains inner functions. Used to check if we can relazify the
+ // script.
+ bool hasInnerFunctions_:1;
+
+ bool needsHomeObject_:1;
+
+ bool isDerivedClassConstructor_:1;
+ bool isDefaultClassConstructor_:1;
+
+ bool isAsync_:1;
+
+ // Add padding so JSScript is gc::Cell aligned. Make padding protected
+ // instead of private to suppress -Wunused-private-field compiler warnings.
+ protected:
+#if JS_BITS_PER_WORD == 32
+ // Currently no padding is needed.
+#endif
+
+ //
+ // End of fields. Start methods.
+ //
+
+ public:
+ static JSScript* Create(js::ExclusiveContext* cx,
+ const JS::ReadOnlyCompileOptions& options,
+ js::HandleObject sourceObject, uint32_t sourceStart,
+ uint32_t sourceEnd);
+
+ void initCompartment(js::ExclusiveContext* cx);
+
+ // Three ways ways to initialize a JSScript. Callers of partiallyInit()
+ // are responsible for notifying the debugger after successfully creating
+ // any kind (function or other) of new JSScript. However, callers of
+ // fullyInitFromEmitter() do not need to do this.
+ static bool partiallyInit(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
+ uint32_t nscopes, uint32_t nconsts, uint32_t nobjects,
+ uint32_t ntrynotes, uint32_t nscopenotes, uint32_t nyieldoffsets,
+ uint32_t nTypeSets);
+
+ private:
+ static void initFromFunctionBox(js::ExclusiveContext* cx, js::HandleScript script,
+ js::frontend::FunctionBox* funbox);
+ static void initFromModuleContext(js::ExclusiveContext* cx, js::HandleScript script,
+ js::frontend::ModuleSharedContext* modulesc);
+
+ public:
+ static bool fullyInitFromEmitter(js::ExclusiveContext* cx, js::HandleScript script,
+ js::frontend::BytecodeEmitter* bce);
+
+ // Initialize the Function.prototype script.
+ static bool initFunctionPrototype(js::ExclusiveContext* cx, js::HandleScript script,
+ JS::HandleFunction functionProto);
+
+#ifdef DEBUG
+ private:
+ // Assert that jump targets are within the code array of the script.
+ void assertValidJumpTargets() const;
+#endif
+
+ public:
+ inline JSPrincipals* principals();
+
+ JSCompartment* compartment() const { return compartment_; }
+ JSCompartment* maybeCompartment() const { return compartment(); }
+
+ void setVersion(JSVersion v) { version = v; }
+
+ js::SharedScriptData* scriptData() {
+ return scriptData_;
+ }
+
+ // Script bytecode is immutable after creation.
+ jsbytecode* code() const {
+ if (!scriptData_)
+ return nullptr;
+ return scriptData_->code();
+ }
+ size_t length() const {
+ MOZ_ASSERT(scriptData_);
+ return scriptData_->codeLength();
+ }
+
+ jsbytecode* codeEnd() const { return code() + length(); }
+
+ jsbytecode* lastPC() const {
+ jsbytecode* pc = codeEnd() - js::JSOP_RETRVAL_LENGTH;
+ MOZ_ASSERT(*pc == JSOP_RETRVAL);
+ return pc;
+ }
+
+ bool containsPC(const jsbytecode* pc) const {
+ return pc >= code() && pc < codeEnd();
+ }
+
+ size_t pcToOffset(const jsbytecode* pc) const {
+ MOZ_ASSERT(containsPC(pc));
+ return size_t(pc - code());
+ }
+
+ jsbytecode* offsetToPC(size_t offset) const {
+ MOZ_ASSERT(offset < length());
+ return code() + offset;
+ }
+
+ size_t mainOffset() const {
+ return mainOffset_;
+ }
+
+ size_t lineno() const {
+ return lineno_;
+ }
+
+ size_t column() const {
+ return column_;
+ }
+
+ void setColumn(size_t column) { column_ = column; }
+
+ // The fixed part of a stack frame is comprised of vars (in function and
+ // module code) and block-scoped locals (in all kinds of code).
+ size_t nfixed() const {
+ return nfixed_;
+ }
+
+ // Number of fixed slots reserved for slots that are always live. Only
+ // nonzero for function or module code.
+ size_t numAlwaysLiveFixedSlots() const {
+ if (bodyScope()->is<js::FunctionScope>())
+ return bodyScope()->as<js::FunctionScope>().nextFrameSlot();
+ if (bodyScope()->is<js::ModuleScope>())
+ return bodyScope()->as<js::ModuleScope>().nextFrameSlot();
+ return 0;
+ }
+
+ // Calculate the number of fixed slots that are live at a particular bytecode.
+ size_t calculateLiveFixed(jsbytecode* pc);
+
+ size_t nslots() const {
+ return nslots_;
+ }
+
+ unsigned numArgs() const {
+ if (bodyScope()->is<js::FunctionScope>())
+ return bodyScope()->as<js::FunctionScope>().numPositionalFormalParameters();
+ return 0;
+ }
+
+ inline js::Shape* initialEnvironmentShape() const;
+
+ bool functionHasParameterExprs() const {
+ // Only functions have parameters.
+ js::Scope* scope = bodyScope();
+ if (!scope->is<js::FunctionScope>())
+ return false;
+ return scope->as<js::FunctionScope>().hasParameterExprs();
+ }
+
+ size_t nTypeSets() const {
+ return nTypeSets_;
+ }
+
+ size_t funLength() const {
+ return funLength_;
+ }
+
+ size_t sourceStart() const {
+ return sourceStart_;
+ }
+
+ size_t sourceEnd() const {
+ return sourceEnd_;
+ }
+
+ bool noScriptRval() const {
+ return noScriptRval_;
+ }
+
+ bool strict() const {
+ return strict_;
+ }
+
+ bool explicitUseStrict() const { return explicitUseStrict_; }
+
+ bool hasNonSyntacticScope() const {
+ return hasNonSyntacticScope_;
+ }
+
+ bool selfHosted() const { return selfHosted_; }
+ bool bindingsAccessedDynamically() const { return bindingsAccessedDynamically_; }
+ bool funHasExtensibleScope() const {
+ return funHasExtensibleScope_;
+ }
+ bool funHasAnyAliasedFormal() const {
+ return funHasAnyAliasedFormal_;
+ }
+
+ bool hasSingletons() const { return hasSingletons_; }
+ bool treatAsRunOnce() const {
+ return treatAsRunOnce_;
+ }
+ bool hasRunOnce() const { return hasRunOnce_; }
+ bool hasBeenCloned() const { return hasBeenCloned_; }
+
+ void setTreatAsRunOnce() { treatAsRunOnce_ = true; }
+ void setHasRunOnce() { hasRunOnce_ = true; }
+ void setHasBeenCloned() { hasBeenCloned_ = true; }
+
+ bool isActiveEval() const { return isActiveEval_; }
+ bool isCachedEval() const { return isCachedEval_; }
+
+ void cacheForEval() {
+ MOZ_ASSERT(isActiveEval() && !isCachedEval());
+ isActiveEval_ = false;
+ isCachedEval_ = true;
+ // IsEvalCacheCandidate will make sure that there's nothing in this
+ // script that would prevent reexecution even if isRunOnce is
+ // true. So just pretend like we never ran this script.
+ hasRunOnce_ = false;
+ }
+
+ void uncacheForEval() {
+ MOZ_ASSERT(isCachedEval() && !isActiveEval());
+ isCachedEval_ = false;
+ isActiveEval_ = true;
+ }
+
+ void setActiveEval() { isActiveEval_ = true; }
+
+ bool isLikelyConstructorWrapper() const {
+ return isLikelyConstructorWrapper_;
+ }
+ void setLikelyConstructorWrapper() { isLikelyConstructorWrapper_ = true; }
+
+ bool isGeneratorExp() const { return isGeneratorExp_; }
+
+ bool failedBoundsCheck() const {
+ return failedBoundsCheck_;
+ }
+ bool failedShapeGuard() const {
+ return failedShapeGuard_;
+ }
+ bool hadFrequentBailouts() const {
+ return hadFrequentBailouts_;
+ }
+ bool hadOverflowBailout() const {
+ return hadOverflowBailout_;
+ }
+ bool uninlineable() const {
+ return uninlineable_;
+ }
+ bool invalidatedIdempotentCache() const {
+ return invalidatedIdempotentCache_;
+ }
+ bool failedLexicalCheck() const {
+ return failedLexicalCheck_;
+ }
+ bool isDefaultClassConstructor() const {
+ return isDefaultClassConstructor_;
+ }
+
+ void setFailedBoundsCheck() { failedBoundsCheck_ = true; }
+ void setFailedShapeGuard() { failedShapeGuard_ = true; }
+ void setHadFrequentBailouts() { hadFrequentBailouts_ = true; }
+ void setHadOverflowBailout() { hadOverflowBailout_ = true; }
+ void setUninlineable() { uninlineable_ = true; }
+ void setInvalidatedIdempotentCache() { invalidatedIdempotentCache_ = true; }
+ void setFailedLexicalCheck() { failedLexicalCheck_ = true; }
+ void setIsDefaultClassConstructor() { isDefaultClassConstructor_ = true; }
+
+ bool hasScriptCounts() const { return hasScriptCounts_; }
+
+ bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
+ void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
+
+ bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; }
+ void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; }
+
+ /* See ContextFlags::funArgumentsHasLocalBinding comment. */
+ bool argumentsHasVarBinding() const {
+ return argsHasVarBinding_;
+ }
+ void setArgumentsHasVarBinding();
+ bool argumentsAliasesFormals() const {
+ return argumentsHasVarBinding() && hasMappedArgsObj();
+ }
+
+ js::GeneratorKind generatorKind() const {
+ return js::GeneratorKindFromBits(generatorKindBits_);
+ }
+ bool isGenerator() const { return generatorKind() != js::NotGenerator; }
+ bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
+ bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
+ void setGeneratorKind(js::GeneratorKind kind) {
+ // A script only gets its generator kind set as part of initialization,
+ // so it can only transition from not being a generator.
+ MOZ_ASSERT(!isGenerator());
+ generatorKindBits_ = GeneratorKindAsBits(kind);
+ }
+
+ js::FunctionAsyncKind asyncKind() const {
+ return isAsync_ ? js::AsyncFunction : js::SyncFunction;
+ }
+
+ void setAsyncKind(js::FunctionAsyncKind kind) {
+ isAsync_ = kind == js::AsyncFunction;
+ }
+
+ void setNeedsHomeObject() {
+ needsHomeObject_ = true;
+ }
+ bool needsHomeObject() const {
+ return needsHomeObject_;
+ }
+
+ bool isDerivedClassConstructor() const {
+ return isDerivedClassConstructor_;
+ }
+
+ /*
+ * As an optimization, even when argsHasLocalBinding, the function prologue
+ * may not need to create an arguments object. This is determined by
+ * needsArgsObj which is set by AnalyzeArgumentsUsage. When !needsArgsObj,
+ * the prologue may simply write MagicValue(JS_OPTIMIZED_ARGUMENTS) to
+ * 'arguments's slot and any uses of 'arguments' will be guaranteed to
+ * handle this magic value. To avoid spurious arguments object creation, we
+ * maintain the invariant that needsArgsObj is only called after the script
+ * has been analyzed.
+ */
+ bool analyzedArgsUsage() const { return !needsArgsAnalysis_; }
+ inline bool ensureHasAnalyzedArgsUsage(JSContext* cx);
+ bool needsArgsObj() const {
+ MOZ_ASSERT(analyzedArgsUsage());
+ return needsArgsObj_;
+ }
+ void setNeedsArgsObj(bool needsArgsObj);
+ static bool argumentsOptimizationFailed(JSContext* cx, js::HandleScript script);
+
+ bool hasMappedArgsObj() const {
+ return hasMappedArgsObj_;
+ }
+
+ bool functionHasThisBinding() const {
+ return functionHasThisBinding_;
+ }
+
+ /*
+ * Arguments access (via JSOP_*ARG* opcodes) must access the canonical
+ * location for the argument. If an arguments object exists AND it's mapped
+ * ('arguments' aliases formals), then all access must go through the
+ * arguments object. Otherwise, the local slot is the canonical location for
+ * the arguments. Note: if a formal is aliased through the scope chain, then
+ * script->formalIsAliased and JSOP_*ARG* opcodes won't be emitted at all.
+ */
+ bool argsObjAliasesFormals() const {
+ return needsArgsObj() && hasMappedArgsObj();
+ }
+
+ uint32_t typesGeneration() const {
+ return (uint32_t) typesGeneration_;
+ }
+
+ void setTypesGeneration(uint32_t generation) {
+ MOZ_ASSERT(generation <= 1);
+ typesGeneration_ = (bool) generation;
+ }
+
+ void setDoNotRelazify(bool b) {
+ doNotRelazify_ = b;
+ }
+
+ void setHasInnerFunctions(bool b) {
+ hasInnerFunctions_ = b;
+ }
+
+ bool hasInnerFunctions() const {
+ return hasInnerFunctions_;
+ }
+
+ bool hasAnyIonScript() const {
+ return hasIonScript();
+ }
+
+ bool hasIonScript() const {
+ bool res = ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT &&
+ ion != ION_PENDING_SCRIPT;
+ MOZ_ASSERT_IF(res, baseline);
+ return res;
+ }
+ bool canIonCompile() const {
+ return ion != ION_DISABLED_SCRIPT;
+ }
+ bool isIonCompilingOffThread() const {
+ return ion == ION_COMPILING_SCRIPT;
+ }
+
+ js::jit::IonScript* ionScript() const {
+ MOZ_ASSERT(hasIonScript());
+ return ion;
+ }
+ js::jit::IonScript* maybeIonScript() const {
+ return ion;
+ }
+ js::jit::IonScript* const* addressOfIonScript() const {
+ return &ion;
+ }
+ void setIonScript(JSRuntime* maybeRuntime, js::jit::IonScript* ionScript);
+
+ bool hasBaselineScript() const {
+ bool res = baseline && baseline != BASELINE_DISABLED_SCRIPT;
+ MOZ_ASSERT_IF(!res, !ion || ion == ION_DISABLED_SCRIPT);
+ return res;
+ }
+ bool canBaselineCompile() const {
+ return baseline != BASELINE_DISABLED_SCRIPT;
+ }
+ js::jit::BaselineScript* baselineScript() const {
+ MOZ_ASSERT(hasBaselineScript());
+ return baseline;
+ }
+ inline void setBaselineScript(JSRuntime* maybeRuntime, js::jit::BaselineScript* baselineScript);
+
+ void updateBaselineOrIonRaw(JSRuntime* maybeRuntime);
+
+ static size_t offsetOfBaselineScript() {
+ return offsetof(JSScript, baseline);
+ }
+ static size_t offsetOfIonScript() {
+ return offsetof(JSScript, ion);
+ }
+ static size_t offsetOfBaselineOrIonRaw() {
+ return offsetof(JSScript, baselineOrIonRaw);
+ }
+ uint8_t* baselineOrIonRawPointer() const {
+ return baselineOrIonRaw;
+ }
+ static size_t offsetOfBaselineOrIonSkipArgCheck() {
+ return offsetof(JSScript, baselineOrIonSkipArgCheck);
+ }
+
+ bool isRelazifiable() const {
+ return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ &&
+ !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() &&
+ !doNotRelazify_;
+ }
+ void setLazyScript(js::LazyScript* lazy) {
+ lazyScript = lazy;
+ }
+ js::LazyScript* maybeLazyScript() {
+ return lazyScript;
+ }
+
+ /*
+ * Original compiled function for the script, if it has a function.
+ * nullptr for global and eval scripts.
+ * The delazifying variant ensures that the function isn't lazy. The
+ * non-delazifying variant must only be used after earlier code has
+ * called ensureNonLazyCanonicalFunction and while the function can't
+ * have been relazified.
+ */
+ inline JSFunction* functionDelazifying() const;
+ JSFunction* functionNonDelazifying() const {
+ if (bodyScope()->is<js::FunctionScope>())
+ return bodyScope()->as<js::FunctionScope>().canonicalFunction();
+ return nullptr;
+ }
+ /*
+ * De-lazifies the canonical function. Must be called before entering code
+ * that expects the function to be non-lazy.
+ */
+ inline void ensureNonLazyCanonicalFunction(JSContext* cx);
+
+ js::ModuleObject* module() const {
+ if (bodyScope()->is<js::ModuleScope>())
+ return bodyScope()->as<js::ModuleScope>().module();
+ return nullptr;
+ }
+
+ bool isGlobalOrEvalCode() const {
+ return bodyScope()->is<js::GlobalScope>() || bodyScope()->is<js::EvalScope>();
+ }
+ bool isGlobalCode() const {
+ return bodyScope()->is<js::GlobalScope>();
+ }
+
+ // Returns true if the script may read formal arguments on the stack
+ // directly, via lazy arguments or a rest parameter.
+ bool mayReadFrameArgsDirectly();
+
+ JSFlatString* sourceData(JSContext* cx);
+
+ static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);
+
+ void setSourceObject(JSObject* object);
+ JSObject* sourceObject() const {
+ return sourceObject_;
+ }
+ js::ScriptSourceObject& scriptSourceUnwrap() const;
+ js::ScriptSource* scriptSource() const;
+ js::ScriptSource* maybeForwardedScriptSource() const;
+ bool mutedErrors() const { return scriptSource()->mutedErrors(); }
+ const char* filename() const { return scriptSource()->filename(); }
+ const char* maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); }
+
+ public:
+
+ /* Return whether this script was compiled for 'eval' */
+ bool isForEval() const {
+ MOZ_ASSERT_IF(isCachedEval() || isActiveEval(), bodyScope()->is<js::EvalScope>());
+ return isCachedEval() || isActiveEval();
+ }
+
+ /* Return whether this is a 'direct eval' script in a function scope. */
+ bool isDirectEvalInFunction() const {
+ if (!isForEval())
+ return false;
+ return bodyScope()->hasOnChain(js::ScopeKind::Function);
+ }
+
+ /*
+ * Return whether this script is a top-level script.
+ *
+ * If we evaluate some code which contains a syntax error, then we might
+ * produce a JSScript which has no associated bytecode. Testing with
+ * |code()| filters out this kind of scripts.
+ *
+ * If this script has a function associated to it, then it is not the
+ * top-level of a file.
+ */
+ bool isTopLevel() { return code() && !functionNonDelazifying(); }
+
+ /* Ensure the script has a TypeScript. */
+ inline bool ensureHasTypes(JSContext* cx);
+
+ inline js::TypeScript* types();
+
+ void maybeSweepTypes(js::AutoClearTypeInferenceStateOnOOM* oom);
+
+ inline js::GlobalObject& global() const;
+ js::GlobalObject& uninlinedGlobal() const;
+
+ uint32_t bodyScopeIndex() const {
+ return bodyScopeIndex_;
+ }
+
+ js::Scope* bodyScope() const {
+ return getScope(bodyScopeIndex_);
+ }
+
+ js::Scope* outermostScope() const {
+ // The body scope may not be the outermost scope in the script when
+ // the decl env scope is present.
+ size_t index = 0;
+ return getScope(index);
+ }
+
+ bool functionHasExtraBodyVarScope() const {
+ MOZ_ASSERT_IF(functionHasExtraBodyVarScope_, functionHasParameterExprs());
+ return functionHasExtraBodyVarScope_;
+ }
+
+ js::VarScope* functionExtraBodyVarScope() const {
+ MOZ_ASSERT(functionHasExtraBodyVarScope());
+ for (uint32_t i = 0; i < scopes()->length; i++) {
+ js::Scope* scope = getScope(i);
+ if (scope->kind() == js::ScopeKind::FunctionBodyVar)
+ return &scope->as<js::VarScope>();
+ }
+ MOZ_CRASH("Function extra body var scope not found");
+ }
+
+ inline js::LexicalScope* maybeNamedLambdaScope() const;
+
+ js::Scope* enclosingScope() const {
+ return outermostScope()->enclosing();
+ }
+
+ private:
+ bool makeTypes(JSContext* cx);
+
+ bool createScriptData(js::ExclusiveContext* cx, uint32_t codeLength, uint32_t srcnotesLength,
+ uint32_t natoms);
+ bool shareScriptData(js::ExclusiveContext* cx);
+ void freeScriptData();
+ void setScriptData(js::SharedScriptData* data);
+
+ public:
+ uint32_t getWarmUpCount() const { return warmUpCount; }
+ uint32_t incWarmUpCounter(uint32_t amount = 1) { return warmUpCount += amount; }
+ uint32_t* addressOfWarmUpCounter() { return reinterpret_cast<uint32_t*>(&warmUpCount); }
+ static size_t offsetOfWarmUpCounter() { return offsetof(JSScript, warmUpCount); }
+ void resetWarmUpCounter() { incWarmUpResetCounter(); warmUpCount = 0; }
+
+ uint16_t getWarmUpResetCount() const { return warmUpResetCount; }
+ uint16_t incWarmUpResetCounter(uint16_t amount = 1) { return warmUpResetCount += amount; }
+ void resetWarmUpResetCounter() { warmUpResetCount = 0; }
+
+ public:
+ bool initScriptCounts(JSContext* cx);
+ js::ScriptCounts& getScriptCounts();
+ js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
+ const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
+ js::PCCounts* getThrowCounts(jsbytecode* pc);
+ uint64_t getHitCount(jsbytecode* pc);
+ void incHitCount(jsbytecode* pc); // Used when we bailout out of Ion.
+ void addIonCounts(js::jit::IonScriptCounts* ionCounts);
+ js::jit::IonScriptCounts* getIonCounts();
+ void releaseScriptCounts(js::ScriptCounts* counts);
+ void destroyScriptCounts(js::FreeOp* fop);
+ // The entry should be removed after using this function.
+ void takeOverScriptCountsMapEntry(js::ScriptCounts* entryValue);
+
+ jsbytecode* main() const {
+ return code() + mainOffset();
+ }
+
+ /*
+ * computedSizeOfData() is the in-use size of all the data sections.
+ * sizeOfData() is the size of the block allocated to hold all the data
+ * sections (which can be larger than the in-use size).
+ */
+ size_t computedSizeOfData() const;
+ size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const;
+ size_t sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ uint32_t numNotes(); /* Number of srcnote slots in the srcnotes section */
+
+ /* Script notes are allocated right after the code. */
+ jssrcnote* notes() { return (jssrcnote*)(code() + length()); }
+
+ bool hasArray(ArrayKind kind) const {
+ return hasArrayBits & (1 << kind);
+ }
+ void setHasArray(ArrayKind kind) { hasArrayBits |= (1 << kind); }
+ void cloneHasArray(JSScript* script) { hasArrayBits = script->hasArrayBits; }
+
+ bool hasConsts() const { return hasArray(CONSTS); }
+ bool hasObjects() const { return hasArray(OBJECTS); }
+ bool hasTrynotes() const { return hasArray(TRYNOTES); }
+ bool hasScopeNotes() const { return hasArray(SCOPENOTES); }
+ bool hasYieldOffsets() const { return isGenerator(); }
+
+#define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0))
+
+ size_t scopesOffset() const { return 0; }
+ size_t constsOffset() const { return scopesOffset() + sizeof(js::ScopeArray); }
+ size_t objectsOffset() const { return OFF(constsOffset, hasConsts, js::ConstArray); }
+ size_t trynotesOffset() const { return OFF(objectsOffset, hasObjects, js::ObjectArray); }
+ size_t scopeNotesOffset() const { return OFF(trynotesOffset, hasTrynotes, js::TryNoteArray); }
+ size_t yieldOffsetsOffset() const { return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray); }
+
+#undef OFF
+
+ size_t dataSize() const { return dataSize_; }
+
+ js::ConstArray* consts() {
+ MOZ_ASSERT(hasConsts());
+ return reinterpret_cast<js::ConstArray*>(data + constsOffset());
+ }
+
+ js::ObjectArray* objects() {
+ MOZ_ASSERT(hasObjects());
+ return reinterpret_cast<js::ObjectArray*>(data + objectsOffset());
+ }
+
+ js::ScopeArray* scopes() const {
+ return reinterpret_cast<js::ScopeArray*>(data + scopesOffset());
+ }
+
+ js::TryNoteArray* trynotes() const {
+ MOZ_ASSERT(hasTrynotes());
+ return reinterpret_cast<js::TryNoteArray*>(data + trynotesOffset());
+ }
+
+ js::ScopeNoteArray* scopeNotes() {
+ MOZ_ASSERT(hasScopeNotes());
+ return reinterpret_cast<js::ScopeNoteArray*>(data + scopeNotesOffset());
+ }
+
+ js::YieldOffsetArray& yieldOffsets() {
+ MOZ_ASSERT(hasYieldOffsets());
+ return *reinterpret_cast<js::YieldOffsetArray*>(data + yieldOffsetsOffset());
+ }
+
+ bool hasLoops();
+
+ size_t natoms() const {
+ MOZ_ASSERT(scriptData_);
+ return scriptData_->natoms();
+ }
+ js::GCPtrAtom* atoms() const {
+ MOZ_ASSERT(scriptData_);
+ return scriptData_->atoms();
+ }
+
+ js::GCPtrAtom& getAtom(size_t index) const {
+ MOZ_ASSERT(index < natoms());
+ return atoms()[index];
+ }
+
+ js::GCPtrAtom& getAtom(jsbytecode* pc) const {
+ MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
+ return getAtom(GET_UINT32_INDEX(pc));
+ }
+
+ js::PropertyName* getName(size_t index) {
+ return getAtom(index)->asPropertyName();
+ }
+
+ js::PropertyName* getName(jsbytecode* pc) const {
+ MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
+ return getAtom(GET_UINT32_INDEX(pc))->asPropertyName();
+ }
+
+ JSObject* getObject(size_t index) {
+ js::ObjectArray* arr = objects();
+ MOZ_ASSERT(index < arr->length);
+ MOZ_ASSERT(arr->vector[index]->isTenured());
+ return arr->vector[index];
+ }
+
+ JSObject* getObject(jsbytecode* pc) {
+ MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
+ return getObject(GET_UINT32_INDEX(pc));
+ }
+
+ js::Scope* getScope(size_t index) const {
+ js::ScopeArray* array = scopes();
+ MOZ_ASSERT(index < array->length);
+ return array->vector[index];
+ }
+
+ js::Scope* getScope(jsbytecode* pc) const {
+ // This method is used to get a scope directly using a JSOp with an
+ // index. To search through ScopeNotes to look for a Scope using pc,
+ // use lookupScope.
+ MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
+ MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE,
+ "Did you mean to use lookupScope(pc)?");
+ return getScope(GET_UINT32_INDEX(pc));
+ }
+
+ JSVersion getVersion() const {
+ return JSVersion(version);
+ }
+
+ inline JSFunction* getFunction(size_t index);
+ JSFunction* function() const {
+ if (functionNonDelazifying())
+ return functionNonDelazifying();
+ return nullptr;
+ }
+
+ inline js::RegExpObject* getRegExp(size_t index);
+ inline js::RegExpObject* getRegExp(jsbytecode* pc);
+
+ const js::Value& getConst(size_t index) {
+ js::ConstArray* arr = consts();
+ MOZ_ASSERT(index < arr->length);
+ return arr->vector[index];
+ }
+
+ // The following 3 functions find the static scope just before the
+ // execution of the instruction pointed to by pc.
+
+ js::Scope* lookupScope(jsbytecode* pc);
+
+ js::Scope* innermostScope(jsbytecode* pc);
+ js::Scope* innermostScope() { return innermostScope(main()); }
+
+ /*
+ * The isEmpty method tells whether this script has code that computes any
+ * result (not return value, result AKA normal completion value) other than
+ * JSVAL_VOID, or any other effects.
+ */
+ bool isEmpty() const {
+ if (length() > 3)
+ return false;
+
+ jsbytecode* pc = code();
+ if (noScriptRval() && JSOp(*pc) == JSOP_FALSE)
+ ++pc;
+ return JSOp(*pc) == JSOP_RETRVAL;
+ }
+
+ bool formalIsAliased(unsigned argSlot);
+ bool formalLivesInArgumentsObject(unsigned argSlot);
+
+ private:
+ /* Change this->stepMode to |newValue|. */
+ void setNewStepMode(js::FreeOp* fop, uint32_t newValue);
+
+ bool ensureHasDebugScript(JSContext* cx);
+ js::DebugScript* debugScript();
+ js::DebugScript* releaseDebugScript();
+ void destroyDebugScript(js::FreeOp* fop);
+
+ public:
+ bool hasBreakpointsAt(jsbytecode* pc);
+ bool hasAnyBreakpointsOrStepMode() { return hasDebugScript_; }
+
+ // See comment above 'debugMode' in jscompartment.h for explanation of
+ // invariants of debuggee compartments, scripts, and frames.
+ inline bool isDebuggee() const;
+
+ js::BreakpointSite* getBreakpointSite(jsbytecode* pc)
+ {
+ return hasDebugScript_ ? debugScript()->breakpoints[pcToOffset(pc)] : nullptr;
+ }
+
+ js::BreakpointSite* getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc);
+
+ void destroyBreakpointSite(js::FreeOp* fop, jsbytecode* pc);
+
+ void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JSObject* handler);
+
+ /*
+ * Increment or decrement the single-step count. If the count is non-zero
+ * then the script is in single-step mode.
+ *
+ * Only incrementing is fallible, as it could allocate a DebugScript.
+ */
+ bool incrementStepModeCount(JSContext* cx);
+ void decrementStepModeCount(js::FreeOp* fop);
+
+ bool stepModeEnabled() { return hasDebugScript_ && !!debugScript()->stepMode; }
+
+#ifdef DEBUG
+ uint32_t stepModeCount() { return hasDebugScript_ ? debugScript()->stepMode : 0; }
+#endif
+
+ void finalize(js::FreeOp* fop);
+
+ static const JS::TraceKind TraceKind = JS::TraceKind::Script;
+
+ void traceChildren(JSTracer* trc);
+
+ // A helper class to prevent relazification of the given function's script
+ // while it's holding on to it. This class automatically roots the script.
+ class AutoDelazify;
+ friend class AutoDelazify;
+
+ class AutoDelazify
+ {
+ JS::RootedScript script_;
+ JSContext* cx_;
+ bool oldDoNotRelazify_;
+ public:
+ explicit AutoDelazify(JSContext* cx, JS::HandleFunction fun = nullptr)
+ : script_(cx)
+ , cx_(cx)
+ {
+ holdScript(fun);
+ }
+
+ ~AutoDelazify()
+ {
+ dropScript();
+ }
+
+ void operator=(JS::HandleFunction fun)
+ {
+ dropScript();
+ holdScript(fun);
+ }
+
+ operator JS::HandleScript() const { return script_; }
+ explicit operator bool() const { return script_; }
+
+ private:
+ void holdScript(JS::HandleFunction fun);
+ void dropScript();
+ };
+};
+
+/* If this fails, add/remove padding within JSScript. */
+static_assert(sizeof(JSScript) % js::gc::CellSize == 0,
+ "Size of JSScript must be an integral multiple of js::gc::CellSize");
+
+namespace js {
+
+// Information about a script which may be (or has been) lazily compiled to
+// bytecode from its source.
+class LazyScript : public gc::TenuredCell
+{
+ private:
+ // If non-nullptr, the script has been compiled and this is a forwarding
+ // pointer to the result. This is a weak pointer: after relazification, we
+ // can collect the script if there are no other pointers to it.
+ WeakRef<JSScript*> script_;
+
+ // Original function with which the lazy script is associated.
+ GCPtrFunction function_;
+
+ // Scope in which the script is nested.
+ GCPtrScope enclosingScope_;
+
+ // ScriptSourceObject. We leave this set to nullptr until we generate
+ // bytecode for our immediate parent. This is never a CCW; we don't clone
+ // LazyScripts into other compartments.
+ GCPtrObject sourceObject_;
+
+ // Heap allocated table with any free variables or inner functions.
+ void* table_;
+
+ // Add padding so LazyScript is gc::Cell aligned. Make padding protected
+ // instead of private to suppress -Wunused-private-field compiler warnings.
+ protected:
+#if JS_BITS_PER_WORD == 32
+ uint32_t padding;
+#endif
+
+ private:
+ static const uint32_t NumClosedOverBindingsBits = 21;
+ static const uint32_t NumInnerFunctionsBits = 20;
+
+ struct PackedView {
+ // Assorted bits that should really be in ScriptSourceObject.
+ uint32_t version : 8;
+
+ uint32_t shouldDeclareArguments : 1;
+ uint32_t hasThisBinding : 1;
+ uint32_t isAsync : 1;
+ uint32_t numClosedOverBindings : NumClosedOverBindingsBits;
+ uint32_t numInnerFunctions : NumInnerFunctionsBits;
+
+ uint32_t generatorKindBits : 2;
+
+ // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
+ // If you add another boolean here, make sure to initialze it in
+ // LazyScript::CreateRaw().
+ uint32_t strict : 1;
+ uint32_t bindingsAccessedDynamically : 1;
+ uint32_t hasDebuggerStatement : 1;
+ uint32_t hasDirectEval : 1;
+ uint32_t isLikelyConstructorWrapper : 1;
+ uint32_t hasBeenCloned : 1;
+ uint32_t treatAsRunOnce : 1;
+ uint32_t isDerivedClassConstructor : 1;
+ uint32_t needsHomeObject : 1;
+ };
+
+ union {
+ PackedView p_;
+ uint64_t packedFields_;
+ };
+
+ // Source location for the script.
+ uint32_t begin_;
+ uint32_t end_;
+ uint32_t lineno_;
+ uint32_t column_;
+
+ LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
+ uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
+
+ // Create a LazyScript without initializing the closedOverBindings and the
+ // innerFunctions. To be GC-safe, the caller must initialize both vectors
+ // with valid atoms and functions.
+ static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
+ uint64_t packedData, uint32_t begin, uint32_t end,
+ uint32_t lineno, uint32_t column);
+
+ public:
+ static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits;
+ static const uint32_t NumInnerFunctionsLimit = 1 << NumInnerFunctionsBits;
+
+ // Create a LazyScript and initialize closedOverBindings and innerFunctions
+ // with the provided vectors.
+ static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
+ const frontend::AtomVector& closedOverBindings,
+ Handle<GCVector<JSFunction*, 8>> innerFunctions,
+ JSVersion version, uint32_t begin, uint32_t end,
+ uint32_t lineno, uint32_t column);
+
+ // Create a LazyScript and initialize the closedOverBindings and the
+ // innerFunctions with dummy values to be replaced in a later initialization
+ // phase.
+ //
+ // The "script" argument to this function can be null. If it's non-null,
+ // then this LazyScript should be associated with the given JSScript.
+ //
+ // The enclosingScript and enclosingScope arguments may be null if the
+ // enclosing function is also lazy.
+ static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
+ HandleScript script, HandleScope enclosingScope,
+ HandleScript enclosingScript,
+ uint64_t packedData, uint32_t begin, uint32_t end,
+ uint32_t lineno, uint32_t column);
+
+ void initRuntimeFields(uint64_t packedFields);
+
+ inline JSFunction* functionDelazifying(JSContext* cx) const;
+ JSFunction* functionNonDelazifying() const {
+ return function_;
+ }
+
+ void initScript(JSScript* script);
+ void resetScript();
+
+ JSScript* maybeScript() {
+ return script_;
+ }
+ const JSScript* maybeScriptUnbarriered() const {
+ return script_.unbarrieredGet();
+ }
+ bool hasScript() const {
+ return bool(script_);
+ }
+
+ Scope* enclosingScope() const {
+ return enclosingScope_;
+ }
+
+ ScriptSourceObject* sourceObject() const;
+ ScriptSource* scriptSource() const {
+ return sourceObject()->source();
+ }
+ ScriptSource* maybeForwardedScriptSource() const;
+ bool mutedErrors() const {
+ return scriptSource()->mutedErrors();
+ }
+ JSVersion version() const {
+ JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1);
+ return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version);
+ }
+
+ void setEnclosingScopeAndSource(Scope* enclosingScope, ScriptSourceObject* sourceObject);
+
+ uint32_t numClosedOverBindings() const {
+ return p_.numClosedOverBindings;
+ }
+ JSAtom** closedOverBindings() {
+ return (JSAtom**)table_;
+ }
+
+ uint32_t numInnerFunctions() const {
+ return p_.numInnerFunctions;
+ }
+ GCPtrFunction* innerFunctions() {
+ return (GCPtrFunction*)&closedOverBindings()[numClosedOverBindings()];
+ }
+
+ GeneratorKind generatorKind() const { return GeneratorKindFromBits(p_.generatorKindBits); }
+
+ bool isGenerator() const { return generatorKind() != NotGenerator; }
+
+ bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
+
+ bool isStarGenerator() const { return generatorKind() == StarGenerator; }
+
+ void setGeneratorKind(GeneratorKind kind) {
+ // A script only gets its generator kind set as part of initialization,
+ // so it can only transition from NotGenerator.
+ MOZ_ASSERT(!isGenerator());
+ // Legacy generators cannot currently be lazy.
+ MOZ_ASSERT(kind != LegacyGenerator);
+ p_.generatorKindBits = GeneratorKindAsBits(kind);
+ }
+
+ FunctionAsyncKind asyncKind() const {
+ return p_.isAsync ? AsyncFunction : SyncFunction;
+ }
+
+ void setAsyncKind(FunctionAsyncKind kind) {
+ p_.isAsync = kind == AsyncFunction;
+ }
+
+ bool strict() const {
+ return p_.strict;
+ }
+ void setStrict() {
+ p_.strict = true;
+ }
+
+ bool bindingsAccessedDynamically() const {
+ return p_.bindingsAccessedDynamically;
+ }
+ void setBindingsAccessedDynamically() {
+ p_.bindingsAccessedDynamically = true;
+ }
+
+ bool hasDebuggerStatement() const {
+ return p_.hasDebuggerStatement;
+ }
+ void setHasDebuggerStatement() {
+ p_.hasDebuggerStatement = true;
+ }
+
+ bool hasDirectEval() const {
+ return p_.hasDirectEval;
+ }
+ void setHasDirectEval() {
+ p_.hasDirectEval = true;
+ }
+
+ bool isLikelyConstructorWrapper() const {
+ return p_.isLikelyConstructorWrapper;
+ }
+ void setLikelyConstructorWrapper() {
+ p_.isLikelyConstructorWrapper = true;
+ }
+
+ bool hasBeenCloned() const {
+ return p_.hasBeenCloned;
+ }
+ void setHasBeenCloned() {
+ p_.hasBeenCloned = true;
+ }
+
+ bool treatAsRunOnce() const {
+ return p_.treatAsRunOnce;
+ }
+ void setTreatAsRunOnce() {
+ p_.treatAsRunOnce = true;
+ }
+
+ bool isDerivedClassConstructor() const {
+ return p_.isDerivedClassConstructor;
+ }
+ void setIsDerivedClassConstructor() {
+ p_.isDerivedClassConstructor = true;
+ }
+
+ bool needsHomeObject() const {
+ return p_.needsHomeObject;
+ }
+ void setNeedsHomeObject() {
+ p_.needsHomeObject = true;
+ }
+
+ bool shouldDeclareArguments() const {
+ return p_.shouldDeclareArguments;
+ }
+ void setShouldDeclareArguments() {
+ p_.shouldDeclareArguments = true;
+ }
+
+ bool hasThisBinding() const {
+ return p_.hasThisBinding;
+ }
+ void setHasThisBinding() {
+ p_.hasThisBinding = true;
+ }
+
+ const char* filename() const {
+ return scriptSource()->filename();
+ }
+ uint32_t begin() const {
+ return begin_;
+ }
+ uint32_t end() const {
+ return end_;
+ }
+ uint32_t lineno() const {
+ return lineno_;
+ }
+ uint32_t column() const {
+ return column_;
+ }
+
+ bool hasUncompiledEnclosingScript() const;
+
+ friend class GCMarker;
+ void traceChildren(JSTracer* trc);
+ void finalize(js::FreeOp* fop);
+
+ static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript;
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+ {
+ return mallocSizeOf(table_);
+ }
+
+ uint64_t packedFields() const {
+ return packedFields_;
+ }
+};
+
+/* If this fails, add/remove padding within LazyScript. */
+JS_STATIC_ASSERT(sizeof(LazyScript) % js::gc::CellSize == 0);
+
+struct ScriptAndCounts
+{
+ /* This structure is stored and marked from the JSRuntime. */
+ JSScript* script;
+ ScriptCounts scriptCounts;
+
+ inline explicit ScriptAndCounts(JSScript* script);
+ inline ScriptAndCounts(ScriptAndCounts&& sac);
+
+ const PCCounts* maybeGetPCCounts(jsbytecode* pc) const {
+ return scriptCounts.maybeGetPCCounts(script->pcToOffset(pc));
+ }
+ const PCCounts* maybeGetThrowCounts(jsbytecode* pc) const {
+ return scriptCounts.maybeGetThrowCounts(script->pcToOffset(pc));
+ }
+
+ jit::IonScriptCounts* getIonCounts() const {
+ return scriptCounts.ionCounts_;
+ }
+
+ void trace(JSTracer* trc) {
+ TraceRoot(trc, &script, "ScriptAndCounts::script");
+ }
+};
+
+struct GSNCache;
+
+jssrcnote*
+GetSrcNote(GSNCache& cache, JSScript* script, jsbytecode* pc);
+
+extern jssrcnote*
+GetSrcNote(JSContext* cx, JSScript* script, jsbytecode* pc);
+
+extern jsbytecode*
+LineNumberToPC(JSScript* script, unsigned lineno);
+
+extern JS_FRIEND_API(unsigned)
+GetScriptLineExtent(JSScript* script);
+
+} /* namespace js */
+
+namespace js {
+
+extern unsigned
+PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp = nullptr);
+
+extern unsigned
+PCToLineNumber(unsigned startLine, jssrcnote* notes, jsbytecode* code, jsbytecode* pc,
+ unsigned* columnp = nullptr);
+
+/*
+ * This function returns the file and line number of the script currently
+ * executing on cx. If there is no current script executing on cx (e.g., a
+ * native called directly through JSAPI (e.g., by setTimeout)), nullptr and 0
+ * are returned as the file and line. Additionally, this function avoids the
+ * full linear scan to compute line number when the caller guarantees that the
+ * script compilation occurs at a JSOP_EVAL/JSOP_SPREADEVAL.
+ */
+
+enum LineOption {
+ CALLED_FROM_JSOP_EVAL,
+ NOT_CALLED_FROM_JSOP_EVAL
+};
+
+extern void
+DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScript,
+ const char** file, unsigned* linenop,
+ uint32_t* pcOffset, bool* mutedErrors,
+ LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
+
+JSScript*
+CloneScriptIntoFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction fun,
+ HandleScript src);
+
+JSScript*
+CloneGlobalScript(JSContext* cx, ScopeKind scopeKind, HandleScript src);
+
+} /* namespace js */
+
+// JS::ubi::Nodes can point to js::LazyScripts; they're js::gc::Cell instances
+// with no associated compartment.
+namespace JS {
+namespace ubi {
+template<>
+class Concrete<js::LazyScript> : TracerConcrete<js::LazyScript> {
+ protected:
+ explicit Concrete(js::LazyScript *ptr) : TracerConcrete<js::LazyScript>(ptr) { }
+
+ public:
+ static void construct(void *storage, js::LazyScript *ptr) { new (storage) Concrete(ptr); }
+
+ CoarseType coarseType() const final { return CoarseType::Script; }
+ Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+ const char* scriptFilename() const final;
+
+ const char16_t* typeName() const override { return concreteTypeName; }
+ static const char16_t concreteTypeName[];
+};
+} // namespace ubi
+} // namespace JS
+
+#endif /* jsscript_h */