mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
2125 lines
68 KiB
C++
2125 lines
68 KiB
C++
/* -*- 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 "mozilla/UniquePtr.h"
|
|
|
|
#include "jsatom.h"
|
|
#include "jslock.h"
|
|
#include "jsobj.h"
|
|
#include "jsopcode.h"
|
|
#include "jstypes.h"
|
|
|
|
#include "gc/Barrier.h"
|
|
#include "gc/Rooting.h"
|
|
#include "jit/IonCode.h"
|
|
#include "js/UbiNode.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 {
|
|
struct 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 parameter, 'var' (also a stand in for
|
|
// body-level 'let' declarations), 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<Bindings *> 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;
|
|
|
|
HeapPtrShape callObjShape_;
|
|
uintptr_t bindingArrayAndFlag_;
|
|
uint16_t numArgs_;
|
|
uint16_t numBlockScoped_;
|
|
uint16_t numBodyLevelLexicals_;
|
|
uint16_t aliasedBodyLevelLexicalBegin_;
|
|
uint32_t numVars_;
|
|
|
|
#if JS_BITS_PER_WORD == 32
|
|
// Bindings is allocated inline inside JSScript, which needs to be
|
|
// gc::Cell aligned.
|
|
uint32_t padding_;
|
|
#endif
|
|
|
|
/*
|
|
* 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<Binding *>(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT);
|
|
}
|
|
|
|
inline Bindings();
|
|
|
|
/*
|
|
* Initialize a Bindings with a pointer into temporary storage.
|
|
* bindingArray must have length numArgs + numVars +
|
|
* numBodyLevelLexicals. 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,
|
|
uint32_t numArgs, uint32_t numVars,
|
|
uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
|
|
Binding *bindingArray);
|
|
|
|
// 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);
|
|
|
|
uint32_t numArgs() const { return numArgs_; }
|
|
uint32_t numVars() const { return numVars_; }
|
|
uint32_t numBodyLevelLexicals() const { return numBodyLevelLexicals_; }
|
|
uint32_t numBlockScoped() const { return numBlockScoped_; }
|
|
uint32_t numBodyLevelLocals() const { return numVars_ + numBodyLevelLexicals_; }
|
|
uint32_t numLocals() const { return numVars() + numBodyLevelLexicals() + numBlockScoped(); }
|
|
uint32_t lexicalBegin() const { return numArgs() + numVars(); }
|
|
uint32_t aliasedBodyLevelLexicalBegin() const { return aliasedBodyLevelLexicalBegin_; }
|
|
|
|
// Return the size of the bindingArray.
|
|
uint32_t count() const { return numArgs() + numVars() + numBodyLevelLexicals(); }
|
|
|
|
/* 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();
|
|
}
|
|
|
|
static js::ThingRootKind rootKind() { return js::THING_ROOT_BINDINGS; }
|
|
void trace(JSTracer *trc);
|
|
};
|
|
|
|
template <>
|
|
struct GCMethods<Bindings> {
|
|
static Bindings initial();
|
|
static bool poisoned(const Bindings &bindings) {
|
|
return IsPoisonedPtr(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<JSScript *,
|
|
ScriptCounts,
|
|
DefaultHasher<JSScript *>,
|
|
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<JSScript *,
|
|
DebugScript *,
|
|
DefaultHasher<JSScript *>,
|
|
SystemAllocPolicy> DebugScriptMap;
|
|
|
|
class ScriptSource;
|
|
|
|
class UncompressedSourceCache
|
|
{
|
|
typedef HashMap<ScriptSource *,
|
|
const char16_t *,
|
|
DefaultHasher<ScriptSource *>,
|
|
SystemAllocPolicy> Map;
|
|
|
|
public:
|
|
// Hold an entry in the source data cache and prevent it from being purged on GC.
|
|
class AutoHoldEntry
|
|
{
|
|
UncompressedSourceCache *cache_;
|
|
ScriptSource *source_;
|
|
const char16_t *charsToFree_;
|
|
public:
|
|
explicit AutoHoldEntry();
|
|
~AutoHoldEntry();
|
|
private:
|
|
void holdEntry(UncompressedSourceCache *cache, ScriptSource *source);
|
|
void deferDelete(const char16_t *chars);
|
|
ScriptSource *source() const { return source_; }
|
|
friend class UncompressedSourceCache;
|
|
};
|
|
|
|
private:
|
|
Map *map_;
|
|
AutoHoldEntry *holder_;
|
|
|
|
public:
|
|
UncompressedSourceCache() : map_(nullptr), holder_(nullptr) {}
|
|
|
|
const char16_t *lookup(ScriptSource *ss, AutoHoldEntry &asp);
|
|
bool put(ScriptSource *ss, const char16_t *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 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.
|
|
enum {
|
|
DataMissing,
|
|
DataUncompressed,
|
|
DataCompressed,
|
|
DataParent
|
|
} dataType;
|
|
|
|
union {
|
|
struct {
|
|
const char16_t *chars;
|
|
bool ownsChars;
|
|
} uncompressed;
|
|
|
|
struct {
|
|
void *raw;
|
|
size_t nbytes;
|
|
HashNumber hash;
|
|
} compressed;
|
|
|
|
ScriptSource *parent;
|
|
} data;
|
|
|
|
uint32_t length_;
|
|
|
|
// The filename of this script.
|
|
mozilla::UniquePtr<char[], JS::FreePolicy> filename_;
|
|
|
|
mozilla::UniquePtr<char16_t[], JS::FreePolicy> displayURL_;
|
|
mozilla::UniquePtr<char16_t[], JS::FreePolicy> 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".
|
|
//
|
|
// 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.
|
|
mozilla::UniquePtr<char[], JS::FreePolicy> 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 <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;
|
|
|
|
// Whether this is in the runtime's set of compressed ScriptSources.
|
|
bool inCompressedSourceSet:1;
|
|
|
|
public:
|
|
explicit ScriptSource()
|
|
: refs(0),
|
|
dataType(DataMissing),
|
|
length_(0),
|
|
filename_(nullptr),
|
|
displayURL_(nullptr),
|
|
sourceMapURL_(nullptr),
|
|
originPrincipals_(nullptr),
|
|
introductionOffset_(0),
|
|
introducerFilename_(nullptr),
|
|
introductionType_(nullptr),
|
|
sourceRetrievable_(false),
|
|
argumentsNotIncluded_(false),
|
|
hasIntroductionOffset_(false),
|
|
inCompressedSourceSet(false)
|
|
{
|
|
}
|
|
~ScriptSource();
|
|
void incref() { refs++; }
|
|
void decref() {
|
|
JS_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 dataType != DataMissing; }
|
|
bool hasCompressedSource() const { return dataType == DataCompressed; }
|
|
size_t length() const {
|
|
JS_ASSERT(hasSourceData());
|
|
return length_;
|
|
}
|
|
bool argumentsNotIncluded() const {
|
|
JS_ASSERT(hasSourceData());
|
|
return argumentsNotIncluded_;
|
|
}
|
|
const char16_t *chars(JSContext *cx, UncompressedSourceCache::AutoHoldEntry &asp);
|
|
JSFlatString *substring(JSContext *cx, uint32_t start, uint32_t stop);
|
|
JSFlatString *substringDontDeflate(JSContext *cx, uint32_t start, uint32_t stop);
|
|
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
|
JS::ScriptSourceInfo *info) const;
|
|
|
|
const char16_t *uncompressedChars() const {
|
|
JS_ASSERT(dataType == DataUncompressed);
|
|
return data.uncompressed.chars;
|
|
}
|
|
|
|
bool ownsUncompressedChars() const {
|
|
JS_ASSERT(dataType == DataUncompressed);
|
|
return data.uncompressed.ownsChars;
|
|
}
|
|
|
|
void *compressedData() const {
|
|
JS_ASSERT(dataType == DataCompressed);
|
|
return data.compressed.raw;
|
|
}
|
|
|
|
size_t compressedBytes() const {
|
|
JS_ASSERT(dataType == DataCompressed);
|
|
return data.compressed.nbytes;
|
|
}
|
|
|
|
HashNumber compressedHash() const {
|
|
JS_ASSERT(dataType == DataCompressed);
|
|
return data.compressed.hash;
|
|
}
|
|
|
|
ScriptSource *parent() const {
|
|
JS_ASSERT(dataType == DataParent);
|
|
return data.parent;
|
|
}
|
|
|
|
void setSource(const char16_t *chars, size_t length, bool ownsChars = true);
|
|
void setCompressedSource(JSRuntime *maybert, void *raw, size_t nbytes, HashNumber hash);
|
|
void updateCompressedSourceSet(JSRuntime *rt);
|
|
bool ensureOwnsSource(ExclusiveContext *cx);
|
|
|
|
// 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 {
|
|
JS_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();
|
|
}
|
|
|
|
JSPrincipals *originPrincipals() const { return originPrincipals_; }
|
|
|
|
bool hasIntroductionOffset() const { return hasIntroductionOffset_; }
|
|
uint32_t introductionOffset() const {
|
|
JS_ASSERT(hasIntroductionOffset());
|
|
return introductionOffset_;
|
|
}
|
|
void setIntroductionOffset(uint32_t offset) {
|
|
JS_ASSERT(!hasIntroductionOffset());
|
|
JS_ASSERT(offset <= (uint32_t)INT32_MAX);
|
|
introductionOffset_ = offset;
|
|
hasIntroductionOffset_ = true;
|
|
}
|
|
|
|
private:
|
|
size_t computedSizeOfData() const;
|
|
};
|
|
|
|
class ScriptSourceHolder
|
|
{
|
|
ScriptSource *ss;
|
|
public:
|
|
explicit ScriptSourceHolder(ScriptSource *ss)
|
|
: ss(ss)
|
|
{
|
|
ss->incref();
|
|
}
|
|
~ScriptSourceHolder()
|
|
{
|
|
ss->decref();
|
|
}
|
|
};
|
|
|
|
struct CompressedSourceHasher
|
|
{
|
|
typedef ScriptSource *Lookup;
|
|
|
|
static HashNumber computeHash(const void *data, size_t nbytes) {
|
|
return mozilla::HashBytes(data, nbytes);
|
|
}
|
|
|
|
static HashNumber hash(const ScriptSource *ss) {
|
|
return ss->compressedHash();
|
|
}
|
|
|
|
static bool match(const ScriptSource *a, const ScriptSource *b) {
|
|
return a->compressedBytes() == b->compressedBytes() &&
|
|
a->compressedHash() == b->compressedHash() &&
|
|
!memcmp(a->compressedData(), b->compressedData(), a->compressedBytes());
|
|
}
|
|
};
|
|
|
|
typedef HashSet<ScriptSource *, CompressedSourceHasher, SystemAllocPolicy> CompressedSourceSet;
|
|
|
|
class ScriptSourceObject : public JSObject
|
|
{
|
|
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 };
|
|
|
|
static inline unsigned
|
|
GeneratorKindAsBits(GeneratorKind generatorKind) {
|
|
return static_cast<unsigned>(generatorKind);
|
|
}
|
|
|
|
static inline GeneratorKind
|
|
GeneratorKindFromBits(unsigned val) {
|
|
JS_ASSERT(val <= StarGenerator);
|
|
return static_cast<GeneratorKind>(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
|
|
* js_CallNewScriptHook.
|
|
*/
|
|
template<XDRMode mode>
|
|
bool
|
|
XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
|
|
HandleFunction fun, MutableHandleScript scriptp);
|
|
|
|
JSScript *
|
|
CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script,
|
|
NewObjectKind newKind = GenericObject);
|
|
|
|
template<XDRMode mode>
|
|
bool
|
|
XDRLazyScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
|
|
HandleFunction fun, MutableHandle<LazyScript *> lazy);
|
|
|
|
/*
|
|
* Code any constant value.
|
|
*/
|
|
template<XDRMode mode>
|
|
bool
|
|
XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp);
|
|
|
|
} /* namespace js */
|
|
|
|
class JSScript : public js::gc::TenuredCell
|
|
{
|
|
template <js::XDRMode mode>
|
|
friend
|
|
bool
|
|
js::XDRScript(js::XDRState<mode> *xdr, js::HandleObject enclosingScope, js::HandleScript enclosingScript,
|
|
js::HandleFunction fun, js::MutableHandleScript scriptp);
|
|
|
|
friend JSScript *
|
|
js::CloneScript(JSContext *cx, js::HandleObject enclosingScope, js::HandleFunction fun, js::HandleScript src,
|
|
js::NewObjectKind newKind);
|
|
|
|
public:
|
|
//
|
|
// We order fields according to their size in order to avoid wasting space
|
|
// for alignment.
|
|
//
|
|
|
|
// Larger-than-word-sized fields.
|
|
|
|
public:
|
|
js::Bindings bindings; /* names of top-level variables in this script
|
|
(and arguments if this is a function script) */
|
|
|
|
bool hasAnyAliasedBindings() const {
|
|
return bindings.hasAnyAliasedBindings();
|
|
}
|
|
|
|
js::Binding *bindingArray() const {
|
|
return bindings.bindingArray();
|
|
}
|
|
|
|
unsigned numArgs() const {
|
|
return bindings.numArgs();
|
|
}
|
|
|
|
js::Shape *callObjShape() const {
|
|
return bindings.callObjShape();
|
|
}
|
|
|
|
// Word-sized fields.
|
|
|
|
private:
|
|
jsbytecode *code_; /* bytecodes and their immediate operands */
|
|
public:
|
|
uint8_t *data; /* pointer to variable-length data array (see
|
|
comment above Create() for details) */
|
|
|
|
js::HeapPtrAtom *atoms; /* maps immediate index to literal struct */
|
|
|
|
JSCompartment *compartment_;
|
|
|
|
/* Persistent type information retained across GCs. */
|
|
js::types::TypeScript *types;
|
|
|
|
private:
|
|
// 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::HeapPtrObject sourceObject_;
|
|
|
|
js::HeapPtrFunction function_;
|
|
|
|
// For callsite clones, which cannot have enclosing scopes, the original
|
|
// function; otherwise the enclosing scope
|
|
js::HeapPtrObject enclosingScopeOrOriginalFunction_;
|
|
|
|
/* Information attached by Baseline/Ion for sequential mode execution. */
|
|
js::jit::IonScript *ion;
|
|
js::jit::BaselineScript *baseline;
|
|
|
|
/* Information attached by Ion for parallel mode execution */
|
|
js::jit::IonScript *parallelIon;
|
|
|
|
/* 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 length_; /* length of code vector */
|
|
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 prolog */
|
|
|
|
uint32_t natoms_; /* length of atoms array */
|
|
uint32_t nslots_; /* vars plus maximum stack depth */
|
|
|
|
/* Range of characters in scriptSource which contains this script's source. */
|
|
uint32_t sourceStart_;
|
|
uint32_t sourceEnd_;
|
|
|
|
uint32_t warmUpCount; /* 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. */
|
|
|
|
#ifdef DEBUG
|
|
// Unique identifier within the compartment for this script, used for
|
|
// printing analysis information.
|
|
uint32_t id_;
|
|
uint32_t idpad;
|
|
#endif
|
|
|
|
// 16-bit fields.
|
|
|
|
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 */
|
|
|
|
uint16_t staticLevel_;/* static level for display maintenance */
|
|
|
|
// Bit fields.
|
|
|
|
public:
|
|
// The kinds of the optional arrays.
|
|
enum ArrayKind {
|
|
CONSTS,
|
|
OBJECTS,
|
|
REGEXPS,
|
|
TRYNOTES,
|
|
BLOCK_SCOPES,
|
|
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;
|
|
|
|
// Can call getCallerFunction().
|
|
bool savedCallerFun_:1;
|
|
|
|
// Code is in strict mode.
|
|
bool strict_:1;
|
|
|
|
// Code has "use strict"; explicitly.
|
|
bool explicitUseStrict_:1;
|
|
|
|
// See Parser::compileAndGo.
|
|
bool compileAndGo_:1;
|
|
|
|
// see Parser::selfHostingMode.
|
|
bool selfHosted_:1;
|
|
|
|
// See FunctionContextFlags.
|
|
bool bindingsAccessedDynamically_:1;
|
|
bool funHasExtensibleScope_:1;
|
|
bool funNeedsDeclEnvObject_: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.
|
|
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;
|
|
|
|
// Set for functions defined at the top level within an 'eval' script.
|
|
bool directlyInsideEval_:1;
|
|
|
|
// Both 'arguments' and f.apply() are used. This is likely to be a wrapper.
|
|
bool usesArgumentsAndApply_:1;
|
|
|
|
/* script is attempted to be cloned anew at each callsite. This is
|
|
temporarily needed for ParallelArray selfhosted code until type
|
|
information can be made context sensitive. See discussion in
|
|
bug 826148. */
|
|
bool shouldCloneAtCallsite_:1;
|
|
bool isCallsiteClone_:1; /* is a callsite clone; has a link to the original function */
|
|
bool shouldInline_:1; /* hint to inline when possible */
|
|
|
|
// 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 uninlineable_:1; /* explicitly marked as uninlineable */
|
|
|
|
// Idempotent cache has triggered invalidation.
|
|
bool invalidatedIdempotentCache_: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;
|
|
|
|
//
|
|
// End of fields. Start methods.
|
|
//
|
|
|
|
public:
|
|
static JSScript *Create(js::ExclusiveContext *cx,
|
|
js::HandleObject enclosingScope, bool savedCallerFun,
|
|
const JS::ReadOnlyCompileOptions &options, unsigned staticLevel,
|
|
js::HandleObject sourceObject, uint32_t sourceStart,
|
|
uint32_t sourceEnd);
|
|
|
|
void initCompartment(js::ExclusiveContext *cx);
|
|
|
|
// Three ways ways to initialize a JSScript. Callers of partiallyInit()
|
|
// and fullyInitTrivial() 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 nconsts, uint32_t nobjects, uint32_t nregexps,
|
|
uint32_t ntrynotes, uint32_t nblockscopes,
|
|
uint32_t nTypeSets);
|
|
static bool fullyInitFromEmitter(js::ExclusiveContext *cx, JS::Handle<JSScript*> script,
|
|
js::frontend::BytecodeEmitter *bce);
|
|
// Initialize a no-op script.
|
|
static bool fullyInitTrivial(js::ExclusiveContext *cx, JS::Handle<JSScript*> script);
|
|
|
|
inline JSPrincipals *principals();
|
|
|
|
JSCompartment *compartment() const { return compartment_; }
|
|
|
|
void setVersion(JSVersion v) { version = v; }
|
|
|
|
// Script bytecode is immutable after creation.
|
|
jsbytecode *code() const {
|
|
return code_;
|
|
}
|
|
size_t length() const {
|
|
return length_;
|
|
}
|
|
|
|
void setCode(jsbytecode *code) { code_ = code; }
|
|
void setLength(size_t length) { length_ = length; }
|
|
|
|
jsbytecode *codeEnd() const { return code() + length(); }
|
|
|
|
bool containsPC(const jsbytecode *pc) const {
|
|
return pc >= code() && pc < codeEnd();
|
|
}
|
|
|
|
size_t pcToOffset(const jsbytecode *pc) const {
|
|
JS_ASSERT(containsPC(pc));
|
|
return size_t(pc - code());
|
|
}
|
|
|
|
jsbytecode *offsetToPC(size_t offset) const {
|
|
JS_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 code)
|
|
// and block-scoped locals (in all kinds of code).
|
|
size_t nfixed() const {
|
|
return function_ ? bindings.numLocals() : bindings.numBlockScoped();
|
|
}
|
|
|
|
// Number of fixed slots reserved for vars. Only nonzero for function
|
|
// code.
|
|
size_t nfixedvars() const {
|
|
return function_ ? bindings.numVars() : 0;
|
|
}
|
|
|
|
// Number of fixed slots reserved for body-level lexicals and vars. This
|
|
// value minus nfixedvars() is the number of body-level lexicals. Only
|
|
// nonzero for function code.
|
|
size_t nbodyfixed() const {
|
|
return function_ ? bindings.numBodyLevelLocals() : 0;
|
|
}
|
|
|
|
// Aliases for clarity when dealing with lexical slots.
|
|
size_t fixedLexicalBegin() const {
|
|
return nfixedvars();
|
|
}
|
|
|
|
size_t fixedLexicalEnd() const {
|
|
return nfixed();
|
|
}
|
|
|
|
size_t nslots() const {
|
|
return nslots_;
|
|
}
|
|
|
|
size_t staticLevel() const {
|
|
return staticLevel_;
|
|
}
|
|
|
|
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 savedCallerFun() const { return savedCallerFun_; }
|
|
|
|
bool strict() const {
|
|
return strict_;
|
|
}
|
|
|
|
bool explicitUseStrict() const { return explicitUseStrict_; }
|
|
|
|
bool compileAndGo() const {
|
|
return compileAndGo_;
|
|
}
|
|
|
|
bool selfHosted() const { return selfHosted_; }
|
|
bool bindingsAccessedDynamically() const { return bindingsAccessedDynamically_; }
|
|
bool funHasExtensibleScope() const {
|
|
return funHasExtensibleScope_;
|
|
}
|
|
bool funNeedsDeclEnvObject() const {
|
|
return funNeedsDeclEnvObject_;
|
|
}
|
|
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_; }
|
|
bool directlyInsideEval() const { return directlyInsideEval_; }
|
|
|
|
void cacheForEval() {
|
|
JS_ASSERT(isActiveEval() && !isCachedEval());
|
|
isActiveEval_ = false;
|
|
isCachedEval_ = true;
|
|
}
|
|
|
|
void uncacheForEval() {
|
|
JS_ASSERT(isCachedEval() && !isActiveEval());
|
|
isCachedEval_ = false;
|
|
isActiveEval_ = true;
|
|
}
|
|
|
|
void setActiveEval() { isActiveEval_ = true; }
|
|
void setDirectlyInsideEval() { directlyInsideEval_ = true; }
|
|
|
|
bool usesArgumentsAndApply() const {
|
|
return usesArgumentsAndApply_;
|
|
}
|
|
void setUsesArgumentsAndApply() { usesArgumentsAndApply_ = true; }
|
|
|
|
bool shouldCloneAtCallsite() const {
|
|
return shouldCloneAtCallsite_;
|
|
}
|
|
bool shouldInline() const {
|
|
return shouldInline_;
|
|
}
|
|
|
|
void setShouldCloneAtCallsite() { shouldCloneAtCallsite_ = true; }
|
|
void setShouldInline() { shouldInline_ = true; }
|
|
|
|
bool isCallsiteClone() const {
|
|
return isCallsiteClone_;
|
|
}
|
|
bool isGeneratorExp() const { return isGeneratorExp_; }
|
|
|
|
bool failedBoundsCheck() const {
|
|
return failedBoundsCheck_;
|
|
}
|
|
bool failedShapeGuard() const {
|
|
return failedShapeGuard_;
|
|
}
|
|
bool hadFrequentBailouts() const {
|
|
return hadFrequentBailouts_;
|
|
}
|
|
bool uninlineable() const {
|
|
return uninlineable_;
|
|
}
|
|
bool invalidatedIdempotentCache() const {
|
|
return invalidatedIdempotentCache_;
|
|
}
|
|
|
|
void setFailedBoundsCheck() { failedBoundsCheck_ = true; }
|
|
void setFailedShapeGuard() { failedShapeGuard_ = true; }
|
|
void setHadFrequentBailouts() { hadFrequentBailouts_ = true; }
|
|
void setUninlineable() { uninlineable_ = true; }
|
|
void setInvalidatedIdempotentCache() { invalidatedIdempotentCache_ = true; }
|
|
|
|
bool hasScriptCounts() const { return hasScriptCounts_; }
|
|
|
|
bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
|
|
void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
|
|
void clearHasFreezeConstraints() { hasFreezeConstraints_ = false; }
|
|
|
|
bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; }
|
|
void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; }
|
|
|
|
/* See ContextFlags::funArgumentsHasLocalBinding comment. */
|
|
bool argumentsHasVarBinding() const {
|
|
return argsHasVarBinding_;
|
|
}
|
|
jsbytecode *argumentsBytecode() const { JS_ASSERT(code()[0] == JSOP_ARGUMENTS); return code(); }
|
|
void setArgumentsHasVarBinding();
|
|
bool argumentsAliasesFormals() const {
|
|
return argumentsHasVarBinding() && !strict();
|
|
}
|
|
|
|
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.
|
|
JS_ASSERT(!isGenerator());
|
|
generatorKindBits_ = GeneratorKindAsBits(kind);
|
|
}
|
|
|
|
/*
|
|
* 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 before running
|
|
* the script the first time. 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.
|
|
* So 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 {
|
|
JS_ASSERT(analyzedArgsUsage());
|
|
return needsArgsObj_;
|
|
}
|
|
void setNeedsArgsObj(bool needsArgsObj);
|
|
static bool argumentsOptimizationFailed(JSContext *cx, js::HandleScript script);
|
|
|
|
/*
|
|
* Arguments access (via JSOP_*ARG* opcodes) must access the canonical
|
|
* location for the argument. If an arguments object exists AND this is a
|
|
* non-strict function (where '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() && !strict();
|
|
}
|
|
|
|
bool hasAnyIonScript() const {
|
|
return hasIonScript() || hasParallelIonScript();
|
|
}
|
|
|
|
bool hasIonScript() const {
|
|
bool res = ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_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 {
|
|
JS_ASSERT(hasIonScript());
|
|
return ion;
|
|
}
|
|
js::jit::IonScript *maybeIonScript() const {
|
|
return ion;
|
|
}
|
|
js::jit::IonScript *const *addressOfIonScript() const {
|
|
return &ion;
|
|
}
|
|
void setIonScript(JSContext *maybecx, js::jit::IonScript *ionScript) {
|
|
if (hasIonScript())
|
|
js::jit::IonScript::writeBarrierPre(zone(), ion);
|
|
ion = ionScript;
|
|
MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
|
|
updateBaselineOrIonRaw(maybecx);
|
|
}
|
|
|
|
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 {
|
|
JS_ASSERT(hasBaselineScript());
|
|
return baseline;
|
|
}
|
|
inline void setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript);
|
|
|
|
void updateBaselineOrIonRaw(JSContext *maybecx);
|
|
|
|
void setPendingIonBuilder(JSContext *maybecx, js::jit::IonBuilder *builder) {
|
|
JS_ASSERT(!builder || !ion->pendingBuilder());
|
|
ion->setPendingBuilderPrivate(builder);
|
|
updateBaselineOrIonRaw(maybecx);
|
|
}
|
|
js::jit::IonBuilder *pendingIonBuilder() {
|
|
JS_ASSERT(hasIonScript());
|
|
return ion->pendingBuilder();
|
|
}
|
|
|
|
bool hasParallelIonScript() const {
|
|
return parallelIon && parallelIon != ION_DISABLED_SCRIPT && parallelIon != ION_COMPILING_SCRIPT;
|
|
}
|
|
|
|
bool canParallelIonCompile() const {
|
|
return parallelIon != ION_DISABLED_SCRIPT;
|
|
}
|
|
|
|
bool isParallelIonCompilingOffThread() const {
|
|
return parallelIon == ION_COMPILING_SCRIPT;
|
|
}
|
|
|
|
js::jit::IonScript *parallelIonScript() const {
|
|
JS_ASSERT(hasParallelIonScript());
|
|
return parallelIon;
|
|
}
|
|
js::jit::IonScript *maybeParallelIonScript() const {
|
|
return parallelIon;
|
|
}
|
|
void setParallelIonScript(js::jit::IonScript *ionScript) {
|
|
if (hasParallelIonScript())
|
|
js::jit::IonScript::writeBarrierPre(zone(), parallelIon);
|
|
parallelIon = ionScript;
|
|
}
|
|
|
|
static size_t offsetOfBaselineScript() {
|
|
return offsetof(JSScript, baseline);
|
|
}
|
|
static size_t offsetOfIonScript() {
|
|
return offsetof(JSScript, ion);
|
|
}
|
|
static size_t offsetOfParallelIonScript() {
|
|
return offsetof(JSScript, parallelIon);
|
|
}
|
|
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) &&
|
|
!isGenerator() && !hasBaselineScript() && !hasAnyIonScript();
|
|
}
|
|
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 {
|
|
return function_;
|
|
}
|
|
inline void setFunction(JSFunction *fun);
|
|
/*
|
|
* De-lazifies the canonical function. Must be called before entering code
|
|
* that expects the function to be non-lazy.
|
|
*/
|
|
inline void ensureNonLazyCanonicalFunction(JSContext *cx);
|
|
|
|
/*
|
|
* Donor provided itself to callsite clone; null if this is non-clone.
|
|
*/
|
|
JSFunction *donorFunction() const;
|
|
void setIsCallsiteClone(JSObject *fun);
|
|
|
|
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;
|
|
JSPrincipals *originPrincipals() const { return scriptSource()->originPrincipals(); }
|
|
const char *filename() const { return scriptSource()->filename(); }
|
|
|
|
public:
|
|
|
|
/* Return whether this script was compiled for 'eval' */
|
|
bool isForEval() { return isCachedEval() || isActiveEval(); }
|
|
|
|
#ifdef DEBUG
|
|
unsigned id();
|
|
#else
|
|
unsigned id() { return 0; }
|
|
#endif
|
|
|
|
/* Ensure the script has a TypeScript. */
|
|
inline bool ensureHasTypes(JSContext *cx);
|
|
|
|
inline js::GlobalObject &global() const;
|
|
js::GlobalObject &uninlinedGlobal() const;
|
|
|
|
/* See StaticScopeIter comment. */
|
|
JSObject *enclosingStaticScope() const {
|
|
if (isCallsiteClone())
|
|
return nullptr;
|
|
return enclosingScopeOrOriginalFunction_;
|
|
}
|
|
|
|
private:
|
|
bool makeTypes(JSContext *cx);
|
|
|
|
public:
|
|
uint32_t getWarmUpCount() const {
|
|
return warmUpCount;
|
|
}
|
|
uint32_t incWarmUpCounter(uint32_t amount = 1) { return warmUpCount += amount; }
|
|
uint32_t *addressOfWarmUpCounter() { return &warmUpCount; }
|
|
static size_t offsetOfWarmUpCounter() { return offsetof(JSScript, warmUpCount); }
|
|
void resetWarmUpCounter() { warmUpCount = 0; }
|
|
|
|
public:
|
|
bool initScriptCounts(JSContext *cx);
|
|
js::PCCounts getPCCounts(jsbytecode *pc);
|
|
void addIonCounts(js::jit::IonScriptCounts *ionCounts);
|
|
js::jit::IonScriptCounts *getIonCounts();
|
|
js::ScriptCounts releaseScriptCounts();
|
|
void destroyScriptCounts(js::FreeOp *fop);
|
|
|
|
jsbytecode *main() {
|
|
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) {
|
|
return hasArrayBits & (1 << kind);
|
|
}
|
|
void setHasArray(ArrayKind kind) { hasArrayBits |= (1 << kind); }
|
|
void cloneHasArray(JSScript *script) { hasArrayBits = script->hasArrayBits; }
|
|
|
|
bool hasConsts() { return hasArray(CONSTS); }
|
|
bool hasObjects() { return hasArray(OBJECTS); }
|
|
bool hasRegexps() { return hasArray(REGEXPS); }
|
|
bool hasTrynotes() { return hasArray(TRYNOTES); }
|
|
bool hasBlockScopes() { return hasArray(BLOCK_SCOPES); }
|
|
|
|
#define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0))
|
|
|
|
size_t constsOffset() { return 0; }
|
|
size_t objectsOffset() { return OFF(constsOffset, hasConsts, js::ConstArray); }
|
|
size_t regexpsOffset() { return OFF(objectsOffset, hasObjects, js::ObjectArray); }
|
|
size_t trynotesOffset() { return OFF(regexpsOffset, hasRegexps, js::ObjectArray); }
|
|
size_t blockScopesOffset(){ return OFF(trynotesOffset, hasTrynotes, js::TryNoteArray); }
|
|
|
|
size_t dataSize() const { return dataSize_; }
|
|
|
|
js::ConstArray *consts() {
|
|
JS_ASSERT(hasConsts());
|
|
return reinterpret_cast<js::ConstArray *>(data + constsOffset());
|
|
}
|
|
|
|
js::ObjectArray *objects() {
|
|
JS_ASSERT(hasObjects());
|
|
return reinterpret_cast<js::ObjectArray *>(data + objectsOffset());
|
|
}
|
|
|
|
js::ObjectArray *regexps() {
|
|
JS_ASSERT(hasRegexps());
|
|
return reinterpret_cast<js::ObjectArray *>(data + regexpsOffset());
|
|
}
|
|
|
|
js::TryNoteArray *trynotes() {
|
|
JS_ASSERT(hasTrynotes());
|
|
return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset());
|
|
}
|
|
|
|
js::BlockScopeArray *blockScopes() {
|
|
JS_ASSERT(hasBlockScopes());
|
|
return reinterpret_cast<js::BlockScopeArray *>(data + blockScopesOffset());
|
|
}
|
|
|
|
bool hasLoops();
|
|
|
|
size_t natoms() const { return natoms_; }
|
|
|
|
js::HeapPtrAtom &getAtom(size_t index) const {
|
|
JS_ASSERT(index < natoms());
|
|
return atoms[index];
|
|
}
|
|
|
|
js::HeapPtrAtom &getAtom(jsbytecode *pc) const {
|
|
JS_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 {
|
|
JS_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
|
|
return getAtom(GET_UINT32_INDEX(pc))->asPropertyName();
|
|
}
|
|
|
|
JSObject *getObject(size_t index) {
|
|
js::ObjectArray *arr = objects();
|
|
JS_ASSERT(index < arr->length);
|
|
return arr->vector[index];
|
|
}
|
|
|
|
size_t innerObjectsStart() {
|
|
// The first object contains the caller if savedCallerFun is used.
|
|
return savedCallerFun() ? 1 : 0;
|
|
}
|
|
|
|
JSObject *getObject(jsbytecode *pc) {
|
|
JS_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
|
|
return getObject(GET_UINT32_INDEX(pc));
|
|
}
|
|
|
|
JSVersion getVersion() const {
|
|
return JSVersion(version);
|
|
}
|
|
|
|
inline JSFunction *getFunction(size_t index);
|
|
inline JSFunction *getCallerFunction();
|
|
inline JSFunction *functionOrCallerFunction();
|
|
|
|
inline js::RegExpObject *getRegExp(size_t index);
|
|
inline js::RegExpObject *getRegExp(jsbytecode *pc);
|
|
|
|
const js::Value &getConst(size_t index) {
|
|
js::ConstArray *arr = consts();
|
|
JS_ASSERT(index < arr->length);
|
|
return arr->vector[index];
|
|
}
|
|
|
|
js::NestedScopeObject *getStaticScope(jsbytecode *pc);
|
|
|
|
/*
|
|
* 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 varIsAliased(uint32_t varSlot);
|
|
bool bodyLevelLocalIsAliased(uint32_t localSlot);
|
|
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_; }
|
|
|
|
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 inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
|
|
|
|
void markChildren(JSTracer *trc);
|
|
};
|
|
|
|
/* 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 {
|
|
|
|
/*
|
|
* Iterator over a script's bindings (formals and variables).
|
|
* The order of iteration is:
|
|
* - first, formal arguments, from index 0 to numArgs
|
|
* - next, variables, from index 0 to numLocals
|
|
*/
|
|
class BindingIter
|
|
{
|
|
const InternalBindingsHandle bindings_;
|
|
uint32_t i_;
|
|
|
|
friend class Bindings;
|
|
|
|
public:
|
|
explicit BindingIter(const InternalBindingsHandle &bindings) : bindings_(bindings), i_(0) {}
|
|
explicit BindingIter(const HandleScript &script) : bindings_(script, &script->bindings), i_(0) {}
|
|
|
|
bool done() const { return i_ == bindings_->count(); }
|
|
operator bool() const { return !done(); }
|
|
void operator++(int) { JS_ASSERT(!done()); i_++; }
|
|
BindingIter &operator++() { (*this)++; return *this; }
|
|
|
|
uint32_t frameIndex() const {
|
|
JS_ASSERT(!done());
|
|
return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs();
|
|
}
|
|
|
|
const Binding &operator*() const { JS_ASSERT(!done()); return bindings_->bindingArray()[i_]; }
|
|
const Binding *operator->() const { JS_ASSERT(!done()); return &bindings_->bindingArray()[i_]; }
|
|
};
|
|
|
|
/*
|
|
* This helper function fills the given BindingVector with the sequential
|
|
* values of BindingIter.
|
|
*/
|
|
|
|
typedef Vector<Binding, 32> BindingVector;
|
|
|
|
extern bool
|
|
FillBindingVector(HandleScript fromScript, BindingVector *vec);
|
|
|
|
/*
|
|
* Iterator over the aliased formal bindings in ascending index order. This can
|
|
* be veiwed as a filtering of BindingIter with predicate
|
|
* bi->aliased() && bi->kind() == Binding::ARGUMENT
|
|
*/
|
|
class AliasedFormalIter
|
|
{
|
|
const Binding *begin_, *p_, *end_;
|
|
unsigned slot_;
|
|
|
|
void settle() {
|
|
while (p_ != end_ && !p_->aliased())
|
|
p_++;
|
|
}
|
|
|
|
public:
|
|
explicit inline AliasedFormalIter(JSScript *script);
|
|
|
|
bool done() const { return p_ == end_; }
|
|
operator bool() const { return !done(); }
|
|
void operator++(int) { JS_ASSERT(!done()); p_++; slot_++; settle(); }
|
|
|
|
const Binding &operator*() const { JS_ASSERT(!done()); return *p_; }
|
|
const Binding *operator->() const { JS_ASSERT(!done()); return p_; }
|
|
unsigned frameIndex() const { JS_ASSERT(!done()); return p_ - begin_; }
|
|
unsigned scopeSlot() const { JS_ASSERT(!done()); return slot_; }
|
|
};
|
|
|
|
// Information about a script which may be (or has been) lazily compiled to
|
|
// bytecode from its source.
|
|
class LazyScript : public gc::TenuredCell
|
|
{
|
|
public:
|
|
class FreeVariable
|
|
{
|
|
// Free variable names are possible tagged JSAtom *s.
|
|
uintptr_t bits_;
|
|
|
|
static const uintptr_t HOISTED_USE_BIT = 0x1;
|
|
static const uintptr_t MASK = ~HOISTED_USE_BIT;
|
|
|
|
public:
|
|
explicit FreeVariable()
|
|
: bits_(0)
|
|
{ }
|
|
|
|
explicit FreeVariable(JSAtom *name)
|
|
: bits_(uintptr_t(name))
|
|
{
|
|
// We rely on not requiring any write barriers so we can tag the
|
|
// pointer. This code needs to change if we start allocating
|
|
// JSAtoms inside the nursery.
|
|
MOZ_ASSERT(!IsInsideNursery(name));
|
|
}
|
|
|
|
JSAtom *atom() const { return (JSAtom *)(bits_ & MASK); }
|
|
void setIsHoistedUse() { bits_ |= HOISTED_USE_BIT; }
|
|
bool isHoistedUse() const { return bool(bits_ & HOISTED_USE_BIT); }
|
|
};
|
|
|
|
private:
|
|
// If non-nullptr, the script has been compiled and this is a forwarding
|
|
// pointer to the result.
|
|
HeapPtrScript script_;
|
|
|
|
// Original function with which the lazy script is associated.
|
|
HeapPtrFunction function_;
|
|
|
|
// Function or block chain in which the script is nested, or nullptr.
|
|
HeapPtrObject enclosingScope_;
|
|
|
|
// ScriptSourceObject, or nullptr if the script in which this is nested
|
|
// has not been compiled yet. This is never a CCW; we don't clone
|
|
// LazyScripts into other compartments.
|
|
HeapPtrObject sourceObject_;
|
|
|
|
// Heap allocated table with any free variables or inner functions.
|
|
void *table_;
|
|
|
|
#if JS_BITS_PER_WORD == 32
|
|
uint32_t padding;
|
|
#endif
|
|
|
|
struct PackedView {
|
|
// Assorted bits that should really be in ScriptSourceObject.
|
|
uint32_t version : 8;
|
|
|
|
uint32_t numFreeVariables : 24;
|
|
uint32_t numInnerFunctions : 23;
|
|
|
|
uint32_t generatorKindBits : 2;
|
|
|
|
// N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
|
|
uint32_t strict : 1;
|
|
uint32_t bindingsAccessedDynamically : 1;
|
|
uint32_t hasDebuggerStatement : 1;
|
|
uint32_t directlyInsideEval : 1;
|
|
uint32_t usesArgumentsAndApply : 1;
|
|
uint32_t hasBeenCloned : 1;
|
|
uint32_t treatAsRunOnce : 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 freeVariables 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:
|
|
// Create a LazyScript without initializing the freeVariables 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,
|
|
uint32_t numFreeVariables, uint32_t numInnerFunctions,
|
|
JSVersion version, uint32_t begin, uint32_t end,
|
|
uint32_t lineno, uint32_t column);
|
|
|
|
// Create a LazyScript and initialize the freeVariables and the
|
|
// innerFunctions with dummy values to be replaced in a later initialization
|
|
// phase.
|
|
static LazyScript *Create(ExclusiveContext *cx, HandleFunction fun,
|
|
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_;
|
|
}
|
|
|
|
JSObject *enclosingScope() const {
|
|
return enclosingScope_;
|
|
}
|
|
ScriptSourceObject *sourceObject() const;
|
|
ScriptSource *scriptSource() const {
|
|
return sourceObject()->source();
|
|
}
|
|
JSPrincipals *originPrincipals() const {
|
|
return scriptSource()->originPrincipals();
|
|
}
|
|
JSVersion version() const {
|
|
JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1);
|
|
return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version);
|
|
}
|
|
|
|
void setParent(JSObject *enclosingScope, ScriptSourceObject *sourceObject);
|
|
|
|
uint32_t numFreeVariables() const {
|
|
return p_.numFreeVariables;
|
|
}
|
|
FreeVariable *freeVariables() {
|
|
return (FreeVariable *)table_;
|
|
}
|
|
|
|
uint32_t numInnerFunctions() const {
|
|
return p_.numInnerFunctions;
|
|
}
|
|
HeapPtrFunction *innerFunctions() {
|
|
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
|
|
}
|
|
|
|
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.
|
|
JS_ASSERT(!isGenerator());
|
|
// Legacy generators cannot currently be lazy.
|
|
JS_ASSERT(kind != LegacyGenerator);
|
|
p_.generatorKindBits = GeneratorKindAsBits(kind);
|
|
}
|
|
|
|
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 directlyInsideEval() const {
|
|
return p_.directlyInsideEval;
|
|
}
|
|
void setDirectlyInsideEval() {
|
|
p_.directlyInsideEval = true;
|
|
}
|
|
|
|
bool usesArgumentsAndApply() const {
|
|
return p_.usesArgumentsAndApply;
|
|
}
|
|
void setUsesArgumentsAndApply() {
|
|
p_.usesArgumentsAndApply = true;
|
|
}
|
|
|
|
bool hasBeenCloned() const {
|
|
return p_.hasBeenCloned;
|
|
}
|
|
void setHasBeenCloned() {
|
|
p_.hasBeenCloned = true;
|
|
}
|
|
|
|
bool treatAsRunOnce() const {
|
|
return p_.treatAsRunOnce;
|
|
}
|
|
void setTreatAsRunOnce() {
|
|
p_.treatAsRunOnce = true;
|
|
}
|
|
|
|
ScriptSource *source() const {
|
|
return sourceObject()->source();
|
|
}
|
|
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;
|
|
uint32_t staticLevel(JSContext *cx) const;
|
|
|
|
void markChildren(JSTracer *trc);
|
|
void finalize(js::FreeOp *fop);
|
|
|
|
static inline js::ThingRootKind rootKind() { return js::THING_ROOT_LAZY_SCRIPT; }
|
|
|
|
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 SharedScriptData
|
|
{
|
|
uint32_t length;
|
|
uint32_t natoms;
|
|
bool marked;
|
|
jsbytecode data[1];
|
|
|
|
static SharedScriptData *new_(ExclusiveContext *cx, uint32_t codeLength,
|
|
uint32_t srcnotesLength, uint32_t natoms);
|
|
|
|
HeapPtrAtom *atoms() {
|
|
if (!natoms)
|
|
return nullptr;
|
|
return reinterpret_cast<HeapPtrAtom *>(data + length - sizeof(JSAtom *) * natoms);
|
|
}
|
|
|
|
static SharedScriptData *fromBytecode(const jsbytecode *bytecode) {
|
|
return (SharedScriptData *)(bytecode - offsetof(SharedScriptData, data));
|
|
}
|
|
|
|
private:
|
|
SharedScriptData() MOZ_DELETE;
|
|
SharedScriptData(const SharedScriptData&) MOZ_DELETE;
|
|
};
|
|
|
|
struct ScriptBytecodeHasher
|
|
{
|
|
struct Lookup
|
|
{
|
|
jsbytecode *code;
|
|
uint32_t length;
|
|
|
|
explicit Lookup(SharedScriptData *ssd) : code(ssd->data), length(ssd->length) {}
|
|
};
|
|
static HashNumber hash(const Lookup &l) { return mozilla::HashBytes(l.code, l.length); }
|
|
static bool match(SharedScriptData *entry, const Lookup &lookup) {
|
|
if (entry->length != lookup.length)
|
|
return false;
|
|
return mozilla::PodEqual<jsbytecode>(entry->data, lookup.code, lookup.length);
|
|
}
|
|
};
|
|
|
|
typedef HashSet<SharedScriptData*,
|
|
ScriptBytecodeHasher,
|
|
SystemAllocPolicy> ScriptDataTable;
|
|
|
|
extern void
|
|
UnmarkScriptData(JSRuntime *rt);
|
|
|
|
extern void
|
|
SweepScriptData(JSRuntime *rt);
|
|
|
|
extern void
|
|
FreeScriptData(JSRuntime *rt);
|
|
|
|
struct ScriptAndCounts
|
|
{
|
|
/* This structure is stored and marked from the JSRuntime. */
|
|
JSScript *script;
|
|
ScriptCounts scriptCounts;
|
|
|
|
PCCounts &getPCCounts(jsbytecode *pc) const {
|
|
return scriptCounts.pcCountsVector[script->pcToOffset(pc)];
|
|
}
|
|
|
|
jit::IonScriptCounts *getIonCounts() const {
|
|
return scriptCounts.ionCounts;
|
|
}
|
|
};
|
|
|
|
struct GSNCache;
|
|
|
|
jssrcnote *
|
|
GetSrcNote(GSNCache &cache, JSScript *script, jsbytecode *pc);
|
|
|
|
} /* namespace js */
|
|
|
|
extern jssrcnote *
|
|
js_GetSrcNote(JSContext *cx, JSScript *script, jsbytecode *pc);
|
|
|
|
extern jsbytecode *
|
|
js_LineNumberToPC(JSScript *script, unsigned lineno);
|
|
|
|
extern JS_FRIEND_API(unsigned)
|
|
js_GetScriptLineExtent(JSScript *script);
|
|
|
|
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, JSPrincipals **origin,
|
|
LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
|
|
|
|
bool
|
|
CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone,
|
|
NewObjectKind newKind = GenericObject);
|
|
|
|
/*
|
|
* JSAPI clients are allowed to leave CompileOptions.originPrincipals nullptr in
|
|
* which case the JS engine sets options.originPrincipals = origin.principals.
|
|
* This normalization step must occur before the originPrincipals get stored in
|
|
* the JSScript/ScriptSource.
|
|
*/
|
|
|
|
static inline JSPrincipals *
|
|
NormalizeOriginPrincipals(JSPrincipals *principals, JSPrincipals *originPrincipals)
|
|
{
|
|
return originPrincipals ? originPrincipals : principals;
|
|
}
|
|
|
|
} /* 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<> struct Concrete<js::LazyScript> : TracerConcrete<js::LazyScript> { };
|
|
}
|
|
}
|
|
|
|
#endif /* jsscript_h */
|