mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:25:44 +00:00
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:
@@ -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
@@ -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 };
|
||||
|
||||
@@ -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___ */
|
||||
|
||||
@@ -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]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* http://dom.spec.whatwg.org
|
||||
*/
|
||||
|
||||
[ProbablyShortLivingObject]
|
||||
interface MutationRecord {
|
||||
[Constant]
|
||||
readonly attribute DOMString type;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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_),
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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()) {
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -555,7 +555,6 @@ enum class SymbolicAddress
|
||||
RuntimeInterruptUint32,
|
||||
StackLimit,
|
||||
ReportOverRecursed,
|
||||
OnDetached,
|
||||
OnOutOfBounds,
|
||||
OnImpreciseConversion,
|
||||
HandleExecutionInterrupt,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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)},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()();
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
function f() {
|
||||
for (var e = 1; e < 3000; e++) {
|
||||
(function(arguments) {
|
||||
eval("var y");
|
||||
})();
|
||||
}
|
||||
}
|
||||
f();
|
||||
@@ -0,0 +1,10 @@
|
||||
x = 0;
|
||||
try {
|
||||
a;
|
||||
b;
|
||||
} catch (e) {}
|
||||
var g = newGlobal();
|
||||
oomTest(function() {
|
||||
return Debugger(g);
|
||||
});
|
||||
eval("function g() {}");
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]));
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -428,10 +428,6 @@ class RegExpObject : public NativeObject
|
||||
return RegExpFlag(flags);
|
||||
}
|
||||
|
||||
bool needUpdateLastIndex() const {
|
||||
return sticky() || global();
|
||||
}
|
||||
|
||||
/* Flags. */
|
||||
|
||||
void setIgnoreCase(bool enabled) {
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2360,5 +2360,5 @@ XPCJSObjectHolder::~XPCJSObjectHolder()
|
||||
void
|
||||
XPCJSObjectHolder::TraceJS(JSTracer* trc)
|
||||
{
|
||||
JS_CallObjectTracer(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
|
||||
JS::TraceEdge(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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_ = ¤tEdge;
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -113,6 +113,7 @@ UNIFIED_SOURCES += [
|
||||
'nsConsoleMessage.cpp',
|
||||
'nsConsoleService.cpp',
|
||||
'nsCycleCollector.cpp',
|
||||
'nsCycleCollectorTraceJSHelpers.cpp',
|
||||
'nsDumpUtils.cpp',
|
||||
'nsErrorService.cpp',
|
||||
'nsGZFileWriter.cpp',
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user