mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 23:08:39 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
5ee6b84d50
78 changed files with 3077 additions and 1727 deletions
|
|
@ -128,7 +128,8 @@ class CPOWProxyHandler : public BaseProxyHandler
|
|||
virtual bool isArray(JSContext* cx, HandleObject obj,
|
||||
IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<RegExpShared*> shared) const override;
|
||||
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
|
||||
virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
|
||||
virtual bool isCallable(JSObject* obj) const override;
|
||||
|
|
@ -854,13 +855,14 @@ WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* is
|
|||
}
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
|
||||
CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<RegExpShared*> shared) const
|
||||
{
|
||||
FORWARD(regexp_toShared, (cx, proxy, g));
|
||||
FORWARD(regexp_toShared, (cx, proxy, shared));
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
|
||||
WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandle<RegExpShared*> shared)
|
||||
{
|
||||
ObjectId objId = idOf(proxy);
|
||||
|
||||
|
|
@ -880,7 +882,7 @@ WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
|
|||
if (!regexp)
|
||||
return false;
|
||||
|
||||
return js::RegExpToSharedNonInline(cx, regexp, g);
|
||||
return js::RegExpToSharedNonInline(cx, regexp, shared);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ class WrapperOwner : public virtual JavaScriptShared
|
|||
bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
|
||||
JS::MutableHandleObject protop);
|
||||
|
||||
bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, js::RegExpGuard* g);
|
||||
bool regexp_toShared(JSContext* cx, JS::HandleObject proxy,
|
||||
js::MutableHandle<js::RegExpShared*> shared);
|
||||
|
||||
nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);
|
||||
|
||||
|
|
|
|||
|
|
@ -581,7 +581,8 @@ struct UnusedGCThingSizes
|
|||
macro(Other, GCHeapUnused, string) \
|
||||
macro(Other, GCHeapUnused, symbol) \
|
||||
macro(Other, GCHeapUnused, jitcode) \
|
||||
macro(Other, GCHeapUnused, scope)
|
||||
macro(Other, GCHeapUnused, scope) \
|
||||
macro(Other, GCHeapUnused, regExpShared)
|
||||
|
||||
UnusedGCThingSizes()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
|
|
@ -595,16 +596,17 @@ struct UnusedGCThingSizes
|
|||
|
||||
void addToKind(JS::TraceKind kind, intptr_t n) {
|
||||
switch (kind) {
|
||||
case JS::TraceKind::Object: object += n; break;
|
||||
case JS::TraceKind::String: string += n; break;
|
||||
case JS::TraceKind::Symbol: symbol += n; break;
|
||||
case JS::TraceKind::Script: script += n; break;
|
||||
case JS::TraceKind::Shape: shape += n; break;
|
||||
case JS::TraceKind::BaseShape: baseShape += n; break;
|
||||
case JS::TraceKind::JitCode: jitcode += n; break;
|
||||
case JS::TraceKind::LazyScript: lazyScript += n; break;
|
||||
case JS::TraceKind::ObjectGroup: objectGroup += n; break;
|
||||
case JS::TraceKind::Scope: scope += n; break;
|
||||
case JS::TraceKind::Object: object += n; break;
|
||||
case JS::TraceKind::String: string += n; break;
|
||||
case JS::TraceKind::Symbol: symbol += n; break;
|
||||
case JS::TraceKind::Script: script += n; break;
|
||||
case JS::TraceKind::Shape: shape += n; break;
|
||||
case JS::TraceKind::BaseShape: baseShape += n; break;
|
||||
case JS::TraceKind::JitCode: jitcode += n; break;
|
||||
case JS::TraceKind::LazyScript: lazyScript += n; break;
|
||||
case JS::TraceKind::ObjectGroup: objectGroup += n; break;
|
||||
case JS::TraceKind::Scope: scope += n; break;
|
||||
case JS::TraceKind::RegExpShared: regExpShared += n; break;
|
||||
default:
|
||||
MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
|
||||
}
|
||||
|
|
@ -646,6 +648,8 @@ struct ZoneStats
|
|||
macro(Other, MallocHeap, objectGroupsMallocHeap) \
|
||||
macro(Other, GCHeapUsed, scopesGCHeap) \
|
||||
macro(Other, MallocHeap, scopesMallocHeap) \
|
||||
macro(Other, GCHeapUsed, regExpSharedsGCHeap) \
|
||||
macro(Other, MallocHeap, regExpSharedsMallocHeap) \
|
||||
macro(Other, MallocHeap, typePool) \
|
||||
macro(Other, MallocHeap, baselineStubsOptimized) \
|
||||
macro(Other, MallocHeap, uniqueIdMap) \
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ using JS::PrivateValue;
|
|||
using JS::PropertyDescriptor;
|
||||
using JS::Value;
|
||||
|
||||
class RegExpGuard;
|
||||
class RegExpShared;
|
||||
|
||||
class JS_FRIEND_API(Wrapper);
|
||||
|
||||
/*
|
||||
|
|
@ -327,7 +328,8 @@ class JS_FRIEND_API(BaseProxyHandler)
|
|||
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<js::RegExpShared*> shared) const;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
|
||||
virtual void trace(JSTracer* trc, JSObject* proxy) const;
|
||||
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace js {
|
|||
class BaseShape;
|
||||
class LazyScript;
|
||||
class ObjectGroup;
|
||||
class RegExpShared;
|
||||
class Shape;
|
||||
class Scope;
|
||||
namespace jit {
|
||||
|
|
@ -58,13 +59,15 @@ enum class TraceKind
|
|||
BaseShape = 0x0F,
|
||||
JitCode = 0x1F,
|
||||
LazyScript = 0x2F,
|
||||
Scope = 0x3F
|
||||
Scope = 0x3F,
|
||||
RegExpShared = 0x4F
|
||||
};
|
||||
const static uintptr_t OutOfLineTraceKindMask = 0x07;
|
||||
static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
static_assert(uintptr_t(JS::TraceKind::Scope) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
static_assert(uintptr_t(JS::TraceKind::RegExpShared) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
|
||||
// When this header is imported inside SpiderMonkey, the class definitions are
|
||||
// available and we can query those definitions to find the correct kind
|
||||
|
|
@ -87,7 +90,8 @@ struct MapTypeToTraceKind {
|
|||
D(Script, JSScript, true) \
|
||||
D(Shape, js::Shape, true) \
|
||||
D(String, JSString, false) \
|
||||
D(Symbol, JS::Symbol, false)
|
||||
D(Symbol, JS::Symbol, false) \
|
||||
D(RegExpShared, js::RegExpShared, true)
|
||||
|
||||
// Map from all public types to their trace kind.
|
||||
#define JS_EXPAND_DEF(name, type, _) \
|
||||
|
|
|
|||
|
|
@ -160,6 +160,9 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer
|
|||
virtual void onScopeEdge(js::Scope** scopep) {
|
||||
onChild(JS::GCCellPtr(*scopep, JS::TraceKind::Scope));
|
||||
}
|
||||
virtual void onRegExpSharedEdge(js::RegExpShared** sharedp) {
|
||||
onChild(JS::GCCellPtr(*sharedp, JS::TraceKind::RegExpShared));
|
||||
}
|
||||
|
||||
// Override this method to receive notification when a node in the GC
|
||||
// heap graph is visited.
|
||||
|
|
@ -230,6 +233,7 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer
|
|||
void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
|
||||
void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
|
||||
void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
|
||||
void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); }
|
||||
|
||||
private:
|
||||
friend class AutoTracingName;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -16,11 +16,7 @@ enum PromiseSlots {
|
|||
PromiseSlot_ReactionsOrResult,
|
||||
PromiseSlot_RejectFunction,
|
||||
PromiseSlot_AwaitGenerator = PromiseSlot_RejectFunction,
|
||||
PromiseSlot_AllocationSite,
|
||||
PromiseSlot_ResolutionSite,
|
||||
PromiseSlot_AllocationTime,
|
||||
PromiseSlot_ResolutionTime,
|
||||
PromiseSlot_Id,
|
||||
PromiseSlot_DebugInfo,
|
||||
PromiseSlots,
|
||||
};
|
||||
|
||||
|
|
@ -28,9 +24,8 @@ enum PromiseSlots {
|
|||
#define PROMISE_FLAG_FULFILLED 0x2
|
||||
#define PROMISE_FLAG_HANDLED 0x4
|
||||
#define PROMISE_FLAG_REPORTED 0x8
|
||||
#define PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION 0x10
|
||||
#define PROMISE_FLAG_DEFAULT_REJECT_FUNCTION 0x20
|
||||
#define PROMISE_FLAG_ASYNC 0x40
|
||||
#define PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS 0x10
|
||||
#define PROMISE_FLAG_ASYNC 0x20
|
||||
|
||||
class AutoSetNewObjectMetadata;
|
||||
|
||||
|
|
@ -48,8 +43,11 @@ class PromiseObject : public NativeObject
|
|||
static JSObject* unforgeableResolve(JSContext* cx, HandleValue value);
|
||||
static JSObject* unforgeableReject(JSContext* cx, HandleValue value);
|
||||
|
||||
int32_t flags() {
|
||||
return getFixedSlot(PromiseSlot_Flags).toInt32();
|
||||
}
|
||||
JS::PromiseState state() {
|
||||
int32_t flags = getFixedSlot(PromiseSlot_Flags).toInt32();
|
||||
int32_t flags = this->flags();
|
||||
if (!(flags & PROMISE_FLAG_RESOLVED)) {
|
||||
MOZ_ASSERT(!(flags & PROMISE_FLAG_FULFILLED));
|
||||
return JS::PromiseState::Pending;
|
||||
|
|
@ -58,6 +56,10 @@ class PromiseObject : public NativeObject
|
|||
return JS::PromiseState::Fulfilled;
|
||||
return JS::PromiseState::Rejected;
|
||||
}
|
||||
Value reactions() {
|
||||
MOZ_ASSERT(state() == JS::PromiseState::Pending);
|
||||
return getFixedSlot(PromiseSlot_ReactionsOrResult);
|
||||
}
|
||||
Value value() {
|
||||
MOZ_ASSERT(state() == JS::PromiseState::Fulfilled);
|
||||
return getFixedSlot(PromiseSlot_ReactionsOrResult);
|
||||
|
|
@ -66,6 +68,10 @@ class PromiseObject : public NativeObject
|
|||
MOZ_ASSERT(state() == JS::PromiseState::Rejected);
|
||||
return getFixedSlot(PromiseSlot_ReactionsOrResult);
|
||||
}
|
||||
Value valueOrReason() {
|
||||
MOZ_ASSERT(state() != JS::PromiseState::Pending);
|
||||
return getFixedSlot(PromiseSlot_ReactionsOrResult);
|
||||
}
|
||||
|
||||
static MOZ_MUST_USE bool resolve(JSContext* cx, Handle<PromiseObject*> promise,
|
||||
HandleValue resolutionValue);
|
||||
|
|
@ -74,14 +80,10 @@ class PromiseObject : public NativeObject
|
|||
|
||||
static void onSettled(JSContext* cx, Handle<PromiseObject*> promise);
|
||||
|
||||
double allocationTime() { return getFixedSlot(PromiseSlot_AllocationTime).toNumber(); }
|
||||
double resolutionTime() { return getFixedSlot(PromiseSlot_ResolutionTime).toNumber(); }
|
||||
JSObject* allocationSite() {
|
||||
return getFixedSlot(PromiseSlot_AllocationSite).toObjectOrNull();
|
||||
}
|
||||
JSObject* resolutionSite() {
|
||||
return getFixedSlot(PromiseSlot_ResolutionSite).toObjectOrNull();
|
||||
}
|
||||
double allocationTime();
|
||||
double resolutionTime();
|
||||
JSObject* allocationSite();
|
||||
JSObject* resolutionSite();
|
||||
double lifetime();
|
||||
double timeToResolution() {
|
||||
MOZ_ASSERT(state() != JS::PromiseState::Pending);
|
||||
|
|
@ -91,7 +93,7 @@ class PromiseObject : public NativeObject
|
|||
uint64_t getID();
|
||||
bool isUnhandled() {
|
||||
MOZ_ASSERT(state() == JS::PromiseState::Rejected);
|
||||
return !(getFixedSlot(PromiseSlot_Flags).toInt32() & PROMISE_FLAG_HANDLED);
|
||||
return !(flags() & PROMISE_FLAG_HANDLED);
|
||||
}
|
||||
void markAsReported() {
|
||||
MOZ_ASSERT(isUnhandled());
|
||||
|
|
@ -114,6 +116,12 @@ class PromiseObject : public NativeObject
|
|||
MOZ_MUST_USE JSObject*
|
||||
GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises);
|
||||
|
||||
enum class CreateDependentPromise {
|
||||
Always,
|
||||
SkipIfCtorUnobservable,
|
||||
Never
|
||||
};
|
||||
|
||||
/**
|
||||
* Enqueues resolve/reject reactions in the given Promise's reactions lists
|
||||
* as though calling the original value of Promise.prototype.then.
|
||||
|
|
@ -127,7 +135,7 @@ GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises);
|
|||
MOZ_MUST_USE bool
|
||||
OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
|
||||
HandleValue onFulfilled, HandleValue onRejected,
|
||||
MutableHandleObject dependent, bool createDependent);
|
||||
MutableHandleObject dependent, CreateDependentPromise createDependent);
|
||||
|
||||
/**
|
||||
* PromiseResolve ( C, x )
|
||||
|
|
@ -213,13 +221,6 @@ class PromiseTask : public JS::AsyncTask
|
|||
bool executeAndFinish(JSContext* cx);
|
||||
};
|
||||
|
||||
bool
|
||||
Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool
|
||||
Promise_reject(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool
|
||||
Promise_then(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_Promise_h */
|
||||
|
|
|
|||
|
|
@ -164,10 +164,11 @@ CreateRegExpSearchResult(JSContext* cx, const MatchPairs& matches)
|
|||
* steps 3, 9-14, except 12.a.i, 12.c.i.1.
|
||||
*/
|
||||
static RegExpRunStatus
|
||||
ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
|
||||
size_t searchIndex, MatchPairs* matches, size_t* endIndex)
|
||||
ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, MutableHandleRegExpShared re,
|
||||
HandleLinearString input, size_t searchIndex, MatchPairs* matches,
|
||||
size_t* endIndex)
|
||||
{
|
||||
RegExpRunStatus status = re.execute(cx, input, searchIndex, matches, endIndex);
|
||||
RegExpRunStatus status = RegExpShared::execute(cx, re, input, searchIndex, matches, endIndex);
|
||||
|
||||
/* Out of spec: Update RegExpStatics. */
|
||||
if (status == RegExpRunStatus_Success && res) {
|
||||
|
|
@ -175,7 +176,7 @@ ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLin
|
|||
if (!res->updateFromMatchPairs(cx, input, *matches))
|
||||
return RegExpRunStatus_Error;
|
||||
} else {
|
||||
res->updateLazily(cx, input, &re, searchIndex);
|
||||
res->updateLazily(cx, input, re, searchIndex);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
|
|
@ -187,13 +188,13 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*>
|
|||
HandleLinearString input, size_t* lastIndex, bool test,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
RegExpGuard shared(cx);
|
||||
RootedRegExpShared shared(cx);
|
||||
if (!RegExpObject::getShared(cx, reobj, &shared))
|
||||
return false;
|
||||
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex,
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &shared, input, *lastIndex,
|
||||
&matches, nullptr);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
|
@ -275,7 +276,7 @@ RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
|
|||
|
||||
if (sharedUse == UseRegExpShared) {
|
||||
/* Steps 7-8. */
|
||||
RegExpGuard re(cx);
|
||||
RootedRegExpShared re(cx);
|
||||
if (!cx->compartment()->regExps.get(cx, pattern, flags, &re))
|
||||
return false;
|
||||
|
||||
|
|
@ -381,7 +382,7 @@ regexp_compile_impl(JSContext* cx, const CallArgs& args)
|
|||
RegExpFlag flags;
|
||||
{
|
||||
// Step 3b.
|
||||
RegExpGuard g(cx);
|
||||
RootedRegExpShared g(cx);
|
||||
if (!RegExpToShared(cx, patternObj, &g))
|
||||
return false;
|
||||
|
||||
|
|
@ -476,7 +477,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
RegExpFlag flags;
|
||||
{
|
||||
// Step 4.a.
|
||||
RegExpGuard g(cx);
|
||||
RootedRegExpShared g(cx);
|
||||
if (!RegExpToShared(cx, patternObj, &g))
|
||||
return false;
|
||||
sourceAtom = g->getSource();
|
||||
|
|
@ -603,7 +604,7 @@ js::regexp_clone(JSContext* cx, unsigned argc, Value* vp)
|
|||
RootedAtom sourceAtom(cx);
|
||||
RegExpFlag flags;
|
||||
{
|
||||
RegExpGuard g(cx);
|
||||
RootedRegExpShared g(cx);
|
||||
if (!RegExpToShared(cx, from, &g))
|
||||
return false;
|
||||
sourceAtom = g->getSource();
|
||||
|
|
@ -985,7 +986,7 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
/* Steps 1-2 performed by the caller. */
|
||||
Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>());
|
||||
|
||||
RegExpGuard re(cx);
|
||||
RootedRegExpShared re(cx);
|
||||
if (!RegExpObject::getShared(cx, reobj, &re))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
|
|
@ -1036,7 +1037,7 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
}
|
||||
|
||||
/* Steps 3, 11-14, except 12.a.i, 12.c.i.1. */
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, matches, endIndex);
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &re, input, lastIndex, matches, endIndex);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
|
|
@ -1070,7 +1071,7 @@ RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
|
||||
/* Steps 16-25 */
|
||||
Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>());
|
||||
RegExpGuard shared(cx);
|
||||
RootedRegExpShared shared(cx);
|
||||
if (!RegExpObject::getShared(cx, reobj, &shared))
|
||||
return false;
|
||||
return CreateRegExpMatchResult(cx, *shared, string, matches, rval);
|
||||
|
|
@ -1117,7 +1118,7 @@ js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
// successful only if the pairs have actually been filled in.
|
||||
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
|
||||
Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>());
|
||||
RegExpGuard shared(cx);
|
||||
RootedRegExpShared shared(cx);
|
||||
if (!RegExpObject::getShared(cx, reobj, &shared))
|
||||
return false;
|
||||
return CreateRegExpMatchResult(cx, *shared, input, *maybeMatches, output);
|
||||
|
|
|
|||
|
|
@ -1334,8 +1334,8 @@ SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
RootedNativeObject promise(cx, &args[0].toObject().as<NativeObject>());
|
||||
int32_t flags = promise->getFixedSlot(PromiseSlot_Flags).toInt32();
|
||||
Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
|
||||
int32_t flags = promise->flags();
|
||||
promise->setFixedSlot(PromiseSlot_Flags,
|
||||
Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
|
||||
promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ struct MovingTracer : JS::CallbackTracer
|
|||
void onLazyScriptEdge(LazyScript** lazyp) override;
|
||||
void onBaseShapeEdge(BaseShape** basep) override;
|
||||
void onScopeEdge(Scope** basep) override;
|
||||
void onRegExpSharedEdge(RegExpShared** sharedp) override;
|
||||
void onChild(const JS::GCCellPtr& thing) override {
|
||||
MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing.asCell()));
|
||||
}
|
||||
|
|
@ -82,6 +83,10 @@ struct MovingTracer : JS::CallbackTracer
|
|||
#ifdef DEBUG
|
||||
TracerKind getTracerKind() const override { return TracerKind::Moving; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void updateEdge(T** thingp);
|
||||
};
|
||||
|
||||
// Structure for counting how many times objects in a particular group have
|
||||
|
|
|
|||
|
|
@ -735,6 +735,8 @@ class GCRuntime
|
|||
void enableCompactingGC();
|
||||
bool isCompactingGCEnabled() const;
|
||||
|
||||
bool isShrinkingGC() const { return invocationKind == GC_SHRINK; }
|
||||
|
||||
void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ enum class AllocKind {
|
|||
SYMBOL,
|
||||
JITCODE,
|
||||
SCOPE,
|
||||
REGEXP_SHARED,
|
||||
LIMIT,
|
||||
LAST = LIMIT - 1
|
||||
};
|
||||
|
|
@ -122,38 +123,39 @@ enum class AllocKind {
|
|||
// Macro to enumerate the different allocation kinds supplying information about
|
||||
// the trace kind, C++ type and allocation size.
|
||||
#define FOR_EACH_OBJECT_ALLOCKIND(D) \
|
||||
/* AllocKind TraceKind TypeName SizedType */ \
|
||||
D(FUNCTION, Object, JSObject, JSFunction) \
|
||||
D(FUNCTION_EXTENDED, Object, JSObject, FunctionExtended) \
|
||||
D(OBJECT0, Object, JSObject, JSObject_Slots0) \
|
||||
D(OBJECT0_BACKGROUND, Object, JSObject, JSObject_Slots0) \
|
||||
D(OBJECT2, Object, JSObject, JSObject_Slots2) \
|
||||
D(OBJECT2_BACKGROUND, Object, JSObject, JSObject_Slots2) \
|
||||
D(OBJECT4, Object, JSObject, JSObject_Slots4) \
|
||||
D(OBJECT4_BACKGROUND, Object, JSObject, JSObject_Slots4) \
|
||||
D(OBJECT8, Object, JSObject, JSObject_Slots8) \
|
||||
D(OBJECT8_BACKGROUND, Object, JSObject, JSObject_Slots8) \
|
||||
D(OBJECT12, Object, JSObject, JSObject_Slots12) \
|
||||
D(OBJECT12_BACKGROUND, Object, JSObject, JSObject_Slots12) \
|
||||
D(OBJECT16, Object, JSObject, JSObject_Slots16) \
|
||||
D(OBJECT16_BACKGROUND, Object, JSObject, JSObject_Slots16)
|
||||
/* AllocKind TraceKind TypeName SizedType */ \
|
||||
D(FUNCTION, Object, JSObject, JSFunction) \
|
||||
D(FUNCTION_EXTENDED, Object, JSObject, FunctionExtended) \
|
||||
D(OBJECT0, Object, JSObject, JSObject_Slots0) \
|
||||
D(OBJECT0_BACKGROUND, Object, JSObject, JSObject_Slots0) \
|
||||
D(OBJECT2, Object, JSObject, JSObject_Slots2) \
|
||||
D(OBJECT2_BACKGROUND, Object, JSObject, JSObject_Slots2) \
|
||||
D(OBJECT4, Object, JSObject, JSObject_Slots4) \
|
||||
D(OBJECT4_BACKGROUND, Object, JSObject, JSObject_Slots4) \
|
||||
D(OBJECT8, Object, JSObject, JSObject_Slots8) \
|
||||
D(OBJECT8_BACKGROUND, Object, JSObject, JSObject_Slots8) \
|
||||
D(OBJECT12, Object, JSObject, JSObject_Slots12) \
|
||||
D(OBJECT12_BACKGROUND, Object, JSObject, JSObject_Slots12) \
|
||||
D(OBJECT16, Object, JSObject, JSObject_Slots16) \
|
||||
D(OBJECT16_BACKGROUND, Object, JSObject, JSObject_Slots16)
|
||||
|
||||
#define FOR_EACH_NONOBJECT_ALLOCKIND(D) \
|
||||
/* AllocKind TraceKind TypeName SizedType */ \
|
||||
D(SCRIPT, Script, JSScript, JSScript) \
|
||||
D(LAZY_SCRIPT, LazyScript, js::LazyScript, js::LazyScript) \
|
||||
D(SHAPE, Shape, js::Shape, js::Shape) \
|
||||
D(ACCESSOR_SHAPE, Shape, js::AccessorShape, js::AccessorShape) \
|
||||
D(BASE_SHAPE, BaseShape, js::BaseShape, js::BaseShape) \
|
||||
D(OBJECT_GROUP, ObjectGroup, js::ObjectGroup, js::ObjectGroup) \
|
||||
D(FAT_INLINE_STRING, String, JSFatInlineString, JSFatInlineString) \
|
||||
D(STRING, String, JSString, JSString) \
|
||||
D(EXTERNAL_STRING, String, JSExternalString, JSExternalString) \
|
||||
D(FAT_INLINE_ATOM, String, js::FatInlineAtom, js::FatInlineAtom) \
|
||||
D(ATOM, String, js::NormalAtom, js::NormalAtom) \
|
||||
D(SYMBOL, Symbol, JS::Symbol, JS::Symbol) \
|
||||
D(JITCODE, JitCode, js::jit::JitCode, js::jit::JitCode) \
|
||||
D(SCOPE, Scope, js::Scope, js::Scope)
|
||||
/* AllocKind TraceKind TypeName SizedType */ \
|
||||
D(SCRIPT, Script, JSScript, JSScript) \
|
||||
D(LAZY_SCRIPT, LazyScript, js::LazyScript, js::LazyScript) \
|
||||
D(SHAPE, Shape, js::Shape, js::Shape) \
|
||||
D(ACCESSOR_SHAPE, Shape, js::AccessorShape, js::AccessorShape) \
|
||||
D(BASE_SHAPE, BaseShape, js::BaseShape, js::BaseShape) \
|
||||
D(OBJECT_GROUP, ObjectGroup, js::ObjectGroup, js::ObjectGroup) \
|
||||
D(FAT_INLINE_STRING, String, JSFatInlineString, JSFatInlineString) \
|
||||
D(STRING, String, JSString, JSString) \
|
||||
D(EXTERNAL_STRING, String, JSExternalString, JSExternalString) \
|
||||
D(FAT_INLINE_ATOM, String, js::FatInlineAtom, js::FatInlineAtom) \
|
||||
D(ATOM, String, js::NormalAtom, js::NormalAtom) \
|
||||
D(SYMBOL, Symbol, JS::Symbol, JS::Symbol) \
|
||||
D(JITCODE, JitCode, js::jit::JitCode, js::jit::JitCode) \
|
||||
D(SCOPE, Scope, js::Scope, js::Scope) \
|
||||
D(REGEXP_SHARED, RegExpShared, js::RegExpShared, js::RegExpShared)
|
||||
|
||||
#define FOR_EACH_ALLOCKIND(D) \
|
||||
FOR_EACH_OBJECT_ALLOCKIND(D) \
|
||||
|
|
|
|||
|
|
@ -436,6 +436,14 @@ js::TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char*
|
|||
DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
js::TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
|
||||
{
|
||||
if (InternalBarrierMethods<T>::isMarkable(thingp->unbarrieredGet()))
|
||||
DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
JS_PUBLIC_API(void)
|
||||
JS::TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
|
||||
|
|
@ -550,6 +558,7 @@ js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name)
|
|||
template void js::TraceEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
|
||||
template void js::TraceEdge<type>(JSTracer*, ReadBarriered<type>*, const char*); \
|
||||
template void js::TraceNullableEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
|
||||
template void js::TraceNullableEdge<type>(JSTracer*, ReadBarriered<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*); \
|
||||
|
|
@ -866,6 +875,7 @@ js::GCMarker::markAndTraceChildren(T* thing)
|
|||
namespace js {
|
||||
template <> void GCMarker::traverse(BaseShape* thing) { markAndTraceChildren(thing); }
|
||||
template <> void GCMarker::traverse(JS::Symbol* thing) { markAndTraceChildren(thing); }
|
||||
template <> void GCMarker::traverse(RegExpShared* thing) { markAndTraceChildren(thing); }
|
||||
} // namespace js
|
||||
|
||||
// Strings, LazyScripts, Shapes, and Scopes are extremely common, but have
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ class JitCode;
|
|||
D(js::PlainObject*) \
|
||||
D(js::PropertyName*) \
|
||||
D(js::RegExpObject*) \
|
||||
D(js::RegExpShared*) \
|
||||
D(js::SavedFrame*) \
|
||||
D(js::Scope*) \
|
||||
D(js::ScriptSourceObject*) \
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ static const PhaseInfo phases[] = {
|
|||
{ PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_REGEXP_SHARED, "Sweep RegExpShared", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP },
|
||||
{ PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP },
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ enum Phase : uint8_t {
|
|||
PHASE_SWEEP_STRING,
|
||||
PHASE_SWEEP_SCRIPT,
|
||||
PHASE_SWEEP_SCOPE,
|
||||
PHASE_SWEEP_REGEXP_SHARED,
|
||||
PHASE_SWEEP_SHAPE,
|
||||
PHASE_SWEEP_JITCODE,
|
||||
PHASE_FINALIZE_END,
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ template <typename T>
|
|||
void
|
||||
TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name);
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name);
|
||||
|
||||
// Trace through a "root" edge. These edges are the initial edges in the object
|
||||
// graph traversal. Root edges are asserted to only be traversed in the initial
|
||||
// phase of a GC.
|
||||
|
|
|
|||
|
|
@ -1259,7 +1259,7 @@ IsNativeRegExpEnabled(JSContext* cx)
|
|||
}
|
||||
|
||||
RegExpCode
|
||||
irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
|
||||
irregexp::CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
|
||||
HandleLinearString sample, bool is_global, bool ignore_case,
|
||||
bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
|
||||
bool unicode)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ struct RegExpCode
|
|||
};
|
||||
|
||||
RegExpCode
|
||||
CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
|
||||
CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
|
||||
HandleLinearString sample, bool is_global, bool ignore_case,
|
||||
bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
|
||||
bool unicode);
|
||||
|
|
|
|||
12
js/src/jit-test/tests/auto-regress/bug1416809-1.js
Normal file
12
js/src/jit-test/tests/auto-regress/bug1416809-1.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// https://tc39.github.io/proposal-async-iteration
|
||||
|
||||
// Recursion between:
|
||||
// 11.4.3.3 AsyncGeneratorResolve, step 8
|
||||
// 11.4.3.5 AsyncGeneratorResumeNext, step 11.
|
||||
|
||||
var asyncIter = async function*(){ yield; }();
|
||||
asyncIter.next();
|
||||
|
||||
for (var i = 0; i < 20000; i++) {
|
||||
asyncIter.next();
|
||||
}
|
||||
12
js/src/jit-test/tests/auto-regress/bug1416809-2.js
Normal file
12
js/src/jit-test/tests/auto-regress/bug1416809-2.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// https://tc39.github.io/proposal-async-iteration
|
||||
|
||||
// Recursion between:
|
||||
// 11.4.3.4 AsyncGeneratorReject, step 7.
|
||||
// 11.4.3.5 AsyncGeneratorResumeNext, step 10.b.ii.2.
|
||||
|
||||
var asyncIter = async function*(){ yield; }();
|
||||
asyncIter.next();
|
||||
|
||||
for (var i = 0; i < 20000; i++) {
|
||||
asyncIter.throw();
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Promise.race(...) may add a dummy PromiseReaction which is only used for the
|
||||
// debugger.
|
||||
//
|
||||
// See BlockOnPromise when called from PerformPromiseRace for when this dummy
|
||||
// reaction is created.
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger();
|
||||
var gw = dbg.addDebuggee(g);
|
||||
|
||||
function test(withFastPath) {
|
||||
g.eval(`
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
var {promise: alwaysPending} = newPromiseCapability();
|
||||
|
||||
if (!${withFastPath}) {
|
||||
// Disable the BlockOnPromise fast path by giving |alwaysPending| a
|
||||
// non-default "then" function property. This will ensure the dummy
|
||||
// reaction is created.
|
||||
alwaysPending.then = function() {};
|
||||
}
|
||||
|
||||
var result = Promise.race([alwaysPending]);
|
||||
`);
|
||||
|
||||
var alwaysPending = gw.makeDebuggeeValue(g.alwaysPending);
|
||||
var result = gw.makeDebuggeeValue(g.result);
|
||||
|
||||
assertEq(alwaysPending.promiseDependentPromises.length, 1);
|
||||
assertEq(alwaysPending.promiseDependentPromises[0], result);
|
||||
|
||||
assertEq(result.promiseDependentPromises.length, 0);
|
||||
}
|
||||
|
||||
// No dummy reaction created when the fast path is taken.
|
||||
test(true);
|
||||
|
||||
// Dummy reaction is created when we can't take the fast path.
|
||||
test(false);
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// Promise.race(...) may add a dummy PromiseReaction which is only used for the
|
||||
// debugger. Ensure that this dummy reaction can't influence the normal Promise
|
||||
// resolution behaviour.
|
||||
//
|
||||
// See BlockOnPromise when called from PerformPromiseRace for when this dummy
|
||||
// reaction is created.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
function neverCalled() {
|
||||
// Quit with non-zero exit code to ensure a test suite error is shown,
|
||||
// even when this function is called within promise handlers which normally
|
||||
// swallow any exceptions.
|
||||
quit(1);
|
||||
}
|
||||
|
||||
var c = 0;
|
||||
var g_resolve;
|
||||
|
||||
var resolvedValues = [];
|
||||
|
||||
function resolveCapability(v) {
|
||||
resolvedValues.push(v);
|
||||
}
|
||||
|
||||
class P extends Promise {
|
||||
constructor(executor) {
|
||||
// Only the very first object created through this constructor gets
|
||||
// special treatment, all other invocations create built-in Promise
|
||||
// objects.
|
||||
if (c++ > 1) {
|
||||
return new Promise(executor);
|
||||
}
|
||||
|
||||
executor(resolveCapability, neverCalled);
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
g_resolve = resolve;
|
||||
|
||||
// Use an async function to create a Promise without resolving functions.
|
||||
var p = async function(){ await promise; return 456; }();
|
||||
|
||||
// Ensure the species constructor is not the built-in Promise constructor
|
||||
// to avoid falling into the fast path.
|
||||
p.constructor = {
|
||||
[Symbol.species]: P
|
||||
};
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
var {promise: alwaysPending} = newPromiseCapability();
|
||||
|
||||
// The promise returned from race() should never be resolved.
|
||||
P.race([alwaysPending]).then(neverCalled, neverCalled);
|
||||
|
||||
g_resolve(123);
|
||||
|
||||
drainJobQueue();
|
||||
|
||||
// Check |resolvedValues| to ensure resolving functions were properly called.
|
||||
assertEq(resolvedValues.length, 2);
|
||||
assertEq(resolvedValues[0], alwaysPending);
|
||||
assertEq(resolvedValues[1], 456);
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
function newPromiseCapability() {
|
||||
let resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
function neverCalled() {
|
||||
// Quit with non-zero exit code to ensure a test suite error is shown,
|
||||
// even when this function is called within promise handlers which normally
|
||||
// swallow any exceptions.
|
||||
quit(1);
|
||||
}
|
||||
|
||||
var c = 0;
|
||||
var g_resolve;
|
||||
|
||||
class P extends Promise {
|
||||
constructor(executor) {
|
||||
// Only the very first object created through this constructor gets
|
||||
// special treatment, all other invocations create built-in Promise
|
||||
// objects.
|
||||
if (c++ > 1) {
|
||||
return new Promise(executor);
|
||||
}
|
||||
|
||||
// Pass a native ResolvePromiseFunction function as the resolve handler.
|
||||
// (It's okay that the promise of this promise capability is never used.)
|
||||
executor(newPromiseCapability().resolve, neverCalled);
|
||||
|
||||
let {promise, resolve} = newPromiseCapability();
|
||||
g_resolve = resolve;
|
||||
|
||||
// Use an async function to create a Promise without resolving functions.
|
||||
return async function(){ await promise; return 456; }();
|
||||
}
|
||||
|
||||
// Ensure we don't take the (spec) fast path in Promise.resolve and instead
|
||||
// create a new promise object. (We could not provide an override at all
|
||||
// and rely on the default behaviour, but giving an explicit definition
|
||||
// may help to interpret this test case.)
|
||||
static resolve(v) {
|
||||
return super.resolve(v);
|
||||
}
|
||||
}
|
||||
|
||||
let {promise: alwaysPending} = newPromiseCapability();
|
||||
|
||||
P.race([alwaysPending]).then(neverCalled, neverCalled);
|
||||
|
||||
g_resolve(123);
|
||||
|
||||
drainJobQueue();
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
function neverCalled() {
|
||||
// Quit with non-zero exit code to ensure a test suite error is shown,
|
||||
// even when this function is called within promise handlers which normally
|
||||
// swallow any exceptions.
|
||||
quit(1);
|
||||
}
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
var getterCount = 0;
|
||||
|
||||
class P extends Promise {
|
||||
constructor(executor) {
|
||||
var {promise, resolve, reject} = newPromiseCapability();
|
||||
|
||||
executor(function(v) {
|
||||
// Resolve the promise.
|
||||
resolve(v);
|
||||
|
||||
// But then return an object from the resolve function. This object
|
||||
// must be treated as the resolution value for the otherwise
|
||||
// skipped promise which gets created when Promise.prototype.then is
|
||||
// called in PerformPromiseRace.
|
||||
return {
|
||||
get then() {
|
||||
getterCount++;
|
||||
}
|
||||
};
|
||||
}, neverCalled);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Default to the standard Promise.resolve function, so we don't create
|
||||
// another instance of this class when resolving the passed promise objects
|
||||
// in Promise.race.
|
||||
static resolve(v) {
|
||||
return Promise.resolve(v);
|
||||
}
|
||||
}
|
||||
|
||||
P.race([promise]);
|
||||
|
||||
resolve(0);
|
||||
|
||||
drainJobQueue();
|
||||
|
||||
assertEq(getterCount, 1);
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
resolve(Promise.resolve(0));
|
||||
|
||||
// Don't assert when the Promise was already resolved.
|
||||
resolvePromise(promise, 123);
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Test we don't assert when the promise is settled after enqueuing a PromiseReactionJob.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
var p = Promise.resolve(0);
|
||||
|
||||
// Enqueue a PromiseResolveThenableJob followed by a PromiseReactionJob.
|
||||
resolve(p);
|
||||
|
||||
// The PromiseReactionJob expects a pending promise, but this settlePromiseNow
|
||||
// call will already have settled the promise.
|
||||
settlePromiseNow(promise);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Don't assert when the promise in the resolving functions is wrapped in a CCW.
|
||||
|
||||
function newPromiseCapability(newTarget) {
|
||||
var resolve, reject, promise = Reflect.construct(Promise, [function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
}], newTarget);
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
var g = newGlobal();
|
||||
|
||||
var {promise, resolve} = newPromiseCapability(g.Promise);
|
||||
|
||||
g.settlePromiseNow(promise);
|
||||
|
||||
// Don't assert when resolving the promise.
|
||||
resolve(0);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Don't assert when the promise in the resolving functions is wrapped in a CCW.
|
||||
|
||||
function newPromiseCapability(newTarget) {
|
||||
var resolve, reject, promise = Reflect.construct(Promise, [function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
}], newTarget);
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
var g = newGlobal();
|
||||
|
||||
var {promise, reject} = newPromiseCapability(g.Promise);
|
||||
|
||||
g.settlePromiseNow(promise);
|
||||
|
||||
// Don't assert when rejecting the promise.
|
||||
reject(0);
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// Test we don't assert when the promise is settled and the SpeciesConstructor
|
||||
// call in Promise.prototype.then throws an exception.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
var p = Promise.resolve(0);
|
||||
|
||||
p.constructor = {
|
||||
[Symbol.species]: function() {
|
||||
throw new Error();
|
||||
}
|
||||
};
|
||||
|
||||
// Enqueue a PromiseResolveThenableJob.
|
||||
resolve(p);
|
||||
|
||||
// Settle the promise after the resolve call.
|
||||
settlePromiseNow(promise);
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// Test we don't assert when the promise is settled and the SpeciesConstructor
|
||||
// call in Promise.prototype.then throws an exception.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
var p = Promise.resolve(0);
|
||||
|
||||
p.constructor = {
|
||||
[Symbol.species]: function() {
|
||||
// Settle the promise in the SpeciesConstructor call.
|
||||
settlePromiseNow(promise);
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
};
|
||||
|
||||
// Enqueue a PromiseResolveThenableJob.
|
||||
resolve(p);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Test we don't assert when the promise is settled and we then try to call the
|
||||
// resolving function.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
settlePromiseNow(promise);
|
||||
|
||||
// Don't assert when the promise is already settled.
|
||||
resolve(0);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Test we don't assert when the promise is settled and we then try to call the
|
||||
// rejecting function.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, reject} = newPromiseCapability();
|
||||
|
||||
settlePromiseNow(promise);
|
||||
|
||||
// Don't assert when the promise is already settled.
|
||||
reject(0);
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Don't assert when a side-effect when getting the "then" property settled the promise.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
var thenable = {
|
||||
get then() {
|
||||
settlePromiseNow(promise);
|
||||
}
|
||||
};
|
||||
|
||||
resolve(thenable);
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Don't assert when a side-effect when getting the "then" property settled the promise.
|
||||
|
||||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve} = newPromiseCapability();
|
||||
|
||||
var thenable = {
|
||||
get then() {
|
||||
settlePromiseNow(promise);
|
||||
|
||||
// Throw an error to reject the promise.
|
||||
throw new Error();
|
||||
}
|
||||
};
|
||||
|
||||
resolve(thenable);
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
// Don't assert when settlePromiseNow() is called on an async-function promise.
|
||||
|
||||
var promise = async function(){ await 0; }();
|
||||
|
||||
try {
|
||||
settlePromiseNow(promise);
|
||||
} catch {}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
function newPromiseCapability() {
|
||||
var resolve, reject, promise = new Promise(function(r1, r2) {
|
||||
resolve = r1;
|
||||
reject = r2;
|
||||
});
|
||||
return {promise, resolve, reject};
|
||||
}
|
||||
|
||||
|
||||
var {promise, resolve, reject} = newPromiseCapability();
|
||||
|
||||
settlePromiseNow(promise);
|
||||
|
||||
assertEq(resolve(0), undefined);
|
||||
assertEq(reject(0), undefined);
|
||||
30
js/src/jit-test/tests/realms/promise-then.js
Normal file
30
js/src/jit-test/tests/realms/promise-then.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
load(libdir + "asserts.js");
|
||||
|
||||
const g = newGlobal({sameCompartmentAs: this});
|
||||
|
||||
let resolve, reject;
|
||||
let promise = new Promise((resolveFn, rejectFn) => {
|
||||
resolve = resolveFn;
|
||||
reject = rejectFn;
|
||||
});
|
||||
|
||||
// Set to a built-in Promise.prototype.then function, but from a different realm.
|
||||
promise.then = g.Promise.prototype.then;
|
||||
|
||||
// Make SpeciesConstructor throw a TypeError exception.
|
||||
promise.constructor = {
|
||||
[Symbol.species]: "not a constructor"
|
||||
};
|
||||
|
||||
async function f(p) {
|
||||
await p;
|
||||
}
|
||||
|
||||
let error;
|
||||
f(promise).catch(e => { error = e; });
|
||||
|
||||
resolve(promise);
|
||||
|
||||
drainJobQueue();
|
||||
|
||||
assertEq(error.constructor === g.TypeError, true);
|
||||
|
|
@ -4994,7 +4994,8 @@ JS::RejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue re
|
|||
static bool
|
||||
CallOriginalPromiseThenImpl(JSContext* cx, JS::HandleObject promiseObj,
|
||||
JS::HandleObject onResolvedObj_, JS::HandleObject onRejectedObj_,
|
||||
JS::MutableHandleObject resultObj, bool createDependent)
|
||||
JS::MutableHandleObject resultObj,
|
||||
CreateDependentPromise createDependent)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
|
@ -5044,8 +5045,11 @@ JS::CallOriginalPromiseThen(JSContext* cx, JS::HandleObject promiseObj,
|
|||
JS::HandleObject onResolvedObj, JS::HandleObject onRejectedObj)
|
||||
{
|
||||
RootedObject resultPromise(cx);
|
||||
if (!CallOriginalPromiseThenImpl(cx, promiseObj, onResolvedObj, onRejectedObj, &resultPromise, true))
|
||||
if (!CallOriginalPromiseThenImpl(cx, promiseObj, onResolvedObj, onRejectedObj, &resultPromise,
|
||||
CreateDependentPromise::Always))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return resultPromise;
|
||||
}
|
||||
|
||||
|
|
@ -5054,7 +5058,8 @@ JS::AddPromiseReactions(JSContext* cx, JS::HandleObject promiseObj,
|
|||
JS::HandleObject onResolvedObj, JS::HandleObject onRejectedObj)
|
||||
{
|
||||
RootedObject resultPromise(cx);
|
||||
bool result = CallOriginalPromiseThenImpl(cx, promiseObj, onResolvedObj, onRejectedObj, &resultPromise, false);
|
||||
bool result = CallOriginalPromiseThenImpl(cx, promiseObj, onResolvedObj, onRejectedObj,
|
||||
&resultPromise, CreateDependentPromise::Never);
|
||||
MOZ_ASSERT(!resultPromise);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -6141,10 +6146,10 @@ JS_GetRegExpFlags(JSContext* cx, HandleObject obj)
|
|||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
RegExpGuard shared(cx);
|
||||
RootedRegExpShared shared(cx);
|
||||
if (!RegExpToShared(cx, obj, &shared))
|
||||
return false;
|
||||
return shared.re()->getFlags();
|
||||
return shared->getFlags();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString*)
|
||||
|
|
@ -6153,10 +6158,10 @@ JS_GetRegExpSource(JSContext* cx, HandleObject obj)
|
|||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
RegExpGuard shared(cx);
|
||||
RootedRegExpShared shared(cx);
|
||||
if (!RegExpToShared(cx, obj, &shared))
|
||||
return nullptr;
|
||||
return shared.re()->getSource();
|
||||
return shared->getSource();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
|
|||
data(nullptr),
|
||||
allocationMetadataBuilder(nullptr),
|
||||
lastAnimationTime(0),
|
||||
regExps(runtime_),
|
||||
regExps(zone),
|
||||
globalWriteBarriered(0),
|
||||
detachedTypedObjects(0),
|
||||
objectMetadataState(ImmediateMetadata()),
|
||||
|
|
@ -210,6 +210,13 @@ JSCompartment::ensureJitCompartmentExists(JSContext* cx)
|
|||
}
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
|
||||
void
|
||||
js::DtoaCache::checkCacheAfterMovingGC()
|
||||
{
|
||||
MOZ_ASSERT(!s || !IsForwarded(s));
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CheckGCThingAfterMovingGCFunctor {
|
||||
template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
|
||||
|
|
@ -232,7 +239,8 @@ JSCompartment::checkWrapperMapAfterMovingGC()
|
|||
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // JSGC_HASH_TABLE_CHECKS
|
||||
|
||||
bool
|
||||
JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class DtoaCache {
|
|||
}
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); }
|
||||
void checkCacheAfterMovingGC();
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1114,9 +1114,9 @@ extern JS_FRIEND_API(unsigned)
|
|||
GetEnterCompartmentDepth(JSContext* cx);
|
||||
#endif
|
||||
|
||||
class RegExpGuard;
|
||||
extern JS_FRIEND_API(bool)
|
||||
RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared);
|
||||
RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp,
|
||||
JS::MutableHandle<RegExpShared*> shared);
|
||||
|
||||
/* Implemented in jswrapper.cpp. */
|
||||
typedef enum NukeReferencesToWindow {
|
||||
|
|
|
|||
|
|
@ -357,7 +357,12 @@ static const FinalizePhase BackgroundFinalizePhases[] = {
|
|||
},
|
||||
{
|
||||
gcstats::PHASE_SWEEP_SCOPE, {
|
||||
AllocKind::SCOPE
|
||||
AllocKind::SCOPE,
|
||||
}
|
||||
},
|
||||
{
|
||||
gcstats::PHASE_SWEEP_REGEXP_SHARED, {
|
||||
AllocKind::REGEXP_SHARED,
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1599,7 +1604,6 @@ static const AllocKind AllocKindsToRelocate[] = {
|
|||
AllocKind::OBJECT16_BACKGROUND,
|
||||
AllocKind::SCRIPT,
|
||||
AllocKind::LAZY_SCRIPT,
|
||||
AllocKind::SCOPE,
|
||||
AllocKind::SHAPE,
|
||||
AllocKind::ACCESSOR_SHAPE,
|
||||
AllocKind::BASE_SHAPE,
|
||||
|
|
@ -1607,7 +1611,9 @@ static const AllocKind AllocKindsToRelocate[] = {
|
|||
AllocKind::STRING,
|
||||
AllocKind::EXTERNAL_STRING,
|
||||
AllocKind::FAT_INLINE_ATOM,
|
||||
AllocKind::ATOM
|
||||
AllocKind::ATOM,
|
||||
AllocKind::SCOPE,
|
||||
AllocKind::REGEXP_SHARED
|
||||
};
|
||||
|
||||
Arena*
|
||||
|
|
@ -1931,61 +1937,23 @@ GCRuntime::relocateArenas(Zone* zone, JS::gcreason::Reason reason, Arena*& reloc
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MovingTracer::onObjectEdge(JSObject** objp)
|
||||
template <typename T>
|
||||
inline void
|
||||
MovingTracer::updateEdge(T** thingp)
|
||||
{
|
||||
JSObject* obj = *objp;
|
||||
if (obj->runtimeFromAnyThread() == runtime() && IsForwarded(obj))
|
||||
*objp = Forwarded(obj);
|
||||
auto thing = *thingp;
|
||||
if (thing->runtimeFromAnyThread() == runtime() && IsForwarded(thing))
|
||||
*thingp = Forwarded(thing);
|
||||
}
|
||||
|
||||
void
|
||||
MovingTracer::onShapeEdge(Shape** shapep)
|
||||
{
|
||||
Shape* shape = *shapep;
|
||||
if (shape->runtimeFromAnyThread() == runtime() && IsForwarded(shape))
|
||||
*shapep = Forwarded(shape);
|
||||
}
|
||||
|
||||
void
|
||||
MovingTracer::onStringEdge(JSString** stringp)
|
||||
{
|
||||
JSString* string = *stringp;
|
||||
if (string->runtimeFromAnyThread() == runtime() && IsForwarded(string))
|
||||
*stringp = Forwarded(string);
|
||||
}
|
||||
|
||||
void
|
||||
MovingTracer::onScriptEdge(JSScript** scriptp)
|
||||
{
|
||||
JSScript* script = *scriptp;
|
||||
if (script->runtimeFromAnyThread() == runtime() && IsForwarded(script))
|
||||
*scriptp = Forwarded(script);
|
||||
}
|
||||
|
||||
void
|
||||
MovingTracer::onLazyScriptEdge(LazyScript** lazyp)
|
||||
{
|
||||
LazyScript* lazy = *lazyp;
|
||||
if (lazy->runtimeFromAnyThread() == runtime() && IsForwarded(lazy))
|
||||
*lazyp = Forwarded(lazy);
|
||||
}
|
||||
|
||||
void
|
||||
MovingTracer::onBaseShapeEdge(BaseShape** basep)
|
||||
{
|
||||
BaseShape* base = *basep;
|
||||
if (base->runtimeFromAnyThread() == runtime() && IsForwarded(base))
|
||||
*basep = Forwarded(base);
|
||||
}
|
||||
|
||||
void
|
||||
MovingTracer::onScopeEdge(Scope** scopep)
|
||||
{
|
||||
Scope* scope = *scopep;
|
||||
if (scope->runtimeFromAnyThread() == runtime() && IsForwarded(scope))
|
||||
*scopep = Forwarded(scope);
|
||||
}
|
||||
void MovingTracer::onObjectEdge(JSObject** objp) { updateEdge(objp); }
|
||||
void MovingTracer::onShapeEdge(Shape** shapep) { updateEdge(shapep); }
|
||||
void MovingTracer::onStringEdge(JSString** stringp) { updateEdge(stringp); }
|
||||
void MovingTracer::onScriptEdge(JSScript** scriptp) { updateEdge(scriptp); }
|
||||
void MovingTracer::onLazyScriptEdge(LazyScript** lazyp) { updateEdge(lazyp); }
|
||||
void MovingTracer::onBaseShapeEdge(BaseShape** basep) { updateEdge(basep); }
|
||||
void MovingTracer::onScopeEdge(Scope** scopep) { updateEdge(scopep); }
|
||||
void MovingTracer::onRegExpSharedEdge(RegExpShared** sharedp) { updateEdge(sharedp); }
|
||||
|
||||
void
|
||||
Zone::prepareForCompacting()
|
||||
|
|
|
|||
100
js/src/jsgc.h
100
js/src/jsgc.h
|
|
@ -121,6 +121,7 @@ IsNurseryAllocable(AllocKind kind)
|
|||
false, /* AllocKind::SYMBOL */
|
||||
false, /* AllocKind::JITCODE */
|
||||
false, /* AllocKind::SCOPE */
|
||||
false, /* AllocKind::REGEXP_SHARED */
|
||||
};
|
||||
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
|
||||
return map[size_t(kind)];
|
||||
|
|
@ -159,6 +160,7 @@ IsBackgroundFinalized(AllocKind kind)
|
|||
true, /* AllocKind::SYMBOL */
|
||||
false, /* AllocKind::JITCODE */
|
||||
true, /* AllocKind::SCOPE */
|
||||
true, /* AllocKind::REGEXP_SHARED */
|
||||
};
|
||||
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
|
||||
return map[size_t(kind)];
|
||||
|
|
@ -1172,109 +1174,29 @@ class RelocationOverlay
|
|||
// to allow slots to be accessed.
|
||||
|
||||
template <typename T>
|
||||
struct MightBeForwarded
|
||||
{
|
||||
static_assert(mozilla::IsBaseOf<Cell, T>::value,
|
||||
"T must derive from Cell");
|
||||
static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
|
||||
"T must not be Cell or TenuredCell");
|
||||
|
||||
static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
|
||||
mozilla::IsBaseOf<Shape, T>::value ||
|
||||
mozilla::IsBaseOf<BaseShape, T>::value ||
|
||||
mozilla::IsBaseOf<JSString, T>::value ||
|
||||
mozilla::IsBaseOf<JSScript, T>::value ||
|
||||
mozilla::IsBaseOf<js::LazyScript, T>::value ||
|
||||
mozilla::IsBaseOf<js::Scope, T>::value;
|
||||
};
|
||||
inline bool IsForwarded(T* t);
|
||||
inline bool IsForwarded(const JS::Value& value);
|
||||
|
||||
template <typename T>
|
||||
inline bool
|
||||
IsForwarded(T* t)
|
||||
{
|
||||
RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
|
||||
if (!MightBeForwarded<T>::value) {
|
||||
MOZ_ASSERT(!overlay->isForwarded());
|
||||
return false;
|
||||
}
|
||||
inline T* Forwarded(T* t);
|
||||
|
||||
return overlay->isForwarded();
|
||||
}
|
||||
|
||||
struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
|
||||
template <typename T> bool operator()(T* t) { return IsForwarded(t); }
|
||||
};
|
||||
|
||||
inline bool
|
||||
IsForwarded(const JS::Value& value)
|
||||
{
|
||||
return DispatchTyped(IsForwardedFunctor(), value);
|
||||
}
|
||||
inline Value Forwarded(const JS::Value& value);
|
||||
|
||||
template <typename T>
|
||||
inline T*
|
||||
Forwarded(T* t)
|
||||
{
|
||||
RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
|
||||
MOZ_ASSERT(overlay->isForwarded());
|
||||
return reinterpret_cast<T*>(overlay->forwardingAddress());
|
||||
}
|
||||
|
||||
struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
|
||||
template <typename T> inline Value operator()(T* t) {
|
||||
return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
|
||||
}
|
||||
};
|
||||
|
||||
inline Value
|
||||
Forwarded(const JS::Value& value)
|
||||
{
|
||||
return DispatchTyped(ForwardedFunctor(), value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T
|
||||
MaybeForwarded(T t)
|
||||
{
|
||||
if (IsForwarded(t))
|
||||
t = Forwarded(t);
|
||||
MakeAccessibleAfterMovingGC(t);
|
||||
return t;
|
||||
}
|
||||
inline T MaybeForwarded(T t);
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
|
||||
template <typename T>
|
||||
inline bool
|
||||
IsGCThingValidAfterMovingGC(T* t)
|
||||
{
|
||||
return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
|
||||
}
|
||||
inline bool IsGCThingValidAfterMovingGC(T* t);
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
CheckGCThingAfterMovingGC(T* t)
|
||||
{
|
||||
if (t)
|
||||
MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
|
||||
}
|
||||
inline void CheckGCThingAfterMovingGC(T* t);
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
|
||||
{
|
||||
CheckGCThingAfterMovingGC(t.unbarrieredGet());
|
||||
}
|
||||
inline void CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t);
|
||||
|
||||
struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
|
||||
template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
|
||||
};
|
||||
|
||||
inline void
|
||||
CheckValueAfterMovingGC(const JS::Value& value)
|
||||
{
|
||||
DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
|
||||
}
|
||||
inline void CheckValueAfterMovingGC(const JS::Value& value);
|
||||
|
||||
#endif // JSGC_HASH_TABLE_CHECKS
|
||||
|
||||
|
|
|
|||
|
|
@ -477,6 +477,114 @@ RelocationOverlay::forwardTo(Cell* cell)
|
|||
newLocation_ = cell;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct MightBeForwarded
|
||||
{
|
||||
static_assert(mozilla::IsBaseOf<Cell, T>::value,
|
||||
"T must derive from Cell");
|
||||
static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
|
||||
"T must not be Cell or TenuredCell");
|
||||
|
||||
static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
|
||||
mozilla::IsBaseOf<Shape, T>::value ||
|
||||
mozilla::IsBaseOf<BaseShape, T>::value ||
|
||||
mozilla::IsBaseOf<JSString, T>::value ||
|
||||
mozilla::IsBaseOf<JSScript, T>::value ||
|
||||
mozilla::IsBaseOf<js::LazyScript, T>::value ||
|
||||
mozilla::IsBaseOf<js::Scope, T>::value ||
|
||||
mozilla::IsBaseOf<js::RegExpShared, T>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline bool
|
||||
IsForwarded(T* t)
|
||||
{
|
||||
RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
|
||||
if (!MightBeForwarded<T>::value) {
|
||||
MOZ_ASSERT(!overlay->isForwarded());
|
||||
return false;
|
||||
}
|
||||
|
||||
return overlay->isForwarded();
|
||||
}
|
||||
|
||||
struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
|
||||
template <typename T> bool operator()(T* t) { return IsForwarded(t); }
|
||||
};
|
||||
|
||||
inline bool
|
||||
IsForwarded(const JS::Value& value)
|
||||
{
|
||||
return DispatchTyped(IsForwardedFunctor(), value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T*
|
||||
Forwarded(T* t)
|
||||
{
|
||||
RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
|
||||
MOZ_ASSERT(overlay->isForwarded());
|
||||
return reinterpret_cast<T*>(overlay->forwardingAddress());
|
||||
}
|
||||
|
||||
struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
|
||||
template <typename T> inline Value operator()(T* t) {
|
||||
return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
|
||||
}
|
||||
};
|
||||
|
||||
inline Value
|
||||
Forwarded(const JS::Value& value)
|
||||
{
|
||||
return DispatchTyped(ForwardedFunctor(), value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T
|
||||
MaybeForwarded(T t)
|
||||
{
|
||||
if (IsForwarded(t))
|
||||
t = Forwarded(t);
|
||||
MakeAccessibleAfterMovingGC(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
|
||||
template <typename T>
|
||||
inline bool
|
||||
IsGCThingValidAfterMovingGC(T* t)
|
||||
{
|
||||
return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
CheckGCThingAfterMovingGC(T* t)
|
||||
{
|
||||
if (t)
|
||||
MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
|
||||
{
|
||||
CheckGCThingAfterMovingGC(t.unbarrieredGet());
|
||||
}
|
||||
|
||||
struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
|
||||
template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
|
||||
};
|
||||
|
||||
inline void
|
||||
CheckValueAfterMovingGC(const JS::Value& value)
|
||||
{
|
||||
DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
|
||||
}
|
||||
|
||||
#endif // JSGC_HASH_TABLE_CHECKS
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
|||
|
|
@ -566,6 +566,12 @@ IsNativeFunction(const js::Value& v, JSNative native)
|
|||
return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const JSObject* obj, JSNative native)
|
||||
{
|
||||
return obj->is<JSFunction>() && obj->as<JSFunction>().maybeNative() == native;
|
||||
}
|
||||
|
||||
// Return whether looking up a method on 'obj' definitely resolves to the
|
||||
// original specified native function. The method may conservatively return
|
||||
// 'false' in the case of proxies or other non-native objects.
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ class JS_FRIEND_API(Wrapper) : public BaseProxyHandler
|
|||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||
bool isToSource) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
RegExpGuard* g) const override;
|
||||
MutableHandle<RegExpShared*> shared) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleValue vp) const override;
|
||||
virtual bool isCallable(JSObject* obj) const override;
|
||||
|
|
@ -211,7 +211,8 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
|
|||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
|
||||
bool isToSource) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<RegExpShared*> shared) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
||||
|
||||
// Allocate CrossCompartmentWrappers in the nursery.
|
||||
|
|
@ -312,7 +313,8 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
|
|||
const CallArgs& args) const override;
|
||||
virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<RegExpShared*> shared) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSourc
|
|||
|
||||
bool
|
||||
BaseProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
RegExpGuard* g) const
|
||||
MutableHandleRegExpShared shared) const
|
||||
{
|
||||
MOZ_CRASH("This should have been a wrapped regexp");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,18 +437,19 @@ CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, bool
|
|||
}
|
||||
|
||||
bool
|
||||
CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, RegExpGuard* g) const
|
||||
CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper,
|
||||
MutableHandleRegExpShared shared) const
|
||||
{
|
||||
RegExpGuard wrapperGuard(cx);
|
||||
RootedRegExpShared re(cx);
|
||||
{
|
||||
AutoCompartment call(cx, wrappedObject(wrapper));
|
||||
if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard))
|
||||
if (!Wrapper::regexp_toShared(cx, wrapper, &re))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get an equivalent RegExpShared associated with the current compartment.
|
||||
RegExpShared* re = wrapperGuard.re();
|
||||
return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g);
|
||||
RootedAtom source(cx, re->getSource());
|
||||
return cx->compartment()->regExps.get(cx, source, re->getFlags(), shared);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -142,7 +142,8 @@ DeadObjectProxy::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource
|
|||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
|
||||
DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<RegExpShared*> shared) const
|
||||
{
|
||||
ReportDead(cx);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ class DeadObjectProxy : public BaseProxyHandler
|
|||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||
bool isToSource) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<RegExpShared*> shared) const override;
|
||||
|
||||
virtual bool isCallable(JSObject* obj) const override;
|
||||
virtual bool isConstructor(JSObject* obj) const override;
|
||||
|
|
|
|||
|
|
@ -488,10 +488,10 @@ Proxy::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource)
|
|||
}
|
||||
|
||||
bool
|
||||
Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
|
||||
Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
|
||||
return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, shared);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
class RegExpGuard;
|
||||
|
||||
/*
|
||||
* Dispatch point for handlers that executes the appropriate C++ or scripted traps.
|
||||
*
|
||||
|
|
@ -61,7 +59,8 @@ class Proxy
|
|||
static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
|
||||
static const char* className(JSContext* cx, HandleObject proxy);
|
||||
static JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource);
|
||||
static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
|
||||
static bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandle<RegExpShared*> shared);
|
||||
static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
|
||||
|
||||
static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,
|
||||
|
|
|
|||
|
|
@ -1265,7 +1265,8 @@ ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToS
|
|||
}
|
||||
|
||||
bool
|
||||
ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
|
||||
ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleRegExpShared shared) const
|
||||
{
|
||||
MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class ScriptedProxyHandler : public BaseProxyHandler
|
|||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||
bool isToSource) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
RegExpGuard* g) const override;
|
||||
MutableHandle<RegExpShared*> shared) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleValue vp) const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -93,9 +93,10 @@ SecurityWrapper<Base>::isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswe
|
|||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj, RegExpGuard* g) const
|
||||
SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj,
|
||||
MutableHandle<RegExpShared*> shared) const
|
||||
{
|
||||
return Base::regexp_toShared(cx, obj, g);
|
||||
return Base::regexp_toShared(cx, obj, shared);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
|
|
|
|||
|
|
@ -267,10 +267,10 @@ Wrapper::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
|
|||
}
|
||||
|
||||
bool
|
||||
Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
|
||||
Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return RegExpToShared(cx, target, g);
|
||||
return RegExpToShared(cx, target, shared);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell) -- needs setPromiseRejectionTrackerCallback
|
||||
|
||||
const UNHANDLED = 0;
|
||||
const HANDLED = 1;
|
||||
|
||||
let rejections = new Map();
|
||||
function rejectionTracker(promise, state) {
|
||||
rejections.set(promise, state);
|
||||
}
|
||||
setPromiseRejectionTrackerCallback(rejectionTracker);
|
||||
|
||||
// If the return value of then is not used, the promise object is optimized
|
||||
// away, but if a rejection happens, the rejection should be notified.
|
||||
Promise.resolve().then(() => { throw 1; });
|
||||
drainJobQueue();
|
||||
|
||||
assertEq(rejections.size, 1);
|
||||
|
||||
let [[promise, state]] = rejections;
|
||||
assertEq(state, UNHANDLED);
|
||||
|
||||
let exc;
|
||||
promise.catch(x => { exc = x; });
|
||||
drainJobQueue();
|
||||
|
||||
// we handled it after all
|
||||
assertEq(rejections.get(promise), HANDLED);
|
||||
|
||||
// the right exception was reported
|
||||
assertEq(exc, 1);
|
||||
|
||||
if (this.reportCompare) {
|
||||
reportCompare(true,true);
|
||||
}
|
||||
|
|
@ -319,7 +319,7 @@ AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue
|
|||
/* static */ AsyncGeneratorRequest*
|
||||
AsyncGeneratorObject::createRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
|
||||
CompletionKind completionKind, HandleValue completionValue,
|
||||
HandleObject promise)
|
||||
Handle<PromiseObject*> promise)
|
||||
{
|
||||
if (!asyncGenObj->hasCachedRequest())
|
||||
return AsyncGeneratorRequest::create(cx, completionKind, completionValue, promise);
|
||||
|
|
@ -444,7 +444,7 @@ const Class AsyncGeneratorRequest::class_ = {
|
|||
// Async Iteration proposal 11.4.3.1.
|
||||
/* static */ AsyncGeneratorRequest*
|
||||
AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind,
|
||||
HandleValue completionValue, HandleObject promise)
|
||||
HandleValue completionValue, Handle<PromiseObject*> promise)
|
||||
{
|
||||
RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr));
|
||||
if (!obj)
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ class AsyncGeneratorRequest : public NativeObject
|
|||
Slots,
|
||||
};
|
||||
|
||||
void init(CompletionKind completionKind, HandleValue completionValue,
|
||||
HandleObject promise) {
|
||||
void init(CompletionKind completionKind, const Value& completionValue, PromiseObject* promise) {
|
||||
setFixedSlot(Slot_CompletionKind,
|
||||
Int32Value(static_cast<int32_t>(completionKind)));
|
||||
setFixedSlot(Slot_CompletionValue, completionValue);
|
||||
|
|
@ -81,7 +80,8 @@ class AsyncGeneratorRequest : public NativeObject
|
|||
static const Class class_;
|
||||
|
||||
static AsyncGeneratorRequest* create(JSContext* cx, CompletionKind completionKind,
|
||||
HandleValue completionValue, HandleObject promise);
|
||||
HandleValue completionValue,
|
||||
Handle<PromiseObject*> promise);
|
||||
|
||||
CompletionKind completionKind() const {
|
||||
return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32());
|
||||
|
|
@ -89,8 +89,8 @@ class AsyncGeneratorRequest : public NativeObject
|
|||
JS::Value completionValue() const {
|
||||
return getFixedSlot(Slot_CompletionValue);
|
||||
}
|
||||
JSObject* promise() const {
|
||||
return &getFixedSlot(Slot_Promise).toObject();
|
||||
PromiseObject* promise() const {
|
||||
return &getFixedSlot(Slot_Promise).toObject().as<PromiseObject>();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ class AsyncGeneratorObject : public NativeObject
|
|||
Handle<AsyncGeneratorObject*> asyncGenObj,
|
||||
CompletionKind completionKind,
|
||||
HandleValue completionValue,
|
||||
HandleObject promise);
|
||||
Handle<PromiseObject*> promise);
|
||||
|
||||
// Stores the given request to the generator's cache after clearing its data
|
||||
// slots. The cached request will be reused in the subsequent createRequest
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame
|
|||
if (genObj->hasExpressionStack()) {
|
||||
MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() == 0);
|
||||
auto result = SetOrExtendAnyBoxedOrUnboxedDenseElements(cx,
|
||||
&genObj->expressionStack().as<JSObject>(),
|
||||
0, vp, nvalues, ShouldUpdateTypes::DontUpdate);
|
||||
&genObj->expressionStack(), 0, vp, nvalues,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
if (result == DenseElementResult::Success) {
|
||||
MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() == nvalues);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -595,6 +595,13 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin
|
|||
break;
|
||||
}
|
||||
|
||||
case JS::TraceKind::RegExpShared: {
|
||||
auto regexp = static_cast<RegExpShared*>(thing);
|
||||
zStats->regExpSharedsGCHeap += thingSize;
|
||||
zStats->regExpSharedsMallocHeap += regexp->sizeOfExcludingThis(rtStats->mallocSizeOf_);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("invalid traceKind in StatsCellCallback");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,19 @@ NativeObject::clearShouldConvertDoubleElements()
|
|||
}
|
||||
|
||||
inline void
|
||||
NativeObject::setDenseElementWithType(ExclusiveContext* cx, uint32_t index, const Value& val)
|
||||
NativeObject::addDenseElementType(ExclusiveContext* cx, uint32_t index, const Value& val)
|
||||
{
|
||||
// Avoid a slow AddTypePropertyId call if the type is the same as the type
|
||||
// of the previous element.
|
||||
TypeSet::Type thisType = TypeSet::GetValueType(val);
|
||||
if (index == 0 || TypeSet::GetValueType(elements_[index - 1]) != thisType)
|
||||
AddTypePropertyId(cx, this, JSID_VOID, thisType);
|
||||
}
|
||||
|
||||
inline void
|
||||
NativeObject::setDenseElementWithType(ExclusiveContext* cx, uint32_t index, const Value& val)
|
||||
{
|
||||
addDenseElementType(cx, index, val);
|
||||
setDenseElementMaybeConvertDouble(index, val);
|
||||
}
|
||||
|
||||
|
|
@ -78,10 +84,9 @@ inline void
|
|||
NativeObject::initDenseElementWithType(ExclusiveContext* cx, uint32_t index, const Value& val)
|
||||
{
|
||||
MOZ_ASSERT(!shouldConvertDoubleElements());
|
||||
if (val.isMagic(JS_ELEMENTS_HOLE))
|
||||
markDenseElementsNotPacked(cx);
|
||||
else
|
||||
AddTypePropertyId(cx, this, JSID_VOID, val);
|
||||
MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
|
||||
|
||||
addDenseElementType(cx, index, val);
|
||||
initDenseElement(index, val);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1696,7 +1696,6 @@ js::NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj, HandleId
|
|||
// Off-thread callers should not get here: they must call this
|
||||
// function only with known-valid arguments. Populating a new
|
||||
// PlainObject with configurable properties is fine.
|
||||
MOZ_ASSERT(!cx->isHelperThreadContext());
|
||||
result.reportError(cx, obj, id);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1076,6 +1076,10 @@ class NativeObject : public ShapedObject
|
|||
setDenseElement(index, val);
|
||||
}
|
||||
|
||||
private:
|
||||
inline void addDenseElementType(ExclusiveContext* cx, uint32_t index, const Value& val);
|
||||
|
||||
public:
|
||||
inline void setDenseElementWithType(ExclusiveContext* cx, uint32_t index,
|
||||
const Value& val);
|
||||
inline void initDenseElementWithType(ExclusiveContext* cx, uint32_t index,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
#include "jshashutil.h"
|
||||
#include "jsstr.h"
|
||||
#ifdef DEBUG
|
||||
#include "jsutil.h"
|
||||
|
|
@ -120,29 +121,16 @@ VectorMatchPairs::allocOrExpandArray(size_t pairCount)
|
|||
|
||||
/* RegExpObject */
|
||||
|
||||
static inline void
|
||||
RegExpSharedReadBarrier(JSContext* cx, RegExpShared* shared)
|
||||
{
|
||||
Zone* zone = cx->zone();
|
||||
if (zone->needsIncrementalBarrier())
|
||||
shared->trace(zone->barrierTracer());
|
||||
if (shared->isMarkedGray())
|
||||
shared->unmarkGray();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
|
||||
RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp,
|
||||
MutableHandleRegExpShared shared)
|
||||
{
|
||||
if (RegExpShared* shared = regexp->maybeShared()) {
|
||||
// Fetching a RegExpShared from an object requires a read
|
||||
// barrier, as the shared pointer might be weak.
|
||||
RegExpSharedReadBarrier(cx, shared);
|
||||
|
||||
g->init(*shared);
|
||||
if (regexp->hasShared()) {
|
||||
shared.set(regexp->sharedRef());
|
||||
return true;
|
||||
}
|
||||
|
||||
return createShared(cx, regexp, g);
|
||||
return createShared(cx, regexp, shared);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
|
@ -179,26 +167,32 @@ RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask)
|
|||
/* static */ void
|
||||
RegExpObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
RegExpShared* shared = obj->as<RegExpObject>().maybeShared();
|
||||
if (!shared)
|
||||
return;
|
||||
obj->as<RegExpObject>().trace(trc);
|
||||
}
|
||||
|
||||
// When tracing through the object normally, we have the option of
|
||||
// unlinking the object from its RegExpShared so that the RegExpShared may
|
||||
// be collected. To detect this we need to test all the following
|
||||
// conditions, since:
|
||||
static inline bool
|
||||
IsMarkingTrace(JSTracer* trc)
|
||||
{
|
||||
// Determine whether tracing is happening during normal marking. We need to
|
||||
// test all the following conditions, since:
|
||||
//
|
||||
// 1. During TraceRuntime, isHeapBusy() is true, but the tracer might not
|
||||
// be a marking tracer.
|
||||
// 2. When a write barrier executes, IsMarkingTracer is true, but
|
||||
// isHeapBusy() will be false.
|
||||
if (trc->runtime()->isHeapCollecting() &&
|
||||
trc->isMarkingTracer() &&
|
||||
!obj->asTenured().zone()->isPreservingCode())
|
||||
{
|
||||
obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
|
||||
} else {
|
||||
shared->trace(trc);
|
||||
}
|
||||
|
||||
return trc->runtime()->isHeapCollecting() && trc->isMarkingTracer();
|
||||
}
|
||||
|
||||
void
|
||||
RegExpObject::trace(JSTracer* trc)
|
||||
{
|
||||
// When marking the object normally we have the option of unlinking the
|
||||
// object from its RegExpShared so that the RegExpShared may be collected.
|
||||
if (IsMarkingTrace(trc) && !zone()->isPreservingCode())
|
||||
sharedRef() = nullptr;
|
||||
|
||||
TraceNullableEdge(trc, &sharedRef(), "RegExpObject shared");
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
|
|
@ -285,13 +279,15 @@ RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags,
|
|||
}
|
||||
|
||||
/* static */ bool
|
||||
RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
|
||||
RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp,
|
||||
MutableHandleRegExpShared shared)
|
||||
{
|
||||
MOZ_ASSERT(!regexp->maybeShared());
|
||||
if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g))
|
||||
MOZ_ASSERT(!regexp->hasShared());
|
||||
RootedAtom source(cx, regexp->getSource());
|
||||
if (!cx->compartment()->regExps.get(cx, source, regexp->getFlags(), shared))
|
||||
return false;
|
||||
|
||||
regexp->setShared(**g);
|
||||
regexp->setShared(*shared);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -512,14 +508,15 @@ RegExpObject::toString(JSContext* cx) const
|
|||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input)
|
||||
/* static */ bool
|
||||
RegExpShared::dumpBytecode(JSContext* cx, MutableHandleRegExpShared re, bool match_only,
|
||||
HandleLinearString input)
|
||||
{
|
||||
CompilationMode mode = match_only ? MatchOnly : Normal;
|
||||
if (!compileIfNecessary(cx, input, mode, ForceByteCode))
|
||||
if (!RegExpShared::compileIfNecessary(cx, re, input, mode, ForceByteCode))
|
||||
return false;
|
||||
|
||||
const uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
|
||||
const uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
|
||||
const uint8_t* pc = byteCode;
|
||||
|
||||
auto Load32Aligned = [](const uint8_t* pc) -> int32_t {
|
||||
|
|
@ -898,11 +895,11 @@ RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString in
|
|||
RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
|
||||
bool match_only, HandleLinearString input)
|
||||
{
|
||||
RegExpGuard g(cx);
|
||||
if (!getShared(cx, regexp, &g))
|
||||
RootedRegExpShared shared(cx);
|
||||
if (!getShared(cx, regexp, &shared))
|
||||
return false;
|
||||
|
||||
return g.re()->dumpBytecode(cx, match_only, input);
|
||||
return RegExpShared::dumpBytecode(cx, &shared, match_only, input);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -951,21 +948,16 @@ js::StringHasRegExpMetaChars(JSLinearString* str)
|
|||
/* RegExpShared */
|
||||
|
||||
RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags)
|
||||
: source(source), flags(flags), parenCount(0), canStringMatch(false), marked_(false),
|
||||
: source(source), flags(flags), canStringMatch(false), parenCount(0),
|
||||
numNamedCaptures_(0), groupsTemplate_(nullptr)
|
||||
{}
|
||||
|
||||
RegExpShared::~RegExpShared()
|
||||
{
|
||||
for (size_t i = 0; i < tables.length(); i++)
|
||||
js_delete(tables[i]);
|
||||
}
|
||||
|
||||
void
|
||||
RegExpShared::trace(JSTracer* trc)
|
||||
RegExpShared::traceChildren(JSTracer* trc)
|
||||
{
|
||||
if (trc->isMarkingTracer())
|
||||
marked_ = true;
|
||||
// Discard code to avoid holding onto ExecutablePools.
|
||||
if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC())
|
||||
discardJitCode();
|
||||
|
||||
TraceNullableEdge(trc, &source, "RegExpShared source");
|
||||
for (auto& comp : compilationArray)
|
||||
|
|
@ -973,44 +965,40 @@ RegExpShared::trace(JSTracer* trc)
|
|||
TraceNullableEdge(trc, &groupsTemplate_, "RegExpShared groupsTemplate");
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::isMarkedGray() const
|
||||
void
|
||||
RegExpShared::discardJitCode()
|
||||
{
|
||||
if (source && source->isMarked(gc::GRAY))
|
||||
return true;
|
||||
for (const auto& comp : compilationArray) {
|
||||
if (comp.jitCode && comp.jitCode->isMarked(gc::GRAY))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
for (auto& comp : compilationArray)
|
||||
comp.jitCode = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
RegExpShared::unmarkGray()
|
||||
RegExpShared::finalize(FreeOp* fop)
|
||||
{
|
||||
if (source)
|
||||
JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(source));
|
||||
for (const auto& comp : compilationArray) {
|
||||
if (comp.jitCode)
|
||||
JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(comp.jitCode.get()));
|
||||
}
|
||||
for (auto& comp : compilationArray)
|
||||
js_free(comp.byteCode);
|
||||
for (size_t i = 0; i < tables.length(); i++)
|
||||
js_free(tables[i]);
|
||||
tables.~JitCodeTables();
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::compile(JSContext* cx, HandleLinearString input,
|
||||
/* static */ bool
|
||||
RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force)
|
||||
{
|
||||
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
|
||||
AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
|
||||
|
||||
RootedAtom pattern(cx, source);
|
||||
return compile(cx, pattern, input, mode, force);
|
||||
RootedAtom pattern(cx, re->source);
|
||||
return compile(cx, re, pattern, input, mode, force);
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVector* names, irregexp::IntegerVector* indices)
|
||||
/* static */ bool
|
||||
RegExpShared::initializeNamedCaptures(JSContext* cx, HandleRegExpShared re,
|
||||
irregexp::CharacterVectorVector* names,
|
||||
irregexp::IntegerVector* indices)
|
||||
{
|
||||
MOZ_ASSERT(!groupsTemplate_);
|
||||
MOZ_ASSERT(!re->groupsTemplate_);
|
||||
MOZ_ASSERT(names);
|
||||
MOZ_ASSERT(indices);
|
||||
MOZ_ASSERT(names->length() == indices->length());
|
||||
|
|
@ -1052,17 +1040,17 @@ RegExpShared::initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVe
|
|||
AddTypePropertyId(cx, templateObject, id, TypeSet::Int32Type());
|
||||
}
|
||||
|
||||
groupsTemplate_ = templateObject;
|
||||
numNamedCaptures_ = numNamedCaptures;
|
||||
re->groupsTemplate_ = templateObject;
|
||||
re->numNamedCaptures_ = numNamedCaptures;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force)
|
||||
/* static */ bool
|
||||
RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleAtom pattern,
|
||||
HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force)
|
||||
{
|
||||
if (!ignoreCase() && !StringHasRegExpMetaChars(pattern))
|
||||
canStringMatch = true;
|
||||
if (!re->ignoreCase() && !StringHasRegExpMetaChars(pattern))
|
||||
re->canStringMatch = true;
|
||||
|
||||
CompileOptions options(cx);
|
||||
TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
|
||||
|
|
@ -1072,34 +1060,36 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu
|
|||
/* Parse the pattern. */
|
||||
irregexp::RegExpCompileData data;
|
||||
if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
|
||||
multiline(), mode == MatchOnly, unicode(), ignoreCase(),
|
||||
global(), sticky(), dotAll(), &data))
|
||||
re->multiline(), mode == MatchOnly, re->unicode(),
|
||||
re->ignoreCase(), re->global(), re->sticky(),
|
||||
re->dotAll(), &data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->parenCount = data.capture_count;
|
||||
re->parenCount = data.capture_count;
|
||||
if (data.capture_name_list) {
|
||||
// convert LifoAlloc'd named capture info to NativeObject
|
||||
if (!initializeNamedCaptures(cx, data.capture_name_list, data.capture_index_list)) {
|
||||
if (!initializeNamedCaptures(cx, re, data.capture_name_list, data.capture_index_list)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
|
||||
irregexp::RegExpCode code = irregexp::CompilePattern(cx, re, &data, input,
|
||||
false /* global() */,
|
||||
ignoreCase(),
|
||||
re->ignoreCase(),
|
||||
input->hasLatin1Chars(),
|
||||
mode == MatchOnly,
|
||||
force == ForceByteCode,
|
||||
sticky(), unicode());
|
||||
re->sticky(),
|
||||
re->unicode());
|
||||
if (code.empty())
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(!code.jitCode || !code.byteCode);
|
||||
MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode);
|
||||
|
||||
RegExpCompilation& compilation = this->compilation(mode, input->hasLatin1Chars());
|
||||
RegExpCompilation& compilation = re->compilation(mode, input->hasLatin1Chars());
|
||||
if (code.jitCode)
|
||||
compilation.jitCode = code.jitCode;
|
||||
else if (code.byteCode)
|
||||
|
|
@ -1108,18 +1098,19 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force)
|
||||
/* static */ bool
|
||||
RegExpShared::compileIfNecessary(JSContext* cx, MutableHandleRegExpShared re,
|
||||
HandleLinearString input, CompilationMode mode,
|
||||
ForceByteCodeEnum force)
|
||||
{
|
||||
if (isCompiled(mode, input->hasLatin1Chars(), force))
|
||||
if (re->isCompiled(mode, input->hasLatin1Chars(), force))
|
||||
return true;
|
||||
return compile(cx, input, mode, force);
|
||||
return compile(cx, re, input, mode, force);
|
||||
}
|
||||
|
||||
RegExpRunStatus
|
||||
RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
|
||||
MatchPairs* matches, size_t* endIndex)
|
||||
/* static */ RegExpRunStatus
|
||||
RegExpShared::execute(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
|
||||
size_t start, MatchPairs* matches, size_t* endIndex)
|
||||
{
|
||||
MOZ_ASSERT_IF(matches, !endIndex);
|
||||
MOZ_ASSERT_IF(!matches, endIndex);
|
||||
|
|
@ -1128,14 +1119,14 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
|
|||
CompilationMode mode = matches ? Normal : MatchOnly;
|
||||
|
||||
/* Compile the code at point-of-use. */
|
||||
if (!compileIfNecessary(cx, input, mode, DontForceByteCode))
|
||||
if (!compileIfNecessary(cx, re, input, mode, DontForceByteCode))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
/*
|
||||
* Ensure sufficient memory for output vector.
|
||||
* No need to initialize it. The RegExp engine fills them in on a match.
|
||||
*/
|
||||
if (matches && !matches->allocOrExpandArray(pairCount())) {
|
||||
if (matches && !matches->allocOrExpandArray(re->pairCount())) {
|
||||
ReportOutOfMemory(cx);
|
||||
return RegExpRunStatus_Error;
|
||||
}
|
||||
|
|
@ -1145,14 +1136,14 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
|
|||
// Reset the Irregexp backtrack stack if it grows during execution.
|
||||
irregexp::RegExpStackScope stackScope(cx->runtime());
|
||||
|
||||
if (canStringMatch) {
|
||||
MOZ_ASSERT(pairCount() == 1);
|
||||
size_t sourceLength = source->length();
|
||||
if (sticky()) {
|
||||
if (re->canStringMatch) {
|
||||
MOZ_ASSERT(re->pairCount() == 1);
|
||||
size_t sourceLength = re->source->length();
|
||||
if (re->sticky()) {
|
||||
// First part checks size_t overflow.
|
||||
if (sourceLength + start < sourceLength || sourceLength + start > length)
|
||||
return RegExpRunStatus_Success_NotFound;
|
||||
if (!HasSubstringAt(input, source, start))
|
||||
if (!HasSubstringAt(input, re->source, start))
|
||||
return RegExpRunStatus_Success_NotFound;
|
||||
|
||||
if (matches) {
|
||||
|
|
@ -1166,7 +1157,7 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
|
|||
return RegExpRunStatus_Success;
|
||||
}
|
||||
|
||||
int res = StringFindPattern(input, source, start);
|
||||
int res = StringFindPattern(input, re->source, start);
|
||||
if (res == -1)
|
||||
return RegExpRunStatus_Success_NotFound;
|
||||
|
||||
|
|
@ -1182,7 +1173,7 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
|
|||
}
|
||||
|
||||
do {
|
||||
jit::JitCode* code = compilation(mode, input->hasLatin1Chars()).jitCode;
|
||||
jit::JitCode* code = re->compilation(mode, input->hasLatin1Chars()).jitCode;
|
||||
if (!code)
|
||||
break;
|
||||
|
||||
|
|
@ -1221,10 +1212,10 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
|
|||
} while (false);
|
||||
|
||||
// Compile bytecode for the RegExp if necessary.
|
||||
if (!compileIfNecessary(cx, input, mode, ForceByteCode))
|
||||
if (!compileIfNecessary(cx, re, input, mode, ForceByteCode))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
|
||||
uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
|
||||
AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
|
||||
|
||||
AutoStableStringChars inputChars(cx);
|
||||
|
|
@ -1246,9 +1237,9 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
|
|||
}
|
||||
|
||||
size_t
|
||||
RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
size_t n = mallocSizeOf(this);
|
||||
size_t n = 0;
|
||||
|
||||
for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
|
||||
const RegExpCompilation& compilation = compilationArray[i];
|
||||
|
|
@ -1265,8 +1256,8 @@ RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
|||
|
||||
/* RegExpCompartment */
|
||||
|
||||
RegExpCompartment::RegExpCompartment(JSRuntime* rt)
|
||||
: set_(rt),
|
||||
RegExpCompartment::RegExpCompartment(Zone* zone)
|
||||
: set_(zone, Set(zone->runtimeFromMainThread())),
|
||||
matchResultTemplateObject_(nullptr),
|
||||
optimizableRegExpPrototypeShape_(nullptr),
|
||||
optimizableRegExpInstanceShape_(nullptr)
|
||||
|
|
@ -1274,14 +1265,7 @@ RegExpCompartment::RegExpCompartment(JSRuntime* rt)
|
|||
|
||||
RegExpCompartment::~RegExpCompartment()
|
||||
{
|
||||
// Because of stray mark bits being set (see RegExpCompartment::sweep)
|
||||
// there might still be RegExpShared instances which haven't been deleted.
|
||||
if (set_.initialized()) {
|
||||
for (Set::Enum e(set_); !e.empty(); e.popFront()) {
|
||||
RegExpShared* shared = e.front();
|
||||
js_delete(shared);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT_IF(set_.initialized(), set_.empty());
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
|
|
@ -1291,7 +1275,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
|
|||
|
||||
/* Create template array object */
|
||||
RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount,
|
||||
nullptr, TenuredObject));
|
||||
nullptr, TenuredObject));
|
||||
if (!templateObject)
|
||||
return matchResultTemplateObject_; // = nullptr
|
||||
|
||||
|
|
@ -1357,59 +1341,9 @@ RegExpCompartment::init(JSContext* cx)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::needsSweep(JSRuntime* rt)
|
||||
{
|
||||
// Sometimes RegExpShared instances are marked without the compartment
|
||||
// being subsequently cleared. This can happen if a GC is restarted while
|
||||
// in progress (i.e. performing a full GC in the middle of an incremental
|
||||
// GC) or if a RegExpShared referenced via the stack is traced but is not
|
||||
// in a zone being collected.
|
||||
//
|
||||
// Because of this we only treat the marked_ bit as a hint, and destroy the
|
||||
// RegExpShared if it was accidentally marked earlier but wasn't marked by
|
||||
// the current trace.
|
||||
bool keep = marked() && IsMarked(rt, &source);
|
||||
for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
|
||||
RegExpShared::RegExpCompilation& compilation = compilationArray[i];
|
||||
if (compilation.jitCode && gc::IsAboutToBeFinalized(&compilation.jitCode))
|
||||
keep = false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(rt->isHeapMajorCollecting());
|
||||
if (keep || rt->gc.isHeapCompacting()) {
|
||||
clearMarked();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RegExpShared::discardJitCode()
|
||||
{
|
||||
for (size_t i = 0; i < ArrayLength(compilationArray); i++)
|
||||
compilationArray[i].jitCode = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
RegExpCompartment::sweep(JSRuntime* rt)
|
||||
{
|
||||
if (!set_.initialized())
|
||||
return;
|
||||
|
||||
for (Set::Enum e(set_); !e.empty(); e.popFront()) {
|
||||
RegExpShared* shared = e.front();
|
||||
if (shared->needsSweep(rt)) {
|
||||
js_delete(shared);
|
||||
e.removeFront();
|
||||
} else {
|
||||
// Discard code to avoid holding onto ExecutablePools.
|
||||
if (rt->gc.isHeapCompacting())
|
||||
shared->discardJitCode();
|
||||
}
|
||||
}
|
||||
|
||||
if (matchResultTemplateObject_ &&
|
||||
IsAboutToBeFinalized(&matchResultTemplateObject_))
|
||||
{
|
||||
|
|
@ -1430,55 +1364,45 @@ RegExpCompartment::sweep(JSRuntime* rt)
|
|||
}
|
||||
|
||||
bool
|
||||
RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
|
||||
RegExpCompartment::get(JSContext* cx, HandleAtom source, RegExpFlag flags,
|
||||
MutableHandleRegExpShared result)
|
||||
{
|
||||
Key key(source, flags);
|
||||
Set::AddPtr p = set_.lookupForAdd(key);
|
||||
DependentAddPtr<Set> p(cx, set_.get(), Key(source, flags));
|
||||
if (p) {
|
||||
// Trigger a read barrier on existing RegExpShared instances fetched
|
||||
// from the table (which only holds weak references).
|
||||
RegExpSharedReadBarrier(cx, *p);
|
||||
|
||||
g->init(**p);
|
||||
result.set(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopedJSDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(source, flags));
|
||||
auto shared = Allocate<RegExpShared>(cx);
|
||||
if (!shared)
|
||||
return false;
|
||||
|
||||
if (!set_.add(p, shared)) {
|
||||
new (shared) RegExpShared(source, flags);
|
||||
|
||||
if (!p.add(cx, set_.get(), Key(source, flags), shared)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Trace RegExpShared instances created during an incremental GC.
|
||||
RegExpSharedReadBarrier(cx, shared);
|
||||
|
||||
g->init(*shared.forget());
|
||||
result.set(shared);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt, RegExpGuard* g)
|
||||
RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt,
|
||||
MutableHandleRegExpShared shared)
|
||||
{
|
||||
RegExpFlag flags = RegExpFlag(0);
|
||||
if (opt && !ParseRegExpFlags(cx, opt, &flags))
|
||||
return false;
|
||||
|
||||
return get(cx, atom, flags, g);
|
||||
return get(cx, atom, flags, shared);
|
||||
}
|
||||
|
||||
size_t
|
||||
RegExpCompartment::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
size_t n = 0;
|
||||
n += set_.sizeOfExcludingThis(mallocSizeOf);
|
||||
for (Set::Enum e(set_); !e.empty(); e.popFront()) {
|
||||
RegExpShared* shared = e.front();
|
||||
n += shared->sizeOfIncludingThis(mallocSizeOf);
|
||||
}
|
||||
return n;
|
||||
return set_.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
/* Functions */
|
||||
|
|
@ -1501,12 +1425,12 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_)
|
|||
|
||||
Rooted<JSAtom*> source(cx, regex->getSource());
|
||||
|
||||
RegExpGuard g(cx);
|
||||
if (!RegExpObject::getShared(cx, regex, &g))
|
||||
RootedRegExpShared shared(cx);
|
||||
if (!RegExpObject::getShared(cx, regex, &shared))
|
||||
return nullptr;
|
||||
|
||||
clone->initAndZeroLastIndex(source, g->getFlags(), cx);
|
||||
clone->setShared(*g.re());
|
||||
clone->initAndZeroLastIndex(source, shared->getFlags(), cx);
|
||||
clone->setShared(*shared);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
|
@ -1645,7 +1569,14 @@ js::CloneScriptRegExpObject(JSContext* cx, RegExpObject& reobj)
|
|||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, js::RegExpGuard* g)
|
||||
js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
|
||||
{
|
||||
return RegExpToShared(cx, obj, g);
|
||||
return RegExpToShared(cx, obj, shared);
|
||||
}
|
||||
|
||||
JS::ubi::Node::Size
|
||||
JS::ubi::Concrete<RegExpShared>::size(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
|
||||
get().sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,8 @@
|
|||
*
|
||||
* To save memory, a RegExpShared is not created for a RegExpObject until it is
|
||||
* needed for execution. When a RegExpShared needs to be created, it is looked
|
||||
* up in a per-compartment table to allow reuse between objects. Lastly, on
|
||||
* GC, every RegExpShared (that is not active on the callstack) is discarded.
|
||||
* Because of the last point, any code using a RegExpShared (viz., by executing
|
||||
* a regexp) must indicate the RegExpShared is active via RegExpGuard.
|
||||
* up in a per-compartment table to allow reuse between objects. Lastly, on GC,
|
||||
* every RegExpShared that is not in active use is discarded.
|
||||
*/
|
||||
namespace js {
|
||||
|
||||
|
|
@ -44,9 +42,13 @@ class MatchPairs;
|
|||
class RegExpShared;
|
||||
class RegExpStatics;
|
||||
|
||||
using RootedRegExpShared = JS::Rooted<RegExpShared*>;
|
||||
using HandleRegExpShared = JS::Handle<RegExpShared*>;
|
||||
using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
|
||||
|
||||
namespace frontend { class TokenStream; }
|
||||
|
||||
enum RegExpFlag
|
||||
enum RegExpFlag : uint8_t
|
||||
{
|
||||
IgnoreCaseFlag = 0x01,
|
||||
GlobalFlag = 0x02,
|
||||
|
|
@ -94,7 +96,7 @@ CloneRegExpObject(JSContext* cx, JSObject* regexp);
|
|||
* objects when we are preserving jitcode in their zone, to avoid the same
|
||||
* recompilation inefficiencies as normal Ion and baseline compilation.
|
||||
*/
|
||||
class RegExpShared
|
||||
class RegExpShared : public gc::TenuredCell
|
||||
{
|
||||
public:
|
||||
enum CompilationMode {
|
||||
|
|
@ -115,11 +117,10 @@ class RegExpShared
|
|||
|
||||
struct RegExpCompilation
|
||||
{
|
||||
HeapPtr<jit::JitCode*> jitCode;
|
||||
ReadBarriered<jit::JitCode*> jitCode;
|
||||
uint8_t* byteCode;
|
||||
|
||||
RegExpCompilation() : byteCode(nullptr) {}
|
||||
~RegExpCompilation() { js_free(byteCode); }
|
||||
|
||||
bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
|
||||
return byteCode || (force == DontForceByteCode && jitCode);
|
||||
|
|
@ -127,15 +128,14 @@ class RegExpShared
|
|||
};
|
||||
|
||||
/* Source to the RegExp, for lazy compilation. */
|
||||
HeapPtr<JSAtom*> source;
|
||||
GCPtr<JSAtom*> source;
|
||||
|
||||
RegExpFlag flags;
|
||||
size_t parenCount;
|
||||
bool canStringMatch;
|
||||
bool marked_;
|
||||
size_t parenCount;
|
||||
|
||||
uint32_t numNamedCaptures_;
|
||||
HeapPtr<PlainObject*> groupsTemplate_;
|
||||
uint32_t numNamedCaptures_;
|
||||
GCPtr<PlainObject*> groupsTemplate_;
|
||||
|
||||
RegExpCompilation compilationArray[4];
|
||||
|
||||
|
|
@ -148,16 +148,20 @@ class RegExpShared
|
|||
}
|
||||
|
||||
// Tables referenced by JIT code.
|
||||
Vector<uint8_t*, 0, SystemAllocPolicy> tables;
|
||||
using JitCodeTables = Vector<uint8_t*, 0, SystemAllocPolicy>;
|
||||
JitCodeTables tables;
|
||||
|
||||
/* Internal functions. */
|
||||
bool compile(JSContext* cx, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force);
|
||||
bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force);
|
||||
RegExpShared(JSAtom* source, RegExpFlag flags);
|
||||
|
||||
bool compileIfNecessary(JSContext* cx, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force);
|
||||
static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force);
|
||||
static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleAtom pattern,
|
||||
HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force);
|
||||
|
||||
static bool compileIfNecessary(JSContext* cx, MutableHandleRegExpShared res,
|
||||
HandleLinearString input, CompilationMode mode,
|
||||
ForceByteCodeEnum force);
|
||||
|
||||
const RegExpCompilation& compilation(CompilationMode mode, bool latin1) const {
|
||||
return compilationArray[CompilationIndex(mode, latin1)];
|
||||
|
|
@ -168,13 +172,13 @@ class RegExpShared
|
|||
}
|
||||
|
||||
public:
|
||||
RegExpShared(JSAtom* source, RegExpFlag flags);
|
||||
~RegExpShared();
|
||||
~RegExpShared() = delete;
|
||||
|
||||
// Execute this RegExp on input starting from searchIndex, filling in
|
||||
// matches if specified and otherwise only determining if there is a match.
|
||||
RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
|
||||
MatchPairs* matches, size_t* endIndex);
|
||||
static RegExpRunStatus execute(JSContext* cx, MutableHandleRegExpShared res,
|
||||
HandleLinearString input, size_t searchIndex,
|
||||
MatchPairs* matches, size_t* endIndex);
|
||||
|
||||
// Register a table with this RegExpShared, and take ownership.
|
||||
bool addTable(uint8_t* table) {
|
||||
|
|
@ -192,7 +196,9 @@ class RegExpShared
|
|||
size_t pairCount() const { return getParenCount() + 1; }
|
||||
|
||||
// not public due to circular inclusion problems
|
||||
bool initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVector* names, irregexp::IntegerVector* indices);
|
||||
static bool initializeNamedCaptures(JSContext* cx, HandleRegExpShared re,
|
||||
irregexp::CharacterVectorVector* names,
|
||||
irregexp::IntegerVector* indices);
|
||||
PlainObject* getGroupsTemplate() { return groupsTemplate_; }
|
||||
uint32_t numNamedCaptures() const { return numNamedCaptures_; }
|
||||
|
||||
|
|
@ -214,15 +220,9 @@ class RegExpShared
|
|||
|| isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
bool needsSweep(JSRuntime* rt);
|
||||
void traceChildren(JSTracer* trc);
|
||||
void discardJitCode();
|
||||
|
||||
bool marked() const { return marked_; }
|
||||
void clearMarked() { marked_ = false; }
|
||||
|
||||
bool isMarkedGray() const;
|
||||
void unmarkGray();
|
||||
void finalize(FreeOp* fop);
|
||||
|
||||
static size_t offsetOfSource() {
|
||||
return offsetof(RegExpShared, source);
|
||||
|
|
@ -251,60 +251,14 @@ class RegExpShared
|
|||
return offsetof(RegExpShared, groupsTemplate_);
|
||||
}
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
#ifdef DEBUG
|
||||
bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input);
|
||||
static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res, bool match_only,
|
||||
HandleLinearString input);
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Extend the lifetime of a given RegExpShared to at least the lifetime of
|
||||
* the guard object. See Regular Expression comment at the top.
|
||||
*/
|
||||
class RegExpGuard : public JS::CustomAutoRooter
|
||||
{
|
||||
RegExpShared* re_;
|
||||
|
||||
RegExpGuard(const RegExpGuard&) = delete;
|
||||
void operator=(const RegExpGuard&) = delete;
|
||||
|
||||
public:
|
||||
explicit RegExpGuard(ExclusiveContext* cx)
|
||||
: CustomAutoRooter(cx), re_(nullptr)
|
||||
{}
|
||||
|
||||
RegExpGuard(ExclusiveContext* cx, RegExpShared& re)
|
||||
: CustomAutoRooter(cx), re_(nullptr)
|
||||
{
|
||||
init(re);
|
||||
}
|
||||
|
||||
~RegExpGuard() {
|
||||
release();
|
||||
}
|
||||
|
||||
public:
|
||||
void init(RegExpShared& re) {
|
||||
MOZ_ASSERT(!initialized());
|
||||
re_ = &re;
|
||||
}
|
||||
|
||||
void release() {
|
||||
re_ = nullptr;
|
||||
}
|
||||
|
||||
virtual void trace(JSTracer* trc) {
|
||||
if (re_)
|
||||
re_->trace(trc);
|
||||
}
|
||||
|
||||
bool initialized() const { return !!re_; }
|
||||
RegExpShared* re() const { MOZ_ASSERT(initialized()); return re_; }
|
||||
RegExpShared* operator->() { return re(); }
|
||||
RegExpShared& operator*() { return *re(); }
|
||||
};
|
||||
|
||||
class RegExpCompartment
|
||||
{
|
||||
struct Key {
|
||||
|
|
@ -315,8 +269,9 @@ class RegExpCompartment
|
|||
Key(JSAtom* atom, RegExpFlag flag)
|
||||
: atom(atom), flag(flag)
|
||||
{ }
|
||||
MOZ_IMPLICIT Key(RegExpShared* shared)
|
||||
: atom(shared->getSource()), flag(shared->getFlags())
|
||||
MOZ_IMPLICIT Key(const ReadBarriered<RegExpShared*>& shared)
|
||||
: atom(shared.unbarrieredGet()->getSource()),
|
||||
flag(shared.unbarrieredGet()->getFlags())
|
||||
{ }
|
||||
|
||||
typedef Key Lookup;
|
||||
|
|
@ -332,8 +287,8 @@ class RegExpCompartment
|
|||
* The set of all RegExpShareds in the compartment. On every GC, every
|
||||
* RegExpShared that was not marked is deleted and removed from the set.
|
||||
*/
|
||||
typedef HashSet<RegExpShared*, Key, RuntimeAllocPolicy> Set;
|
||||
Set set_;
|
||||
using Set = GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
|
||||
JS::WeakCache<Set> set_;
|
||||
|
||||
/*
|
||||
* This is the template object where the result of re.exec() is based on,
|
||||
|
|
@ -366,7 +321,7 @@ class RegExpCompartment
|
|||
ArrayObject* createMatchResultTemplateObject(JSContext* cx);
|
||||
|
||||
public:
|
||||
explicit RegExpCompartment(JSRuntime* rt);
|
||||
explicit RegExpCompartment(Zone* zone);
|
||||
~RegExpCompartment();
|
||||
|
||||
bool init(JSContext* cx);
|
||||
|
|
@ -374,10 +329,11 @@ class RegExpCompartment
|
|||
|
||||
bool empty() { return set_.empty(); }
|
||||
|
||||
bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g);
|
||||
bool get(JSContext* cx, HandleAtom source, RegExpFlag flags, MutableHandleRegExpShared shared);
|
||||
|
||||
/* Like 'get', but compile 'maybeOpt' (if non-null). */
|
||||
bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt, RegExpGuard* g);
|
||||
bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt,
|
||||
MutableHandleRegExpShared shared);
|
||||
|
||||
/* Get or create template object used to base the result of .exec() on. */
|
||||
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
|
||||
|
|
@ -500,14 +456,19 @@ class RegExpObject : public NativeObject
|
|||
static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
|
||||
|
||||
static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> regexp,
|
||||
RegExpGuard* g);
|
||||
MutableHandleRegExpShared shared);
|
||||
|
||||
bool hasShared() {
|
||||
return !!sharedRef();
|
||||
}
|
||||
|
||||
void setShared(RegExpShared& shared) {
|
||||
MOZ_ASSERT(!maybeShared());
|
||||
NativeObject::setPrivate(&shared);
|
||||
MOZ_ASSERT(!hasShared());
|
||||
sharedRef() = &shared;
|
||||
}
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
void initIgnoringLastIndex(HandleAtom source, RegExpFlag flags);
|
||||
|
||||
|
|
@ -527,9 +488,11 @@ class RegExpObject : public NativeObject
|
|||
* Side effect: sets the private field.
|
||||
*/
|
||||
static MOZ_MUST_USE bool createShared(JSContext* cx, Handle<RegExpObject*> regexp,
|
||||
RegExpGuard* g);
|
||||
RegExpShared* maybeShared() const {
|
||||
return static_cast<RegExpShared*>(NativeObject::getPrivate(PRIVATE_SLOT));
|
||||
MutableHandleRegExpShared shared);
|
||||
|
||||
ReadBarriered<RegExpShared*>& sharedRef() {
|
||||
auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
|
||||
return reinterpret_cast<ReadBarriered<RegExpShared*>&>(ref);
|
||||
}
|
||||
|
||||
/* Call setShared in preference to setPrivate. */
|
||||
|
|
@ -547,12 +510,12 @@ ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
|
|||
|
||||
/* Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for obj. */
|
||||
inline bool
|
||||
RegExpToShared(JSContext* cx, HandleObject obj, RegExpGuard* g)
|
||||
RegExpToShared(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
|
||||
{
|
||||
if (obj->is<RegExpObject>())
|
||||
return RegExpObject::getShared(cx, obj.as<RegExpObject>(), g);
|
||||
return RegExpObject::getShared(cx, obj.as<RegExpObject>(), shared);
|
||||
|
||||
return Proxy::regexp_toShared(cx, obj, g);
|
||||
return Proxy::regexp_toShared(cx, obj, shared);
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
|
|
@ -575,4 +538,29 @@ StringHasRegExpMetaChars(JSLinearString* str);
|
|||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
||||
template <>
|
||||
class Concrete<js::RegExpShared> : TracerConcrete<js::RegExpShared>
|
||||
{
|
||||
protected:
|
||||
explicit Concrete(js::RegExpShared* ptr) : TracerConcrete<js::RegExpShared>(ptr) { }
|
||||
|
||||
public:
|
||||
static void construct(void* storage, js::RegExpShared* ptr) {
|
||||
new (storage) Concrete(ptr);
|
||||
}
|
||||
|
||||
CoarseType coarseType() const final { return CoarseType::Other; }
|
||||
|
||||
Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
|
||||
|
||||
const char16_t* typeName() const override { return concreteTypeName; }
|
||||
static const char16_t concreteTypeName[];
|
||||
};
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
||||
#endif /* vm_RegExpObject_h */
|
||||
|
|
|
|||
|
|
@ -80,8 +80,9 @@ RegExpStatics::executeLazy(JSContext* cx)
|
|||
MOZ_ASSERT(lazyIndex != size_t(-1));
|
||||
|
||||
/* Retrieve or create the RegExpShared in this compartment. */
|
||||
RegExpGuard g(cx);
|
||||
if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &g))
|
||||
RootedRegExpShared shared(cx);
|
||||
RootedAtom source(cx, lazySource);
|
||||
if (!cx->compartment()->regExps.get(cx, source, lazyFlags, &shared))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
|
@ -91,7 +92,8 @@ RegExpStatics::executeLazy(JSContext* cx)
|
|||
|
||||
/* Execute the full regular expression. */
|
||||
RootedLinearString input(cx, matchesInput);
|
||||
RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches, nullptr);
|
||||
RegExpRunStatus status = RegExpShared::execute(cx, &shared, input, lazyIndex, &this->matches,
|
||||
nullptr);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -721,8 +721,8 @@ JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, HandleObject pro
|
|||
if (promise) {
|
||||
RootedObject unwrappedPromise(cx, promise);
|
||||
// While the job object is guaranteed to be unwrapped, the promise
|
||||
// might be wrapped. See the comments in
|
||||
// intrinsic_EnqueuePromiseReactionJob for details.
|
||||
// might be wrapped. See the comments in EnqueuePromiseReactionJob in
|
||||
// builtin/Promise.cpp for details.
|
||||
if (IsWrapper(promise))
|
||||
unwrappedPromise = UncheckedUnwrap(promise);
|
||||
if (unwrappedPromise->is<PromiseObject>())
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#include "jit/JitFrames.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
#include "jsgcinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "jsatominlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsgcinlines.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
|
|
|||
|
|
@ -1402,7 +1402,7 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
|
|||
return false;
|
||||
|
||||
if (cls == ESClass::RegExp) {
|
||||
RegExpGuard re(context());
|
||||
RootedRegExpShared re(context());
|
||||
if (!RegExpToShared(context(), obj, &re))
|
||||
return false;
|
||||
return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
|
||||
|
|
|
|||
|
|
@ -311,6 +311,7 @@ template JS::Zone* TracerConcrete<js::LazyScript>::zone() const;
|
|||
template JS::Zone* TracerConcrete<js::Shape>::zone() const;
|
||||
template JS::Zone* TracerConcrete<js::BaseShape>::zone() const;
|
||||
template JS::Zone* TracerConcrete<js::ObjectGroup>::zone() const;
|
||||
template JS::Zone* TracerConcrete<js::RegExpShared>::zone() const;
|
||||
template JS::Zone* TracerConcrete<js::Scope>::zone() const;
|
||||
template JS::Zone* TracerConcrete<JS::Symbol>::zone() const;
|
||||
template JS::Zone* TracerConcrete<JSString>::zone() const;
|
||||
|
|
@ -333,6 +334,7 @@ template UniquePtr<EdgeRange> TracerConcrete<js::LazyScript>::edges(JSContext* c
|
|||
template UniquePtr<EdgeRange> TracerConcrete<js::Shape>::edges(JSContext* cx, bool wantNames) const;
|
||||
template UniquePtr<EdgeRange> TracerConcrete<js::BaseShape>::edges(JSContext* cx, bool wantNames) const;
|
||||
template UniquePtr<EdgeRange> TracerConcrete<js::ObjectGroup>::edges(JSContext* cx, bool wantNames) const;
|
||||
template UniquePtr<EdgeRange> TracerConcrete<js::RegExpShared>::edges(JSContext* cx, bool wantNames) const;
|
||||
template UniquePtr<EdgeRange> TracerConcrete<js::Scope>::edges(JSContext* cx, bool wantNames) const;
|
||||
template UniquePtr<EdgeRange> TracerConcrete<JS::Symbol>::edges(JSContext* cx, bool wantNames) const;
|
||||
template UniquePtr<EdgeRange> TracerConcrete<JSString>::edges(JSContext* cx, bool wantNames) const;
|
||||
|
|
@ -397,6 +399,7 @@ const char16_t Concrete<js::Shape>::concreteTypeName[] = u"js::Shape";
|
|||
const char16_t Concrete<js::BaseShape>::concreteTypeName[] = u"js::BaseShape";
|
||||
const char16_t Concrete<js::ObjectGroup>::concreteTypeName[] = u"js::ObjectGroup";
|
||||
const char16_t Concrete<js::Scope>::concreteTypeName[] = u"js::Scope";
|
||||
const char16_t Concrete<js::RegExpShared>::concreteTypeName[] = u"js::RegExpShared";
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
|
|
|||
|
|
@ -1837,6 +1837,14 @@ ReportZoneStats(const JS::ZoneStats& zStats,
|
|||
zStats.scopesMallocHeap,
|
||||
"Arrays of binding names and other binding-related data.");
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/gc-heap"),
|
||||
zStats.regExpSharedsGCHeap,
|
||||
"Shared compiled regexp data.");
|
||||
|
||||
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/malloc-heap"),
|
||||
zStats.regExpSharedsMallocHeap,
|
||||
"Shared compiled regexp data.");
|
||||
|
||||
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
|
||||
zStats.typePool,
|
||||
"Type sets and related data.");
|
||||
|
|
@ -2855,6 +2863,10 @@ JSReporter::CollectReports(WindowPaths* windowPaths,
|
|||
KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode,
|
||||
"Unused jitcode cells within non-empty arenas.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/regexp-shareds"),
|
||||
KIND_OTHER, rtStats.zTotals.unusedGCThings.regExpShared,
|
||||
"Unused regexpshared cells within non-empty arenas.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
|
||||
KIND_OTHER, rtStats.gcHeapChunkAdmin,
|
||||
"The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
|
||||
|
|
@ -2906,6 +2918,10 @@ JSReporter::CollectReports(WindowPaths* windowPaths,
|
|||
KIND_OTHER, rtStats.zTotals.jitCodesGCHeap,
|
||||
"Used jitcode cells.");
|
||||
|
||||
MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/regexp-shareds"),
|
||||
KIND_OTHER, rtStats.zTotals.regExpSharedsGCHeap,
|
||||
"Used regexpshared cells.");
|
||||
|
||||
MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings);
|
||||
|
||||
// Report xpconnect.
|
||||
|
|
|
|||
|
|
@ -21,13 +21,15 @@
|
|||
|
||||
pref("keyword.enabled", false);
|
||||
pref("general.useragent.locale", "chrome://global/locale/intl.properties");
|
||||
|
||||
// Platform User-agent compatibility mode default settings
|
||||
pref("general.useragent.compatMode.gecko", false);
|
||||
pref("general.useragent.compatMode.firefox", false);
|
||||
pref("general.useragent.compatMode.version", "68.0");
|
||||
pref("general.useragent.compatMode.version", "102.0");
|
||||
pref("general.useragent.appVersionIsBuildID", false);
|
||||
|
||||
// This pref exists only for testing purposes. In order to disable all
|
||||
// overrides by default, don't initialize UserAgentOverrides.jsm.
|
||||
// In order to disable all overrides by default, don't initialize
|
||||
// UserAgentOverrides.jsm.
|
||||
pref("general.useragent.site_specific_overrides", true);
|
||||
|
||||
pref("general.config.obscure_value", 13); // for MCD .cfg files
|
||||
|
|
|
|||
|
|
@ -543,7 +543,10 @@ void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
|
|||
// Returns true if the JS::TraceKind is one the cycle collector cares about.
|
||||
inline bool AddToCCKind(JS::TraceKind aKind)
|
||||
{
|
||||
return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script || aKind == JS::TraceKind::Scope;
|
||||
return aKind == JS::TraceKind::Object ||
|
||||
aKind == JS::TraceKind::Script ||
|
||||
aKind == JS::TraceKind::Scope ||
|
||||
aKind == JS::TraceKind::RegExpShared;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -623,13 +623,11 @@ TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
|
|||
return insertSlot - mTimers.Elements();
|
||||
}
|
||||
|
||||
// This function must be called from within a lock.
|
||||
// Also: we hold the mutex for the nsTimerImpl.
|
||||
// Note: this function must be called from within a lock.
|
||||
bool
|
||||
TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
|
||||
{
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
aTimer->mMutex.AssertCurrentThreadOwns();
|
||||
if (!mTimers.RemoveElement(aTimer)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue