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/jsscript.h | 2276 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2276 insertions(+) create mode 100644 js/src/jsscript.h (limited to 'js/src/jsscript.h') 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> 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> 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 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, + 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, + 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::hash(ssc.ss), ssc.chunk); + } + static bool match(const ScriptSourceChunk& c1, const ScriptSourceChunk& c2) { + return c1 == c2; + } +}; + +class UncompressedSourceCache +{ + typedef HashMap 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_; + 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; + 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—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