diff -r 000000000000 -r 6474c204b198 js/src/jsscript.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/src/jsscript.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1998 @@ +/* -*- 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/MemoryReporting.h" +#include "mozilla/PodOperations.h" + +#include "jsatom.h" +#ifdef JS_THREADSAFE +#include "jslock.h" +#endif +#include "jsobj.h" +#include "jsopcode.h" +#include "jstypes.h" + +#include "gc/Barrier.h" +#include "gc/Rooting.h" +#include "jit/IonCode.h" +#include "vm/Shape.h" + +namespace JS { +struct ScriptSourceInfo; +} + +namespace js { + +namespace jit { + struct BaselineScript; + struct IonScriptCounts; +} + +# define ION_DISABLED_SCRIPT ((js::jit::IonScript *)0x1) +# define ION_COMPILING_SCRIPT ((js::jit::IonScript *)0x2) + +# define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript *)0x1) + +class BreakpointSite; +class BindingIter; +class LazyScript; +class RegExpObject; +struct SourceCompressionTask; +class Shape; +class WatchpointMap; +class NestedScopeObject; + +namespace frontend { + class BytecodeEmitter; +} + +} + +/* + * 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. + */ +typedef enum JSTryNoteKind { + JSTRY_CATCH, + JSTRY_FINALLY, + JSTRY_ITER, + JSTRY_LOOP +} JSTryNoteKind; + +/* + * 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 +// BlockScopeNote 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 NoBlockScopeIndex. +// +struct BlockScopeNote { + static const uint32_t NoBlockScopeIndex = UINT32_MAX; + + uint32_t index; // Index of NestedScopeObject in the object + // array, or NoBlockScopeIndex 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 UINT32_MAX. +}; + +struct ConstArray { + js::HeapValue *vector; /* array of indexed constant values */ + uint32_t length; +}; + +struct ObjectArray { + js::HeapPtrObject *vector; // Array of indexed objects. + uint32_t length; // Count of indexed objects. +}; + +struct TryNoteArray { + JSTryNote *vector; // Array of indexed try notes. + uint32_t length; // Count of indexed try notes. +}; + +struct BlockScopeArray { + BlockScopeNote *vector; // Array of indexed BlockScopeNote records. + uint32_t length; // Count of indexed try notes. +}; + +class Binding +{ + // One JSScript stores one Binding per formal/variable so we use a + // packed-word representation. + uintptr_t bits_; + + static const uintptr_t KIND_MASK = 0x3; + static const uintptr_t ALIASED_BIT = 0x4; + static const uintptr_t NAME_MASK = ~(KIND_MASK | ALIASED_BIT); + + public: + // A "binding" is a formal, 'var', or 'const' declaration. A function's + // lexical scope is composed of these three kinds of bindings. + enum Kind { ARGUMENT, VARIABLE, CONSTANT }; + + explicit Binding() : bits_(0) {} + + Binding(PropertyName *name, Kind kind, bool aliased) { + JS_STATIC_ASSERT(CONSTANT <= KIND_MASK); + JS_ASSERT((uintptr_t(name) & ~NAME_MASK) == 0); + JS_ASSERT((uintptr_t(kind) & ~KIND_MASK) == 0); + bits_ = uintptr_t(name) | uintptr_t(kind) | (aliased ? ALIASED_BIT : 0); + } + + PropertyName *name() const { + return (PropertyName *)(bits_ & NAME_MASK); + } + + Kind kind() const { + return Kind(bits_ & KIND_MASK); + } + + bool aliased() const { + return bool(bits_ & ALIASED_BIT); + } +}; + +JS_STATIC_ASSERT(sizeof(Binding) == sizeof(uintptr_t)); + +class Bindings; +typedef InternalHandle InternalBindingsHandle; + +/* + * Formal parameters and local variables are stored in a shape tree + * path encapsulated within this class. This class represents bindings for + * both function and top-level scripts (the latter is needed to track names in + * strict mode eval code, to give such code its own lexical environment). + */ +class Bindings +{ + friend class BindingIter; + friend class AliasedFormalIter; + + HeapPtr callObjShape_; + uintptr_t bindingArrayAndFlag_; + uint16_t numArgs_; + uint16_t numBlockScoped_; + uint32_t numVars_; + + /* + * During parsing, bindings are allocated out of a temporary LifoAlloc. + * After parsing, a JSScript object is created and the bindings are + * permanently transferred to it. On error paths, the JSScript object may + * end up with bindings that still point to the (new released) LifoAlloc + * memory. To avoid tracing these bindings during GC, we keep track of + * whether the bindings are temporary or permanent in the low bit of + * bindingArrayAndFlag_. + */ + static const uintptr_t TEMPORARY_STORAGE_BIT = 0x1; + bool bindingArrayUsingTemporaryStorage() const { + return bindingArrayAndFlag_ & TEMPORARY_STORAGE_BIT; + } + + public: + + Binding *bindingArray() const { + return reinterpret_cast(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT); + } + + inline Bindings(); + + /* + * Initialize a Bindings with a pointer into temporary storage. + * bindingArray must have length numArgs+numVars. Before the temporary + * storage is release, switchToScriptStorage must be called, providing a + * pointer into the Binding array stored in script->data. + */ + static bool initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self, + unsigned numArgs, uint32_t numVars, + Binding *bindingArray, unsigned numBlockScoped); + + // CompileScript parses and compiles one statement at a time, but the result + // is one Script object. There will be no vars or bindings, because those + // go on the global, but there may be block-scoped locals, and the number of + // block-scoped locals may increase as we parse more expressions. This + // helper updates the number of block scoped variables in a script as it is + // being parsed. + void updateNumBlockScoped(unsigned numBlockScoped) { + JS_ASSERT(!callObjShape_); + JS_ASSERT(numVars_ == 0); + JS_ASSERT(numBlockScoped < LOCALNO_LIMIT); + JS_ASSERT(numBlockScoped >= numBlockScoped_); + numBlockScoped_ = numBlockScoped; + } + + uint8_t *switchToScriptStorage(Binding *newStorage); + + /* + * Clone srcScript's bindings (as part of js::CloneScript). dstScriptData + * is the pointer to what will eventually be dstScript->data. + */ + static bool clone(JSContext *cx, InternalBindingsHandle self, uint8_t *dstScriptData, + HandleScript srcScript); + + unsigned numArgs() const { return numArgs_; } + uint32_t numVars() const { return numVars_; } + unsigned numBlockScoped() const { return numBlockScoped_; } + uint32_t numLocals() const { return numVars() + numBlockScoped(); } + + // Return the size of the bindingArray. + uint32_t count() const { return numArgs() + numVars(); } + + /* Return the initial shape of call objects created for this scope. */ + Shape *callObjShape() const { return callObjShape_; } + + /* Convenience method to get the var index of 'arguments'. */ + static uint32_t argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle); + + /* Return whether the binding at bindingIndex is aliased. */ + bool bindingIsAliased(uint32_t bindingIndex); + + /* Return whether this scope has any aliased bindings. */ + bool hasAnyAliasedBindings() const { + if (!callObjShape_) + return false; + + return !callObjShape_->isEmptyShape(); + } + + void trace(JSTracer *trc); +}; + +template <> +struct GCMethods { + static Bindings initial(); + static ThingRootKind kind() { return THING_ROOT_BINDINGS; } + static bool poisoned(const Bindings &bindings) { + return IsPoisonedPtr(static_cast(bindings.callObjShape())); + } +}; + +class ScriptCounts +{ + friend class ::JSScript; + friend struct ScriptAndCounts; + + /* + * This points to a single block that holds an array of PCCounts followed + * by an array of doubles. Each element in the PCCounts array has a + * pointer into the array of doubles. + */ + PCCounts *pcCountsVector; + + /* Information about any Ion compilations for the script. */ + jit::IonScriptCounts *ionCounts; + + public: + ScriptCounts() : pcCountsVector(nullptr), ionCounts(nullptr) { } + + inline void destroy(FreeOp *fop); + + void set(js::ScriptCounts counts) { + pcCountsVector = counts.pcCountsVector; + ionCounts = counts.ionCounts; + } +}; + +typedef HashMap, + SystemAllocPolicy> ScriptCountsMap; + +class DebugScript +{ + friend class ::JSScript; + + /* + * 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. */ + uint32_t numSites; + + /* + * Array with all breakpoints installed at opcodes in the script, indexed + * by the offset of the opcode into the script. + */ + BreakpointSite *breakpoints[1]; +}; + +typedef HashMap, + SystemAllocPolicy> DebugScriptMap; + +class ScriptSource; + +class SourceDataCache +{ + typedef HashMap, + SystemAllocPolicy> Map; + + public: + // Hold an entry in the source data cache and prevent it from being purged on GC. + class AutoHoldEntry + { + SourceDataCache *cache_; + ScriptSource *source_; + const jschar *charsToFree_; + public: + explicit AutoHoldEntry(); + ~AutoHoldEntry(); + private: + void holdEntry(SourceDataCache *cache, ScriptSource *source); + void deferDelete(const jschar *chars); + ScriptSource *source() const { return source_; } + friend class SourceDataCache; + }; + + private: + Map *map_; + AutoHoldEntry *holder_; + + public: + SourceDataCache() : map_(nullptr), holder_(nullptr) {} + + const jschar *lookup(ScriptSource *ss, AutoHoldEntry &asp); + bool put(ScriptSource *ss, const jschar *chars, AutoHoldEntry &asp); + + void purge(); + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); + + private: + void holdEntry(AutoHoldEntry &holder, ScriptSource *ss); + void releaseEntry(AutoHoldEntry &holder); +}; + +class ScriptSource +{ + friend class SourceCompressionTask; + + // A note on concurrency: + // + // The source may be compressed by a worker thread during parsing. (See + // SourceCompressionTask.) When compression is running in the background, + // ready() returns false. The compression thread touches the |data| union + // and |compressedLength_|. Therefore, it is not safe to read these members + // unless ready() is true. With that said, users of the public ScriptSource + // API should be fine. + + union { + // Before setSourceCopy or setSource are successfully called, this union + // has a nullptr pointer. When the script source is ready, + // compressedLength_ != 0 implies compressed holds the compressed data; + // otherwise, source holds the uncompressed source. There is a special + // pointer |emptySource| for source code for length 0. + // + // The only function allowed to malloc, realloc, or free the pointers in + // this union is adjustDataSize(). Don't do it elsewhere. + jschar *source; + unsigned char *compressed; + } data; + uint32_t refs; + uint32_t length_; + uint32_t compressedLength_; + char *filename_; + jschar *displayURL_; + jschar *sourceMapURL_; + JSPrincipals *originPrincipals_; + + // 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". + char *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