import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1212298 - Use inner script instead of outer script in CodeGenerator::visitCallDirectEval. r=shu (cee3f366a6)
- Bug 1233331 - CodeGenerator: Properly indent IonScript::New. r=jandem (6d110c45a3)
- Bug 1233331 - CodeGenerator: Prepare the invalidation of the recompileInfo as soon as the contraints are recorded. r=jandem (679d22dd8e)
- Bug 1238417 - Part 1: Fix wrong rebase for _SetCanonicalName call on RegExpToString. r=till (31ee926189)
- Bug 1238417 - Part 2: Fix argument count of RegExpMatcher and RegExpTester. r=till (9dc5dcadd5)
- Bug 1238417 - Part 4: Enable recover instruction for RegExpMatcher and RegExpTester. r=h4writer (5479b238ac)
- Bug 1238417 - Part 5: Add RegExpMatcher to MustCloneRegExp optimization. r=h4writer (554905fa3a)
- Bug 1238417 - Part 6: Make RegExpMatcher and RegExpTester movable. r=h4writer (72091090ee)
- Bug 1238417 - Part 7: Add comment for OutOfLineRegExpMatcher and OutOfLineRegExpTester. r=nbp (f5e4519728)
- Bug 1238630 - Fix unicode surrogate pair handling in RegExp. r=h4writer (d4c1e1d49d)
- Bug 1236600 - Properly pre-barrier sets to inline TypedObject Any-type Elements. (r=jandem) (1f23bb6d61)
- Bug 1149245 - Make DeserializedEdgeRange re-use its referents edge vector; r=vporof (ea861bfd43)
- Bug 1235631 - Odin: remove change-heap support (r=bbouvier) (940a0d58bc)
- Bug 1231224 part 11 - Add missing OOM checks in Module::setProfilingEnabled. r=luke (0d264fa46b)
- Bug 1234402 - Crash on OOM in AlternativeGenerationList constructor. r=bbouvier (baa7b3da17)
- Bug 1231224 part 12 - Use InfallibleVector in irregexp code to avoid MOZ_WARN_UNUSED_RESULT warnings. r=luke (72ac897dab)
- Bug 1231224 part 13 - Add OOM checks to Statistics::initialize. r=jonco (5033150018)
- Bug 1237508 - Odin: remove function index from Export (r=bbouvier) (d368ef7f85)
- Bug 1236541 - Odin: when enabling profiling, only patch actual callsites (r=bbouvie) (713dbcc45c)
- Bug 1235046 - Optimize JIT-code poisoning to be fast with W^X. r=bhackett (25972b36a9)
- Bug 1215479 - Turn on W^X JIT code by default. r=luke (82c4b94315)
- Bug 1235868 - Change nonWritableJITCode to ifdefs. r=jandem (4dee262ff4)
- Bug 1237508 - Add missing #include to fix non-unified builds (r=me) (327242e706)
- Bug 1236530 - Make ExecutableAllocator::reprotectRegion fallible and handle in asm.js (r=jandem) (9444127563)
- Bug 1229399: Make initialization of asm.js local variables closer to wasm; r=luke (732d40b42c)
- Bug 1229399: Store line/column info in the FuncIR rather than the bytecode stream; r=luke (483faefbdd)
- Bug 1235989 - Add a null check for filename in ModuleValidator::finish. r=luke (abc62aa437)
- Bug 1235041 - Cast value to uint64_t in order to prevent int overflow when value is greater than 2^12. r=jonco (ef754091ea)
- Bug 1182369 - Remove js/Class.h include from nsWrapperCache.h. - r=bz (cc7b3c856b)
- Bug 1231964 - Move CC participant code that touches JS out of mozglue. r=smaug (100fceeb2b)
- Bug 1120016 - Allocate short lived JS wrappers in the Nursery, r=mccr8,terrence (2a17a5484d)
- Bug 1235277 - Define MOZ_FALLTHROUGH_ASSERT to workaround -Wunreachable-code warnings about MOZ_FALLTHROUGH in debug builds. r=botond (262589e609)
- Bug 1247679, part 1 - Make ClearJSHolder publicly inherit from TraceCallbacks. r=smaug (1a3543fd31)
- Bug 1235598 - Part 1: Add better SpiderMonkey API support for tracing in C++; r=sfinxk (f23bf81919)
- Bug 1235598 - Part 2: Use TraceEdge exclusively in Gecko; r=smaug (a3ad4d0ef7)
This commit is contained in:
2023-08-02 11:57:35 +08:00
parent 7d2b02d5fd
commit df6f7b7065
101 changed files with 1216 additions and 2301 deletions
+25 -1
View File
@@ -6,8 +6,10 @@
#include "nsWrapperCacheInlines.h"
#include "js/Class.h"
#include "js/Proxy.h"
#include "mozilla/dom/DOMJSProxyHandler.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/HoldDropJSObjects.h"
#include "nsCycleCollectionTraversalCallback.h"
#include "nsCycleCollector.h"
@@ -15,11 +17,33 @@
using namespace mozilla;
using namespace mozilla::dom;
/* static */ void
#ifdef DEBUG
/* static */ bool
nsWrapperCache::HasJSObjectMovedOp(JSObject* aWrapper)
{
return js::HasObjectMovedOp(aWrapper);
}
#endif
void
nsWrapperCache::HoldJSObjects(void* aScriptObjectHolder,
nsScriptObjectTracer* aTracer)
{
cyclecollector::HoldJSObjectsImpl(aScriptObjectHolder, aTracer);
if (mWrapper && !JS::ObjectIsTenured(mWrapper)) {
CycleCollectedJSRuntime::Get()->NurseryWrapperPreserved(mWrapper);
}
}
void
nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper)
{
mWrapper = aWrapper;
UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_NOT_DOM_BINDING);
if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
}
}
void
+17 -15
View File
@@ -9,7 +9,6 @@
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Assertions.h"
#include "js/Class.h"
#include "js/Id.h" // must come before js/RootingAPI.h
#include "js/Value.h" // must come before js/RootingAPI.h
#include "js/RootingAPI.h"
@@ -66,6 +65,7 @@ class nsWindowRoot;
* have to include some JS headers that don't play nicely with the rest of the
* codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
*/
class nsWrapperCache
{
public:
@@ -103,11 +103,18 @@ public:
return GetWrapperJSObject();
}
#ifdef DEBUG
private:
static bool HasJSObjectMovedOp(JSObject* aWrapper);
public:
#endif
void SetWrapper(JSObject* aWrapper)
{
MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
MOZ_ASSERT(js::HasObjectMovedOp(aWrapper),
MOZ_ASSERT(HasJSObjectMovedOp(aWrapper),
"Object has not provided the hook to update the wrapper if it is moved");
SetWrapperJSObject(aWrapper);
@@ -251,14 +258,15 @@ protected:
void TraceWrapper(JSTracer* aTrc, const char* name)
{
if (mWrapper) {
JS_CallObjectTracer(aTrc, &mWrapper, name);
JS_CallUnbarrieredObjectTracer(aTrc, &mWrapper, name);
}
}
void PoisonWrapper()
{
if (mWrapper) {
mWrapper.setToCrashOnTouch();
// See setToCrashOnTouch() in RootingAPI.h
mWrapper = reinterpret_cast<JSObject*>(1);
}
}
@@ -280,13 +288,7 @@ private:
return mWrapper;
}
void SetWrapperJSObject(JSObject* aWrapper)
{
mWrapper = aWrapper;
UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_NOT_DOM_BINDING);
}
void TraceWrapperJSObject(JSTracer* aTrc, const char* aName);
void SetWrapperJSObject(JSObject* aWrapper);
FlagsType GetWrapperFlags() const
{
@@ -311,8 +313,8 @@ private:
mFlags &= ~aFlagsToUnset;
}
static void HoldJSObjects(void* aScriptObjectHolder,
nsScriptObjectTracer* aTracer);
void HoldJSObjects(void* aScriptObjectHolder,
nsScriptObjectTracer* aTracer);
#ifdef DEBUG
public:
@@ -342,8 +344,8 @@ private:
enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_NOT_DOM_BINDING) };
JS::Heap<JSObject*> mWrapper;
FlagsType mFlags;
JSObject* mWrapper;
FlagsType mFlags;
};
enum { WRAPPER_CACHE_FLAGS_BITS_USED = 2 };
-6
View File
@@ -53,10 +53,4 @@ nsWrapperCache::IsBlackAndDoesNotNeedTracing(nsISupports* aThis)
return IsBlack() && HasNothingToTrace(aThis);
}
inline void
nsWrapperCache::TraceWrapperJSObject(JSTracer* aTrc, const char* aName)
{
JS_CallObjectTracer(aTrc, &mWrapper, aName);
}
#endif /* nsWrapperCache_h___ */
+2 -6
View File
@@ -334,9 +334,7 @@ class ProtoAndIfaceCache
void Trace(JSTracer* aTracer) {
for (size_t i = 0; i < ArrayLength(*this); ++i) {
if ((*this)[i]) {
JS_CallObjectTracer(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
}
JS::TraceNullableEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
}
}
@@ -395,9 +393,7 @@ class ProtoAndIfaceCache
Page* p = mPages[i];
if (p) {
for (size_t j = 0; j < ArrayLength(*p); ++j) {
if ((*p)[j]) {
JS_CallObjectTracer(trc, &(*p)[j], "protoAndIfaceCache[i]");
}
JS::TraceNullableEdge(trc, &(*p)[j], "protoAndIfaceCache[i]");
}
}
}
+2
View File
@@ -422,6 +422,8 @@ class CGDOMJSClass(CGThing):
classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
traceHook = 'nullptr'
reservedSlots = slotCount
if self.descriptor.interface.isProbablyShortLivingObject():
classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
resolveHook = RESOLVE_HOOK_NAME
mayResolveHook = MAY_RESOLVE_HOOK_NAME
+13 -1
View File
@@ -564,6 +564,9 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
def isJSImplemented(self):
return False
def isProbablyShortLivingObject(self):
return False
def getNavigatorProperty(self):
return None
@@ -1408,7 +1411,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
identifier == "ChromeOnly" or
identifier == "Unforgeable" or
identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit"):
identifier == "LegacyEventInit" or
identifier == "ProbablyShortLivingObject"):
# Known extended attributes that do not take values
if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
@@ -1522,6 +1526,14 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
def isJSImplemented(self):
return bool(self.getJSImplementation())
def isProbablyShortLivingObject(self):
current = self
while current:
if current.getExtendedAttribute("ProbablyShortLivingObject"):
return True
current = current.parent
return False
def getNavigatorProperty(self):
naviProp = self.getExtendedAttribute("NavigatorProperty")
if not naviProp:
-3
View File
@@ -237,9 +237,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
JSObject*
Event::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
if (mIsMainThreadEvent && !GetWrapperPreserveColor()) {
nsJSContext::LikelyShortLivingObjectCreated();
}
return WrapObjectInternal(aCx, aGivenProto);
}
+5 -12
View File
@@ -289,9 +289,7 @@ TraceJSObjWrappers(JSTracer *trc, void *data)
nsJSObjWrapperKey key = e.front().key();
JS_CallUnbarrieredObjectTracer(trc, &key.mJSObj, "sJSObjWrappers key object");
nsJSObjWrapper *wrapper = e.front().value();
if (wrapper->mJSObj) {
JS_CallObjectTracer(trc, &wrapper->mJSObj, "sJSObjWrappers wrapper object");
}
JS::TraceNullableEdge(trc, &wrapper->mJSObj, "sJSObjWrappers wrapper object");
if (key != e.front().key()) {
e.rekeyFront(key);
}
@@ -2268,21 +2266,16 @@ NPObjectMember_Trace(JSTracer* trc, JSObject* obj)
if (!memberPrivate)
return;
// Our NPIdentifier is not always interned, so we must root it explicitly.
JS_CallIdTracer(trc, &memberPrivate->methodName, "NPObjectMemberPrivate.methodName");
// Our NPIdentifier is not always interned, so we must trace it.
JS::TraceEdge(trc, &memberPrivate->methodName, "NPObjectMemberPrivate.methodName");
if (!memberPrivate->fieldValue.isPrimitive()) {
JS_CallValueTracer(trc, &memberPrivate->fieldValue,
"NPObject Member => fieldValue");
}
JS::TraceEdge(trc, &memberPrivate->fieldValue, "NPObject Member => fieldValue");
// There's no strong reference from our private data to the
// NPObject, so make sure to mark the NPObject wrapper to keep the
// NPObject alive as long as this NPObjectMember is alive.
if (memberPrivate->npobjWrapper) {
JS_CallObjectTracer(trc, &memberPrivate->npobjWrapper,
JS::TraceNullableEdge(trc, &memberPrivate->npobjWrapper,
"NPObject Member => npobjWrapper");
}
}
static bool
+1 -1
View File
@@ -11,7 +11,7 @@
*/
[Constructor(DOMString type, optional EventInit eventInitDict),
Exposed=(Window,Worker,System)]
Exposed=(Window,Worker,System), ProbablyShortLivingObject]
interface Event {
[Pure]
readonly attribute DOMString type;
+1
View File
@@ -7,6 +7,7 @@
* http://dom.spec.whatwg.org
*/
[ProbablyShortLivingObject]
interface MutationRecord {
[Constant]
readonly attribute DOMString type;
+2 -2
View File
@@ -559,8 +559,8 @@ public:
private:
virtual void trace(JSTracer* aTrc)
{
JS_CallValueTracer(aTrc, &mStateData->mResponse,
"XMLHttpRequest::StateData::mResponse");
JS::TraceEdge(aTrc, &mStateData->mResponse,
"XMLHttpRequest::StateData::mResponse");
}
};
+1 -3
View File
@@ -249,9 +249,7 @@ public:
void TraceScriptObject(JSTracer* aTrc)
{
if (mScriptObject) {
JS_CallScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script");
}
JS::TraceNullableEdge(aTrc, &mScriptObject, "active window XUL prototype script");
}
void Trace(const TraceCallbacks& aCallbacks, void* aClosure)
+1 -1
View File
@@ -608,7 +608,7 @@ static PLDHashOperator
MarkScriptsInGC(nsIURI* aKey, JS::Heap<JSScript*>& aScript, void* aClosure)
{
JSTracer* trc = static_cast<JSTracer*>(aClosure);
JS_CallScriptTracer(trc, &aScript, "nsXULPrototypeCache script");
JS::TraceEdge(trc, &aScript, "nsXULPrototypeCache script");
return PL_DHASH_NEXT;
}
+2 -4
View File
@@ -35,10 +35,8 @@ IdToObjectMap::init()
void
IdToObjectMap::trace(JSTracer* trc)
{
for (Table::Range r(table_.all()); !r.empty(); r.popFront()) {
DebugOnly<JSObject*> prior = r.front().value().get();
JS_CallObjectTracer(trc, &r.front().value(), "ipc-object");
}
for (Table::Range r(table_.all()); !r.empty(); r.popFront())
JS::TraceEdge(trc, &r.front().value(), "ipc-object");
}
void
+19 -30
View File
@@ -281,40 +281,29 @@ JSTracer::asCallbackTracer()
return static_cast<JS::CallbackTracer*>(this);
}
// The JS_Call*Tracer family of functions traces the given GC thing reference.
// This performs the tracing action configured on the given JSTracer:
// typically calling the JSTracer::callback or marking the thing as live.
//
// The argument to JS_Call*Tracer is an in-out param: when the function
// returns, the garbage collector might have moved the GC thing. In this case,
// the reference passed to JS_Call*Tracer will be updated to the object's new
// location. Callers of this method are responsible for updating any state
// that is dependent on the object's address. For example, if the object's
// address is used as a key in a hashtable, then the object must be removed
// and re-inserted with the correct hash.
//
extern JS_PUBLIC_API(void)
JS_CallValueTracer(JSTracer* trc, JS::Heap<JS::Value>* valuep, const char* name);
extern JS_PUBLIC_API(void)
JS_CallIdTracer(JSTracer* trc, JS::Heap<jsid>* idp, const char* name);
extern JS_PUBLIC_API(void)
JS_CallObjectTracer(JSTracer* trc, JS::Heap<JSObject*>* objp, const char* name);
extern JS_PUBLIC_API(void)
JS_CallStringTracer(JSTracer* trc, JS::Heap<JSString*>* strp, const char* name);
extern JS_PUBLIC_API(void)
JS_CallScriptTracer(JSTracer* trc, JS::Heap<JSScript*>* scriptp, const char* name);
extern JS_PUBLIC_API(void)
JS_CallFunctionTracer(JSTracer* trc, JS::Heap<JSFunction*>* funp, const char* name);
namespace JS {
// The JS::TraceEdge family of functions traces the given GC thing reference.
// This performs the tracing action configured on the given JSTracer: typically
// calling the JSTracer::callback or marking the thing as live.
//
// The argument to JS::TraceEdge is an in-out param: when the function returns,
// the garbage collector might have moved the GC thing. In this case, the
// reference passed to JS::TraceEdge will be updated to the thing's new
// location. Callers of this method are responsible for updating any state that
// is dependent on the object's address. For example, if the object's address
// is used as a key in a hashtable, then the object must be removed and
// re-inserted with the correct hash.
template <typename T>
extern JS_PUBLIC_API(void)
TraceEdge(JSTracer* trc, JS::Heap<T>* edgep, const char* name);
// As with JS::TraceEdge, but checks if *edgep is a nullptr before proceeding.
// Note that edgep itself must always be non-null.
template <typename T>
extern JS_PUBLIC_API(void)
TraceNullableEdge(JSTracer* trc, JS::Heap<T>* edgep, const char* name);
} // namespace JS
// The following JS_CallUnbarriered*Tracer functions should only be called where
+124 -555
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -109,9 +109,6 @@ IsValidAsmJSHeapLength(uint32_t length);
extern uint32_t
RoundUpToNextValidAsmJSHeapLength(uint32_t length);
extern bool
OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
// The assumed page size; dynamically checked in CompileAsmJS.
#ifdef _MIPS_ARCH_LOONGSON3A
static const size_t AsmJSPageSize = 16384;
+8 -9
View File
@@ -49,6 +49,7 @@ ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
freeTasks_(cx),
funcBytes_(0),
funcEntryOffsets_(cx),
exportFuncIndices_(cx),
activeFunc_(nullptr),
finishedFuncs_(false)
{
@@ -312,16 +313,16 @@ ModuleGenerator::defineImport(uint32_t index, ProfilingOffsets interpExit, Profi
}
bool
ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index)
ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex)
{
*index = exports_.length();
return exports_.emplaceBack(Move(sig), funcIndex);
return exports_.emplaceBack(Move(sig)) &&
exportFuncIndices_.append(funcIndex);
}
uint32_t
ModuleGenerator::exportFuncIndex(uint32_t index) const
{
return exports_[index].funcIndex();
return exportFuncIndices_[index];
}
const MallocSig&
@@ -499,8 +500,7 @@ ModuleGenerator::defineOutOfBoundsStub(Offsets offsets)
}
Module*
ModuleGenerator::finish(Module::HeapBool usesHeap,
Module::SharedBool sharedHeap,
ModuleGenerator::finish(HeapUsage heapUsage,
Module::MutedBool mutedErrors,
CacheableChars filename,
CacheableTwoByteChars displayURL,
@@ -510,7 +510,7 @@ ModuleGenerator::finish(Module::HeapBool usesHeap,
MOZ_ASSERT(!activeFunc_);
MOZ_ASSERT(finishedFuncs_);
if (!GenerateStubs(*this, usesHeap))
if (!GenerateStubs(*this, UsesHeap(heapUsage)))
return nullptr;
masm_.finish();
@@ -616,8 +616,7 @@ ModuleGenerator::finish(Module::HeapBool usesHeap,
funcBytes_,
codeBytes,
globalBytes_,
usesHeap,
sharedHeap,
heapUsage,
mutedErrors,
Move(code),
Move(imports_),
+4 -3
View File
@@ -54,6 +54,7 @@ typedef Vector<SlowFunction> SlowFunctionVector;
class MOZ_STACK_CLASS ModuleGenerator
{
typedef Vector<uint32_t> FuncOffsetVector;
typedef Vector<uint32_t> FuncIndexVector;
struct SigHashPolicy
{
@@ -93,6 +94,7 @@ class MOZ_STACK_CLASS ModuleGenerator
// Function compilation
uint32_t funcBytes_;
FuncOffsetVector funcEntryOffsets_;
FuncIndexVector exportFuncIndices_;
DebugOnly<FunctionGenerator*> activeFunc_;
DebugOnly<bool> finishedFuncs_;
@@ -123,7 +125,7 @@ class MOZ_STACK_CLASS ModuleGenerator
bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
// Exports:
bool declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index);
bool declareExport(MallocSig&& sig, uint32_t funcIndex);
uint32_t numDeclaredExports() const;
uint32_t exportFuncIndex(uint32_t index) const;
const MallocSig& exportSig(uint32_t index) const;
@@ -147,8 +149,7 @@ class MOZ_STACK_CLASS ModuleGenerator
// Null return indicates failure. The caller must immediately root a
// non-null return value.
Module* finish(Module::HeapBool usesHeap,
Module::SharedBool sharedHeap,
Module* finish(HeapUsage heapUsage,
Module::MutedBool mutedErrors,
CacheableChars filename,
CacheableTwoByteChars displayURL,
+41 -21
View File
@@ -430,9 +430,22 @@ enum NeedsBoundsCheck : uint8_t
// referenced by the FuncIR.
class FuncIR
{
typedef Vector<wasm::Val, 4, LifoAllocPolicy<Fallible>> VarInitVector;
typedef Vector<ValType, 4, LifoAllocPolicy<Fallible>> ValTypeVector;
typedef Vector<uint8_t, 4096, LifoAllocPolicy<Fallible>> Bytecode;
public:
// Source coordinates for a call site. As they're read sequentially, we
// don't need to store the call's bytecode offset, unless we want to
// check its consistency in debug mode.
struct SourceCoords {
DebugOnly<uint32_t> offset; // after call opcode
uint32_t line;
uint32_t column;
};
private:
typedef Vector<SourceCoords, 4, LifoAllocPolicy<Fallible>> SourceCoordsVector;
// Note: this unrooted field assumes AutoKeepAtoms via TokenStream via
// asm.js compilation.
PropertyName* name_;
@@ -440,8 +453,9 @@ class FuncIR
unsigned column_;
uint32_t index_;
const wasm::LifoSig* sig_;
VarInitVector varInits_;
const LifoSig* sig_;
ValTypeVector localVars_;
SourceCoordsVector callSourceCoords_;
Bytecode bytecode_;
unsigned generateTime_;
@@ -452,16 +466,21 @@ class FuncIR
column_(column),
index_(UINT_MAX),
sig_(nullptr),
varInits_(alloc),
localVars_(alloc),
callSourceCoords_(alloc),
bytecode_(alloc),
generateTime_(UINT_MAX)
{}
bool addVariable(wasm::Val v) {
return varInits_.append(v);
bool addVariable(ValType v) {
return localVars_.append(v);
}
bool addSourceCoords(uint32_t line, uint32_t column) {
SourceCoords sc = { bytecode_.length(), line, column };
return callSourceCoords_.append(sc);
}
void finish(uint32_t funcIndex, const wasm::LifoSig& sig, unsigned generateTime) {
void finish(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime) {
MOZ_ASSERT(index_ == UINT_MAX);
MOZ_ASSERT(!sig_);
MOZ_ASSERT(generateTime_ == UINT_MAX);
@@ -506,12 +525,12 @@ class FuncIR
return pos;
}
uint8_t readU8 (size_t* pc) const { return readPrimitive<uint8_t>(pc); }
int32_t readI32(size_t* pc) const { return readPrimitive<int32_t>(pc); }
float readF32(size_t* pc) const { return readPrimitive<float>(pc); }
uint32_t readU32(size_t* pc) const { return readPrimitive<uint32_t>(pc); }
double readF64(size_t* pc) const { return readPrimitive<double>(pc); }
const wasm::LifoSig* readSig(size_t* pc) const { return readPrimitive<wasm::LifoSig*>(pc); }
uint8_t readU8 (size_t* pc) const { return readPrimitive<uint8_t>(pc); }
int32_t readI32(size_t* pc) const { return readPrimitive<int32_t>(pc); }
float readF32(size_t* pc) const { return readPrimitive<float>(pc); }
uint32_t readU32(size_t* pc) const { return readPrimitive<uint32_t>(pc); }
double readF64(size_t* pc) const { return readPrimitive<double>(pc); }
const LifoSig* readSig(size_t* pc) const { return readPrimitive<LifoSig*>(pc); }
jit::SimdConstant readI32X4(size_t* pc) const {
int32_t x = readI32(pc);
@@ -532,7 +551,7 @@ class FuncIR
bool pcIsPatchable(size_t pc, unsigned size) const {
bool patchable = true;
for (unsigned i = 0; patchable && i < size; i++)
patchable &= wasm::Stmt(bytecode_[pc]) == wasm::Stmt::Bad;
patchable &= Stmt(bytecode_[pc]) == Stmt::Bad;
return patchable;
}
#endif
@@ -550,9 +569,9 @@ class FuncIR
memcpy(&bytecode_[pc], &i, sizeof(uint32_t));
}
void patchSig(size_t pc, const wasm::LifoSig* ptr) {
MOZ_ASSERT(pcIsPatchable(pc, sizeof(wasm::LifoSig*)));
memcpy(&bytecode_[pc], &ptr, sizeof(wasm::LifoSig*));
void patchSig(size_t pc, const LifoSig* ptr) {
MOZ_ASSERT(pcIsPatchable(pc, sizeof(LifoSig*)));
memcpy(&bytecode_[pc], &ptr, sizeof(LifoSig*));
}
// Read-only interface
@@ -561,11 +580,12 @@ class FuncIR
unsigned column() const { return column_; }
uint32_t index() const { MOZ_ASSERT(index_ != UINT32_MAX); return index_; }
size_t size() const { return bytecode_.length(); }
const wasm::LifoSig& sig() const { MOZ_ASSERT(sig_); return *sig_; }
size_t numVarInits() const { return varInits_.length(); }
wasm::Val varInit(size_t i) const { return varInits_[i]; }
size_t numLocals() const { return sig_->args().length() + varInits_.length(); }
const LifoSig& sig() const { MOZ_ASSERT(sig_); return *sig_; }
size_t numLocalVars() const { return localVars_.length(); }
ValType localVarType(size_t i) const { return localVars_[i]; }
size_t numLocals() const { return sig_->args().length() + numLocalVars(); }
unsigned generateTime() const { MOZ_ASSERT(generateTime_ != UINT_MAX); return generateTime_; }
const SourceCoords& sourceCoords(size_t i) const { return callSourceCoords_[i]; }
};
} // namespace wasm
+22 -31
View File
@@ -38,10 +38,10 @@ class FunctionCompiler
typedef HashMap<uint32_t, BlockVector, DefaultHasher<uint32_t>, SystemAllocPolicy> LabeledBlockMap;
typedef HashMap<size_t, BlockVector, DefaultHasher<uint32_t>, SystemAllocPolicy> UnlabeledBlockMap;
typedef Vector<size_t, 4, SystemAllocPolicy> PositionStack;
typedef Vector<ValType, 4, SystemAllocPolicy> LocalTypes;
const FuncIR& func_;
size_t pc_;
size_t lastReadCallSite_;
TempAllocator& alloc_;
MIRGraph& graph_;
@@ -57,14 +57,13 @@ class FunctionCompiler
LabeledBlockMap labeledBreaks_;
LabeledBlockMap labeledContinues_;
LocalTypes localTypes_;
FuncCompileResults& compileResults_;
public:
FunctionCompiler(const FuncIR& func, MIRGenerator& mirGen, FuncCompileResults& compileResults)
: func_(func),
pc_(0),
lastReadCallSite_(0),
alloc_(mirGen.alloc()),
graph_(mirGen.graph()),
info_(mirGen.info()),
@@ -101,34 +100,31 @@ class FunctionCompiler
curBlock_->initSlot(info().localSlot(i.index()), ins);
if (!mirGen_.ensureBallast())
return false;
if (!localTypes_.append(args[i.index()]))
return false;
}
for (unsigned i = 0; i < func_.numVarInits(); i++) {
Val v = func_.varInit(i);
for (size_t i = 0; i < func_.numLocalVars(); i++) {
MInstruction* ins = nullptr;
switch (v.type()) {
switch (func_.localVarType(i)) {
case ValType::I32:
ins = MConstant::NewAsmJS(alloc(), Int32Value(v.i32()), MIRType_Int32);
ins = MConstant::NewAsmJS(alloc(), Int32Value(0), MIRType_Int32);
break;
case ValType::I64:
MOZ_CRASH("int64");
case ValType::F32:
ins = MConstant::NewAsmJS(alloc(), Float32Value(v.f32()), MIRType_Float32);
ins = MConstant::NewAsmJS(alloc(), Float32Value(0.f), MIRType_Float32);
break;
case ValType::F64:
ins = MConstant::NewAsmJS(alloc(), DoubleValue(v.f64()), MIRType_Double);
ins = MConstant::NewAsmJS(alloc(), DoubleValue(0.0), MIRType_Double);
break;
case ValType::I32x4:
ins = MSimdConstant::New(alloc(), SimdConstant::CreateX4(v.i32x4()), MIRType_Int32x4);
ins = MSimdConstant::New(alloc(), SimdConstant::SplatX4(0), MIRType_Int32x4);
break;
case ValType::F32x4:
ins = MSimdConstant::New(alloc(), SimdConstant::CreateX4(v.f32x4()), MIRType_Float32x4);
ins = MSimdConstant::New(alloc(), SimdConstant::SplatX4(0.f), MIRType_Float32x4);
break;
case ValType::B32x4:
// Bool32x4 uses the same data layout as Int32x4.
ins = MSimdConstant::New(alloc(), SimdConstant::CreateX4(v.i32x4()), MIRType_Bool32x4);
ins = MSimdConstant::New(alloc(), SimdConstant::SplatX4(0), MIRType_Bool32x4);
break;
}
@@ -136,8 +132,6 @@ class FunctionCompiler
curBlock_->initSlot(info().localSlot(firstVarSlot + i), ins);
if (!mirGen_.ensureBallast())
return false;
if (!localTypes_.append(v.type()))
return false;
}
return true;
@@ -1176,10 +1170,15 @@ class FunctionCompiler
SimdConstant readF32X4() { return func_.readF32X4(&pc_); }
Stmt readStmtOp() { return Stmt(readU8()); }
void readCallLineCol(uint32_t* line, uint32_t* column) {
const FuncIR::SourceCoords& sc = func_.sourceCoords(lastReadCallSite_++);
MOZ_ASSERT(pc_ == sc.offset);
*line = sc.line;
*column = sc.column;
}
void assertDebugCheckPoint() {
#ifdef DEBUG
MOZ_ASSERT(Stmt(readU8()) == Stmt::DebugCheckPoint);
#endif
}
bool done() const { return pc_ == func_.size(); }
@@ -1563,13 +1562,6 @@ EmitCallArgs(FunctionCompiler& f, const LifoSig& sig, FunctionCompiler::Call* ca
return true;
}
static void
ReadCallLineCol(FunctionCompiler& f, uint32_t* line, uint32_t* column)
{
*line = f.readU32();
*column = f.readU32();
}
static bool
EmitInternalCall(FunctionCompiler& f, ExprType ret, MDefinition** def)
{
@@ -1578,7 +1570,7 @@ EmitInternalCall(FunctionCompiler& f, ExprType ret, MDefinition** def)
MOZ_ASSERT_IF(!IsVoid(sig.ret()), sig.ret() == ret);
uint32_t lineno, column;
ReadCallLineCol(f, &lineno, &column);
f.readCallLineCol(&lineno, &column);
FunctionCompiler::Call call(f, lineno, column);
if (!EmitCallArgs(f, sig, &call))
@@ -1597,7 +1589,7 @@ EmitFuncPtrCall(FunctionCompiler& f, ExprType ret, MDefinition** def)
MOZ_ASSERT_IF(!IsVoid(sig.ret()), sig.ret() == ret);
uint32_t lineno, column;
ReadCallLineCol(f, &lineno, &column);
f.readCallLineCol(&lineno, &column);
MDefinition *index;
if (!EmitI32Expr(f, &index))
@@ -1619,7 +1611,7 @@ EmitFFICall(FunctionCompiler& f, ExprType ret, MDefinition** def)
MOZ_ASSERT_IF(!IsVoid(sig.ret()), sig.ret() == ret);
uint32_t lineno, column;
ReadCallLineCol(f, &lineno, &column);
f.readCallLineCol(&lineno, &column);
FunctionCompiler::Call call(f, lineno, column);
if (!EmitCallArgs(f, sig, &call))
@@ -1634,7 +1626,7 @@ EmitMathBuiltinCall(FunctionCompiler& f, F32 f32, MDefinition** def)
MOZ_ASSERT(f32 == F32::Ceil || f32 == F32::Floor);
uint32_t lineno, column;
ReadCallLineCol(f, &lineno, &column);
f.readCallLineCol(&lineno, &column);
FunctionCompiler::Call call(f, lineno, column);
f.startCallArgs(&call);
@@ -1653,7 +1645,7 @@ static bool
EmitMathBuiltinCall(FunctionCompiler& f, F64 f64, MDefinition** def)
{
uint32_t lineno, column;
ReadCallLineCol(f, &lineno, &column);
f.readCallLineCol(&lineno, &column);
FunctionCompiler::Call call(f, lineno, column);
f.startCallArgs(&call);
@@ -3075,7 +3067,6 @@ wasm::IonCompileFunction(IonCompileTask* task)
// Compile MIR graph
{
jit::SpewBeginFunction(&mir, nullptr);
jit::AutoSpewEndFunction spewEndFunction(&mir);
+55 -125
View File
@@ -511,8 +511,9 @@ Module::activation()
void
Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
{
MOZ_ASSERT(usesHeap());
MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
MOZ_ASSERT(!maybeHeap_);
MOZ_ASSERT(!heap_);
MOZ_ASSERT(!rawHeapPtr());
uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - protected by Module methods*/);
@@ -550,14 +551,17 @@ Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
Assembler::UpdateBoundsCheck(heapLength, (Instruction*)(access.insnOffset() + code()));
#endif
maybeHeap_ = heap;
heap_ = heap;
rawHeapPtr() = ptrBase;
}
void
Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap)
{
MOZ_ASSERT_IF(maybeHeap_, maybeHeap_ == heap);
// heap_/rawHeapPtr can be null if this module holds cloned code from
// another dynamically-linked module which we are despecializing from that
// module's heap.
MOZ_ASSERT_IF(heap_, heap_ == heap);
MOZ_ASSERT_IF(rawHeapPtr(), rawHeapPtr() == heap->dataPointerEither().unwrap());
#if defined(JS_CODEGEN_X86)
@@ -581,7 +585,7 @@ Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap)
}
#endif
maybeHeap_ = nullptr;
heap_ = nullptr;
rawHeapPtr() = nullptr;
}
@@ -636,28 +640,35 @@ Module::sendCodeRangesToProfiler(JSContext* cx)
#endif
}
void
Module::setProfilingEnabled(bool enabled, JSContext* cx)
bool
Module::setProfilingEnabled(JSContext* cx, bool enabled)
{
MOZ_ASSERT(dynamicallyLinked_);
MOZ_ASSERT(!activation());
if (profilingEnabled_ == enabled)
return;
return true;
// When enabled, generate profiling labels for every name in funcNames_
// that is the name of some Function CodeRange. This involves malloc() so
// do it now since, once we start sampling, we'll be in a signal-handing
// context where we cannot malloc.
if (enabled) {
funcLabels_.resize(funcNames_.length());
if (!funcLabels_.resize(funcNames_.length())) {
ReportOutOfMemory(cx);
return false;
}
for (const CodeRange& codeRange : codeRanges_) {
if (!codeRange.isFunction())
continue;
unsigned lineno = codeRange.funcLineNumber();
const char* name = funcNames_[codeRange.funcNameIndex()].get();
funcLabels_[codeRange.funcNameIndex()] =
UniqueChars(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
UniqueChars label(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
if (!label) {
ReportOutOfMemory(cx);
return false;
}
funcLabels_[codeRange.funcNameIndex()] = Move(label);
}
} else {
funcLabels_.clear();
@@ -689,6 +700,7 @@ Module::setProfilingEnabled(bool enabled, JSContext* cx)
}
profilingEnabled_ = enabled;
return true;
}
Module::ImportExit&
@@ -700,7 +712,7 @@ Module::importToExit(const Import& import)
/* static */ Module::CacheablePod
Module::zeroPod()
{
CacheablePod pod = {0, 0, 0, false, false, false, false, false};
CacheablePod pod = {0, 0, 0, HeapUsage::None, false, false, false};
return pod;
}
@@ -711,9 +723,6 @@ Module::init()
interrupt_ = nullptr;
outOfBounds_ = nullptr;
dynamicallyLinked_ = false;
prev_ = nullptr;
next_ = nullptr;
interrupted_ = false;
*(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
*(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
@@ -757,8 +766,7 @@ Module::Module(CompileArgs args,
uint32_t functionBytes,
uint32_t codeBytes,
uint32_t globalBytes,
HeapBool usesHeap,
SharedBool sharedHeap,
HeapUsage heapUsage,
MutedBool mutedErrors,
UniqueCodePtr code,
ImportVector&& imports,
@@ -786,20 +794,16 @@ Module::Module(CompileArgs args,
const_cast<uint32_t&>(pod.functionBytes_) = functionBytes;
const_cast<uint32_t&>(pod.codeBytes_) = codeBytes;
const_cast<uint32_t&>(pod.globalBytes_) = globalBytes;
const_cast<bool&>(pod.usesHeap_) = bool(usesHeap);
const_cast<bool&>(pod.sharedHeap_) = bool(sharedHeap);
const_cast<HeapUsage&>(pod.heapUsage_) = heapUsage;
const_cast<bool&>(pod.mutedErrors_) = bool(mutedErrors);
const_cast<bool&>(pod.usesSignalHandlersForOOB_) = args.useSignalHandlersForOOB;
const_cast<bool&>(pod.usesSignalHandlersForInterrupt_) = args.useSignalHandlersForInterrupt;
MOZ_ASSERT_IF(sharedHeap, usesHeap);
init();
}
Module::~Module()
{
MOZ_ASSERT(!interrupted_);
if (code_) {
for (unsigned i = 0; i < imports_.length(); i++) {
ImportExit& exit = importToExit(imports_[i]);
@@ -807,11 +811,6 @@ Module::~Module()
exit.baselineScript->removeDependentWasmModule(*this, i);
}
}
if (prev_)
*prev_ = next_;
if (next_)
next_->prev_ = prev_;
}
void
@@ -822,8 +821,8 @@ Module::trace(JSTracer* trc)
TraceEdge(trc, &importToExit(import).fun, "wasm function import");
}
if (maybeHeap_)
TraceEdge(trc, &maybeHeap_, "wasm buffer");
if (heap_)
TraceEdge(trc, &heap_, "wasm buffer");
}
CompileArgs
@@ -929,11 +928,19 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
for (StaticLinkData::InternalLink link : linkData.internalLinks) {
uint8_t* patchAt = code() + link.patchAtOffset;
void* target = code() + link.targetOffset;
// If the target of an InternalLink is the non-profiling entry of a
// function, then we assume it is for a call that wants to call the
// profiling entry when profiling is enabled. Note that the target may
// be in the middle of a function (e.g., for a switch table) and in
// these cases we should not modify the target.
if (profilingEnabled_) {
const CodeRange* codeRange = lookupCodeRange(target);
if (codeRange && codeRange->isFunction())
target = code() + codeRange->funcProfilingEntry();
if (const CodeRange* cr = lookupCodeRange(target)) {
if (cr->isFunction() && link.targetOffset == cr->funcNonProfilingEntry())
target = code() + cr->funcProfilingEntry();
}
}
if (link.isRawPointerPatch())
*(void**)(patchAt) = target;
else
@@ -982,13 +989,6 @@ Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> hea
MOZ_ASSERT(!dynamicallyLinked_);
dynamicallyLinked_ = true;
// Add this module to the JSRuntime-wide list of dynamically-linked modules.
next_ = cx->runtime()->linkedWasmModules;
prev_ = &cx->runtime()->linkedWasmModules;
cx->runtime()->linkedWasmModules = this;
if (next_)
next_->prev_ = &next_;
// Push a JitContext for benefit of IsCompilingAsmJS and flush the ICache.
// We've been inhibiting flushing up to this point so flush it all now.
JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
@@ -1007,29 +1007,26 @@ Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> hea
}
// Specialize code to the actual heap.
if (heap)
if (usesHeap())
specializeToHeap(heap);
// See AllocateCode comment above.
ExecutableAllocator::makeExecutable(code(), pod.codeBytes_);
if (!ExecutableAllocator::makeExecutable(code(), pod.codeBytes_)) {
ReportOutOfMemory(cx);
return false;
}
sendCodeRangesToProfiler(cx);
return true;
}
ArrayBufferObjectMaybeShared*
Module::maybeBuffer() const
{
MOZ_ASSERT(dynamicallyLinked_);
return maybeHeap_;
}
SharedMem<uint8_t*>
Module::maybeHeap() const
Module::heap() const
{
MOZ_ASSERT(dynamicallyLinked_);
MOZ_ASSERT_IF(!pod.usesHeap_, rawHeapPtr() == nullptr);
return pod.sharedHeap_
MOZ_ASSERT(usesHeap());
MOZ_ASSERT(rawHeapPtr());
return hasSharedHeap()
? SharedMem<uint8_t*>::shared(rawHeapPtr())
: SharedMem<uint8_t*>::unshared(rawHeapPtr());
}
@@ -1038,7 +1035,8 @@ size_t
Module::heapLength() const
{
MOZ_ASSERT(dynamicallyLinked_);
return maybeHeap_ ? maybeHeap_->byteLength() : 0;
MOZ_ASSERT(usesHeap());
return heap_->byteLength();
}
void
@@ -1051,65 +1049,6 @@ Module::deoptimizeImportExit(uint32_t importIndex)
exit.baselineScript = nullptr;
}
bool
Module::changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext* cx)
{
MOZ_ASSERT(dynamicallyLinked_);
MOZ_ASSERT(pod.usesHeap_);
// Content JS should not be able to run (and change heap) from within an
// interrupt callback, but in case it does, fail to change heap. Otherwise,
// the heap can change at every single instruction which would prevent
// future optimizations like heap-base hoisting.
if (interrupted_)
return false;
AutoMutateCode amc(cx, *this, "Module::changeHeap");
if (maybeHeap_)
despecializeFromHeap(maybeHeap_);
specializeToHeap(newHeap);
return true;
}
bool
Module::detachHeap(JSContext* cx)
{
MOZ_ASSERT(dynamicallyLinked_);
MOZ_ASSERT(pod.usesHeap_);
// Content JS should not be able to run (and detach heap) from within an
// interrupt callback, but in case it does, fail. Otherwise, the heap can
// change at an arbitrary instruction and break the assumption below.
if (interrupted_) {
JS_ReportError(cx, "attempt to detach from inside interrupt handler");
return false;
}
// Even if this->active(), to reach here, the activation must have called
// out via an import exit stub. FFI stubs check if heapDatum() is null on
// reentry and throw an exception if so.
MOZ_ASSERT_IF(activation(), activation()->exitReason() == ExitReason::ImportJit ||
activation()->exitReason() == ExitReason::ImportInterp);
AutoMutateCode amc(cx, *this, "Module::detachHeap");
despecializeFromHeap(maybeHeap_);
return true;
}
void
Module::setInterrupted(bool interrupted)
{
MOZ_ASSERT(dynamicallyLinked_);
interrupted_ = interrupted;
}
Module*
Module::nextLinked() const
{
MOZ_ASSERT(dynamicallyLinked_);
return next_;
}
bool
Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
{
@@ -1121,8 +1060,10 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
// profiling state. Don't do this if the Module is already active on the
// stack since this would leave the Module in a state where profiling is
// enabled but the stack isn't unwindable.
if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation())
setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx);
if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation()) {
if (!setProfilingEnabled(cx, cx->runtime()->spsProfiler.enabled()))
return false;
}
// The calling convention for an external call into wasm is to pass an
// array of 16-byte values where each value contains either a coerced int32
@@ -1179,17 +1120,6 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
}
}
// The correct way to handle this situation would be to allocate a new range
// of PROT_NONE memory and module.changeHeap to this memory. That would
// cause every access to take the out-of-bounds signal-handler path which
// does the right thing. For now, just throw an out-of-memory exception
// since these can technically pop out anywhere and the full fix may
// actually OOM when trying to allocate the PROT_NONE memory.
if (usesHeap() && !maybeHeap_) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
return false;
}
{
// Push a WasmActivation to describe the wasm frames we're about to push
// when running this module. Additionally, push a JitActivation so that
@@ -1518,8 +1448,8 @@ Module::clone(JSContext* cx, const StaticLinkData& linkData) const
// If the copied machine code has been specialized to the heap, it must be
// unspecialized in the copy.
if (maybeHeap_)
out->despecializeFromHeap(maybeHeap_);
if (usesHeap())
out->despecializeFromHeap(heap_);
if (!out->staticallyLink(cx, linkData))
return nullptr;
+26 -32
View File
@@ -22,6 +22,7 @@
#include "asmjs/WasmTypes.h"
#include "gc/Barrier.h"
#include "vm/MallocProvider.h"
#include "vm/NativeObject.h"
namespace js {
@@ -103,16 +104,14 @@ class Export
{
MallocSig sig_;
struct CacheablePod {
uint32_t funcIndex_;
uint32_t stubOffset_;
} pod;
public:
Export() = default;
Export(MallocSig&& sig, uint32_t funcIndex)
explicit Export(MallocSig&& sig)
: sig_(Move(sig))
{
pod.funcIndex_ = funcIndex;
pod.stubOffset_ = UINT32_MAX;
}
Export(Export&& rhs)
@@ -125,9 +124,6 @@ class Export
pod.stubOffset_ = stubOffset;
}
uint32_t funcIndex() const {
return pod.funcIndex_;
}
uint32_t stubOffset() const {
return pod.stubOffset_;
}
@@ -325,6 +321,22 @@ typedef JS::UniquePtr<uint8_t, CodeDeleter> UniqueCodePtr;
UniqueCodePtr
AllocateCode(ExclusiveContext* cx, size_t bytes);
// A wasm module can either use no heap, a unshared heap (ArrayBuffer) or shared
// heap (SharedArrayBuffer).
enum class HeapUsage
{
None = false,
Unshared = 1,
Shared = 2
};
static inline bool
UsesHeap(HeapUsage heapUsage)
{
return bool(heapUsage);
}
// Module represents a compiled WebAssembly module which lives until the last
// reference to any exported functions is dropped. Modules must be wrapped by a
// rooted JSObject immediately after creation so that Module::trace() is called
@@ -375,8 +387,7 @@ class Module
const uint32_t functionBytes_;
const uint32_t codeBytes_;
const uint32_t globalBytes_;
const bool usesHeap_;
const bool sharedHeap_;
const HeapUsage heapUsage_;
const bool mutedErrors_;
const bool usesSignalHandlersForOOB_;
const bool usesSignalHandlersForInterrupt_;
@@ -400,14 +411,11 @@ class Module
// Initialized during dynamicallyLink:
bool dynamicallyLinked_;
BufferPtr maybeHeap_;
Module** prev_;
Module* next_;
BufferPtr heap_;
// Mutated after dynamicallyLink:
bool profilingEnabled_;
FuncLabelVector funcLabels_;
bool interrupted_;
class AutoMutateCode;
@@ -418,7 +426,7 @@ class Module
void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
void sendCodeRangesToProfiler(JSContext* cx);
void setProfilingEnabled(bool enabled, JSContext* cx);
MOZ_WARN_UNUSED_RESULT bool setProfilingEnabled(JSContext* cx, bool enabled);
ImportExit& importToExit(const Import& import);
enum CacheBool { NotLoadedFromCache = false, LoadedFromCache = true };
@@ -448,16 +456,13 @@ class Module
static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
static const unsigned SizeOfEntryArg = sizeof(EntryArg);
enum HeapBool { DoesntUseHeap = false, UsesHeap = true };
enum SharedBool { UnsharedHeap = false, SharedHeap = true };
enum MutedBool { DontMuteErrors = false, MuteErrors = true };
Module(CompileArgs args,
uint32_t functionBytes,
uint32_t codeBytes,
uint32_t globalBytes,
HeapBool usesHeap,
SharedBool sharedHeap,
HeapUsage heapUsage,
MutedBool mutedErrors,
UniqueCodePtr code,
ImportVector&& imports,
@@ -474,8 +479,9 @@ class Module
uint8_t* code() const { return code_.get(); }
uint8_t* globalData() const { return code() + pod.codeBytes_; }
uint32_t globalBytes() const { return pod.globalBytes_; }
bool usesHeap() const { return pod.usesHeap_; }
bool sharedHeap() const { return pod.sharedHeap_; }
HeapUsage heapUsage() const { return pod.heapUsage_; }
bool usesHeap() const { return UsesHeap(pod.heapUsage_); }
bool hasSharedHeap() const { return pod.heapUsage_ == HeapUsage::Shared; }
bool mutedErrors() const { return pod.mutedErrors_; }
CompileArgs compileArgs() const;
const ImportVector& imports() const { return imports_; }
@@ -515,21 +521,9 @@ class Module
// The wasm heap, established by dynamicallyLink.
ArrayBufferObjectMaybeShared* maybeBuffer() const;
SharedMem<uint8_t*> maybeHeap() const;
SharedMem<uint8_t*> heap() const;
size_t heapLength() const;
// asm.js may detach and change the heap at any time. As an internal detail,
// the heap may not be changed while the module has been asynchronously
// interrupted.
//
// N.B. These methods and asm.js change-heap support will be removed soon.
bool changeHeap(Handle<ArrayBufferObject*> newBuffer, JSContext* cx);
bool detachHeap(JSContext* cx);
void setInterrupted(bool interrupted);
Module* nextLinked() const;
// The exports of a wasm module are called by preparing an array of
// arguments (coerced to the corresponding types of the Export signature)
// and calling the export's entry trampoline.
+17 -17
View File
@@ -627,7 +627,7 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
uintptr_t base;
StoreValueFromGPReg(SharedMem<void*>::unshared(&base), sizeof(uintptr_t),
AddressOfGPRegisterSlot(context, address.base()));
MOZ_RELEASE_ASSERT(reinterpret_cast<uint8_t*>(base) == module.maybeHeap());
MOZ_RELEASE_ASSERT(reinterpret_cast<uint8_t*>(base) == module.heap());
}
if (address.hasIndex()) {
uintptr_t index;
@@ -645,11 +645,11 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
MOZ_RELEASE_ASSERT(size_t(faultingAddress - accessAddress) < access.size(),
"Given faulting address does not appear to be within computed "
"faulting address range");
MOZ_RELEASE_ASSERT(accessAddress >= module.maybeHeap(),
MOZ_RELEASE_ASSERT(accessAddress >= module.heap(),
"Access begins outside the asm.js heap");
MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.maybeHeap() + AsmJSMappedSize,
MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.heap() + AsmJSMappedSize,
"Access extends beyond the asm.js heap guard region");
MOZ_RELEASE_ASSERT(accessAddress + access.size() > module.maybeHeap() + module.heapLength(),
MOZ_RELEASE_ASSERT(accessAddress + access.size() > module.heap() + module.heapLength(),
"Computed access address is not actually out of bounds");
// The basic sandbox model is that all heap accesses are a heap base
@@ -666,7 +666,7 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
//
// Taking a signal is really slow, but in theory programs really shouldn't
// be hitting this anyway.
intptr_t unwrappedOffset = accessAddress - module.maybeHeap().unwrap(/*safe - for value*/);
intptr_t unwrappedOffset = accessAddress - module.heap().unwrap(/*safe - for value*/);
uint32_t wrappedOffset = uint32_t(unwrappedOffset);
size_t size = access.size();
MOZ_RELEASE_ASSERT(wrappedOffset + size > wrappedOffset);
@@ -684,10 +684,10 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
// We now know that this is an access that is actually in bounds when
// properly wrapped. Complete the load or store with the wrapped
// address.
SharedMem<uint8_t*> wrappedAddress = module.maybeHeap() + wrappedOffset;
MOZ_RELEASE_ASSERT(wrappedAddress >= module.maybeHeap());
SharedMem<uint8_t*> wrappedAddress = module.heap() + wrappedOffset;
MOZ_RELEASE_ASSERT(wrappedAddress >= module.heap());
MOZ_RELEASE_ASSERT(wrappedAddress + size > wrappedAddress);
MOZ_RELEASE_ASSERT(wrappedAddress + size <= module.maybeHeap() + module.heapLength());
MOZ_RELEASE_ASSERT(wrappedAddress + size <= module.heap() + module.heapLength());
switch (access.kind()) {
case Disassembler::HeapAccess::Load:
SetRegisterToLoadedValue(context, wrappedAddress.cast<void*>(), size, access.otherOperand());
@@ -762,9 +762,9 @@ HandleFault(PEXCEPTION_POINTERS exception)
// These checks aren't necessary, but, since we can, check anyway to make
// sure we aren't covering up a real bug.
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
if (!module.maybeHeap() ||
faultingAddress < module.maybeHeap() ||
faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
if (!module.usesHeap() ||
faultingAddress < module.heap() ||
faultingAddress >= module.heap() + AsmJSMappedSize)
{
return false;
}
@@ -907,9 +907,9 @@ HandleMachException(JSRuntime* rt, const ExceptionRequest& request)
// These checks aren't necessary, but, since we can, check anyway to make
// sure we aren't covering up a real bug.
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
if (!module.maybeHeap() ||
faultingAddress < module.maybeHeap() ||
faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
if (!module.usesHeap() ||
faultingAddress < module.heap() ||
faultingAddress >= module.heap() + AsmJSMappedSize)
{
return false;
}
@@ -1117,9 +1117,9 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
// These checks aren't necessary, but, since we can, check anyway to make
// sure we aren't covering up a real bug.
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
if (!module.maybeHeap() ||
faultingAddress < module.maybeHeap() ||
faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
if (!module.usesHeap() ||
faultingAddress < module.heap() ||
faultingAddress >= module.heap() + AsmJSMappedSize)
{
return false;
}
+21 -84
View File
@@ -99,7 +99,7 @@ static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void
// function has an ABI derived from its specific signature, so this function
// must map from the ABI of CodePtr to the export's signature's ABI.
static bool
GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, Module::HeapBool usesHeap)
GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.exportSig(exportIndex);
@@ -332,30 +332,12 @@ FillArgumentArray(MacroAssembler& masm, const MallocSig::ArgVector& args, unsign
}
}
// If an import call detaches its heap (viz., via ArrayBuffer.transfer), it must
// call change-heap to another heap (viz., the new heap returned by transfer)
// before returning to asm.js code. If the application fails to do this (if the
// heap pointer is null), jump to a stub.
static void
CheckForHeapDetachment(MacroAssembler& masm, Register scratch, Label* onDetached)
{
MOZ_ASSERT(int(masm.framePushed()) >= int(ShadowStackSpace));
AssertStackAlignment(masm, ABIStackAlignment);
#if defined(JS_CODEGEN_X86)
CodeOffset offset = masm.movlWithPatch(PatchedAbsoluteAddress(), scratch);
masm.append(AsmJSGlobalAccess(offset, HeapGlobalDataOffset));
masm.branchTestPtr(Assembler::Zero, scratch, scratch, onDetached);
#else
masm.branchTestPtr(Assembler::Zero, HeapReg, HeapReg, onDetached);
#endif
}
// Generate a stub that is called via the internal ABI derived from the
// signature of the import and calls into an appropriate InvokeImport C++
// function, having boxed all the ABI arguments into a homogeneous Value array.
static bool
GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLabel,
ProfilingOffsets* offsets)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.importSig(importIndex);
@@ -440,13 +422,6 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBo
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
}
// The heap pointer may have changed during the FFI, so reload it and test
// for detachment.
if (usesHeap) {
masm.loadAsmJSHeapRegisterFromGlobalData();
CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
}
GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets);
if (masm.oom())
@@ -466,8 +441,8 @@ static const unsigned MaybeSavedGlobalReg = 0;
// signature of the import and calls into a compatible JIT function,
// having boxed all the ABI arguments into the JIT stack frame layout.
static bool
GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
Label* throwLabel, ProfilingOffsets* offsets)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.importSig(importIndex);
@@ -540,8 +515,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool
// HeapReg are removed from the general register set for asm.js code, so
// these will not have been saved by the caller like all other registers,
// so they must be explicitly preserved. Only save GlobalReg since
// HeapReg must be reloaded (from global data) after the call since the
// heap may change during the FFI call.
// HeapReg can be reloaded (from global data) after the call.
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting");
masm.storePtr(GlobalReg, Address(masm.getStackPointer(), jitFrameBytes));
@@ -698,12 +672,10 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool
Label done;
masm.bind(&done);
// The heap pointer has to be reloaded anyway since JIT code could have
// clobbered it. Additionally, the import may have detached the heap buffer.
if (usesHeap) {
// Ion code does not respect system callee-saved register conventions so
// reload the heap register.
if (usesHeap)
masm.loadAsmJSHeapRegisterFromGlobalData();
CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
}
GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets);
@@ -764,32 +736,6 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool
return true;
}
// Generate a stub that is called when returning from an exit where the module's
// buffer has been detached. This stub first calls a C++ function to report an
// exception and then jumps to the generic throw stub to pop everything off the
// stack.
static bool
GenerateOnDetachedStub(ModuleGenerator& mg, Label* onDetached, Label* throwLabel)
{
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment);
Offsets offsets;
offsets.begin = masm.currentOffset();
masm.bind(onDetached);
// For now, OnDetached always throws (see OnDetached comment).
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::OnDetached);
masm.jump(throwLabel);
if (masm.oom())
return false;
offsets.end = masm.currentOffset();
return mg.defineInlineStub(offsets);
}
// Generate a stub that is called immediately after the prologue when there is a
// stack overflow. This stub calls a C++ function to report the error and then
// jumps to the throw stub to pop the activation.
@@ -929,7 +875,7 @@ static const LiveRegisterSet AllRegsExceptSP(
// after restoring all registers. To hack around this, push the resumePC on the
// stack so that it can be popped directly into PC.
static bool
GenerateAsyncInterruptStub(ModuleGenerator& mg, Module::HeapBool usesHeap, Label* throwLabel)
GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
{
MacroAssembler& masm = mg.masm();
@@ -1126,7 +1072,7 @@ GenerateThrowStub(ModuleGenerator& mg, Label* throwLabel)
}
bool
wasm::GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap)
wasm::GenerateStubs(ModuleGenerator& mg, bool usesHeap)
{
for (unsigned i = 0; i < mg.numDeclaredExports(); i++) {
if (!GenerateEntry(mg, i, usesHeap))
@@ -1135,26 +1081,17 @@ wasm::GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap)
Label onThrow;
{
Label onDetached;
for (size_t i = 0; i < mg.numDeclaredImports(); i++) {
ProfilingOffsets interp;
if (!GenerateInterpExitStub(mg, i, &onThrow, &interp))
return false;
for (size_t i = 0; i < mg.numDeclaredImports(); i++) {
ProfilingOffsets interp;
if (!GenerateInterpExitStub(mg, i, usesHeap, &onThrow, &onDetached, &interp))
return false;
ProfilingOffsets jit;
if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &jit))
return false;
ProfilingOffsets jit;
if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &onDetached, &jit))
return false;
if (!mg.defineImport(i, interp, jit))
return false;
}
if (onDetached.used()) {
if (!GenerateOnDetachedStub(mg, &onDetached, &onThrow))
return false;
}
if (!mg.defineImport(i, interp, jit))
return false;
}
if (mg.masm().asmStackOverflowLabel()->used()) {
@@ -1178,7 +1115,7 @@ wasm::GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap)
return false;
// Generate unconditionally: the async interrupt may be taken at any time.
if (!GenerateAsyncInterruptStub(mg, usesHeap, &onThrow))
if (!GenerateAsyncInterruptStub(mg, &onThrow))
return false;
if (onThrow.used()) {
+1 -1
View File
@@ -25,7 +25,7 @@ namespace js {
namespace wasm {
bool
GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap);
GenerateStubs(ModuleGenerator& mg, bool usesHeap);
} // namespace wasm
} // namespace js
+10 -30
View File
@@ -43,34 +43,16 @@ __aeabi_uidivmod(int, int);
}
#endif
namespace js {
namespace wasm {
void
ReportOverRecursed()
{
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
ReportOverRecursed(cx);
}
bool
HandleExecutionInterrupt()
{
WasmActivation* act = JSRuntime::innermostWasmActivation();
act->module().setInterrupted(true);
bool ret = CheckForInterrupt(act->cx());
act->module().setInterrupted(false);
return ret;
}
} // namespace wasm
} // namespace js
static void
OnDetached()
WasmReportOverRecursed()
{
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
ReportOverRecursed(JSRuntime::innermostWasmActivation()->cx());
}
static bool
WasmHandleExecutionInterrupt()
{
return CheckForInterrupt(JSRuntime::innermostWasmActivation()->cx());
}
static void
@@ -187,15 +169,13 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
case SymbolicAddress::StackLimit:
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
case SymbolicAddress::ReportOverRecursed:
return FuncCast(wasm::ReportOverRecursed, Args_General0);
case SymbolicAddress::OnDetached:
return FuncCast(OnDetached, Args_General0);
return FuncCast(WasmReportOverRecursed, Args_General0);
case SymbolicAddress::OnOutOfBounds:
return FuncCast(OnOutOfBounds, Args_General0);
case SymbolicAddress::OnImpreciseConversion:
return FuncCast(OnImpreciseConversion, Args_General0);
case SymbolicAddress::HandleExecutionInterrupt:
return FuncCast(wasm::HandleExecutionInterrupt, Args_General0);
return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
case SymbolicAddress::InvokeImport_Void:
return FuncCast(InvokeImport_Void, Args_General3);
case SymbolicAddress::InvokeImport_I32:
-1
View File
@@ -555,7 +555,6 @@ enum class SymbolicAddress
RuntimeInterruptUint32,
StackLimit,
ReportOverRecursed,
OnDetached,
OnOutOfBounds,
OnImpreciseConversion,
HandleExecutionInterrupt,
+1 -1
View File
@@ -524,7 +524,7 @@ GetCurrentAsmJSHeap(SharedMem<void*>* heap, size_t* length)
{
JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
wasm::Module& module = rt->wasmActivationStack()->module();
*heap = module.maybeHeap().cast<void*>();
*heap = module.heap().cast<void*>();
*length = module.heapLength();
}
+4 -2
View File
@@ -924,7 +924,8 @@ js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
UpdateRegExpStatics, args.rval());
}
/* Separate interface for use by IonMonkey. */
/* Separate interface for use by IonMonkey.
* This code cannot re-enter Ion code. */
bool
js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
int32_t lastIndex, bool sticky,
@@ -993,7 +994,8 @@ js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
return true;
}
/* Separate interface for use by IonMonkey. */
/* Separate interface for use by IonMonkey.
* This code cannot re-enter Ion code. */
bool
js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
int32_t lastIndex, bool sticky, int32_t* endIndex)
+1 -1
View File
@@ -54,6 +54,7 @@ function RegExpToString()
// Step 7.
return '/' + pattern + '/' + flags;
}
_SetCanonicalName(RegExpToString, "toString");
// ES6 21.2.5.2.
// NOTE: This is not RegExpExec (21.2.5.2.1).
@@ -171,4 +172,3 @@ function RegExpTest(string) {
// Steps 5-6.
return RegExpExec(R, S, true);
}
_SetCanonicalName(RegExpToString, "toString");
+6 -6
View File
@@ -4140,10 +4140,10 @@ CType::Trace(JSTracer* trc, JSObject* obj)
MOZ_ASSERT(fninfo);
// Identify our objects to the tracer.
JS_CallObjectTracer(trc, &fninfo->mABI, "abi");
JS_CallObjectTracer(trc, &fninfo->mReturnType, "returnType");
JS::TraceEdge(trc, &fninfo->mABI, "abi");
JS::TraceEdge(trc, &fninfo->mReturnType, "returnType");
for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i)
JS_CallObjectTracer(trc, &fninfo->mArgTypes[i], "argType");
JS::TraceEdge(trc, &fninfo->mArgTypes[i], "argType");
break;
}
@@ -6947,10 +6947,10 @@ CClosure::Trace(JSTracer* trc, JSObject* obj)
// Identify our objects to the tracer. (There's no need to identify
// 'closureObj', since that's us.)
JS_CallObjectTracer(trc, &cinfo->typeObj, "typeObj");
JS_CallObjectTracer(trc, &cinfo->jsfnObj, "jsfnObj");
JS::TraceEdge(trc, &cinfo->typeObj, "typeObj");
JS::TraceEdge(trc, &cinfo->jsfnObj, "jsfnObj");
if (cinfo->thisObj)
JS_CallObjectTracer(trc, &cinfo->thisObj, "thisObj");
JS::TraceEdge(trc, &cinfo->thisObj, "thisObj");
}
void
+1 -1
View File
@@ -231,7 +231,7 @@ struct FieldInfo
size_t mOffset; // offset of the field in the struct, in bytes
void trace(JSTracer* trc) {
JS_CallObjectTracer(trc, &mType, "fieldType");
JS::TraceEdge(trc, &mType, "fieldType");
}
};
+5 -1
View File
@@ -78,7 +78,8 @@ var ignoreCallees = {
"z_stream_s.zfree" : true,
"GrGLInterface.fCallback" : true,
"std::strstreambuf._M_alloc_fun" : true,
"std::strstreambuf._M_free_fun" : true
"std::strstreambuf._M_free_fun" : true,
"struct js::gc::Callback<void (*)(JSRuntime*, void*)>.op" : true,
};
function fieldCallCannotGC(csu, fullfield)
@@ -187,6 +188,9 @@ var ignoreFunctions = {
"void test::RingbufferDumper::OnTestPartResult(testing::TestPartResult*)" : true,
"float64 JS_GetCurrentEmbedderTime()" : true,
"uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
"uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
};
function isProtobuf(name)
+4
View File
@@ -756,6 +756,9 @@ class GCRuntime
void setGCCallback(JSGCCallback callback, void* data);
void callGCCallback(JSGCStatus status) const;
void setObjectsTenuredCallback(JSObjectsTenuredCallback callback,
void* data);
void callObjectsTenuredCallback();
bool addFinalizeCallback(JSFinalizeCallback callback, void* data);
void removeFinalizeCallback(JSFinalizeCallback func);
bool addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback, void* data);
@@ -1273,6 +1276,7 @@ class GCRuntime
bool fullCompartmentChecks;
Callback<JSGCCallback> gcCallback;
Callback<JSObjectsTenuredCallback> tenuredCallback;
CallbackVector<JSFinalizeCallback> finalizeCallbacks;
CallbackVector<JSWeakPointerZoneGroupCallback> updateWeakPointerZoneGroupCallbacks;
CallbackVector<JSWeakPointerCompartmentCallback> updateWeakPointerCompartmentCallbacks;
+18 -1
View File
@@ -416,6 +416,15 @@ JS::TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
}
template <typename T>
JS_PUBLIC_API(void)
JS::TraceNullableEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
{
MOZ_ASSERT(thingp);
if (InternalGCMethods<T>::isMarkable(thingp->get()))
DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
}
template <typename T>
void
js::TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name)
@@ -494,7 +503,6 @@ js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name)
// Instantiate a copy of the Tracing templates for each derived type.
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
template void js::TraceEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
template JS_PUBLIC_API(void) JS::TraceEdge<type>(JSTracer*, JS::Heap<type>*, const char*); \
template void js::TraceManuallyBarrieredEdge<type>(JSTracer*, type*, const char*); \
template void js::TraceWeakEdge<type>(JSTracer*, WeakRef<type>*, const char*); \
template void js::TraceRoot<type>(JSTracer*, type*, const char*); \
@@ -506,6 +514,13 @@ js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name)
FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
#define INSTANTIATE_PUBLIC_TRACE_FUNCTIONS(type) \
template JS_PUBLIC_API(void) JS::TraceEdge<type>(JSTracer*, JS::Heap<type>*, const char*); \
template JS_PUBLIC_API(void) JS::TraceNullableEdge<type>(JSTracer*, JS::Heap<type>*, \
const char*);
FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS)
#undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS
template <typename T>
void
js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst,
@@ -2220,6 +2235,8 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
} else if (src->is<ArgumentsObject>()) {
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
} else if (JSObjectMovedOp op = dst->getClass()->ext.objectMovedOp) {
op(dst, src);
} else {
// Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
// to ensure any additional nursery buffers they hold are moved.
+5
View File
@@ -497,6 +497,10 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
forwardedBuffers.finish();
TIME_END(updateJitActivations);
TIME_START(objectsTenuredCallback);
rt->gc.callObjectsTenuredCallback();
TIME_END(objectsTenuredCallback);
// Sweep.
TIME_START(freeMallocedBuffers);
freeMallocedBuffers();
@@ -573,6 +577,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
{"mkDbgr", TIME_TOTAL(markDebugger)},
{"clrNOC", TIME_TOTAL(clearNewObjectCache)},
{"collct", TIME_TOTAL(collectToFP)},
{" tenCB", TIME_TOTAL(objectsTenuredCallback)},
{"swpABO", TIME_TOTAL(sweepArrayBufferViewList)},
{"updtIn", TIME_TOTAL(updateJitActivations)},
{"frSlts", TIME_TOTAL(freeMallocedBuffers)},
+8 -4
View File
@@ -778,7 +778,7 @@ Statistics::~Statistics()
fclose(fp);
}
/* static */ void
/* static */ bool
Statistics::initialize()
{
for (size_t i = 0; i < PHASE_LIMIT; i++) {
@@ -802,7 +802,8 @@ Statistics::initialize()
MOZ_ASSERT(phases[child].parent == PHASE_MULTI_PARENTS);
int j = child;
do {
dagDescendants[phaseExtra[parent].dagSlot].append(Phase(j));
if (!dagDescendants[phaseExtra[parent].dagSlot].append(Phase(j)))
return false;
j++;
} while (j != PHASE_LIMIT && phases[j].parent != PHASE_MULTI_PARENTS);
}
@@ -811,7 +812,8 @@ Statistics::initialize()
// Fill in the depth of each node in the tree. Multi-parented nodes
// have depth 0.
mozilla::Vector<Phase> stack;
stack.append(PHASE_LIMIT); // Dummy entry to avoid special-casing the first node
if (!stack.append(PHASE_LIMIT)) // Dummy entry to avoid special-casing the first node
return false;
for (int i = 0; i < PHASE_LIMIT; i++) {
if (phases[i].parent == PHASE_NO_PARENT ||
phases[i].parent == PHASE_MULTI_PARENTS)
@@ -822,9 +824,11 @@ Statistics::initialize()
stack.popBack();
}
phaseExtra[i].depth = stack.length();
stack.append(Phase(i));
if (!stack.append(Phase(i)))
return false;
}
return true;
}
JS::GCSliceCallback
+1 -1
View File
@@ -162,7 +162,7 @@ struct Statistics
/* Create a convenient type for referring to tables of phase times. */
using PhaseTimeTable = int64_t[NumTimingArrays][PHASE_LIMIT];
static void initialize();
static bool initialize();
explicit Statistics(JSRuntime* rt);
~Statistics();
-36
View File
@@ -135,42 +135,6 @@ JS_CallUnbarrieredScriptTracer(JSTracer* trc, JSScript** scriptp, const char* na
TraceManuallyBarrieredEdge(trc, scriptp, name);
}
JS_PUBLIC_API(void)
JS_CallValueTracer(JSTracer* trc, JS::Heap<JS::Value>* valuep, const char* name)
{
TraceManuallyBarrieredEdge(trc, valuep->unsafeGet(), name);
}
JS_PUBLIC_API(void)
JS_CallIdTracer(JSTracer* trc, JS::Heap<jsid>* idp, const char* name)
{
TraceManuallyBarrieredEdge(trc, idp->unsafeGet(), name);
}
JS_PUBLIC_API(void)
JS_CallObjectTracer(JSTracer* trc, JS::Heap<JSObject*>* objp, const char* name)
{
TraceManuallyBarrieredEdge(trc, objp->unsafeGet(), name);
}
JS_PUBLIC_API(void)
JS_CallStringTracer(JSTracer* trc, JS::Heap<JSString*>* strp, const char* name)
{
TraceManuallyBarrieredEdge(trc, strp->unsafeGet(), name);
}
JS_PUBLIC_API(void)
JS_CallScriptTracer(JSTracer* trc, JS::Heap<JSScript*>* scriptp, const char* name)
{
TraceManuallyBarrieredEdge(trc, scriptp->unsafeGet(), name);
}
JS_PUBLIC_API(void)
JS_CallFunctionTracer(JSTracer* trc, JS::Heap<JSFunction*>* funp, const char* name)
{
TraceManuallyBarrieredEdge(trc, funp->unsafeGet(), name);
}
JS_PUBLIC_API(void)
JS_CallTenuredObjectTracer(JSTracer* trc, JS::TenuredHeap<JSObject*>* objp, const char* name)
{
+3 -3
View File
@@ -82,7 +82,7 @@ class RegExpTree
#undef MAKE_ASTYPE
};
typedef Vector<RegExpTree*, 1, LifoAllocPolicy<Infallible> > RegExpTreeVector;
typedef InfallibleVector<RegExpTree*, 1> RegExpTreeVector;
class RegExpDisjunction : public RegExpTree
{
@@ -235,7 +235,7 @@ class RegExpCharacterClass : public RegExpTree
bool is_negated_;
};
typedef Vector<char16_t, 10, LifoAllocPolicy<Infallible> > CharacterVector;
typedef InfallibleVector<char16_t, 10> CharacterVector;
class RegExpAtom : public RegExpTree
{
@@ -403,7 +403,7 @@ class RegExpLookahead : public RegExpTree
int capture_from_;
};
typedef Vector<RegExpCapture*, 1, LifoAllocPolicy<Infallible> > RegExpCaptureVector;
typedef InfallibleVector<RegExpCapture*, 1> RegExpCaptureVector;
class RegExpBackReference : public RegExpTree
{
+7 -8
View File
@@ -1116,9 +1116,7 @@ ChoiceNode::FilterASCII(int depth, bool ignore_case, bool unicode)
alternatives()[i].node()->FilterASCII(depth - 1, ignore_case, unicode);
if (replacement != nullptr) {
alternatives()[i].set_node(replacement);
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!new_alternatives.append(alternatives()[i]))
oomUnsafe.crash("ChoiceNode::FilterASCII");
new_alternatives.append(alternatives()[i]);
}
}
@@ -3249,7 +3247,7 @@ EmitDoubleBoundaryTest(RegExpMacroAssembler* masm,
}
}
typedef Vector<int, 4, LifoAllocPolicy<Infallible> > RangeBoundaryVector;
typedef InfallibleVector<int, 4> RangeBoundaryVector;
// even_label is for ranges[i] to ranges[i + 1] where i - start_index is even.
// odd_label is for ranges[i] to ranges[i + 1] where i - start_index is odd.
@@ -4236,12 +4234,13 @@ class AlternativeGenerationList
{
alt_gens_.reserve(count);
for (size_t i = 0; i < count && i < kAFew; i++)
alt_gens_.infallibleAppend(a_few_alt_gens_ + i);
alt_gens_.append(a_few_alt_gens_ + i);
for (size_t i = kAFew; i < count; i++) {
AutoEnterOOMUnsafeRegion oomUnsafe;
AlternativeGeneration* gen = js_new<AlternativeGeneration>();
if (!gen)
MOZ_CRASH("AlternativeGenerationList js_new");
alt_gens_.infallibleAppend(gen);
oomUnsafe.crash("AlternativeGenerationList js_new");
alt_gens_.append(gen);
}
}
@@ -4258,7 +4257,7 @@ class AlternativeGenerationList
private:
static const size_t kAFew = 10;
Vector<AlternativeGeneration*, 1, LifoAllocPolicy<Infallible> > alt_gens_;
InfallibleVector<AlternativeGeneration*, 1> alt_gens_;
AlternativeGeneration a_few_alt_gens_[kAFew];
};
+40 -8
View File
@@ -128,8 +128,40 @@ InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t
FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
#undef FORWARD_DECLARE
// InfallibleVector is like Vector, but all its methods are infallible (they
// crash on OOM). We use this class instead of Vector to avoid a ton of
// MOZ_WARN_UNUSED_RESULT warnings in irregexp code (imported from V8).
template<typename T, size_t N>
class InfallibleVector
{
Vector<T, N, LifoAllocPolicy<Infallible>> vector_;
InfallibleVector(const InfallibleVector&) = delete;
void operator=(const InfallibleVector&) = delete;
public:
explicit InfallibleVector(const LifoAllocPolicy<Infallible>& alloc) : vector_(alloc) {}
void append(const T& t) { MOZ_ALWAYS_TRUE(vector_.append(t)); }
void append(const T* begin, size_t length) { MOZ_ALWAYS_TRUE(vector_.append(begin, length)); }
void clear() { vector_.clear(); }
void popBack() { vector_.popBack(); }
void reserve(size_t n) { MOZ_ALWAYS_TRUE(vector_.reserve(n)); }
size_t length() const { return vector_.length(); }
T popCopy() { return vector_.popCopy(); }
T* begin() { return vector_.begin(); }
T& operator[](size_t index) { return vector_[index]; }
const T& operator[](size_t index) const { return vector_[index]; }
InfallibleVector& operator=(InfallibleVector&& rhs) { vector_ = Move(rhs.vector_); return *this; }
};
class CharacterRange;
typedef Vector<CharacterRange, 1, LifoAllocPolicy<Infallible> > CharacterRangeVector;
typedef InfallibleVector<CharacterRange, 1> CharacterRangeVector;
// Represents code units in the range from from_ to to_, both ends are
// inclusive.
@@ -211,8 +243,8 @@ class OutSet
static const unsigned kFirstLimit = 32;
private:
typedef Vector<OutSet*, 1, LifoAllocPolicy<Infallible> > OutSetVector;
typedef Vector<unsigned, 1, LifoAllocPolicy<Infallible> > RemainingVector;
typedef InfallibleVector<OutSet*, 1> OutSetVector;
typedef InfallibleVector<unsigned, 1> RemainingVector;
// Destructively set a value in this set. In most cases you want
// to use Extend instead to ensure that only one instance exists
@@ -317,7 +349,7 @@ class TextElement
RegExpTree* tree_;
};
typedef Vector<TextElement, 1, LifoAllocPolicy<Infallible> > TextElementVector;
typedef InfallibleVector<TextElement, 1> TextElementVector;
class NodeVisitor;
class RegExpCompiler;
@@ -956,7 +988,7 @@ class Guard
int value_;
};
typedef Vector<Guard*, 1, LifoAllocPolicy<Infallible> > GuardVector;
typedef InfallibleVector<Guard*, 1> GuardVector;
class GuardedAlternative
{
@@ -975,7 +1007,7 @@ class GuardedAlternative
GuardVector* guards_;
};
typedef Vector<GuardedAlternative, 0, LifoAllocPolicy<Infallible> > GuardedAlternativeVector;
typedef InfallibleVector<GuardedAlternative, 0> GuardedAlternativeVector;
class AlternativeGeneration;
@@ -1189,7 +1221,7 @@ class BoyerMoorePositionInfo
bool is_word() { return w_ == kLatticeIn; }
private:
Vector<bool, 0, LifoAllocPolicy<Infallible> > map_;
InfallibleVector<bool, 0> map_;
int map_count_; // Number of set bits in the map.
ContainedInLattice w_; // The \w character class.
ContainedInLattice s_; // The \s character class.
@@ -1197,7 +1229,7 @@ class BoyerMoorePositionInfo
ContainedInLattice surrogate_; // Surrogate UTF-16 code units.
};
typedef Vector<BoyerMoorePositionInfo*, 1, LifoAllocPolicy<Infallible> > BoyerMoorePositionInfoVector;
typedef InfallibleVector<BoyerMoorePositionInfo*, 1> BoyerMoorePositionInfoVector;
class BoyerMooreLookahead
{
+3 -3
View File
@@ -616,7 +616,7 @@ class WideCharRange
widechar to_;
};
typedef Vector<WideCharRange, 1, LifoAllocPolicy<Infallible> > WideCharRangeVector;
typedef InfallibleVector<WideCharRange, 1> WideCharRangeVector;
static inline CharacterRange
LeadSurrogateRange()
@@ -740,10 +740,10 @@ AddUnicodeRange(LifoAlloc* alloc,
// encompassing the full range of possible values.
template <typename RangeType>
static inline void
NegateUnicodeRanges(LifoAlloc* alloc, Vector<RangeType, 1, LifoAllocPolicy<Infallible> >** ranges,
NegateUnicodeRanges(LifoAlloc* alloc, InfallibleVector<RangeType, 1>** ranges,
RangeType full_range)
{
typedef Vector<RangeType, 1, LifoAllocPolicy<Infallible> > RangeVector;
typedef InfallibleVector<RangeType, 1> RangeVector;
RangeVector* tmp_ranges = alloc->newInfallible<RangeVector>(*alloc);
tmp_ranges->append(full_range);
RangeVector* result_ranges = alloc->newInfallible<RangeVector>(*alloc);
+1 -1
View File
@@ -60,7 +60,7 @@ template <typename T, int initial_size>
class BufferedVector
{
public:
typedef Vector<T*, 1, LifoAllocPolicy<Infallible> > VectorType;
typedef InfallibleVector<T*, 1> VectorType;
BufferedVector() : list_(nullptr), last_(nullptr) {}
@@ -0,0 +1,16 @@
if (typeof TypedObject === "undefined" || typeof Intl === "undefined")
quit();
try {
gczeal(4)
} catch (exc) {}
var T = TypedObject;
var ValueStruct = new T.StructType({
f: T.Any
})
var v = new ValueStruct;
new class get extends Number {};
function writeValue(o, v)
o.f = v
for (var i = 0; i < 5; i++)
writeValue(v, {}, "helo")
@@ -1,33 +0,0 @@
load(libdir + "asm.js");
var byteLength = Function.prototype.call.bind(
Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get
);
var m = asmCompile("glob", "s", "b", `
"use asm";
var I32 = glob.Int32Array;
var i32 = new I32(b);
var len = glob.byteLength;
function ch(b2) {
if (len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 80000000) {
return false;
}
i32 = new I32(b2);
b = b2;
return true
}
function get(i) {
i = i | 0;
return i32[i >> 2] | 0
}
return {
get: get,
changeHeap: ch
}
`);
var buf1 = new ArrayBuffer(16777216)
var { get, changeHeap } = asmLink(m, this, null, buf1)
assertEq(changeHeap(new ArrayBuffer(33554432)), true)
assertEq(get(), 0)
assertEq(changeHeap(buf1), true);
get();
@@ -0,0 +1,15 @@
enableSPSProfiling();
function mod() {
"use asm";
function f(i0) {
i0 = i0 | 0
switch (0) {
case 0:
}
}
return f;
}
mod()();
mod()();
+3 -75
View File
@@ -23,19 +23,8 @@ var buffer = new ArrayBuffer(BUF_MIN);
var {get, set} = asmLink(m, this, null, buffer);
set(4, 42);
assertEq(get(4), 42);
neuter(buffer, "change-data");
neuter(buffer, "same-data");
assertThrowsInstanceOf(() => get(4), InternalError);
var buf1 = new ArrayBuffer(BUF_MIN);
var buf2 = new ArrayBuffer(BUF_MIN);
var {get:get1, set:set1} = asmLink(m, this, null, buf1);
var {get:get2, set:set2} = asmLink(m, this, null, buf2);
set1(0, 13);
set2(0, 42);
neuter(buf1, "change-data");
assertThrowsInstanceOf(() => get1(0), InternalError);
assertEq(get2(0), 42);
assertThrowsInstanceOf(() => neuter(buffer, "change-data"), InternalError);
assertThrowsInstanceOf(() => neuter(buffer, "same-data"), InternalError);
var m = asmCompile('stdlib', 'foreign', 'buffer',
`"use asm";
@@ -49,66 +38,5 @@ var m = asmCompile('stdlib', 'foreign', 'buffer',
return inner`);
var buffer = new ArrayBuffer(BUF_MIN);
function ffi1() { neuter(buffer, "change-data"); }
function ffi1() { assertThrowsInstanceOf(() => neuter(buffer, "change-data"), InternalError) }
var inner = asmLink(m, this, {ffi:ffi1}, buffer);
assertThrowsInstanceOf(() => inner(8), InternalError);
var byteLength = Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
var m = asmCompile('stdlib', 'foreign', 'buffer',
`"use asm";
var ffi = foreign.ffi;
var I32 = stdlib.Int32Array;
var i32 = new I32(buffer);
var len = stdlib.byteLength;
function changeHeap(newBuffer) {
if (len(newBuffer) & 0xffffff || len(newBuffer) <= 0xffffff || len(newBuffer) > 0x80000000)
return false;
i32 = new I32(newBuffer);
buffer = newBuffer;
return true;
}
function get(i) {
i=i|0;
return i32[i>>2]|0;
}
function inner(i) {
i=i|0;
ffi();
return get(i)|0;
}
return {changeHeap:changeHeap, get:get, inner:inner}`);
var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
var buf3 = new ArrayBuffer(BUF_CHANGE_MIN);
var buf4 = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf2)[13] = 42;
new Int32Array(buf3)[13] = 1024;
new Int32Array(buf4)[13] = 1337;
function ffi2() { neuter(buf1, "change-data"); assertEq(changeHeap(buf2), true); }
var {changeHeap, get:get2, inner} = asmLink(m, this, {ffi:ffi2}, buf1);
assertEq(inner(13*4), 42);
function ffi3() {
assertEq(get2(13*4), 42);
assertEq(get2(BUF_CHANGE_MIN), 0)
assertEq(get3(13*4), 42);
assertEq(get3(BUF_CHANGE_MIN), 0)
neuter(buf2, "change-data");
assertThrowsInstanceOf(()=>get2(13*4), InternalError);
assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
assertThrowsInstanceOf(()=>get3(13*4), InternalError);
assertThrowsInstanceOf(()=>get3(BUF_CHANGE_MIN), InternalError);
assertEq(changeHeap(buf3), true);
assertThrowsInstanceOf(()=>get2(13*4), InternalError);
assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
assertEq(get3(13*4), 1024);
assertEq(get3(BUF_CHANGE_MIN), 0);
assertEq(changeHeap(buf4), true);
}
var {changeHeap, get:get3, inner} = asmLink(m, this, {ffi:ffi3}, buf2);
assertEq(inner(13*4), 1337);
assertThrowsInstanceOf(()=>get2(0), InternalError);
assertEq(get3(BUF_CHANGE_MIN), 0);
assertEq(get3(13*4), 1337);
@@ -202,16 +202,6 @@ var stacks = disableSingleStepProfiling();
assertStackContainsSeq(stacks, ">,f1,>,<,f1,>,>,<,f1,>,f2,>,<,f1,>,<,f2,>,<,f1,>,f2,>,<,f1,>,>,<,f1,>,<,f1,>,f1,>,>");
// Detachment exit
var buf = new ArrayBuffer(BUF_CHANGE_MIN);
var ffi = function() { neuter(buf, 'change-data') }
var f = asmLink(asmCompile('g','ffis','buf', USE_ASM + 'var ffi = ffis.ffi; var i32 = new g.Int32Array(buf); function f() { ffi() } return f'), this, {ffi:ffi}, buf);
enableSingleStepProfiling();
assertThrowsInstanceOf(f, InternalError);
var stacks = disableSingleStepProfiling();
assertStackContainsSeq(stacks, ">,f,>,<,f,>,inline stub,f,>,<,f,>,inline stub,f,>");
if (isSimdAvailable() && typeof SIMD !== 'undefined') {
// SIMD out-of-bounds exit
var buf = new ArrayBuffer(0x10000);
-366
View File
@@ -1,366 +0,0 @@
// |jit-test| test-also-noasmjs
load(libdir + "asm.js");
load(libdir + "asserts.js");
// Tests for importing typed array view constructors
assertAsmTypeFail('glob', USE_ASM + "var I32=glob.Int32Arra; function f() {} return f");
var m = asmCompile('glob', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
assertAsmLinkFail(m, {});
assertAsmLinkFail(m, {Int32Array:null});
assertAsmLinkFail(m, {Int32Array:{}});
assertAsmLinkFail(m, {Int32Array:Uint32Array});
assertEq(asmLink(m, {Int32Array:Int32Array})(), undefined);
var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
assertEq(asmLink(m, this)(), undefined);
assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I3(buf); function f() {} return f');
assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=0; var i32=new I32(buf); function f() {} return f');
var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I32(buf); function f() {} return f');
assertAsmLinkFail(m, this, null, {});
assertAsmLinkAlwaysFail(m, this, null, null);
assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
assertAsmLinkFail(m, this, null, {});
assertAsmLinkAlwaysFail(m, this, null, null);
assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var F32=glob.Float32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
assertAsmLinkFail(m, this, null, {});
assertAsmLinkAlwaysFail(m, this, null, null);
assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
// Tests for link-time validation of byteLength import
assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var byteLength=glob.byteLength; function f() { return byteLength(1)|0 } return f');
var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var byteLength=glob.byteLength; function f() { return 42 } return f');
assertEq('byteLength' in this, false);
assertAsmLinkFail(m, this);
this['byteLength'] = null;
assertAsmLinkFail(m, this);
this['byteLength'] = {};
assertAsmLinkFail(m, this);
this['byteLength'] = function(){}
assertAsmLinkFail(m, this);
this['byteLength'] = (function(){}).bind(null);
assertAsmLinkFail(m, this);
this['byteLength'] = Function.prototype.call.bind();
assertAsmLinkFail(m, this);
this['byteLength'] = Function.prototype.call.bind({});
assertAsmLinkFail(m, this);
this['byteLength'] = Function.prototype.call.bind(function f() {});
assertAsmLinkFail(m, this);
this['byteLength'] = Function.prototype.call.bind(Math.sin);
assertAsmLinkFail(m, this);
this['byteLength'] =
Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
assertEq(asmLink(m, this)(), 42);
var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var b1=glob.byteLength, b2=glob.byteLength; function f() { return 43 } return f');
assertEq(asmLink(m, this)(), 43);
// Tests for validation of change-heap function
const BYTELENGTH_IMPORT = "var len = glob.byteLength; ";
const IMPORT0 = BYTELENGTH_IMPORT;
const IMPORT1 = "var I8=glob.Int8Array; var i8=new I8(b); " + BYTELENGTH_IMPORT;
const IMPORT2 = "var I8=glob.Int8Array; var i8=new I8(b); var I32=glob.Int32Array; var i32=new I32(b); var II32=glob.Int32Array; " + BYTELENGTH_IMPORT;
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f() { return 42 } function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function b(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2=1) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2,xyz) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(...r) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2,...r) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch({b2}) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { ;if((len((b2))) & (0xffffff) || (len((b2)) <= (0xffffff)) || len(b2) > 0x80000000) {;;return false;;} ; i8=new I8(b2);; b=b2;; return true;; } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function ch2(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { 3; if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { b2=b2|0; if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || 1 || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || 1 || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(i8(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(xyz) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff && len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) | 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) == 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xfffffe || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0x1ffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0x7fffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) < 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xfffffe || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) < 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || 1 > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0.0) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0xffffff) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffffff || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x1000001) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000001) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) ; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) {} i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) {return false} i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return true; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT0 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i7=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=1; b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new 1; b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I7(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new b(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8; b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(1); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2,1); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); xyz=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=1; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; 1; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return 1 } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return false } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; if (0) return true; 1 } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I32(b2); i32=new I8(b2); b=b2; return true } function f() { return 42 } return f');
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new II32(b2); b=b2; return true } function f() { return 42 } return f');
// Tests for no calls in heap index expressions
const CHANGE_FUN = 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true }';
const SETUP = USE_ASM + IMPORT2 + 'var imul=glob.Math.imul; var ffi=ffis.ffi;' + CHANGE_FUN;
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] = 0 } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = 0 } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(imul(i,i)|0) >> 2] = 0 } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (imul(i,i)|0) } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(ffi()|0) >> 2] } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(TBL[i&0]()|0) >> 2] } function g() { return 0 } var TBL=[g]; return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] = 0 } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = g()|0 } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i32[(g()|0)>>2] >> 2] } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i32[(g()|0)>>2] >> 2] = 0 } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = i32[(g()|0)>>2] } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] = 0 } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (i32[i>>2]|0) + (g()|0) } function g() { return 0 } return f');
if (isSimdAvailable() && typeof SIMD !== 'undefined')
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'var i4 = glob.SIMD.Int32x4; var ext = i4.extractLane; var add = i4.add;' + CHANGE_FUN + 'function f(i) { i=i|0; i32[ext(i4(i,1,2,i),0) >> 2]; i32[ext(add(i4(0,0,0,0),i4(1,1,1,1)),0) >> 2]; } return f');
// Tests for constant heap accesses when change-heap is used
const HEADER = USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= MIN || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } ';
assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { i8[0x1000000] = 0 } return f');
asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { i8[0xffffff] = 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { i8[0x1000001] = 0 } return f');
asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { i8[0x1000000] = 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { return i8[0x1000000]|0 } return f');
asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { return i8[0xffffff]|0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { return i8[0x1000001]|0 } return f');
asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { return i8[0x1000000]|0 } return f');
// Tests for validation of heap length
var body = USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1ffffff || len(b2) > 0x4000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return ch';
var m = asmCompile('glob', 'ffis', 'b', body);
assertAsmLinkFail(m, this, null, new ArrayBuffer(BUF_CHANGE_MIN));
assertAsmLinkFail(m, this, null, new ArrayBuffer(0x1000000));
var changeHeap = asmLink(m, this, null, new ArrayBuffer(0x2000000));
assertEq(changeHeap(new ArrayBuffer(0x1000000)), false);
assertEq(changeHeap(new ArrayBuffer(0x2000000)), true);
assertEq(changeHeap(new ArrayBuffer(0x2000001)), false);
assertEq(changeHeap(new ArrayBuffer(0x4000000)), true);
assertEq(changeHeap(new ArrayBuffer(0x5000000)), false);
assertThrowsInstanceOf(() => changeHeap(null), TypeError);
assertThrowsInstanceOf(() => changeHeap({}), TypeError);
assertThrowsInstanceOf(() => changeHeap(new Int32Array(100)), TypeError);
var detached = new ArrayBuffer(BUF_CHANGE_MIN);
neuter(detached, "change-data");
assertEq(changeHeap(detached), false);
// Tests for runtime changing heap
const CHANGE_HEAP = 'var changeHeap = glob.byteLength;';
var changeHeapSource = `function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }`;
var body = `var I32=glob.Int32Array; var i32=new I32(b);
var len=glob.byteLength;` +
changeHeapSource +
`function get(i) { i=i|0; return i32[i>>2]|0 }
function set(i, v) { i=i|0; v=v|0; i32[i>>2] = v }
return {get:get, set:set, changeHeap:ch}`;
var m = asmCompile('glob', 'ffis', 'b', USE_ASM + body);
var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
var {get, set, changeHeap} = asmLink(m, this, null, buf1);
assertEq(m.toString(), "function anonymous(glob, ffis, b) {\n" + USE_ASM + body + "\n}");
assertEq(m.toSource(), "(function anonymous(glob, ffis, b) {\n" + USE_ASM + body + "\n})");
assertEq(changeHeap.toString(), changeHeapSource);
assertEq(changeHeap.toSource(), changeHeapSource);
set(0, 42);
set(4, 13);
set(4, 13);
assertEq(get(0), 42);
assertEq(get(4), 13);
set(BUF_CHANGE_MIN, 262);
assertEq(get(BUF_CHANGE_MIN), 0);
var buf2 = new ArrayBuffer(2*BUF_CHANGE_MIN);
assertEq(changeHeap(buf2), true);
assertEq(get(0), 0);
assertEq(get(4), 0);
set(BUF_CHANGE_MIN, 262);
assertEq(get(BUF_CHANGE_MIN), 262);
set(2*BUF_CHANGE_MIN, 262);
assertEq(get(2*BUF_CHANGE_MIN), 0);
changeHeap(buf1);
assertEq(get(0), 42);
assertEq(get(4), 13);
set(BUF_CHANGE_MIN, 262);
assertEq(get(BUF_CHANGE_MIN), 0);
if (ArrayBuffer.transfer) {
var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
var {get, set, changeHeap} = asmLink(m, this, null, buf1);
set(0, 100);
set(BUF_CHANGE_MIN - 4, 101);
set(BUF_CHANGE_MIN, 102);
var buf2 = ArrayBuffer.transfer(buf1);
assertEq(changeHeap(buf2), true);
assertEq(buf1.byteLength, 0);
assertEq(buf2.byteLength, BUF_CHANGE_MIN);
assertEq(get(0), 100);
assertEq(get(BUF_CHANGE_MIN-4), 101);
assertEq(get(BUF_CHANGE_MIN), 0);
assertEq(get(2*BUF_CHANGE_MIN-4), 0);
var buf3 = ArrayBuffer.transfer(buf2, 3*BUF_CHANGE_MIN);
assertEq(changeHeap(buf3), true);
assertEq(buf2.byteLength, 0);
assertEq(buf3.byteLength, 3*BUF_CHANGE_MIN);
assertEq(get(0), 100);
assertEq(get(BUF_CHANGE_MIN-4), 101);
assertEq(get(BUF_CHANGE_MIN), 0);
assertEq(get(2*BUF_CHANGE_MIN), 0);
set(BUF_CHANGE_MIN, 102);
set(2*BUF_CHANGE_MIN, 103);
assertEq(get(BUF_CHANGE_MIN), 102);
assertEq(get(2*BUF_CHANGE_MIN), 103);
var buf4 = ArrayBuffer.transfer(buf3, 2*BUF_CHANGE_MIN);
assertEq(changeHeap(buf4), true);
assertEq(buf3.byteLength, 0);
assertEq(buf4.byteLength, 2*BUF_CHANGE_MIN);
assertEq(get(0), 100);
assertEq(get(BUF_CHANGE_MIN-4), 101);
assertEq(get(BUF_CHANGE_MIN), 102);
assertEq(get(2*BUF_CHANGE_MIN), 0);
var buf5 = ArrayBuffer.transfer(buf4, 3*BUF_CHANGE_MIN);
assertEq(changeHeap(buf5), true);
assertEq(buf4.byteLength, 0);
assertEq(buf5.byteLength, 3*BUF_CHANGE_MIN);
assertEq(get(0), 100);
assertEq(get(BUF_CHANGE_MIN-4), 101);
assertEq(get(BUF_CHANGE_MIN), 102);
assertEq(get(2*BUF_CHANGE_MIN), 0);
var buf6 = ArrayBuffer.transfer(buf5, 0);
assertEq(buf5.byteLength, 0);
assertEq(buf6.byteLength, 0);
assertEq(changeHeap(buf6), false);
}
var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
`var len=glob.byteLength;
function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true }
return ch`);
var changeHeap = asmLink(m, this, null, buf1);
assertEq(changeHeap(buf2), true);
neuter(buf2, "change-data");
assertEq(changeHeap(buf1), true);
neuter(buf1, "change-data");
var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf1)[0] = 13;
var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf2)[0] = 42;
// Tests for changing heap during an FFI:
// Set the warmup to '2' so we can hit both interp and ion FFI exits
setJitCompilerOption("ion.warmup.trigger", 2);
setJitCompilerOption("baseline.warmup.trigger", 0);
setJitCompilerOption("offthread-compilation.enable", 0);
var changeToBuf = null;
var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
`var ffi=ffis.ffi;
var I32=glob.Int32Array; var i32=new I32(b);
var len=glob.byteLength;
function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
function test(i) { i=i|0; var sum=0; sum = i32[i>>2]|0; sum = (sum + (ffi()|0))|0; sum = (sum + (i32[i>>2]|0))|0; return sum|0 }
return {test:test, changeHeap:ch}`);
var ffi = function() { changeHeap(changeToBuf); return 1 }
var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf1);
changeToBuf = buf1;
assertEq(test(0), 27);
changeToBuf = buf2;
assertEq(test(0), 56);
changeToBuf = buf2;
assertEq(test(0), 85);
changeToBuf = buf1;
assertEq(test(0), 56);
changeToBuf = buf1;
assertEq(test(0), 27);
var ffi = function() { return { valueOf:function() { changeHeap(changeToBuf); return 100 } } };
var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf1);
changeToBuf = buf1;
assertEq(test(0), 126);
changeToBuf = buf2;
assertEq(test(0), 155);
changeToBuf = buf2;
assertEq(test(0), 184);
changeToBuf = buf1;
assertEq(test(0), 155);
changeToBuf = buf1;
assertEq(test(0), 126);
if (ArrayBuffer.transfer) {
var buf = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf)[0] = 3;
var ffi = function() {
var buf2 = ArrayBuffer.transfer(buf, 2*BUF_CHANGE_MIN);
new Int32Array(buf2)[BUF_CHANGE_MIN/4] = 13;
assertEq(changeHeap(buf2), true);
return 1
}
var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf);
assertEq(test(BUF_CHANGE_MIN), 14);
}
@@ -1,29 +0,0 @@
// |jit-test| exitstatus: 6;
load(libdir + "asm.js");
// This test may iloop for valid reasons if not compiled with asm.js (namely,
// inlining may allow the heap load to be hoisted out of the loop).
if (!isAsmJSCompilationAvailable())
quit(6);
setJitCompilerOption("signals.enable", 0);
var byteLength =
Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf1)[0] = 13;
var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf2)[0] = 42;
// Test changeHeap from interrupt (as if that could ever happen...)
var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
`var I32=glob.Int32Array; var i32=new I32(b);
var len=glob.byteLength;
function changeHeap(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
function f() {}
function loop(i) { i=i|0; while((i32[i>>2]|0) == 13) { f() } }
return {loop:loop, changeHeap:changeHeap}`);
var { loop, changeHeap } = asmLink(m, this, null, buf1);
timeout(1, function() { assertEq(changeHeap(buf2), false); return false });
loop(0);
@@ -1,27 +0,0 @@
// |jit-test| exitstatus: 6;
load(libdir + "asm.js");
// This test may iloop for valid reasons if not compiled with asm.js (namely,
// inlining may allow the heap load to be hoisted out of the loop).
if (!isAsmJSCompilationAvailable())
quit(6);
var byteLength =
Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf1)[0] = 13;
var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
new Int32Array(buf2)[0] = 42;
// Test changeHeap from interrupt (as if that could ever happen...)
var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
`var I32=glob.Int32Array; var i32=new I32(b);
var len=glob.byteLength;
function changeHeap(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
function f() {}
function loop(i) { i=i|0; while((i32[i>>2]|0) == 13) { f() } }
return {loop:loop, changeHeap:changeHeap}`);
var { loop, changeHeap } = asmLink(m, this, null, buf1);
timeout(1, function() { assertEq(changeHeap(buf2), false); return false });
loop(0);
@@ -0,0 +1,7 @@
var g = /1/g;
g.exec('1234561');
g.exec();
var r = /uDC38/ug;
r.lastIndex = 1;
var str = "\uD83D\uDC38";
r.exec(str);
@@ -1,123 +0,0 @@
load(libdir + "asserts.js");
load(libdir + "asm.js");
// Currently, ArrayBuffer.transfer is #ifdef NIGHTLY_BUILD. When
// ArrayBuffer.transfer is enabled on release, this test should be removed.
if (!ArrayBuffer.transfer)
quit();
var XF = ArrayBuffer.transfer;
assertEq(typeof XF, "function");
assertEq(XF.length, 2);
// arg 1 errors
assertThrowsInstanceOf(()=>XF(), Error);
assertThrowsInstanceOf(()=>XF(undefined), Error);
assertThrowsInstanceOf(()=>XF(null), Error);
assertThrowsInstanceOf(()=>XF({}), Error);
assertThrowsInstanceOf(()=>XF(new Int32Array(1)), Error);
var buf = new ArrayBuffer(1);
neuter(buf, 'change-data');
assertThrowsInstanceOf(()=>XF(buf), TypeError);
// arg 2 errors
var buf = new ArrayBuffer(1);
assertThrowsInstanceOf(()=>XF(buf, -1), Error);
assertThrowsInstanceOf(()=>XF(buf, {valueOf() { return -1 }}), Error);
assertThrowsInstanceOf(()=>XF(buf, {toString() { return "-1" }}), Error);
assertThrowsValue(()=>XF(buf, {valueOf() { throw "wee" }}), "wee");
// arg 2 is coerced via ToInt32
var buf = new ArrayBuffer(1);
assertThrowsInstanceOf(()=>XF(buf, Math.pow(2,31)), Error);
buf = XF(buf, Math.pow(2,32));
assertEq(buf.byteLength, 0);
buf = XF(buf, Math.pow(2,32) + 10);
assertEq(buf.byteLength, 10);
assertThrowsInstanceOf(()=>XF(buf, {valueOf() { neuter(buf, "change-data"); return 10; }}), TypeError);
var buf = new ArrayBuffer(100);
assertThrowsInstanceOf(()=>XF(buf, {valueOf() { ArrayBuffer.transfer(buf, 0); return 100; }}), TypeError);
// on undefined second argument, stay the same size:
var buf1 = new ArrayBuffer(0);
var buf2 = XF(buf1);
assertEq(buf1.byteLength, 0);
assertEq(buf2.byteLength, 0);
assertThrowsInstanceOf(()=>XF(buf1), TypeError);
var buf1 = new ArrayBuffer(3);
var buf2 = XF(buf1);
assertEq(buf1.byteLength, 0);
assertEq(buf2.byteLength, 3);
assertThrowsInstanceOf(()=>XF(buf1), TypeError);
var buf1 = new ArrayBuffer(9);
var buf2 = XF(buf1, undefined);
assertEq(buf1.byteLength, 0);
assertEq(buf2.byteLength, 9);
assertThrowsInstanceOf(()=>XF(buf1), TypeError);
// cross-compartment wrapper
var buf3 = newGlobal().eval("new ArrayBuffer(10)");
var buf4 = XF(buf3, 20);
assertEq(buf4.byteLength, 20);
assertThrowsInstanceOf(()=>XF(buf3), TypeError);
// test going to from various sizes
function test(N1, N2) {
var buf1 = new ArrayBuffer(N1);
var i32 = new Int32Array(buf1);
for (var i = 0; i < i32.length; i++)
i32[i] = i;
var buf2 = XF(buf1, N2);
assertEq(buf1.byteLength, 0);
assertEq(i32.length, 0);
assertEq(buf2.byteLength, N2);
var i32 = new Int32Array(buf2);
for (var i = 0; i < Math.min(N1, N2)/4; i++)
assertEq(i32[i], i);
for (var i = Math.min(N1, N2)/4; i < i32.length; i++) {
assertEq(i32[i], 0);
i32[i] = -i;
}
}
test(0, 0);
test(0, 4);
test(4, 0);
test(4, 4);
test(0, 1000);
test(4, 1000);
test(1000, 0);
test(1000, 4);
test(1000, 1000);
// asm.js:
function testAsmJS(N1, N2) {
var buf1 = new ArrayBuffer(N1);
asmLink(asmCompile('stdlib', 'ffis', 'buf', USE_ASM + "var i32=new stdlib.Int32Array(buf); function f() {} return f"), this, null, buf1);
var i32 = new Int32Array(buf1);
for (var i = 0; i < i32.length; i+=100)
i32[i] = i;
var buf2 = XF(buf1, N2);
assertEq(buf1.byteLength, 0);
assertEq(i32.length, 0);
assertEq(buf2.byteLength, N2);
var i32 = new Int32Array(buf2);
var i = 0;
for (; i < Math.min(N1, N2)/4; i+=100)
assertEq(i32[i], i);
for (; i < i32.length; i+=100) {
assertEq(i32[i], 0);
i32[i] = -i;
}
}
testAsmJS(BUF_MIN, 0);
testAsmJS(BUF_MIN, BUF_MIN);
testAsmJS(BUF_MIN, 2*BUF_MIN);
testAsmJS(2*BUF_MIN, BUF_MIN);
@@ -0,0 +1,15 @@
// |jit-test| allow-oom; allow-unhandlable-oom
// Bug 1234402
// Unhandlable OOM in AlternativeGeneration::AlternativeGeneration.
if (typeof oomAfterAllocations == "function" && helperThreadCount() > 0) {
offThreadCompileScript(`
[null, "", ""].forEach(function(locales) {
try {
Intl.NumberFormat(locales)
} catch (e) {}
oomAfterAllocations(100);
})
`);
runOffThreadScript();
}
+8
View File
@@ -0,0 +1,8 @@
function f() {
for (var e = 1; e < 3000; e++) {
(function(arguments) {
eval("var y");
})();
}
}
f();
+10
View File
@@ -0,0 +1,10 @@
x = 0;
try {
a;
b;
} catch (e) {}
var g = newGlobal();
oomTest(function() {
return Debugger(g);
});
eval("function g() {}");
+38 -52
View File
@@ -10,6 +10,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/SizePrintfMacros.h"
#include "jslibmath.h"
@@ -1123,7 +1124,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
masm.branch32(Assembler::Above, temp3, Imm32(unicode::LeadSurrogateMax), &done);
// Move lastIndex to lead surrogate.
masm.subPtr(Imm32(2), lastIndex);
masm.subPtr(Imm32(1), lastIndex);
masm.bind(&done);
}
@@ -1644,6 +1645,8 @@ CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool)
pushArg(input);
pushArg(regexp);
// We are not using oolCallVM because we are in a Call, and that live
// registers are already saved by the the register allocator.
callVM(RegExpMatcherRawInfo, lir);
masm.jump(ool->rejoin());
@@ -1795,6 +1798,8 @@ CodeGenerator::visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool)
pushArg(input);
pushArg(regexp);
// We are not using oolCallVM because we are in a Call, and that live
// registers are already saved by the the register allocator.
callVM(RegExpTesterRawInfo, lir);
masm.jump(ool->rejoin());
@@ -3873,7 +3878,7 @@ CodeGenerator::visitCallDirectEval(LCallDirectEval* lir)
pushArg(ImmPtr(lir->mir()->pc()));
pushArg(string);
pushArg(ToValue(lir, LCallDirectEval::NewTarget));
pushArg(ImmGCPtr(gen->info().script()));
pushArg(ImmGCPtr(current->mir()->info().script()));
pushArg(scopeChain);
callVM(DirectEvalStringInfo, lir);
@@ -7045,7 +7050,7 @@ CodeGenerator::visitStoreElementT(LStoreElementT* store)
const LAllocation* index = store->index();
if (store->mir()->needsBarrier())
emitPreBarrier(elements, index);
emitPreBarrier(elements, index, store->mir()->offsetAdjustment());
if (store->mir()->needsHoleCheck())
emitStoreHoleCheck(elements, index, store->mir()->offsetAdjustment(), store->snapshot());
@@ -7063,7 +7068,7 @@ CodeGenerator::visitStoreElementV(LStoreElementV* lir)
const LAllocation* index = lir->index();
if (lir->mir()->needsBarrier())
emitPreBarrier(elements, index);
emitPreBarrier(elements, index, lir->mir()->offsetAdjustment());
if (lir->mir()->needsHoleCheck())
emitStoreHoleCheck(elements, index, lir->mir()->offsetAdjustment(), lir->snapshot());
@@ -7095,7 +7100,7 @@ CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir)
masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
if (lir->mir()->needsBarrier())
emitPreBarrier(elements, index);
emitPreBarrier(elements, index, 0);
masm.bind(ool->rejoinStore());
emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
@@ -7145,7 +7150,7 @@ CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir)
masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
if (lir->mir()->needsBarrier())
emitPreBarrier(elements, index);
emitPreBarrier(elements, index, 0);
masm.bind(ool->rejoinStore());
if (index->isConstant())
@@ -8208,33 +8213,6 @@ CodeGenerator::generate()
return !masm.oom();
}
struct AutoDiscardIonCode
{
JSContext* cx;
RecompileInfo* recompileInfo;
IonScript* ionScript;
bool keep;
AutoDiscardIonCode(JSContext* cx, RecompileInfo* recompileInfo)
: cx(cx), recompileInfo(recompileInfo), ionScript(nullptr), keep(false) {}
~AutoDiscardIonCode() {
if (keep)
return;
// Use js_free instead of IonScript::Destroy: the cache list and
// backedge list are still uninitialized.
if (ionScript)
js_free(ionScript);
recompileInfo->compilerOutput(cx->zone()->types)->invalidate();
}
void keepIonCode() {
keep = true;
}
};
bool
CodeGenerator::linkSharedStubs(JSContext* cx)
{
@@ -8305,13 +8283,20 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
// Check to make sure we didn't have a mid-build invalidation. If so, we
// will trickle to jit::Compile() and return Method_Skipped.
uint32_t warmUpCount = script->getWarmUpCount();
RecompileInfo recompileInfo;
bool isValid;
if (!FinishCompilation(cx, script, constraints, &recompileInfo, &isValid))
return false;
if (!isValid)
// Record constraints. If an error occured, returns false and potentially
// prevent future compilations. Otherwise, if an invalidation occured, then
// skip the current compilation.
RecompileInfo recompileInfo;
bool validRecompiledInfo = false;
if (!FinishCompilation(cx, script, constraints, &recompileInfo, &validRecompiledInfo))
return false;
if (!validRecompiledInfo)
return true;
auto guardRecordedConstraints = mozilla::MakeScopeExit([&] {
// In case of error, invalidate the current recompileInfo.
recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
});
// IonMonkey could have inferred better type information during
// compilation. Since adding the new information to the actual type
@@ -8329,20 +8314,22 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
if (!encodeSafepoints())
return false;
AutoDiscardIonCode discardIonCode(cx, &recompileInfo);
IonScript* ionScript =
IonScript::New(cx, recompileInfo,
graph.totalSlotCount(), argumentSlots, scriptFrameSize,
snapshots_.listSize(), snapshots_.RVATableSize(),
recovers_.size(), bailouts_.length(), graph.numConstants(),
safepointIndices_.length(), osiIndices_.length(),
cacheList_.length(), runtimeData_.length(),
safepoints_.size(), patchableBackedges_.length(),
sharedStubs_.length(), optimizationLevel);
IonScript::New(cx, recompileInfo,
graph.totalSlotCount(), argumentSlots, scriptFrameSize,
snapshots_.listSize(), snapshots_.RVATableSize(),
recovers_.size(), bailouts_.length(), graph.numConstants(),
safepointIndices_.length(), osiIndices_.length(),
cacheList_.length(), runtimeData_.length(),
safepoints_.size(), patchableBackedges_.length(),
sharedStubs_.length(), optimizationLevel);
if (!ionScript)
return false;
discardIonCode.ionScript = ionScript;
auto guardIonScript = mozilla::MakeScopeExit([&ionScript] {
// Use js_free instead of IonScript::Destroy: the cache list and
// backedge list are still uninitialized.
js_free(ionScript);
});
// Also, note that creating the code here during an incremental GC will
// trace the code and mark all GC things it refers to. This captures any
@@ -8545,9 +8532,8 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
if (IonScriptCounts* counts = extractScriptCounts())
script->addIonCounts(counts);
// Make sure that AutoDiscardIonCode does not free the relevant info.
discardIonCode.keepIonCode();
guardIonScript.release();
guardRecordedConstraints.release();
return true;
}
+64 -13
View File
@@ -27,6 +27,7 @@
#include "jit/ExecutableAllocator.h"
#include "jit/JitCompartment.h"
#include "js/MemoryMetrics.h"
#ifdef __APPLE__
@@ -45,6 +46,8 @@ ExecutablePool::~ExecutablePool()
MOZ_ASSERT(m_regexpCodeBytes == 0);
MOZ_ASSERT(m_otherCodeBytes == 0);
MOZ_ASSERT(!isMarked());
m_allocator->releasePoolPages(this);
}
@@ -91,8 +94,8 @@ ExecutablePool::addRef()
// pools have multiple holders, and they have one holder per chunk
// of generated code, and they only hold 16KB or so of code.
MOZ_ASSERT(m_refCount);
MOZ_ASSERT(m_refCount < UINT_MAX);
++m_refCount;
MOZ_ASSERT(m_refCount, "refcount overflow");
}
void*
@@ -338,21 +341,69 @@ ExecutableAllocator::addSizeOfCode(JS::CodeSizes* sizes) const
void
ExecutableAllocator::reprotectAll(ProtectionSetting protection)
{
if (!nonWritableJitCode)
return;
#ifdef NON_WRITABLE_JIT_CODE
if (!m_pools.initialized())
return;
for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) {
ExecutablePool* pool = r.front();
char* start = pool->m_allocation.pages;
reprotectRegion(start, pool->m_freePtr - start, protection);
}
for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront())
reprotectPool(rt_, r.front(), protection);
#endif
}
#if TARGET_OS_IPHONE
bool ExecutableAllocator::nonWritableJitCode = true;
#else
bool ExecutableAllocator::nonWritableJitCode = false;
/* static */ void
ExecutableAllocator::reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection)
{
#ifdef NON_WRITABLE_JIT_CODE
// Don't race with reprotectAll called from the signal handler.
MOZ_ASSERT(rt->jitRuntime()->preventBackedgePatching() || rt->handlingJitInterrupt());
char* start = pool->m_allocation.pages;
if (!reprotectRegion(start, pool->m_freePtr - start, protection))
MOZ_CRASH();
#endif
}
/* static */ void
ExecutableAllocator::poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges)
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
// Don't race with reprotectAll called from the signal handler.
JitRuntime::AutoPreventBackedgePatching apbp(rt);
#ifdef DEBUG
// Make sure no pools have the mark bit set.
for (size_t i = 0; i < ranges.length(); i++)
MOZ_ASSERT(!ranges[i].pool->isMarked());
#endif
for (size_t i = 0; i < ranges.length(); i++) {
ExecutablePool* pool = ranges[i].pool;
if (pool->m_refCount == 1) {
// This is the last reference so the release() call below will
// unmap the memory. Don't bother poisoning it.
continue;
}
MOZ_ASSERT(pool->m_refCount > 1);
// Use the pool's mark bit to indicate we made the pool writable.
// This avoids reprotecting a pool multiple times.
if (!pool->isMarked()) {
reprotectPool(rt, pool, Writable);
pool->mark();
}
memset(ranges[i].start, JS_SWEPT_CODE_PATTERN, ranges[i].size);
}
// Make the pools executable again and drop references.
for (size_t i = 0; i < ranges.length(); i++) {
ExecutablePool* pool = ranges[i].pool;
if (pool->isMarked()) {
reprotectPool(rt, pool, Executable);
pool->unmark();
}
pool->release();
}
}
+55 -14
View File
@@ -98,7 +98,10 @@ class ExecutablePool
Allocation m_allocation;
// Reference count for automatic reclamation.
unsigned m_refCount;
unsigned m_refCount:31;
// Flag that can be used by algorithms operating on pools.
bool m_mark:1;
// Number of bytes currently used for Method and Regexp JIT code.
size_t m_ionCodeBytes;
@@ -110,25 +113,52 @@ class ExecutablePool
void release(bool willDestroy = false);
void release(size_t n, CodeKind kind);
void addRef();
ExecutablePool(ExecutableAllocator* allocator, Allocation a)
: m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a),
m_refCount(1), m_ionCodeBytes(0), m_baselineCodeBytes(0), m_regexpCodeBytes(0),
m_otherCodeBytes(0)
m_refCount(1), m_mark(false), m_ionCodeBytes(0), m_baselineCodeBytes(0),
m_regexpCodeBytes(0), m_otherCodeBytes(0)
{ }
~ExecutablePool();
void mark() {
MOZ_ASSERT(!m_mark);
m_mark = true;
}
void unmark() {
MOZ_ASSERT(m_mark);
m_mark = false;
}
bool isMarked() const {
return m_mark;
}
private:
ExecutablePool(const ExecutablePool&) = delete;
void operator=(const ExecutablePool&) = delete;
void addRef();
void* alloc(size_t n, CodeKind kind);
size_t available() const;
};
struct JitPoisonRange
{
jit::ExecutablePool* pool;
void* start;
size_t size;
JitPoisonRange(jit::ExecutablePool* pool, void* start, size_t size)
: pool(pool), start(start), size(size)
{}
};
typedef Vector<JitPoisonRange, 0, SystemAllocPolicy> JitPoisonRangeVector;
#define NON_WRITABLE_JIT_CODE 1
class ExecutableAllocator
{
#ifdef XP_WIN
@@ -155,8 +185,6 @@ class ExecutableAllocator
static void initStatic();
static bool nonWritableJitCode;
private:
static size_t pageSize;
static size_t largeAllocSize;
@@ -173,17 +201,27 @@ class ExecutableAllocator
ExecutablePool* createPool(size_t n);
ExecutablePool* poolForSize(size_t n);
static void reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection);
public:
static void makeWritable(void* start, size_t size)
MOZ_WARN_UNUSED_RESULT
static bool makeWritable(void* start, size_t size)
{
if (nonWritableJitCode)
reprotectRegion(start, size, Writable);
#ifdef NON_WRITABLE_JIT_CODE
return reprotectRegion(start, size, Writable);
#else
return true;
#endif
}
static void makeExecutable(void* start, size_t size)
MOZ_WARN_UNUSED_RESULT
static bool makeExecutable(void* start, size_t size)
{
if (nonWritableJitCode)
reprotectRegion(start, size, Executable);
#ifdef NON_WRITABLE_JIT_CODE
return reprotectRegion(start, size, Executable);
#else
return true;
#endif
}
void makeAllWritable() {
@@ -195,6 +233,8 @@ class ExecutableAllocator
static unsigned initialProtectionFlags(ProtectionSetting protection);
static void poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges);
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
static void cacheFlush(void*, size_t)
{
@@ -267,7 +307,8 @@ class ExecutableAllocator
ExecutableAllocator(const ExecutableAllocator&) = delete;
void operator=(const ExecutableAllocator&) = delete;
static void reprotectRegion(void*, size_t, ProtectionSetting);
MOZ_WARN_UNUSED_RESULT
static bool reprotectRegion(void*, size_t, ProtectionSetting);
void reprotectAll(ProtectionSetting);
// These are strong references; they keep pools alive.
+7 -6
View File
@@ -78,10 +78,10 @@ ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
static const unsigned FLAGS_RW = PROT_READ | PROT_WRITE;
static const unsigned FLAGS_RX = PROT_READ | PROT_EXEC;
void
bool
ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
{
MOZ_ASSERT(nonWritableJitCode);
MOZ_ASSERT(NON_WRITABLE_JIT_CODE);
MOZ_ASSERT(pageSize);
// Calculate the start of the page containing this region,
@@ -95,14 +95,15 @@ ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting
size += (pageSize - 1);
size &= ~(pageSize - 1);
mprotect(pageStart, size, (setting == Writable) ? FLAGS_RW : FLAGS_RX);
return !mprotect(pageStart, size, (setting == Writable) ? FLAGS_RW : FLAGS_RX);
}
/* static */ unsigned
ExecutableAllocator::initialProtectionFlags(ProtectionSetting protection)
{
if (!nonWritableJitCode)
return FLAGS_RW | FLAGS_RX;
#ifdef NON_WRITABLE_JIT_CODE
return (protection == Writable) ? FLAGS_RW : FLAGS_RX;
#else
return FLAGS_RW | FLAGS_RX;
#endif
}
+7 -7
View File
@@ -236,10 +236,10 @@ ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize);
}
void
bool
ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
{
MOZ_ASSERT(nonWritableJitCode);
MOZ_ASSERT(NON_WRITABLE_JIT_CODE);
MOZ_ASSERT(pageSize);
// Calculate the start of the page containing this region,
@@ -255,15 +255,15 @@ ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting
DWORD oldProtect;
int flags = (setting == Writable) ? PAGE_READWRITE : PAGE_EXECUTE_READ;
if (!VirtualProtect(pageStart, size, flags, &oldProtect))
MOZ_CRASH();
return VirtualProtect(pageStart, size, flags, &oldProtect);
}
/* static */ unsigned
ExecutableAllocator::initialProtectionFlags(ProtectionSetting protection)
{
if (!nonWritableJitCode)
return PAGE_EXECUTE_READWRITE;
#ifdef NON_WRITABLE_JIT_CODE
return (protection == Writable) ? PAGE_READWRITE : PAGE_EXECUTE_READ;
#else
return PAGE_EXECUTE_READWRITE;
#endif
}
+22 -23
View File
@@ -848,18 +848,16 @@ JitCode::traceChildren(JSTracer* trc)
if (invalidated())
return;
// If we're moving objects, we need writable JIT code.
ReprotectCode reprotect = (trc->runtime()->isHeapMinorCollecting() || zone()->isGCCompacting())
? Reprotect
: DontReprotect;
MaybeAutoWritableJitCode awjc(this, reprotect);
if (jumpRelocTableBytes_) {
uint8_t* start = code_ + jumpRelocTableOffset();
CompactBufferReader reader(start, start + jumpRelocTableBytes_);
MacroAssembler::TraceJumpRelocations(trc, this, reader);
}
if (dataRelocTableBytes_) {
// If we're moving objects, we need writable JIT code.
bool movingObjects = trc->runtime()->isHeapMinorCollecting() || zone()->isGCCompacting();
MaybeAutoWritableJitCode awjc(this, movingObjects ? Reprotect : DontReprotect);
uint8_t* start = code_ + dataRelocTableOffset();
CompactBufferReader reader(start, start + dataRelocTableBytes_);
MacroAssembler::TraceDataRelocations(trc, this, reader);
@@ -879,24 +877,22 @@ JitCode::finalize(FreeOp* fop)
}
#endif
// Buffer can be freed at any time hereafter. Catch use-after-free bugs.
// Don't do this if the Ion code is protected, as the signal handler will
// deadlock trying to reacquire the interrupt lock.
{
AutoWritableJitCode awjc(this);
memset(code_, JS_SWEPT_CODE_PATTERN, bufferSize_);
code_ = nullptr;
}
MOZ_ASSERT(pool_);
// Code buffers are stored inside JSC pools.
// Pools are refcounted. Releasing the pool may free it.
if (pool_) {
// Horrible hack: if we are using perf integration, we don't
// want to reuse code addresses, so we just leak the memory instead.
if (!PerfEnabled())
pool_->release(headerSize_ + bufferSize_, CodeKind(kind_));
pool_ = nullptr;
}
// With W^X JIT code, reprotecting memory for each JitCode instance is
// slow, so we record the ranges and poison them later all at once. It's
// safe to ignore OOM here, it just means we won't poison the code.
if (fop->appendJitPoisonRange(JitPoisonRange(pool_, code_, bufferSize_)))
pool_->addRef();
code_ = nullptr;
// Code buffers are stored inside ExecutablePools. Pools are refcounted.
// Releasing the pool may free it. Horrible hack: if we are using perf
// integration, we don't want to reuse code addresses, so we just leak the
// memory instead.
if (!PerfEnabled())
pool_->release(headerSize_ + bufferSize_, CodeKind(kind_));
pool_ = nullptr;
}
void
@@ -1366,6 +1362,9 @@ IonScript::purgeCaches()
if (invalidated())
return;
if (numCaches() == 0)
return;
AutoWritableJitCode awjc(method());
for (size_t i = 0; i < numCaches(); i++)
getCacheFromIndex(i).reset(DontReprotect);
+4
View File
@@ -372,8 +372,12 @@ class SimdConstant {
MOZ_ASSERT(defined() && rhs.defined());
if (type() != rhs.type())
return false;
// Takes negative zero into accuont, as it's a bit comparison.
return memcmp(&u, &rhs.u, sizeof(u)) == 0;
}
bool operator!=(const SimdConstant& rhs) const {
return !operator==(rhs);
}
// SimdConstant is a HashPolicy
typedef SimdConstant Lookup;
+7 -5
View File
@@ -526,9 +526,9 @@ void FinishInvalidation(FreeOp* fop, JSScript* script);
const unsigned WINDOWS_BIG_FRAME_TOUCH_INCREMENT = 4096 - 1;
#endif
// If ExecutableAllocator::nonWritableJitCode is |true|, this class will ensure
// JIT code is writable (has RW permissions) in its scope. If nonWritableJitCode
// is |false|, it's a no-op.
// If NON_WRITABLE_JIT_CODE is enabled, this class will ensure
// JIT code is writable (has RW permissions) in its scope.
// Otherwise it's a no-op.
class MOZ_STACK_CLASS AutoWritableJitCode
{
// Backedge patching from the signal handler will change memory protection
@@ -543,7 +543,8 @@ class MOZ_STACK_CLASS AutoWritableJitCode
: preventPatching_(rt), rt_(rt), addr_(addr), size_(size)
{
rt_->toggleAutoWritableJitCodeActive(true);
ExecutableAllocator::makeWritable(addr_, size_);
if (!ExecutableAllocator::makeWritable(addr_, size_))
MOZ_CRASH();
}
AutoWritableJitCode(void* addr, size_t size)
: AutoWritableJitCode(TlsPerThreadData.get()->runtimeFromMainThread(), addr, size)
@@ -552,7 +553,8 @@ class MOZ_STACK_CLASS AutoWritableJitCode
: AutoWritableJitCode(code->runtimeFromMainThread(), code->raw(), code->bufferSize())
{}
~AutoWritableJitCode() {
ExecutableAllocator::makeExecutable(addr_, size_);
if (!ExecutableAllocator::makeExecutable(addr_, size_))
MOZ_CRASH();
rt_->toggleAutoWritableJitCodeActive(false);
}
};
+8 -1
View File
@@ -2111,7 +2111,14 @@ MustCloneRegExp(MRegExp* regexp)
return true;
MDefinition* def = node->toDefinition();
if (def->isRegExpTester()) {
if (def->isRegExpMatcher()) {
MRegExpMatcher* test = def->toRegExpMatcher();
if (test->indexOf(*iter) == 1) {
// Optimized RegExp.prototype.exec.
MOZ_ASSERT(test->regexp() == regexp);
continue;
}
} else if (def->isRegExpTester()) {
MRegExpTester* test = def->toRegExpTester();
if (test->indexOf(*iter) == 1) {
// Optimized RegExp.prototype.test.
+4 -11
View File
@@ -7511,6 +7511,7 @@ class MRegExpMatcher
initOperand(2, lastIndex);
initOperand(3, sticky);
setMovable();
// May be object or null.
setResultType(MIRType_Value);
}
@@ -7540,10 +7541,7 @@ class MRegExpMatcher
bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
// XXX: always return false for now, to work around bug 1132128.
if (false && regexp()->isRegExp())
return !regexp()->toRegExp()->source()->needUpdateLastIndex();
return false;
return true;
}
bool possiblyCalls() const override {
@@ -7566,6 +7564,7 @@ class MRegExpTester
initOperand(2, lastIndex);
initOperand(3, sticky);
setMovable();
setResultType(MIRType_Int32);
}
@@ -7597,13 +7596,7 @@ class MRegExpTester
bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
// RegExpTester has a side-effect on the regexp object's lastIndex
// when sticky or global flags are set.
// Return false unless we are sure it's not the case.
// XXX: always return false for now, to work around bug 1132128.
if (false && regexp()->isRegExp())
return !regexp()->toRegExp()->source()->needUpdateLastIndex();
return false;
return true;
}
};
+3 -3
View File
@@ -1530,13 +1530,13 @@ CodeGeneratorShared::emitAsmJSCall(LAsmJSCall* ins)
}
void
CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation* index)
CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation* index, int32_t offsetAdjustment)
{
if (index->isConstant()) {
Address address(base, ToInt32(index) * sizeof(Value));
Address address(base, ToInt32(index) * sizeof(Value) + offsetAdjustment);
masm.patchableCallPreBarrier(address, MIRType_Value);
} else {
BaseIndex address(base, ToRegister(index), TimesEight);
BaseIndex address(base, ToRegister(index), TimesEight, offsetAdjustment);
masm.patchableCallPreBarrier(address, MIRType_Value);
}
}
+1 -1
View File
@@ -338,7 +338,7 @@ class CodeGeneratorShared : public LElementVisitor
void emitAsmJSCall(LAsmJSCall* ins);
void emitPreBarrier(Register base, const LAllocation* index);
void emitPreBarrier(Register base, const LAllocation* index, int32_t offsetAdjustment);
void emitPreBarrier(Address address);
// We don't emit code for trivial blocks, so if we want to branch to the
+8
View File
@@ -1395,6 +1395,14 @@ JS_SetGCCallback(JSRuntime* rt, JSGCCallback cb, void* data)
rt->gc.setGCCallback(cb, data);
}
JS_PUBLIC_API(void)
JS_SetObjectsTenuredCallback(JSRuntime* rt, JSObjectsTenuredCallback cb,
void* data)
{
AssertHeapIsIdle(rt);
rt->gc.setObjectsTenuredCallback(cb, data);
}
JS_PUBLIC_API(bool)
JS_AddFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb, void* data)
{
+7
View File
@@ -564,6 +564,9 @@ typedef enum JSGCStatus {
typedef void
(* JSGCCallback)(JSRuntime* rt, JSGCStatus status, void* data);
typedef void
(* JSObjectsTenuredCallback)(JSRuntime* rt, void* data);
typedef enum JSFinalizeStatus {
/**
* Called when preparing to sweep a group of compartments, before anything
@@ -1658,6 +1661,10 @@ JS_MaybeGC(JSContext* cx);
extern JS_PUBLIC_API(void)
JS_SetGCCallback(JSRuntime* rt, JSGCCallback cb, void* data);
extern JS_PUBLIC_API(void)
JS_SetObjectsTenuredCallback(JSRuntime* rt, JSObjectsTenuredCallback cb,
void* data);
extern JS_PUBLIC_API(bool)
JS_AddFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb, void* data);
+1 -1
View File
@@ -1066,7 +1066,7 @@ JS::ObjectPtr::updateWeakPointerAfterGC()
void
JS::ObjectPtr::trace(JSTracer* trc, const char* name)
{
JS_CallObjectTracer(trc, &value, name);
JS::TraceEdge(trc, &value, name);
}
JS_FRIEND_API(JSObject*)
+2 -2
View File
@@ -725,13 +725,13 @@ class FunctionExtended : public JSFunction
* All asm.js/wasm functions store their compiled module (either
* WasmModuleObject or AsmJSModuleObject) in the first extended slot.
*/
static const unsigned ASM_MODULE_SLOT = 0;
static const unsigned WASM_MODULE_SLOT = 0;
/*
* wasm/asm.js exported functions store the index of the export in the
* module's export vector in the second slot.
*/
static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
static const unsigned WASM_EXPORT_INDEX_SLOT = 1;
static inline size_t offsetOfExtendedSlot(unsigned which) {
MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
+18 -3
View File
@@ -1417,7 +1417,7 @@ GCRuntime::setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock)
setMarkStackLimit(value, lock);
break;
case JSGC_DECOMMIT_THRESHOLD:
decommitThreshold = value * 1024 * 1024;
decommitThreshold = (uint64_t)value * 1024 * 1024;
break;
case JSGC_MODE:
mode = JSGCMode(value);
@@ -1448,14 +1448,14 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL
highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC;
break;
case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
highFrequencyLowLimitBytes_ = value * 1024 * 1024;
highFrequencyLowLimitBytes_ = (uint64_t)value * 1024 * 1024;
if (highFrequencyLowLimitBytes_ >= highFrequencyHighLimitBytes_)
highFrequencyHighLimitBytes_ = highFrequencyLowLimitBytes_ + 1;
MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
break;
case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
MOZ_ASSERT(value > 0);
highFrequencyHighLimitBytes_ = value * 1024 * 1024;
highFrequencyHighLimitBytes_ = (uint64_t)value * 1024 * 1024;
if (highFrequencyHighLimitBytes_ <= highFrequencyLowLimitBytes_)
highFrequencyLowLimitBytes_ = highFrequencyHighLimitBytes_ - 1;
MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
@@ -1606,6 +1606,21 @@ GCRuntime::callGCCallback(JSGCStatus status) const
gcCallback.op(rt, status, gcCallback.data);
}
void
GCRuntime::setObjectsTenuredCallback(JSObjectsTenuredCallback callback,
void* data)
{
tenuredCallback.op = callback;
tenuredCallback.data = data;
}
void
GCRuntime::callObjectsTenuredCallback()
{
if (tenuredCallback.op)
tenuredCallback.op(rt, tenuredCallback.data);
}
namespace {
class AutoNotifyGCActivity {
+12 -8
View File
@@ -51,8 +51,20 @@ enum State {
COMPACT
};
// Expand the given macro D for each publicly exposed GC reference type.
#define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
D(JS::Symbol*) \
D(JSAtom*) \
D(JSFunction*) \
D(JSObject*) \
D(JSScript*) \
D(JSString*) \
D(JS::Value) \
D(jsid)
// Expand the given macro D for each valid GC reference type.
#define FOR_EACH_GC_POINTER_TYPE(D) \
FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
D(AccessorShape*) \
D(BaseShape*) \
D(UnownedBaseShape*) \
@@ -65,8 +77,6 @@ enum State {
D(ArrayBufferViewObject*) \
D(DebugScopeObject*) \
D(GlobalObject*) \
D(JSObject*) \
D(JSFunction*) \
D(ModuleObject*) \
D(ModuleEnvironmentObject*) \
D(ModuleNamespaceObject*) \
@@ -78,18 +88,12 @@ enum State {
D(SharedArrayBufferObject*) \
D(ImportEntryObject*) \
D(ExportEntryObject*) \
D(JSScript*) \
D(LazyScript*) \
D(Shape*) \
D(JSAtom*) \
D(JSString*) \
D(JSFlatString*) \
D(JSLinearString*) \
D(PropertyName*) \
D(JS::Symbol*) \
D(js::ObjectGroup*) \
D(Value) \
D(jsid) \
D(TaggedProto)
/* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
+3 -6
View File
@@ -3779,11 +3779,13 @@ EscapeForShell(AutoCStringVector& argv)
static Vector<const char*, 4, js::SystemAllocPolicy> sPropagatedFlags;
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
static bool
PropagateFlagToNestedShells(const char* flag)
{
return sPropagatedFlags.append(flag);
}
#endif
static bool
NestedShell(JSContext* cx, unsigned argc, Value* vp)
@@ -6712,7 +6714,7 @@ main(int argc, char** argv, char** envp)
|| !op.addIntOption('\0', "baseline-warmup-threshold", "COUNT",
"Wait for COUNT calls or iterations before baseline-compiling "
"(default: 10)", -1)
|| !op.addBoolOption('\0', "non-writable-jitcode", "Allocate JIT code as non-writable memory.")
|| !op.addBoolOption('\0', "non-writable-jitcode", "(NOP for fuzzers) Allocate JIT code as non-writable memory.")
|| !op.addBoolOption('\0', "no-fpu", "Pretend CPU does not support floating-point operations "
"to test JIT codegen (no-op on platforms other than x86).")
|| !op.addBoolOption('\0', "no-sse3", "Pretend CPU does not support SSE3 instructions and above "
@@ -6791,11 +6793,6 @@ main(int argc, char** argv, char** envp)
OOM_printAllocationCount = op.getBoolOption('O');
#endif
if (op.getBoolOption("non-writable-jitcode")) {
js::jit::ExecutableAllocator::nonWritableJitCode = true;
PropagateFlagToNestedShells("--non-writable-jitcode");
}
#ifdef JS_CODEGEN_X86
if (op.getBoolOption("no-fpu"))
js::jit::CPUInfo::SetFloatingPointDisabled();
@@ -3,14 +3,10 @@
// http://creativecommons.org/licenses/publicdomain/
function test() {
// Note: -8 and -200 will trigger asm.js link failures because 8 and 200
// bytes are below the minimum allowed size, and the buffer will not
// actually be converted to an asm.js buffer.
for (var size of [0, 8, 16, 200, 1000, 4096, -8, -200, -8192, -65536]) {
var buffer_ctor = (size < 0) ? AsmJSArrayBuffer : ArrayBuffer;
size = Math.abs(size);
var old = new buffer_ctor(size);
var old = new ArrayBuffer(size);
var copy = deserialize(serialize(old, [old]));
assertEq(old.byteLength, 0);
assertEq(copy.byteLength, size);
@@ -29,7 +25,7 @@ function test() {
for (var ctor of constructors) {
var dataview = (ctor === DataView);
var buf = new buffer_ctor(size);
var buf = new ArrayBuffer(size);
var old_arr = new ctor(buf);
assertEq(buf.byteLength, size);
assertEq(buf, old_arr.buffer);
@@ -52,7 +48,7 @@ function test() {
for (var ctor of constructors) {
var dataview = (ctor === DataView);
var buf = new buffer_ctor(size);
var buf = new ArrayBuffer(size);
var old_arr = new ctor(buf);
var dv = new DataView(buf); // Second view
var copy_arr = deserialize(serialize(old_arr, [ buf ]));
@@ -69,7 +65,7 @@ function test() {
// Mutate the buffer during the clone operation. The modifications should be visible.
if (size >= 4) {
old = new buffer_ctor(size);
old = new ArrayBuffer(size);
var view = new Int32Array(old);
view[0] = 1;
var mutator = { get foo() { view[0] = 2; } };
@@ -81,7 +77,7 @@ function test() {
// Neuter the buffer during the clone operation. Should throw an exception.
if (size >= 4) {
old = new buffer_ctor(size);
old = new ArrayBuffer(size);
var mutator = {
get foo() {
deserialize(serialize(old, [old]));
-14
View File
@@ -16,17 +16,3 @@ if (typeof version != 'undefined')
{
version(185);
}
// Note that AsmJS ArrayBuffers have a minimum size, currently 4096 bytes. If a
// smaller size is given, a regular ArrayBuffer will be returned instead.
function AsmJSArrayBuffer(size) {
var ab = new ArrayBuffer(size);
(new Function('global', 'foreign', 'buffer', '' +
' "use asm";' +
' var i32 = new global.Int32Array(buffer);' +
' function g() {};' +
' return g;' +
''))(Function("return this")(),null,ab);
return ab;
}
+30 -223
View File
@@ -130,9 +130,6 @@ const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
#ifdef NIGHTLY_BUILD
JS_FN("transfer", ArrayBufferObject::fun_transfer, 2, 0),
#endif
JS_FS_END
};
@@ -233,223 +230,6 @@ ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
return true;
}
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
static void
ReleaseAsmJSMappedData(void* base)
{
MOZ_ASSERT(uintptr_t(base) % AsmJSPageSize == 0);
# ifdef XP_WIN
VirtualFree(base, 0, MEM_RELEASE);
# else
munmap(base, AsmJSMappedSize);
# if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
// Tell Valgrind/Memcheck to recommence reporting accesses in the
// previously-inaccessible region.
if (AsmJSMappedSize > 0) {
VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, AsmJSMappedSize);
}
# endif
# endif
MemProfiler::RemoveNative(base);
}
#else
static void
ReleaseAsmJSMappedData(void* base)
{
MOZ_CRASH("asm.js only uses mapped buffers when using signal-handler OOB checking");
}
#endif
#ifdef NIGHTLY_BUILD
# if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
static bool
TransferAsmJSMappedBuffer(JSContext* cx, const CallArgs& args,
Handle<ArrayBufferObject*> oldBuffer, size_t newByteLength)
{
size_t oldByteLength = oldBuffer->byteLength();
MOZ_ASSERT(oldByteLength % AsmJSPageSize == 0);
MOZ_ASSERT(newByteLength % AsmJSPageSize == 0);
ArrayBufferObject::BufferContents stolen =
ArrayBufferObject::stealContents(cx, oldBuffer, /* hasStealableContents = */ true);
if (!stolen)
return false;
MOZ_ASSERT(stolen.kind() == ArrayBufferObject::ASMJS_MAPPED);
uint8_t* data = stolen.data();
if (newByteLength > oldByteLength) {
void* diffStart = data + oldByteLength;
size_t diffLength = newByteLength - oldByteLength;
# ifdef XP_WIN
if (!VirtualAlloc(diffStart, diffLength, MEM_COMMIT, PAGE_READWRITE)) {
ReleaseAsmJSMappedData(data);
ReportOutOfMemory(cx);
return false;
}
# else
// To avoid memset, use MAP_FIXED to clobber the newly-accessible pages
// with zero pages.
int flags = MAP_FIXED | MAP_PRIVATE | MAP_ANON;
if (mmap(diffStart, diffLength, PROT_READ | PROT_WRITE, flags, -1, 0) == MAP_FAILED) {
ReleaseAsmJSMappedData(data);
ReportOutOfMemory(cx);
return false;
}
# endif
MemProfiler::SampleNative(diffStart, diffLength);
} else if (newByteLength < oldByteLength) {
void* diffStart = data + newByteLength;
size_t diffLength = oldByteLength - newByteLength;
# ifdef XP_WIN
if (!VirtualFree(diffStart, diffLength, MEM_DECOMMIT)) {
ReleaseAsmJSMappedData(data);
ReportOutOfMemory(cx);
return false;
}
# else
if (madvise(diffStart, diffLength, MADV_DONTNEED) ||
mprotect(diffStart, diffLength, PROT_NONE))
{
ReleaseAsmJSMappedData(data);
ReportOutOfMemory(cx);
return false;
}
# endif
}
ArrayBufferObject::BufferContents newContents =
ArrayBufferObject::BufferContents::create<ArrayBufferObject::ASMJS_MAPPED>(data);
RootedObject newBuffer(cx, ArrayBufferObject::create(cx, newByteLength, newContents));
if (!newBuffer) {
ReleaseAsmJSMappedData(data);
return false;
}
args.rval().setObject(*newBuffer);
return true;
}
# endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
/*
* Experimental implementation of ArrayBuffer.transfer:
* https://gist.github.com/andhow/95fb9e49996615764eff
* which is currently in the early stages of proposal for ES7.
*/
bool
ArrayBufferObject::fun_transfer(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue oldBufferArg = args.get(0);
HandleValue newByteLengthArg = args.get(1);
if (!oldBufferArg.isObject()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
RootedObject oldBufferObj(cx, &oldBufferArg.toObject());
ESClassValue cls;
if (!GetBuiltinClass(cx, oldBufferObj, &cls))
return false;
if (cls != ESClass_ArrayBuffer) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
// Beware: oldBuffer can point across compartment boundaries. ArrayBuffer
// contents are not compartment-specific so this is safe.
Rooted<ArrayBufferObject*> oldBuffer(cx);
if (oldBufferObj->is<ArrayBufferObject>()) {
oldBuffer = &oldBufferObj->as<ArrayBufferObject>();
} else {
JSObject* unwrapped = CheckedUnwrap(oldBufferObj);
if (!unwrapped || !unwrapped->is<ArrayBufferObject>()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
oldBuffer = &unwrapped->as<ArrayBufferObject>();
}
size_t oldByteLength = oldBuffer->byteLength();
size_t newByteLength;
if (newByteLengthArg.isUndefined()) {
newByteLength = oldByteLength;
} else {
int32_t i32;
if (!ToInt32(cx, newByteLengthArg, &i32))
return false;
if (i32 < 0) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
newByteLength = size_t(i32);
}
if (oldBuffer->isNeutered()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
UniquePtr<uint8_t, JS::FreePolicy> newData;
if (!newByteLength) {
if (!ArrayBufferObject::neuter(cx, oldBuffer, oldBuffer->contents()))
return false;
} else {
# if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
// With a 4gb mapped asm.js buffer, we can simply enable/disable access
// to the delta as long as the requested length is page-sized.
if (oldBuffer->isAsmJSMapped() && (newByteLength % AsmJSPageSize) == 0)
return TransferAsmJSMappedBuffer(cx, args, oldBuffer, newByteLength);
# endif
// Since we try to realloc below, only allow stealing malloc'd buffers.
// If !hasMallocedContents, stealContents will malloc a copy which we
// can then realloc.
bool steal = oldBuffer->hasMallocedContents();
auto stolenContents = ArrayBufferObject::stealContents(cx, oldBuffer, steal);
if (!stolenContents)
return false;
UniquePtr<uint8_t, JS::FreePolicy> oldData(stolenContents.data());
if (newByteLength > oldByteLength) {
// In theory, realloc+memset(0) can be optimized to avoid touching
// any pages (by using OS page mapping tricks). However, in
// practice, we don't seem to get this optimization in Firefox with
// jemalloc so calloc+memcpy are faster.
newData.reset(cx->runtime()->pod_callocCanGC<uint8_t>(newByteLength));
if (newData) {
memcpy(newData.get(), oldData.get(), oldByteLength);
} else {
// Try malloc before giving up since it might be able to succed
// by resizing oldData in-place.
newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength));
if (!newData)
return false;
oldData.release();
memset(newData.get() + oldByteLength, 0, newByteLength - oldByteLength);
}
} else if (newByteLength < oldByteLength) {
newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength));
if (!newData)
return false;
oldData.release();
} else {
newData = Move(oldData);
}
}
RootedObject newBuffer(cx, JS_NewArrayBufferWithContents(cx, newByteLength, newData.get()));
if (!newBuffer)
return false;
newData.release();
args.rval().setObject(*newBuffer);
return true;
}
#endif // defined(NIGHTLY_BUILD)
/*
* new ArrayBuffer(byteLength)
*/
@@ -511,10 +291,10 @@ ArrayBufferObject::neuterView(JSContext* cx, ArrayBufferViewObject* view,
ArrayBufferObject::neuter(JSContext* cx, Handle<ArrayBufferObject*> buffer,
BufferContents newContents)
{
assertSameCompartment(cx, buffer);
if (buffer->isAsmJS() && !OnDetachAsmJSArrayBuffer(cx, buffer))
if (buffer->isAsmJS()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
return false;
}
// When neutering buffers where we don't know all views, the new data must
// match the old data. All missing views are typed objects, which do not
@@ -744,6 +524,33 @@ ArrayBufferObject::dataPointerShared() const
return SharedMem<uint8_t*>::unshared(getSlot(DATA_SLOT).toPrivate());
}
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
static void
ReleaseAsmJSMappedData(void* base)
{
MOZ_ASSERT(uintptr_t(base) % AsmJSPageSize == 0);
# ifdef XP_WIN
VirtualFree(base, 0, MEM_RELEASE);
# else
munmap(base, AsmJSMappedSize);
# if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
// Tell Valgrind/Memcheck to recommence reporting accesses in the
// previously-inaccessible region.
if (AsmJSMappedSize > 0) {
VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, AsmJSMappedSize);
}
# endif
# endif
MemProfiler::RemoveNative(base);
}
#else
static void
ReleaseAsmJSMappedData(void* base)
{
MOZ_CRASH("asm.js only uses mapped buffers when using signal-handler OOB checking");
}
#endif
void
ArrayBufferObject::releaseData(FreeOp* fop)
{
+2 -1
View File
@@ -104,7 +104,8 @@ JS_Init(void)
if (!FutexRuntime::initialize())
return false;
js::gcstats::Statistics::initialize();
if (!js::gcstats::Statistics::initialize())
return false;
libraryInitState = InitState::Running;
return true;
-4
View File
@@ -428,10 +428,6 @@ class RegExpObject : public NativeObject
return RegExpFlag(flags);
}
bool needUpdateLastIndex() const {
return sticky() || global();
}
/* Flags. */
void setIgnoreCase(bool enabled) {
+9 -1
View File
@@ -201,7 +201,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
destroyPrincipals(nullptr),
readPrincipals(nullptr),
errorReporter(nullptr),
linkedWasmModules(nullptr),
propertyRemovals(0),
#if !EXPOSE_INTL_API
thousandsSeparator(0),
@@ -735,6 +734,15 @@ JSRuntime::triggerActivityCallback(bool active)
activityCallback(activityCallbackArg, active);
}
FreeOp::~FreeOp()
{
for (size_t i = 0; i < freeLaterList.length(); i++)
free_(freeLaterList[i]);
if (!jitPoisonRanges.empty())
jit::ExecutableAllocator::poisonCode(runtime(), jitPoisonRanges);
}
void
JSRuntime::updateMallocCounter(size_t nbytes)
{
+14 -7
View File
@@ -380,6 +380,7 @@ class NewObjectCache
class FreeOp : public JSFreeOp
{
Vector<void*, 0, SystemAllocPolicy> freeLaterList;
jit::JitPoisonRangeVector jitPoisonRanges;
ThreadType threadType;
public:
@@ -391,10 +392,7 @@ class FreeOp : public JSFreeOp
: JSFreeOp(rt), threadType(thread)
{}
~FreeOp() {
for (size_t i = 0; i < freeLaterList.length(); i++)
free_(freeLaterList[i]);
}
~FreeOp();
bool onBackgroundThread() {
return threadType == BackgroundThread;
@@ -403,6 +401,8 @@ class FreeOp : public JSFreeOp
inline void free_(void* p);
inline void freeLater(void* p);
inline bool appendJitPoisonRange(const jit::JitPoisonRange& range);
template <class T>
inline void delete_(T* p) {
if (p) {
@@ -1193,9 +1193,6 @@ struct JSRuntime : public JS::shadow::Runtime,
/* AsmJSCache callbacks are runtime-wide. */
JS::AsmJSCacheOps asmJSCacheOps;
/* Head of the linked list of linked wasm modules. */
js::wasm::Module* linkedWasmModules;
/*
* The propertyRemovals counter is incremented for every JSObject::clear,
* and for each JSObject::remove method call that frees a slot in the given
@@ -1603,6 +1600,16 @@ FreeOp::freeLater(void* p)
oomUnsafe.crash("FreeOp::freeLater");
}
inline bool
FreeOp::appendJitPoisonRange(const jit::JitPoisonRange& range)
{
// FreeOps other than the defaultFreeOp() are constructed on the stack,
// and won't hold onto the pointers to free indefinitely.
MOZ_ASSERT(this != runtime()->defaultFreeOp());
return jitPoisonRanges.append(range);
}
/*
* RAII class that takes the GC lock while it is live.
*
+2 -2
View File
@@ -1720,9 +1720,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
IsRegExpObject),
JS_FN("CallRegExpMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0),
JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 5,0,
JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 4,0,
RegExpMatcher),
JS_INLINABLE_FN("RegExpTester", RegExpTester, 5,0,
JS_INLINABLE_FN("RegExpTester", RegExpTester, 4,0,
RegExpTester),
// See builtin/RegExp.h for descriptions of the regexp_* functions.
+1 -1
View File
@@ -66,7 +66,7 @@ XPCTraceableVariant::~XPCTraceableVariant()
void XPCTraceableVariant::TraceJS(JSTracer* trc)
{
MOZ_ASSERT(mJSVal.isMarkable());
JS_CallValueTracer(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
JS::TraceEdge(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
}
NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant)
+1 -1
View File
@@ -291,7 +291,7 @@ void
nsXPCWrappedJS::TraceJS(JSTracer* trc)
{
MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
JS_CallObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
JS::TraceEdge(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
}
NS_IMETHODIMP
+1 -1
View File
@@ -2360,5 +2360,5 @@ XPCJSObjectHolder::~XPCJSObjectHolder()
void
XPCJSObjectHolder::TraceJS(JSTracer* trc)
{
JS_CallObjectTracer(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
JS::TraceEdge(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
}
+2 -2
View File
@@ -473,7 +473,7 @@ XPCWrappedNativeScope::~XPCWrappedNativeScope()
void
XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt)
{
// Do JS_CallTracer for all wrapped natives with external references, as
// Do JS::TraceEdge for all wrapped natives with external references, as
// well as any DOM expando objects.
for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
@@ -485,7 +485,7 @@ XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntim
if (cur->mDOMExpandoSet) {
for (DOMExpandoSet::Enum e(*cur->mDOMExpandoSet); !e.empty(); e.popFront())
JS_CallObjectTracer(trc, &e.mutableFront(), "DOM expando object");
JS::TraceEdge(trc, &e.mutableFront(), "DOM expando object");
}
}
}
+39
View File
@@ -496,6 +496,45 @@ struct AssertionConditionType
MOZ_ASSUME_UNREACHABLE_MARKER(); \
} while (0)
/**
* MOZ_FALLTHROUGH_ASSERT is an annotation to suppress compiler warnings about
* switch cases that MOZ_ASSERT(false) (or its alias MOZ_ASSERT_UNREACHABLE) in
* debug builds, but intentionally fall through in release builds to handle
* unexpected values.
*
* Why do we need MOZ_FALLTHROUGH_ASSERT in addition to MOZ_FALLTHROUGH? In
* release builds, the MOZ_ASSERT(false) will expand to `do { } while (0)`,
* requiring a MOZ_FALLTHROUGH annotation to suppress a -Wimplicit-fallthrough
* warning. In debug builds, the MOZ_ASSERT(false) will expand to something like
* `if (true) { MOZ_CRASH(); }` and the MOZ_FALLTHROUGH annotation will cause
* a -Wunreachable-code warning. The MOZ_FALLTHROUGH_ASSERT macro breaks this
* warning stalemate.
*
* // Example before MOZ_FALLTHROUGH_ASSERT:
* switch (foo) {
* default:
* // This case wants to assert in debug builds, fall through in release.
* MOZ_ASSERT(false); // -Wimplicit-fallthrough warning in release builds!
* MOZ_FALLTHROUGH; // but -Wunreachable-code warning in debug builds!
* case 5:
* return 5;
* }
*
* // Example with MOZ_FALLTHROUGH_ASSERT:
* switch (foo) {
* default:
* // This case asserts in debug builds, falls through in release.
* MOZ_FALLTHROUGH_ASSERT("Unexpected foo value?!");
* case 5:
* return 5;
* }
*/
#ifdef DEBUG
# define MOZ_FALLTHROUGH_ASSERT(reason) MOZ_CRASH("MOZ_FALLTHROUGH_ASSERT: " reason)
#else
# define MOZ_FALLTHROUGH_ASSERT(...) MOZ_FALLTHROUGH
#endif
/*
* MOZ_ALWAYS_TRUE(expr) and MOZ_ALWAYS_FALSE(expr) always evaluate the provided
* expression, in debug builds and in release builds both. Then, in debug
+15 -4
View File
@@ -339,16 +339,27 @@
/**
* MOZ_FALLTHROUGH is an annotation to suppress compiler warnings about switch
* cases that fall through without a break or return statement. MOZ_FALLTHROUGH
* is only needed on cases that have code:
* is only needed on cases that have code.
*
* MOZ_FALLTHROUGH_ASSERT is an annotation to suppress compiler warnings about
* switch cases that MOZ_ASSERT(false) (or its alias MOZ_ASSERT_UNREACHABLE) in
* debug builds, but intentionally fall through in release builds. See comment
* in Assertions.h for more details.
*
* switch (foo) {
* case 1: // These cases have no code. No fallthrough annotations are needed.
* case 2:
* case 3:
* foo = 4; // This case has code, so a fallthrough annotation is needed:
* case 3: // This case has code, so a fallthrough annotation is needed!
* foo++;
* MOZ_FALLTHROUGH;
* default:
* case 4:
* return foo;
*
* default:
* // This case asserts in debug builds, falls through in release.
* MOZ_FALLTHROUGH_ASSERT("Unexpected foo value?!");
* case 5:
* return 5;
* }
*/
#if defined(__clang__) && __cplusplus >= 201103L
+17 -31
View File
@@ -75,45 +75,31 @@ Concrete<DeserializedNode>::size(mozilla::MallocSizeOf mallocSizeof) const
class DeserializedEdgeRange : public EdgeRange
{
EdgeVector edges;
size_t i;
DeserializedNode* node;
Edge currentEdge;
size_t i;
void settle() {
front_ = i < edges.length() ? &edges[i] : nullptr;
if (i >= node->edges.length()) {
front_ = nullptr;
return;
}
auto& edge = node->edges[i];
auto referent = node->getEdgeReferent(edge);
currentEdge = mozilla::Move(Edge(edge.name ? NS_strdup(edge.name) : nullptr,
referent));
front_ = &currentEdge;
}
public:
explicit DeserializedEdgeRange()
: edges()
explicit DeserializedEdgeRange(DeserializedNode& node)
: node(&node)
, i(0)
{
settle();
}
bool init(DeserializedNode& node)
{
if (!edges.reserve(node.edges.length()))
return false;
for (DeserializedEdge* edgep = node.edges.begin();
edgep != node.edges.end();
edgep++)
{
char16_t* name = nullptr;
if (edgep->name) {
name = NS_strdup(edgep->name);
if (!name)
return false;
}
auto referent = node.getEdgeReferent(*edgep);
edges.infallibleAppend(mozilla::Move(Edge(name, referent)));
}
settle();
return true;
}
void popFront() override
{
i++;
@@ -138,9 +124,9 @@ UniquePtr<EdgeRange>
Concrete<DeserializedNode>::edges(JSRuntime* rt, bool) const
{
UniquePtr<DeserializedEdgeRange, JS::DeletePolicy<DeserializedEdgeRange>> range(
js_new<DeserializedEdgeRange>());
js_new<DeserializedEdgeRange>(get()));
if (!range || !range->init(get()))
if (!range)
return nullptr;
return UniquePtr<EdgeRange>(range.release());
@@ -96,5 +96,11 @@ DEF_TEST(DeserializedNodeUbiNodes, {
.Times(1)
.WillOnce(Return(JS::ubi::Node(referent3.get())));
ubi.edges(rt);
auto range = ubi.edges(rt);
ASSERT_TRUE(!!range);
for ( ; !range->empty(); range->popFront()) {
// Nothing to do here. This loop ensures that we get each edge referent
// that we expect above.
}
});
+66 -7
View File
@@ -73,6 +73,7 @@
#include "nsCycleCollector.h"
#include "nsDOMJSUtils.h"
#include "nsJSUtils.h"
#include "nsWrapperCache.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
@@ -397,6 +398,12 @@ NoteJSChildGrayWrapperShim(void* aData, JS::GCCellPtr aThing)
// CycleCollectedJSRuntime. It should never be used directly.
static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
static
void JSObjectsTenuredCb(JSRuntime* aRuntime, void* aData)
{
static_cast<CycleCollectedJSRuntime*>(aData)->JSObjectsTenured(aRuntime);
}
CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
uint32_t aMaxBytes,
uint32_t aMaxNurseryBytes)
@@ -430,6 +437,7 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
JS_SetGCCallback(mJSRuntime, GCCallback, this);
mPrevGCSliceCallback = JS::SetGCSliceCallback(mJSRuntime, GCSliceCallback);
JS_SetObjectsTenuredCallback(mJSRuntime, JSObjectsTenuredCb, this);
JS::SetOutOfMemoryCallback(mJSRuntime, OutOfMemoryCallback, this);
JS::SetLargeAllocationFailureCallback(mJSRuntime,
LargeAllocationFailureCallback, this);
@@ -773,17 +781,22 @@ struct JsGcTracer : public TraceCallbacks
virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
void* aClosure) const override
{
JS_CallValueTracer(static_cast<JSTracer*>(aClosure), aPtr, aName);
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
void* aClosure) const override
{
JS_CallIdTracer(static_cast<JSTracer*>(aClosure), aPtr, aName);
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
void* aClosure) const override
{
JS_CallObjectTracer(static_cast<JSTracer*>(aClosure), aPtr, aName);
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JSObject** aPtr, const char* aName,
void* aClosure) const override
{
JS_CallUnbarrieredObjectTracer(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
void* aClosure) const override
@@ -793,17 +806,17 @@ struct JsGcTracer : public TraceCallbacks
virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
void* aClosure) const override
{
JS_CallStringTracer(static_cast<JSTracer*>(aClosure), aPtr, aName);
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
void* aClosure) const override
{
JS_CallScriptTracer(static_cast<JSTracer*>(aClosure), aPtr, aName);
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
void* aClosure) const override
{
JS_CallFunctionTracer(static_cast<JSTracer*>(aClosure), aPtr, aName);
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
};
@@ -835,7 +848,7 @@ CycleCollectedJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTrace
mJSHolders.Put(aHolder, aTracer);
}
struct ClearJSHolder : TraceCallbacks
struct ClearJSHolder : public TraceCallbacks
{
virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*, void*) const override
{
@@ -852,6 +865,12 @@ struct ClearJSHolder : TraceCallbacks
*aPtr = nullptr;
}
virtual void Trace(JSObject** aPtr, const char* aName,
void* aClosure) const override
{
*aPtr = nullptr;
}
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*, void*) const override
{
*aPtr = nullptr;
@@ -1008,6 +1027,46 @@ CycleCollectedJSRuntime::GarbageCollect(uint32_t aReason) const
JS::GCForReason(mJSRuntime, GC_NORMAL, gcreason);
}
void
CycleCollectedJSRuntime::JSObjectsTenured(JSRuntime* aRuntime)
{
for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
nsWrapperCache* cache = iter.Get();
JSObject* wrapper = cache->GetWrapperPreserveColor();
MOZ_ASSERT(wrapper);
if (!JS::ObjectIsTenured(wrapper)) {
MOZ_ASSERT(!cache->PreservingWrapper());
const JSClass* jsClass = js::GetObjectJSClass(wrapper);
jsClass->finalize(nullptr, wrapper);
}
}
#ifdef DEBUG
for (auto iter = mPreservedNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
MOZ_ASSERT(JS::ObjectIsTenured(iter.Get().get()));
}
#endif
mNurseryObjects.Clear();
mPreservedNurseryObjects.Clear();
}
void
CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache)
{
MOZ_ASSERT(aCache);
MOZ_ASSERT(aCache->GetWrapperPreserveColor());
MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor()));
mNurseryObjects.InfallibleAppend(aCache);
}
void
CycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject* aWrapper)
{
mPreservedNurseryObjects.InfallibleAppend(
JS::PersistentRooted<JSObject*>(mJSRuntime, aWrapper));
}
void
CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
DeferredFinalizeFunction aFunc,
+14
View File
@@ -10,7 +10,9 @@
#include <queue>
#include "mozilla/DeferredFinalize.h"
#include "mozilla/mozalloc.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/SegmentedVector.h"
#include "jsapi.h"
#include "nsCycleCollectionParticipant.h"
@@ -22,6 +24,7 @@ class nsCycleCollectionNoteRootCallback;
class nsIException;
class nsIRunnable;
class nsThread;
class nsWrapperCache;
namespace js {
struct Class;
@@ -280,6 +283,10 @@ public:
bool AreGCGrayBitsValid() const;
void GarbageCollect(uint32_t aReason) const;
void NurseryWrapperAdded(nsWrapperCache* aCache);
void NurseryWrapperPreserved(JSObject* aWrapper);
void JSObjectsTenured(JSRuntime* aRuntime);
void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
DeferredFinalizeFunction aFunc,
void* aThing);
@@ -361,6 +368,13 @@ private:
OOMState mOutOfMemoryState;
OOMState mLargeAllocationFailureState;
static const size_t kSegmentSize = 512;
SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
mNurseryObjects;
SegmentedVector<JS::PersistentRooted<JSObject*>, kSegmentSize,
InfallibleAllocPolicy>
mPreservedNurseryObjects;
};
void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
+1
View File
@@ -113,6 +113,7 @@ UNIFIED_SOURCES += [
'nsConsoleMessage.cpp',
'nsConsoleService.cpp',
'nsCycleCollector.cpp',
'nsCycleCollectorTraceJSHelpers.cpp',
'nsDumpUtils.cpp',
'nsErrorService.cpp',
'nsGZFileWriter.cpp',
+13 -7
View File
@@ -2668,7 +2668,7 @@ public:
}
virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName,
void* aClosure) const
void* aClosure) const override
{
if (aValue->isMarkable() && ValueIsGrayCCThing(*aValue)) {
MOZ_ASSERT(!js::gc::IsInsideNursery(aValue->toGCThing()));
@@ -2677,7 +2677,7 @@ public:
}
virtual void Trace(JS::Heap<jsid>* aId, const char* aName,
void* aClosure) const
void* aClosure) const override
{
}
@@ -2690,29 +2690,35 @@ public:
}
virtual void Trace(JS::Heap<JSObject*>* aObject, const char* aName,
void* aClosure) const
void* aClosure) const override
{
AppendJSObjectToPurpleBuffer(*aObject);
}
virtual void Trace(JSObject** aObject, const char* aName,
void* aClosure) const override
{
AppendJSObjectToPurpleBuffer(*aObject);
}
virtual void Trace(JS::TenuredHeap<JSObject*>* aObject, const char* aName,
void* aClosure) const
void* aClosure) const override
{
AppendJSObjectToPurpleBuffer(*aObject);
}
virtual void Trace(JS::Heap<JSString*>* aString, const char* aName,
void* aClosure) const
void* aClosure) const override
{
}
virtual void Trace(JS::Heap<JSScript*>* aScript, const char* aName,
void* aClosure) const
void* aClosure) const override
{
}
virtual void Trace(JS::Heap<JSFunction*>* aFunction, const char* aName,
void* aClosure) const
void* aClosure) const override
{
}
@@ -0,0 +1,97 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsCycleCollectionParticipant.h"
#include "jsapi.h"
#include "jsfriendapi.h"
void
CycleCollectionNoteEdgeNameImpl(nsCycleCollectionTraversalCallback& aCallback,
const char* aName,
uint32_t aFlags)
{
nsAutoCString arrayEdgeName(aName);
if (aFlags & CycleCollectionEdgeNameArrayFlag) {
arrayEdgeName.AppendLiteral("[i]");
}
aCallback.NoteNextEdgeName(arrayEdgeName.get());
}
void
nsScriptObjectTracer::NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
void* aClosure)
{
nsCycleCollectionTraversalCallback* cb =
static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, aName);
if (aGCThing.is<JSObject>()) {
cb->NoteJSObject(&aGCThing.as<JSObject>());
} else if (aGCThing.is<JSScript>()) {
cb->NoteJSScript(&aGCThing.as<JSScript>());
} else {
MOZ_ASSERT(!mozilla::AddToCCKind(aGCThing.kind()));
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
void* aClosure) const
{
if (aPtr->isMarkable()) {
mCallback(JS::GCCellPtr(*aPtr), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<jsid>* aPtr, const char* aName,
void* aClosure) const
{
if (JSID_IS_GCTHING(*aPtr)) {
mCallback(JSID_TO_GCTHING(*aPtr), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JSObject** aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(*aPtr), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->getPtr()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::Heap<JSString*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
@@ -5,32 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsCycleCollectionParticipant.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "nsCOMPtr.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#ifdef MOZILLA_INTERNAL_API
#include "nsString.h"
#else
#include "nsStringAPI.h"
#endif
void
nsScriptObjectTracer::NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
void* aClosure)
{
nsCycleCollectionTraversalCallback* cb =
static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, aName);
if (aGCThing.is<JSObject>()) {
cb->NoteJSObject(&aGCThing.as<JSObject>());
} else if (aGCThing.is<JSScript>()) {
cb->NoteJSScript(&aGCThing.as<JSScript>());
} else {
MOZ_ASSERT(!mozilla::AddToCCKind(aGCThing.kind()));
}
}
NS_IMETHODIMP_(void)
nsXPCOMCycleCollectionParticipant::Root(void* aPtr)
@@ -62,68 +37,3 @@ nsXPCOMCycleCollectionParticipant::CheckForRightISupports(nsISupports* aSupports
reinterpret_cast<void**>(&foo));
return aSupports == foo;
}
void
CycleCollectionNoteEdgeNameImpl(nsCycleCollectionTraversalCallback& aCallback,
const char* aName,
uint32_t aFlags)
{
nsAutoCString arrayEdgeName(aName);
if (aFlags & CycleCollectionEdgeNameArrayFlag) {
arrayEdgeName.AppendLiteral("[i]");
}
aCallback.NoteNextEdgeName(arrayEdgeName.get());
}
void
TraceCallbackFunc::Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
void* aClosure) const
{
if (aPtr->isMarkable()) {
mCallback(JS::GCCellPtr(*aPtr), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<jsid>* aPtr, const char* aName,
void* aClosure) const
{
if (JSID_IS_GCTHING(*aPtr)) {
mCallback(JSID_TO_GCTHING(*aPtr), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->getPtr()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::Heap<JSString*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
void
TraceCallbackFunc::Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
void* aClosure) const
{
mCallback(JS::GCCellPtr(aPtr->get()), aName, aClosure);
}
+6 -1
View File
@@ -64,6 +64,8 @@ struct TraceCallbacks
void* aClosure) const = 0;
virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
void* aClosure) const = 0;
virtual void Trace(JSObject** aPtr, const char* aName,
void* aClosure) const = 0;
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
void* aClosure) const = 0;
virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
@@ -76,7 +78,7 @@ struct TraceCallbacks
/*
* An implementation of TraceCallbacks that calls a single function for all JS
* GC thing types encountered.
* GC thing types encountered. Implemented in nsCycleCollectorTraceJSHelpers.cpp.
*/
struct TraceCallbackFunc : public TraceCallbacks
{
@@ -90,6 +92,8 @@ struct TraceCallbackFunc : public TraceCallbacks
void* aClosure) const override;
virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
void* aClosure) const override;
virtual void Trace(JSObject** aPtr, const char* aName,
void* aClosure) const override;
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
void* aClosure) const override;
virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
@@ -184,6 +188,7 @@ public:
NS_IMETHOD_(void) Trace(void* aPtr, const TraceCallbacks& aCb,
void* aClosure) override = 0;
// Implemented in nsCycleCollectorTraceJSHelpers.cpp.
static void NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
void* aClosure);
};

Some files were not shown because too many files have changed in this diff Show More