зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 9 changesets (bug 1412202) for bustage on NativeObject-inl.h. CLOSED TREE
Backed out changeset 8127ab469fc8 (bug 1412202) Backed out changeset 6459dd328f07 (bug 1412202) Backed out changeset 50ff9b1a922a (bug 1412202) Backed out changeset 3b4683d1d783 (bug 1412202) Backed out changeset 71f894279ce5 (bug 1412202) Backed out changeset 62490c81ec8c (bug 1412202) Backed out changeset c1f5bdf4ca3c (bug 1412202) Backed out changeset 3909f706c022 (bug 1412202) Backed out changeset 4ff48b83bad6 (bug 1412202)
This commit is contained in:
Родитель
58719da6a9
Коммит
6069ec14db
|
@ -75,6 +75,10 @@ void EmitterScope::updateFrameFixedSlots(BytecodeEmitter* bce,
|
|||
if (nextFrameSlot_ > bce->maxFixedSlots) {
|
||||
bce->maxFixedSlots = nextFrameSlot_;
|
||||
}
|
||||
MOZ_ASSERT_IF(
|
||||
bce->sc->isFunctionBox() && (bce->sc->asFunctionBox()->isGenerator() ||
|
||||
bce->sc->asFunctionBox()->isAsync()),
|
||||
bce->maxFixedSlots == 0);
|
||||
}
|
||||
|
||||
bool EmitterScope::putNameInCache(BytecodeEmitter* bce, const ParserAtom* name,
|
||||
|
@ -416,9 +420,6 @@ bool EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce,
|
|||
// InitializeBinding, after which touching the binding will no longer
|
||||
// throw reference errors. See 13.1.11, 9.2.13, 13.6.3.4, 13.6.4.6,
|
||||
// 13.6.4.8, 13.14.5, 15.1.8, and 15.2.0.15.
|
||||
//
|
||||
// The same code is used to clear slots on exit, in generators and async
|
||||
// functions, to avoid keeping garbage alive indefinitely.
|
||||
if (slotStart != slotEnd) {
|
||||
if (!bce->emit1(JSOp::Uninitialized)) {
|
||||
return false;
|
||||
|
@ -1046,12 +1047,6 @@ bool EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal) {
|
|||
case ScopeKind::Catch:
|
||||
case ScopeKind::FunctionLexical:
|
||||
case ScopeKind::ClassBody:
|
||||
if (bce->sc->isFunctionBox() &&
|
||||
bce->sc->asFunctionBox()->needsClearSlotsOnExit()) {
|
||||
if (!deadZoneFrameSlots(bce)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!bce->emit1(hasEnvironment() ? JSOp::PopLexicalEnv
|
||||
: JSOp::DebugLeaveLexicalEnv)) {
|
||||
return false;
|
||||
|
|
|
@ -135,8 +135,6 @@ enum class PrivateNameKind : uint8_t {
|
|||
GetterSetter,
|
||||
};
|
||||
|
||||
enum class ClosedOver : bool { No = false, Yes = true };
|
||||
|
||||
// Used in Parser to track declared names.
|
||||
class DeclaredNameInfo {
|
||||
uint32_t pos_;
|
||||
|
@ -150,11 +148,10 @@ class DeclaredNameInfo {
|
|||
PrivateNameKind privateNameKind_;
|
||||
|
||||
public:
|
||||
explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos,
|
||||
ClosedOver closedOver = ClosedOver::No)
|
||||
explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos)
|
||||
: pos_(pos),
|
||||
kind_(kind),
|
||||
closedOver_(bool(closedOver)),
|
||||
closedOver_(false),
|
||||
privateNameKind_(PrivateNameKind::None) {}
|
||||
|
||||
// Needed for InlineMap.
|
||||
|
|
|
@ -684,16 +684,15 @@ bool ParseContext::declareFunctionArgumentsObject(
|
|||
}
|
||||
|
||||
bool ParseContext::declareDotGeneratorName() {
|
||||
// The special '.generator' binding must be on the function scope, and must
|
||||
// be marked closed-over, as generators expect to find it on the CallObject.
|
||||
// The special '.generator' binding must be on the function scope, as
|
||||
// generators expect to find it on the CallObject.
|
||||
ParseContext::Scope& funScope = functionScope();
|
||||
const ParserName* dotGenerator = sc()->cx_->parserNames().dotGenerator;
|
||||
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
|
||||
if (!p) {
|
||||
if (!funScope.addDeclaredName(this, p, dotGenerator, DeclarationKind::Var,
|
||||
DeclaredNameInfo::npos, ClosedOver::Yes)) {
|
||||
return false;
|
||||
}
|
||||
if (!p &&
|
||||
!funScope.addDeclaredName(this, p, dotGenerator, DeclarationKind::Var,
|
||||
DeclaredNameInfo::npos)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,10 @@
|
|||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "frontend/CompilationInfo.h"
|
||||
#include "frontend/ErrorReporter.h"
|
||||
#include "frontend/NameAnalysisTypes.h" // DeclaredNameInfo
|
||||
#include "frontend/NameCollections.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
#include "frontend/UsedNameTracker.h"
|
||||
#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
|
||||
#include "vm/GeneratorObject.h" // js::AbstractGeneratorObject::FixedSlotLimit
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -110,13 +108,6 @@ class ParseContext : public Nestable<ParseContext> {
|
|||
// Monotonically increasing id.
|
||||
uint32_t id_;
|
||||
|
||||
// Scope size info, relevant for scopes in generators and async functions
|
||||
// only. During parsing, this is the estimated number of slots needed for
|
||||
// nested scopes inside this one. When the parser leaves a scope, this is
|
||||
// set to UINT32_MAX if there are too many bindings overrall to store them
|
||||
// in stack frames, and 0 otherwise.
|
||||
uint32_t sizeBits_ = 0;
|
||||
|
||||
bool maybeReportOOM(ParseContext* pc, bool result) {
|
||||
if (!result) {
|
||||
ReportOutOfMemory(pc->sc()->cx_);
|
||||
|
@ -149,12 +140,6 @@ class ParseContext : public Nestable<ParseContext> {
|
|||
|
||||
bool isEmpty() const { return declared_->all().empty(); }
|
||||
|
||||
uint32_t declaredCount() const {
|
||||
size_t count = declared_->count();
|
||||
MOZ_ASSERT(count <= UINT32_MAX);
|
||||
return uint32_t(count);
|
||||
}
|
||||
|
||||
DeclaredNamePtr lookupDeclaredName(const ParserAtom* name) {
|
||||
return declared_->lookup(name);
|
||||
}
|
||||
|
@ -165,10 +150,9 @@ class ParseContext : public Nestable<ParseContext> {
|
|||
|
||||
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p,
|
||||
const ParserAtom* name,
|
||||
DeclarationKind kind, uint32_t pos,
|
||||
ClosedOver closedOver = ClosedOver::No) {
|
||||
DeclarationKind kind, uint32_t pos) {
|
||||
return maybeReportOOM(
|
||||
pc, declared_->add(p, name, DeclaredNameInfo(kind, pos, closedOver)));
|
||||
pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
|
||||
}
|
||||
|
||||
// Add a FunctionBox as a possible candidate for Annex B.3.3 semantics.
|
||||
|
@ -189,35 +173,6 @@ class ParseContext : public Nestable<ParseContext> {
|
|||
pc->varScope_ = this;
|
||||
}
|
||||
|
||||
// This is called as we leave a function, var, or lexical scope in a
|
||||
// generator or async function. `ownSlotCount` is the number of `bindings_`
|
||||
// that are not closed over.
|
||||
void setOwnStackSlotCount(uint32_t ownSlotCount) {
|
||||
// Determine if this scope is too big to optimize bindings into stack
|
||||
// slots. The meaning of sizeBits_ changes from "maximum nested slot
|
||||
// count" to "UINT32_MAX if too big".
|
||||
uint32_t slotCount = ownSlotCount + sizeBits_;
|
||||
if (slotCount > AbstractGeneratorObject::FixedSlotLimit) {
|
||||
slotCount = sizeBits_;
|
||||
sizeBits_ = UINT32_MAX;
|
||||
} else {
|
||||
sizeBits_ = 0;
|
||||
}
|
||||
|
||||
// Propagate total size to enclosing scope.
|
||||
if (Scope* parent = enclosing()) {
|
||||
if (slotCount > parent->sizeBits_) {
|
||||
parent->sizeBits_ = slotCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool tooBigToOptimize() const {
|
||||
MOZ_ASSERT(sizeBits_ == 0 || sizeBits_ == UINT32_MAX,
|
||||
"call this only after the parser leaves the scope");
|
||||
return sizeBits_ != 0;
|
||||
}
|
||||
|
||||
// An iterator for the set of names a scope binds: the set of all
|
||||
// declared names for 'var' scopes, and the set of lexically declared
|
||||
// names for non-'var' scopes.
|
||||
|
@ -501,9 +456,7 @@ class ParseContext : public Nestable<ParseContext> {
|
|||
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
|
||||
}
|
||||
|
||||
bool isGeneratorOrAsync() const { return isGenerator() || isAsync(); }
|
||||
|
||||
bool needsDotGeneratorName() const { return isGeneratorOrAsync(); }
|
||||
bool needsDotGeneratorName() const { return isGenerator() || isAsync(); }
|
||||
|
||||
FunctionAsyncKind asyncKind() const {
|
||||
return isAsync() ? FunctionAsyncKind::AsyncFunction
|
||||
|
|
|
@ -838,7 +838,6 @@ bool PerHandlerParser<ParseHandler>::
|
|||
if (handler_.canSkipLazyClosedOverBindings()) {
|
||||
// Scopes are nullptr-delimited in the BaseScript closed over bindings
|
||||
// array.
|
||||
uint32_t slotCount = scope.declaredCount();
|
||||
while (JSAtom* name = handler_.nextLazyClosedOverBinding()) {
|
||||
// TODO-Stencil
|
||||
// After closed-over-bindings are snapshotted in the handler,
|
||||
|
@ -851,12 +850,6 @@ bool PerHandlerParser<ParseHandler>::
|
|||
const ParserName* nameId = mbNameId.unwrap()->asName();
|
||||
|
||||
scope.lookupDeclaredName(nameId)->value()->setClosedOver();
|
||||
MOZ_ASSERT(slotCount > 0);
|
||||
slotCount--;
|
||||
}
|
||||
|
||||
if (pc_->isGeneratorOrAsync()) {
|
||||
scope.setOwnStackSlotCount(slotCount);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -866,7 +859,6 @@ bool PerHandlerParser<ParseHandler>::
|
|||
uint32_t scriptId = pc_->scriptId();
|
||||
uint32_t scopeId = scope.id();
|
||||
|
||||
uint32_t slotCount = 0;
|
||||
for (BindingIter bi = scope.bindings(pc_); bi; bi++) {
|
||||
if (UsedNamePtr p = usedNames_.lookup(bi.name())) {
|
||||
bool closedOver;
|
||||
|
@ -880,16 +872,9 @@ bool PerHandlerParser<ParseHandler>::
|
|||
return false;
|
||||
}
|
||||
}
|
||||
} else if constexpr (!isSyntaxParser) {
|
||||
slotCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (!isSyntaxParser) {
|
||||
if (pc_->isGeneratorOrAsync()) {
|
||||
scope.setOwnStackSlotCount(slotCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Append a nullptr to denote end-of-scope.
|
||||
if constexpr (isSyntaxParser) {
|
||||
|
@ -1180,10 +1165,7 @@ Maybe<ParserFunctionScopeData*> NewFunctionScopeData(JSContext* cx,
|
|||
ParserBindingNameVector formals(cx);
|
||||
ParserBindingNameVector vars(cx);
|
||||
|
||||
bool allBindingsClosedOver =
|
||||
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
|
||||
bool argumentBindingsClosedOver =
|
||||
allBindingsClosedOver || pc->isGeneratorOrAsync();
|
||||
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
|
||||
bool hasDuplicateParams = pc->functionBox()->hasDuplicateParameters;
|
||||
|
||||
// Positional parameter names must be added in order of appearance as they are
|
||||
|
@ -1199,7 +1181,7 @@ Maybe<ParserFunctionScopeData*> NewFunctionScopeData(JSContext* cx,
|
|||
// there are parameter defaults. It is the binding in the defaults
|
||||
// scope that is closed over instead.
|
||||
bool closedOver =
|
||||
argumentBindingsClosedOver || (p && p->value()->closedOver());
|
||||
allBindingsClosedOver || (p && p->value()->closedOver());
|
||||
|
||||
// If the parameter name has duplicates, only the final parameter
|
||||
// name should be on the environment, as otherwise the environment
|
||||
|
@ -1272,8 +1254,7 @@ Maybe<ParserFunctionScopeData*> NewFunctionScopeData(JSContext* cx,
|
|||
// entry marked as closed-over. This is done without the need to allocate the
|
||||
// binding list. If true, an EnvironmentObject will be needed at runtime.
|
||||
bool FunctionScopeHasClosedOverBindings(ParseContext* pc) {
|
||||
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() ||
|
||||
pc->functionScope().tooBigToOptimize();
|
||||
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
|
||||
|
||||
for (BindingIter bi = pc->functionScope().bindings(pc); bi; bi++) {
|
||||
switch (bi.kind()) {
|
||||
|
@ -1307,8 +1288,7 @@ Maybe<ParserVarScopeData*> NewVarScopeData(JSContext* cx,
|
|||
LifoAlloc& alloc, ParseContext* pc) {
|
||||
ParserBindingNameVector vars(cx);
|
||||
|
||||
bool allBindingsClosedOver =
|
||||
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
|
||||
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
|
||||
|
||||
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
|
||||
if (bi.kind() == BindingKind::Var) {
|
||||
|
@ -1359,8 +1339,7 @@ Maybe<ParserLexicalScopeData*> NewLexicalScopeData(JSContext* cx,
|
|||
ParserBindingNameVector lets(cx);
|
||||
ParserBindingNameVector consts(cx);
|
||||
|
||||
bool allBindingsClosedOver =
|
||||
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
|
||||
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
|
||||
|
||||
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
|
||||
ParserBindingName binding(bi.name(),
|
||||
|
@ -1403,8 +1382,7 @@ Maybe<ParserLexicalScopeData*> NewLexicalScopeData(JSContext* cx,
|
|||
// list. If true, an EnvironmentObject will be needed at runtime.
|
||||
bool LexicalScopeHasClosedOverBindings(ParseContext* pc,
|
||||
ParseContext::Scope& scope) {
|
||||
bool allBindingsClosedOver =
|
||||
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
|
||||
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
|
||||
|
||||
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
|
||||
switch (bi.kind()) {
|
||||
|
|
|
@ -263,7 +263,7 @@ class SharedContext {
|
|||
|
||||
ImmutableScriptFlags immutableFlags() { return immutableFlags_; }
|
||||
|
||||
bool allBindingsClosedOver() { return bindingsAccessedDynamically(); }
|
||||
inline bool allBindingsClosedOver();
|
||||
|
||||
// The ImmutableFlag tracks if the entire script is strict, while the
|
||||
// localStrict flag indicates the current region (such as class body) should
|
||||
|
@ -536,7 +536,6 @@ class FunctionBox : public SharedContext {
|
|||
|
||||
bool needsFinalYield() const { return isGenerator() || isAsync(); }
|
||||
bool needsDotGeneratorName() const { return isGenerator() || isAsync(); }
|
||||
bool needsClearSlotsOnExit() const { return isGenerator() || isAsync(); }
|
||||
bool needsIteratorResult() const { return isGenerator() && !isAsync(); }
|
||||
bool needsPromiseResult() const { return isAsync() && !isGenerator(); }
|
||||
|
||||
|
@ -748,6 +747,18 @@ inline FunctionBox* SharedContext::asFunctionBox() {
|
|||
return static_cast<FunctionBox*>(this);
|
||||
}
|
||||
|
||||
// In generators, we treat all bindings as closed so that they get stored on
|
||||
// the heap. This way there is less information to copy off the stack when
|
||||
// suspending, and back on when resuming. It also avoids the need to create
|
||||
// and invalidate DebugScope proxies for unaliased locals in a generator
|
||||
// frame, as the generator frame will be copied out to the heap and released
|
||||
// only by GC.
|
||||
inline bool SharedContext::allBindingsClosedOver() {
|
||||
return bindingsAccessedDynamically() ||
|
||||
(isFunctionBox() &&
|
||||
(asFunctionBox()->isGenerator() || asFunctionBox()->isAsync()));
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -65,11 +65,11 @@ dbg.onEnterFrame = f => {
|
|||
JSON.stringify(frame.environment.names()),
|
||||
JSON.stringify(["arguments", "promises", "value"])
|
||||
);
|
||||
//FIXME assertEq(frame.environment.getVariable("value"), 42);
|
||||
assertEq(frame.environment.getVariable("value"), 42);
|
||||
|
||||
frame.environment.setVariable("value", 43);
|
||||
|
||||
//FIXME assertEq(frame.environment.getVariable("value"), 43);
|
||||
assertEq(frame.environment.getVariable("value"), 43);
|
||||
|
||||
await waitForOnPop(frame);
|
||||
|
||||
|
@ -97,7 +97,7 @@ dbg.onEnterFrame = f => {
|
|||
|
||||
const result = await promise;
|
||||
|
||||
//FIXME assertEq(result, 43);
|
||||
assertEq(result, 43);
|
||||
|
||||
assertThrowsInstanceOf(() => frame.environment, Error);
|
||||
|
||||
|
|
|
@ -45,11 +45,11 @@ assertEq(
|
|||
JSON.stringify(["arguments", "value"])
|
||||
);
|
||||
|
||||
//FIXME assertEq(frame.environment.getVariable("value"), 42);
|
||||
assertEq(frame.environment.getVariable("value"), 42);
|
||||
|
||||
frame.environment.setVariable("value", 43);
|
||||
|
||||
//FIXME assertEq(frame.environment.getVariable("value"), 43);
|
||||
assertEq(frame.environment.getVariable("value"), 43);
|
||||
|
||||
result = it.next();
|
||||
|
||||
|
@ -57,7 +57,7 @@ assertEq(
|
|||
JSON.stringify(frame.environment.names()),
|
||||
JSON.stringify(["block"])
|
||||
);
|
||||
//FIXME assertEq(frame.environment.getVariable("block"), "block");
|
||||
assertEq(frame.environment.getVariable("block"), "block");
|
||||
|
||||
result = it.next();
|
||||
|
||||
|
@ -65,7 +65,7 @@ assertEq(
|
|||
JSON.stringify(frame.environment.names()),
|
||||
JSON.stringify(["loop"])
|
||||
);
|
||||
//FIXME assertEq(frame.environment.getVariable("loop"), "loop");
|
||||
assertEq(frame.environment.getVariable("loop"), "loop");
|
||||
|
||||
result = it.next();
|
||||
|
||||
|
@ -73,11 +73,11 @@ assertEq(
|
|||
JSON.stringify(frame.environment.names()),
|
||||
JSON.stringify(["err"])
|
||||
);
|
||||
//FIXME assertEq(frame.environment.getVariable("err"), "err");
|
||||
assertEq(frame.environment.getVariable("err"), "err");
|
||||
|
||||
result = it.next();
|
||||
|
||||
assertEq(result.done, true);
|
||||
//FIXME assertEq(result.value, 43);
|
||||
assertEq(result.value, 43);
|
||||
|
||||
assertThrowsInstanceOf(() => frame.environment, Error);
|
||||
|
|
|
@ -65,7 +65,7 @@ dbg.onEnterFrame = f => {
|
|||
JSON.stringify(["arguments", "promises", "value"])
|
||||
);
|
||||
|
||||
//FIXME assertEq(frame.environment.getVariable("value"), 42);
|
||||
assertEq(frame.environment.getVariable("value"), 42);
|
||||
|
||||
frame.environment.setVariable("value", 43);
|
||||
|
||||
|
@ -100,7 +100,7 @@ dbg.onEnterFrame = f => {
|
|||
const result = await promise;
|
||||
|
||||
assertEq(result.done, true);
|
||||
//FIXME assertEq(result.value, 43);
|
||||
assertEq(result.value, 43);
|
||||
|
||||
assertThrowsInstanceOf(() => frame.environment, Error);
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ dbg.onEnterFrame = f => {
|
|||
const it = g.f();
|
||||
|
||||
assertEq(!!frame, true);
|
||||
//FIXME assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
|
||||
it.next();
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ var f = async function() {
|
|||
let frame;
|
||||
dbg.onEnterFrame = f => {
|
||||
frame = f;
|
||||
//FIXME assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
dbg.onEnterFrame = undefined;
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ dbg.onEnterFrame = f => {
|
|||
const promise = g.f();
|
||||
|
||||
assertEq(!!frame, true);
|
||||
//FIXME assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
|
||||
await promise;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ var f = async function*() {
|
|||
let frame;
|
||||
dbg.onEnterFrame = f => {
|
||||
frame = f;
|
||||
//FIXME assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
dbg.onEnterFrame = undefined;
|
||||
};
|
||||
|
||||
|
@ -26,11 +26,11 @@ dbg.onEnterFrame = f => {
|
|||
const it = g.f();
|
||||
|
||||
assertEq(!!frame, true);
|
||||
//FIXME assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
|
||||
const promise = it.next();
|
||||
|
||||
//FIXME assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
assertEq(frame.this, gDO.makeDebuggeeValue(g.context));
|
||||
|
||||
await promise;
|
||||
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
// |jit-test| skip-if: !('gc' in this) || !('clearKeptObjects' in this)
|
||||
// In generators, when we exit a lexical scope, its non-aliased bindings go away;
|
||||
// they don't keep their last values gc-alive.
|
||||
|
||||
let cases = [
|
||||
function* onNormalExitFromFunction_VarBinding() {
|
||||
var tmp = yield 1;
|
||||
consumeValue(tmp);
|
||||
},
|
||||
|
||||
function* onNormalExitFromFunction_LetBinding() {
|
||||
let tmp = yield 1;
|
||||
consumeValue(tmp);
|
||||
},
|
||||
|
||||
function* onNormalExitFromBlock() {
|
||||
if (typeof onNormalExitFromBlock === 'function') {
|
||||
let tmp = yield 1;
|
||||
consumeValue(tmp);
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
|
||||
function* onNormalExitFromCStyleForLet() {
|
||||
for (let tmp = yield 1; tmp !== null; tmp = null) {
|
||||
consumeValue(tmp);
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
|
||||
function* onNormalExitFromForLetOf() {
|
||||
for (let tmp of [yield 1]) {
|
||||
consumeValue(tmp);
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
|
||||
function* onNormalExitFromForConstOf() {
|
||||
for (const tmp of [yield 1]) {
|
||||
consumeValue(tmp);
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
|
||||
function* onNormalExitFromForConstDestructuringOf() {
|
||||
for (const {a, b, c, d} of [yield 1]) {
|
||||
consumeValue(a);
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
|
||||
function* onNormalExitFromForConstArrayDestructuringOf() {
|
||||
for (const [x] of [[yield 1]]) {
|
||||
consumeValue(x);
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
|
||||
function* onNormalExitFromBlockInLoop() {
|
||||
for (var i = 0; i < 2; i++) {
|
||||
if (i == 0) {
|
||||
let tmp = yield 1;
|
||||
consumeValue(tmp);
|
||||
} else {
|
||||
yield 2;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
function* onBreakFromBlock() {
|
||||
x: {
|
||||
let tmp = yield 1;
|
||||
consumeValue(tmp);
|
||||
break x;
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
|
||||
function* onExitFromCatchBlock() {
|
||||
try {
|
||||
throw yield 1;
|
||||
} catch (exc) {
|
||||
consumeValue(exc);
|
||||
}
|
||||
yield 2;
|
||||
},
|
||||
];
|
||||
|
||||
var consumeValue;
|
||||
|
||||
function runTest(g) {
|
||||
consumeValue = eval("_ => {}");
|
||||
|
||||
let generator = g();
|
||||
let result = generator.next();
|
||||
assertEq(result.done, false);
|
||||
assertEq(result.value, 1);
|
||||
let object = {};
|
||||
let weakRef = new WeakRef(object);
|
||||
result = generator.next(object);
|
||||
assertEq(result.value, result.done ? undefined : 2);
|
||||
|
||||
object = null;
|
||||
clearKeptObjects();
|
||||
gc();
|
||||
assertEq(weakRef.deref(), undefined);
|
||||
}
|
||||
|
||||
for (let g of cases) {
|
||||
runTest(g);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
// |jit-test| skip-if: !('gc' in this) || !('clearKeptObjects' in this)
|
||||
// Locals in async functions should not keep objects alive after going out of scope.
|
||||
// Test by Mathieu Hofman.
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
let weakRef;
|
||||
let savedCallback;
|
||||
|
||||
const tests = [
|
||||
function() {
|
||||
let object = { id: ++nextId };
|
||||
console.log(`created object ${object.id}`);
|
||||
savedCallback = () => {};
|
||||
weakRef = new WeakRef(object);
|
||||
},
|
||||
async function() {
|
||||
let object = { id: ++nextId };
|
||||
console.log(`created object ${object.id}`);
|
||||
savedCallback = () => {};
|
||||
weakRef = new WeakRef(object);
|
||||
},
|
||||
async function() {
|
||||
function* gen() {
|
||||
{
|
||||
let object = { id: ++nextId };
|
||||
console.log(`created object ${object.id}`);
|
||||
// Yielding here stores the local variable `object` in the generator
|
||||
// object.
|
||||
yield 1;
|
||||
weakRef = new WeakRef(object);
|
||||
}
|
||||
// Yielding here should clear it.
|
||||
yield 2;
|
||||
}
|
||||
let iter = gen();
|
||||
assertEq(iter.next().value, 1);
|
||||
assertEq(iter.next().value, 2);
|
||||
savedCallback = iter; // Keep the generator alive for GC.
|
||||
}
|
||||
];
|
||||
|
||||
(async () => {
|
||||
for (const test of tests) {
|
||||
await test();
|
||||
assertEq(!!weakRef.deref(), true);
|
||||
clearKeptObjects();
|
||||
gc();
|
||||
if (weakRef.deref()) {
|
||||
throw new Error(`object ${nextId} was not collected`);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,31 +0,0 @@
|
|||
// |jit-test| skip-if: !('gc' in this) || !('drainJobQueue' in this)
|
||||
// Test that an unused local in an async function is not kept alive by a closure
|
||||
// (bug 1412202). Based on a test case by Andy Wingo in bug 1664463.
|
||||
|
||||
let nfinalized = 0;
|
||||
let finalizers = new FinalizationRegistry(f => { nfinalized++; });
|
||||
|
||||
class A {
|
||||
constructor(callback) {
|
||||
this.b = {callback};
|
||||
finalizers.register(this, this.b, this);
|
||||
}
|
||||
}
|
||||
|
||||
const LOOP_COUNT = 200;
|
||||
const FINALIZER_COUNT = 200;
|
||||
|
||||
async function main() {
|
||||
for (let j = 0; j < LOOP_COUNT; j++) {
|
||||
for (let i = 0; i < FINALIZER_COUNT; i++) {
|
||||
let console = globalThis.console;
|
||||
let obj = new A(() => console.log("hello"));
|
||||
}
|
||||
drainJobQueue();
|
||||
}
|
||||
|
||||
gc();
|
||||
drainJobQueue();
|
||||
assertEq(nfinalized, LOOP_COUNT * FINALIZER_COUNT, "all objects should be finalized");
|
||||
}
|
||||
main();
|
|
@ -1,88 +0,0 @@
|
|||
// Tests aimed at AbstractGeneratorObject::FixedSlotLimit.
|
||||
|
||||
"use strict";
|
||||
|
||||
function test(n) {
|
||||
const iterate = (start, f) => {
|
||||
let value = start;
|
||||
for (let i = n; i-- > 0; ) {
|
||||
value = f(value, i);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const generate = (start, f) => {
|
||||
let s = iterate(start, f);
|
||||
let gen = eval('(function* () {\n' + s + '})');
|
||||
return gen();
|
||||
};
|
||||
|
||||
// Test 1: many vars in the function scope
|
||||
{
|
||||
let it = generate(
|
||||
"yield 0;",
|
||||
(s, i) => `
|
||||
var v${i} = ${i};
|
||||
${s}
|
||||
assertEq(v${i}, ${i});
|
||||
`
|
||||
);
|
||||
assertEq(it.next().done, false);
|
||||
assertEq(it.next().done, true);
|
||||
}
|
||||
|
||||
// Test 2: many let-bindings in nested lexical scopes
|
||||
{
|
||||
let it = generate(
|
||||
"yield a => v174;",
|
||||
(s, i) => {
|
||||
let block = `
|
||||
let v${i} = ${i};
|
||||
${s}
|
||||
assertEq(v${i}, ${i});
|
||||
`;
|
||||
if (i % 17 == 0) {
|
||||
block = '{\n' + block + '}\n';
|
||||
}
|
||||
return block;
|
||||
}
|
||||
);
|
||||
assertEq(it.next().done, false);
|
||||
assertEq(it.next().done, true);
|
||||
}
|
||||
|
||||
// Test 3: TDZ is preserved across yield
|
||||
{
|
||||
let it = generate(
|
||||
'yield 0;\n' +
|
||||
'try { v1; } catch (exc) { yield [1, exc]; }\n' +
|
||||
`try { v${n - 1}; } catch (exc) { yield [2, exc]; }\n`,
|
||||
(s, i) => {
|
||||
let block = `
|
||||
${s}
|
||||
let v${i};
|
||||
`;
|
||||
if (i % 256 == 0) {
|
||||
block = '{\n' + block + '}\n';
|
||||
}
|
||||
return block;
|
||||
}
|
||||
);
|
||||
let {value, done} = it.next();
|
||||
assertEq(value, 0);
|
||||
({value, done} = it.next());
|
||||
assertEq(value[0], 1);
|
||||
assertEq(value[1].name, "ReferenceError");
|
||||
({value, done} = it.next());
|
||||
assertEq(value[0], 2);
|
||||
assertEq(value[1].name, "ReferenceError");
|
||||
({value, done} = it.next());
|
||||
assertEq(done, true);
|
||||
}
|
||||
}
|
||||
|
||||
for (let exp = 8; exp < 12; exp++) {
|
||||
const n = 2 ** exp;
|
||||
test(n - 2);
|
||||
test(n + 1);
|
||||
}
|
|
@ -5763,46 +5763,71 @@ bool BaselineCodeGen<Handler>::emit_Generator() {
|
|||
}
|
||||
|
||||
template <typename Handler>
|
||||
bool BaselineCodeGen<Handler>::emitSuspend(JSOp op) {
|
||||
MOZ_ASSERT(op == JSOp::InitialYield || op == JSOp::Yield ||
|
||||
op == JSOp::Await);
|
||||
bool BaselineCodeGen<Handler>::emit_InitialYield() {
|
||||
frame.syncStack(0);
|
||||
frame.assertStackDepth(1);
|
||||
|
||||
// Load the generator object in R2, but leave the return value on the
|
||||
// expression stack.
|
||||
Register genObj = R2.scratchReg();
|
||||
if (op == JSOp::InitialYield) {
|
||||
// Generator and return value are one and the same.
|
||||
frame.syncStack(0);
|
||||
frame.assertStackDepth(1);
|
||||
masm.unboxObject(frame.addressOfStackValue(-1), genObj);
|
||||
} else {
|
||||
frame.popRegsAndSync(1);
|
||||
masm.unboxObject(R0, genObj);
|
||||
masm.unboxObject(frame.addressOfStackValue(-1), genObj);
|
||||
|
||||
MOZ_ASSERT_IF(handler.maybePC(), GET_RESUMEINDEX(handler.maybePC()) == 0);
|
||||
masm.storeValue(
|
||||
Int32Value(0),
|
||||
Address(genObj, AbstractGeneratorObject::offsetOfResumeIndexSlot()));
|
||||
|
||||
Register envObj = R0.scratchReg();
|
||||
Register temp = R1.scratchReg();
|
||||
Address envChainSlot(genObj,
|
||||
AbstractGeneratorObject::offsetOfEnvironmentChainSlot());
|
||||
masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
|
||||
masm.guardedCallPreBarrierAnyZone(envChainSlot, MIRType::Value, temp);
|
||||
masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
|
||||
|
||||
Label skipBarrier;
|
||||
masm.branchPtrInNurseryChunk(Assembler::Equal, genObj, temp, &skipBarrier);
|
||||
masm.branchPtrInNurseryChunk(Assembler::NotEqual, envObj, temp, &skipBarrier);
|
||||
masm.push(genObj);
|
||||
MOZ_ASSERT(genObj == R2.scratchReg());
|
||||
masm.call(&postBarrierSlot_);
|
||||
masm.pop(genObj);
|
||||
masm.bind(&skipBarrier);
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, genObj, JSReturnOperand);
|
||||
if (!emitReturn()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame.hasKnownStackDepth(1) && !handler.canHaveFixedSlots()) {
|
||||
// Two extra stack values will be pushed when resuming the generator.
|
||||
frame.incStackDepth(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
bool BaselineCodeGen<Handler>::emit_Yield() {
|
||||
// Store generator in R0.
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
Register genObj = R2.scratchReg();
|
||||
masm.unboxObject(R0, genObj);
|
||||
|
||||
if (frame.hasKnownStackDepth(1)) {
|
||||
// If the expression stack is empty, we can inline the Yield. Note that this
|
||||
// branch is never taken for the interpreter because it doesn't know static
|
||||
// stack depths.
|
||||
MOZ_ASSERT_IF(op == JSOp::InitialYield && handler.maybePC(),
|
||||
GET_RESUMEINDEX(handler.maybePC()) == 0);
|
||||
|
||||
Register temp = R1.scratchReg();
|
||||
Address resumeIndexSlot(genObj,
|
||||
AbstractGeneratorObject::offsetOfResumeIndexSlot());
|
||||
Register temp = R1.scratchReg();
|
||||
if (op == JSOp::InitialYield) {
|
||||
masm.storeValue(Int32Value(0), resumeIndexSlot);
|
||||
} else {
|
||||
jsbytecode* pc = handler.maybePC();
|
||||
MOZ_ASSERT(pc, "compiler-only code never has a null pc");
|
||||
masm.move32(Imm32(GET_RESUMEINDEX(pc)), temp);
|
||||
masm.storeValue(JSVAL_TYPE_INT32, temp, resumeIndexSlot);
|
||||
}
|
||||
jsbytecode* pc = handler.maybePC();
|
||||
MOZ_ASSERT(pc, "compiler-only code never has a null pc");
|
||||
masm.move32(Imm32(GET_RESUMEINDEX(pc)), temp);
|
||||
masm.storeValue(JSVAL_TYPE_INT32, temp, resumeIndexSlot);
|
||||
|
||||
Register envObj = R0.scratchReg();
|
||||
Address envChainSlot(
|
||||
genObj, AbstractGeneratorObject::offsetOfEnvironmentChainSlot());
|
||||
masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
|
||||
masm.guardedCallPreBarrierAnyZone(envChainSlot, MIRType::Value, temp);
|
||||
masm.guardedCallPreBarrier(envChainSlot, MIRType::Value);
|
||||
masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
|
||||
|
||||
Label skipBarrier;
|
||||
|
@ -5834,25 +5859,14 @@ bool BaselineCodeGen<Handler>::emitSuspend(JSOp op) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Three values are pushed onto the stack when resuming the generator,
|
||||
// replacing the one slot that holds the return value.
|
||||
// Two extra stack values will be pushed when resuming the generator.
|
||||
frame.incStackDepth(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
bool BaselineCodeGen<Handler>::emit_InitialYield() {
|
||||
return emitSuspend(JSOp::InitialYield);
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
bool BaselineCodeGen<Handler>::emit_Yield() {
|
||||
return emitSuspend(JSOp::Yield);
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
bool BaselineCodeGen<Handler>::emit_Await() {
|
||||
return emitSuspend(JSOp::Await);
|
||||
return emit_Yield();
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -6134,11 +6148,11 @@ bool BaselineCodeGen<Handler>::emit_Resume() {
|
|||
}
|
||||
masm.bind(&noArgsObj);
|
||||
|
||||
// Push locals and expression slots if needed.
|
||||
Label noStackStorage;
|
||||
Address stackStorageSlot(genObj,
|
||||
AbstractGeneratorObject::offsetOfStackStorageSlot());
|
||||
masm.fallibleUnboxObject(stackStorageSlot, scratch2, &noStackStorage);
|
||||
// Push expression slots if needed.
|
||||
Label noExprStack;
|
||||
Address exprStackSlot(genObj,
|
||||
AbstractGeneratorObject::offsetOfExpressionStackSlot());
|
||||
masm.fallibleUnboxObject(exprStackSlot, scratch2, &noExprStack);
|
||||
{
|
||||
Register initLength = regs.takeAny();
|
||||
masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
|
||||
|
@ -6162,7 +6176,7 @@ bool BaselineCodeGen<Handler>::emit_Resume() {
|
|||
regs.add(initLength);
|
||||
}
|
||||
|
||||
masm.bind(&noStackStorage);
|
||||
masm.bind(&noExprStack);
|
||||
|
||||
// Push arg, generator, resumeKind stack Values, in that order.
|
||||
masm.pushValue(Address(callerStackPtr, sizeof(Value)));
|
||||
|
|
|
@ -142,8 +142,6 @@ class BaselineCodeGen {
|
|||
return emitDebugInstrumentation(ifDebuggee, mozilla::Maybe<F>());
|
||||
}
|
||||
|
||||
bool emitSuspend(JSOp op);
|
||||
|
||||
template <typename F>
|
||||
MOZ_MUST_USE bool emitAfterYieldDebugInstrumentation(const F& ifDebuggee,
|
||||
Register scratch);
|
||||
|
@ -361,8 +359,6 @@ class BaselineCompilerHandler {
|
|||
static constexpr size_t NumSlotsLimit = 128;
|
||||
return script()->nslots() > NumSlotsLimit;
|
||||
}
|
||||
|
||||
bool canHaveFixedSlots() const { return script()->nfixed() != 0; }
|
||||
};
|
||||
|
||||
using BaselineCompilerCodeGen = BaselineCodeGen<BaselineCompilerHandler>;
|
||||
|
@ -484,8 +480,6 @@ class BaselineInterpreterHandler {
|
|||
// The interpreter doesn't know the number of slots statically so we always
|
||||
// include them.
|
||||
bool mustIncludeSlotsInStackCheck() const { return true; }
|
||||
|
||||
bool canHaveFixedSlots() const { return true; }
|
||||
};
|
||||
|
||||
using BaselineInterpreterCodeGen = BaselineCodeGen<BaselineInterpreterHandler>;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include "vm/EnvironmentObject-inl.h"
|
||||
#include "vm/JSScript-inl.h"
|
||||
#include "vm/NativeObject-inl.h" // js::NativeObject::initDenseElementsFromRange
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
@ -40,17 +39,6 @@ inline void BaselineFrame::replaceInnermostEnvironment(EnvironmentObject& env) {
|
|||
envChain_ = &env;
|
||||
}
|
||||
|
||||
inline bool BaselineFrame::saveGeneratorSlots(JSContext* cx, unsigned nslots,
|
||||
ArrayObject* dest) const {
|
||||
// By convention, generator slots are stored in interpreter order,
|
||||
// which is the reverse of BaselineFrame order.
|
||||
|
||||
MOZ_ASSERT(nslots == numValueSlots(debugFrameSize()) - 1);
|
||||
const Value* end = reinterpret_cast<const Value*>(this);
|
||||
mozilla::Span<const Value> span{end - nslots, end};
|
||||
return dest->initDenseElementsFromRange(cx, span.rbegin(), span.rend());
|
||||
}
|
||||
|
||||
inline bool BaselineFrame::pushLexicalEnvironment(JSContext* cx,
|
||||
Handle<LexicalScope*> scope) {
|
||||
LexicalEnvironmentObject* env =
|
||||
|
|
|
@ -195,9 +195,6 @@ class BaselineFrame {
|
|||
BaselineFrame::Size() + offsetOfArg(0));
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
|
||||
ArrayObject* dest) const;
|
||||
|
||||
private:
|
||||
Value* evalNewTargetAddress() const {
|
||||
MOZ_ASSERT(isEvalFrame());
|
||||
|
|
|
@ -1437,13 +1437,33 @@ JSObject* CreateGenerator(JSContext* cx, BaselineFrame* frame) {
|
|||
|
||||
bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame,
|
||||
uint32_t frameSize, jsbytecode* pc) {
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield ||
|
||||
JSOp(*pc) == JSOp::Await);
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOp::Yield || JSOp(*pc) == JSOp::Await);
|
||||
|
||||
// Minus one because we don't want to include the return value.
|
||||
uint32_t numSlots = frame->numValueSlots(frameSize) - 1;
|
||||
MOZ_ASSERT(numSlots >= frame->script()->nfixed());
|
||||
return AbstractGeneratorObject::suspend(cx, obj, frame, pc, numSlots);
|
||||
uint32_t numValueSlots = frame->numValueSlots(frameSize);
|
||||
|
||||
MOZ_ASSERT(numValueSlots > frame->script()->nfixed());
|
||||
uint32_t stackDepth = numValueSlots - frame->script()->nfixed();
|
||||
|
||||
// Return value is still on the stack.
|
||||
MOZ_ASSERT(stackDepth >= 1);
|
||||
|
||||
// The expression stack slots are stored on the stack in reverse order, so
|
||||
// we copy them to a Vector and pass a pointer to that instead. We use
|
||||
// stackDepth - 1 because we don't want to include the return value.
|
||||
RootedValueVector exprStack(cx);
|
||||
if (!exprStack.reserve(stackDepth - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t firstSlot = numValueSlots - stackDepth;
|
||||
for (size_t i = 0; i < stackDepth - 1; i++) {
|
||||
exprStack.infallibleAppend(*frame->valueSlot(firstSlot + i));
|
||||
}
|
||||
|
||||
MOZ_ASSERT(exprStack.length() == stackDepth - 1);
|
||||
|
||||
return AbstractGeneratorObject::normalSuspend(
|
||||
cx, obj, frame, pc, exprStack.begin(), stackDepth - 1);
|
||||
}
|
||||
|
||||
bool FinalSuspend(JSContext* cx, HandleObject obj, jsbytecode* pc) {
|
||||
|
|
|
@ -2457,10 +2457,7 @@ ArrayObject* DebugEnvironmentProxy::maybeSnapshot() const {
|
|||
}
|
||||
|
||||
void DebugEnvironmentProxy::initSnapshot(ArrayObject& o) {
|
||||
MOZ_ASSERT_IF(
|
||||
maybeSnapshot() != nullptr,
|
||||
environment().is<CallObject>() &&
|
||||
environment().as<CallObject>().callee().isGeneratorOrAsync());
|
||||
MOZ_ASSERT(maybeSnapshot() == nullptr);
|
||||
setReservedSlot(SNAPSHOT_SLOT, ObjectValue(o));
|
||||
}
|
||||
|
||||
|
@ -2668,6 +2665,11 @@ bool DebugEnvironments::addDebugEnvironment(
|
|||
Handle<DebugEnvironmentProxy*> debugEnv) {
|
||||
MOZ_ASSERT(!ei.hasSyntacticEnvironment());
|
||||
MOZ_ASSERT(cx->realm() == debugEnv->nonCCWRealm());
|
||||
// Generators should always have environments.
|
||||
MOZ_ASSERT_IF(
|
||||
ei.scope().is<FunctionScope>(),
|
||||
!ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator() &&
|
||||
!ei.scope().as<FunctionScope>().canonicalFunction()->isAsync());
|
||||
|
||||
if (!CanUseDebugEnvironmentMaps(cx)) {
|
||||
return true;
|
||||
|
@ -2833,6 +2835,10 @@ void DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (frame.callee()->isGenerator() || frame.callee()->isAsync()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CallObject& callobj = frame.environmentChain()->as<CallObject>();
|
||||
envs->liveEnvs.remove(&callobj);
|
||||
if (JSObject* obj = envs->proxiedEnvs.lookup(&callobj)) {
|
||||
|
@ -2954,6 +2960,12 @@ bool DebugEnvironments::updateLiveEnvironments(JSContext* cx) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (frame.isFunctionFrame()) {
|
||||
if (frame.callee()->isGenerator() || frame.callee()->isAsync()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!frame.isDebuggee()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -3131,6 +3143,8 @@ static DebugEnvironmentProxy* GetDebugEnvironmentForMissing(
|
|||
if (ei.scope().is<FunctionScope>()) {
|
||||
RootedFunction callee(cx,
|
||||
ei.scope().as<FunctionScope>().canonicalFunction());
|
||||
// Generators should always reify their scopes.
|
||||
MOZ_ASSERT(!callee->isGenerator() && !callee->isAsync());
|
||||
|
||||
JS::ExposeObjectToActiveJS(callee);
|
||||
Rooted<CallObject*> callobj(cx,
|
||||
|
|
|
@ -28,6 +28,7 @@ using namespace js;
|
|||
JSObject* AbstractGeneratorObject::create(JSContext* cx,
|
||||
AbstractFramePtr frame) {
|
||||
MOZ_ASSERT(frame.isGeneratorFrame());
|
||||
MOZ_ASSERT(frame.script()->nfixed() == 0);
|
||||
MOZ_ASSERT(!frame.isConstructing());
|
||||
|
||||
RootedFunction fun(cx, frame.callee());
|
||||
|
@ -49,7 +50,7 @@ JSObject* AbstractGeneratorObject::create(JSContext* cx,
|
|||
if (frame.script()->needsArgsObj()) {
|
||||
genObj->setArgsObj(frame.argsObj());
|
||||
}
|
||||
genObj->clearStackStorage();
|
||||
genObj->clearExpressionStack();
|
||||
|
||||
if (!DebugAPI::onNewGenerator(cx, frame, genObj)) {
|
||||
return nullptr;
|
||||
|
@ -64,33 +65,45 @@ void AbstractGeneratorObject::trace(JSTracer* trc) {
|
|||
|
||||
bool AbstractGeneratorObject::suspend(JSContext* cx, HandleObject obj,
|
||||
AbstractFramePtr frame, jsbytecode* pc,
|
||||
unsigned nvalues) {
|
||||
Value* vp, unsigned nvalues) {
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield ||
|
||||
JSOp(*pc) == JSOp::Await);
|
||||
|
||||
auto genObj = obj.as<AbstractGeneratorObject>();
|
||||
MOZ_ASSERT(!genObj->hasStackStorage() || genObj->isStackStorageEmpty());
|
||||
MOZ_ASSERT(!genObj->hasExpressionStack() || genObj->isExpressionStackEmpty());
|
||||
MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Await, genObj->callee().isAsync());
|
||||
MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Yield, genObj->callee().isGenerator());
|
||||
|
||||
ArrayObject* stack = nullptr;
|
||||
if (nvalues > 0) {
|
||||
ArrayObject* stack = nullptr;
|
||||
if (genObj->hasStackStorage()) {
|
||||
stack = &genObj->stackStorage();
|
||||
} else {
|
||||
stack = NewDenseEmptyArray(cx);
|
||||
do {
|
||||
if (genObj->hasExpressionStack()) {
|
||||
MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() == 0);
|
||||
auto result = genObj->expressionStack().setOrExtendDenseElements(
|
||||
cx, 0, vp, nvalues, ShouldUpdateTypes::DontUpdate);
|
||||
if (result == DenseElementResult::Success) {
|
||||
MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() ==
|
||||
nvalues);
|
||||
break;
|
||||
}
|
||||
if (result == DenseElementResult::Failure) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
stack = NewDenseCopiedArray(cx, nvalues, vp);
|
||||
if (!stack) {
|
||||
return false;
|
||||
}
|
||||
genObj->setStackStorage(*stack);
|
||||
}
|
||||
if (!frame.saveGeneratorSlots(cx, nvalues, stack)) {
|
||||
return false;
|
||||
}
|
||||
} while (false);
|
||||
}
|
||||
|
||||
genObj->setResumeIndex(pc);
|
||||
genObj->setEnvironmentChain(*frame.environmentChain());
|
||||
if (stack) {
|
||||
genObj->setExpressionStack(*stack);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -114,13 +127,23 @@ AbstractGeneratorObject* js::GetGeneratorObjectForFrame(
|
|||
Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
|
||||
Value genValue = callObj.getSlot(shape->slot());
|
||||
|
||||
// If the `Generator; SetAliasedVar ".generator"; InitialYield` bytecode
|
||||
// If the `generator; setaliasedvar ".generator"; initialyield` bytecode
|
||||
// sequence has not run yet, genValue is undefined.
|
||||
return genValue.isObject()
|
||||
? &genValue.toObject().as<AbstractGeneratorObject>()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void js::SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame) {
|
||||
CallObject& callObj = frame.callObj();
|
||||
|
||||
// Get the generator object stored on the scope chain and close it.
|
||||
Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
|
||||
auto& genObj =
|
||||
callObj.getSlot(shape->slot()).toObject().as<AbstractGeneratorObject>();
|
||||
genObj.setClosed();
|
||||
}
|
||||
|
||||
bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
|
||||
Handle<AbstractGeneratorObject*> genObj,
|
||||
HandleValue arg,
|
||||
|
@ -157,13 +180,13 @@ bool AbstractGeneratorObject::resume(JSContext* cx,
|
|||
activation.regs().fp()->initArgsObj(genObj->argsObj());
|
||||
}
|
||||
|
||||
if (genObj->hasStackStorage() && !genObj->isStackStorageEmpty()) {
|
||||
JSScript* script = activation.regs().fp()->script();
|
||||
ArrayObject* storage = &genObj->stackStorage();
|
||||
uint32_t len = storage->getDenseInitializedLength();
|
||||
activation.regs().fp()->restoreGeneratorSlots(storage);
|
||||
activation.regs().sp += len - script->nfixed();
|
||||
storage->setDenseInitializedLength(0);
|
||||
if (genObj->hasExpressionStack() && !genObj->isExpressionStackEmpty()) {
|
||||
uint32_t len = genObj->expressionStack().getDenseInitializedLength();
|
||||
MOZ_ASSERT(activation.regs().spForStackDepth(len));
|
||||
const Value* src = genObj->expressionStack().getDenseElements();
|
||||
mozilla::PodCopy(activation.regs().sp, src, len);
|
||||
activation.regs().sp += len;
|
||||
genObj->expressionStack().setDenseInitializedLength(0);
|
||||
}
|
||||
|
||||
JSScript* script = callee->nonLazyScript();
|
||||
|
|
|
@ -33,21 +33,14 @@ class AbstractGeneratorObject : public NativeObject {
|
|||
CALLEE_SLOT = 0,
|
||||
ENV_CHAIN_SLOT,
|
||||
ARGS_OBJ_SLOT,
|
||||
STACK_STORAGE_SLOT,
|
||||
EXPRESSION_STACK_SLOT,
|
||||
RESUME_INDEX_SLOT,
|
||||
RESERVED_SLOTS
|
||||
};
|
||||
|
||||
// Maximum number of fixed stack slots in a generator or async function
|
||||
// script. If a script would have more, we instead store some variables in
|
||||
// heap EnvironmentObjects.
|
||||
//
|
||||
// This limit is a performance heuristic. Stack slots reduce allocations,
|
||||
// and `Local` opcodes are a bit faster than `AliasedVar` ones; but at each
|
||||
// `yield` or `await` the stack slots must be memcpy'd into a
|
||||
// GeneratorObject. At some point the memcpy is too much. The limit is
|
||||
// plenty for typical human-authored code.
|
||||
static constexpr uint32_t FixedSlotLimit = 256;
|
||||
private:
|
||||
static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame,
|
||||
jsbytecode* pc, Value* vp, unsigned nvalues);
|
||||
|
||||
public:
|
||||
static JSObject* create(JSContext* cx, AbstractFramePtr frame);
|
||||
|
@ -56,8 +49,16 @@ class AbstractGeneratorObject : public NativeObject {
|
|||
Handle<AbstractGeneratorObject*> genObj, HandleValue arg,
|
||||
HandleValue resumeKind);
|
||||
|
||||
static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame,
|
||||
jsbytecode* pc, unsigned nvalues);
|
||||
static bool initialSuspend(JSContext* cx, HandleObject obj,
|
||||
AbstractFramePtr frame, jsbytecode* pc) {
|
||||
return suspend(cx, obj, frame, pc, nullptr, 0);
|
||||
}
|
||||
|
||||
static bool normalSuspend(JSContext* cx, HandleObject obj,
|
||||
AbstractFramePtr frame, jsbytecode* pc, Value* vp,
|
||||
unsigned nvalues) {
|
||||
return suspend(cx, obj, frame, pc, vp, nvalues);
|
||||
}
|
||||
|
||||
static void finalSuspend(HandleObject obj);
|
||||
|
||||
|
@ -83,19 +84,21 @@ class AbstractGeneratorObject : public NativeObject {
|
|||
setFixedSlot(ARGS_OBJ_SLOT, ObjectValue(argsObj));
|
||||
}
|
||||
|
||||
bool hasStackStorage() const {
|
||||
return getFixedSlot(STACK_STORAGE_SLOT).isObject();
|
||||
bool hasExpressionStack() const {
|
||||
return getFixedSlot(EXPRESSION_STACK_SLOT).isObject();
|
||||
}
|
||||
bool isStackStorageEmpty() const {
|
||||
return stackStorage().getDenseInitializedLength() == 0;
|
||||
bool isExpressionStackEmpty() const {
|
||||
return expressionStack().getDenseInitializedLength() == 0;
|
||||
}
|
||||
ArrayObject& stackStorage() const {
|
||||
return getFixedSlot(STACK_STORAGE_SLOT).toObject().as<ArrayObject>();
|
||||
ArrayObject& expressionStack() const {
|
||||
return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as<ArrayObject>();
|
||||
}
|
||||
void setStackStorage(ArrayObject& stackStorage) {
|
||||
setFixedSlot(STACK_STORAGE_SLOT, ObjectValue(stackStorage));
|
||||
void setExpressionStack(ArrayObject& expressionStack) {
|
||||
setFixedSlot(EXPRESSION_STACK_SLOT, ObjectValue(expressionStack));
|
||||
}
|
||||
void clearExpressionStack() {
|
||||
setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
|
||||
}
|
||||
void clearStackStorage() { setFixedSlot(STACK_STORAGE_SLOT, NullValue()); }
|
||||
|
||||
// The resumeIndex slot is abused for a few purposes. It's undefined if
|
||||
// it hasn't been set yet (before the initial yield), and null if the
|
||||
|
@ -150,7 +153,7 @@ class AbstractGeneratorObject : public NativeObject {
|
|||
setFixedSlot(CALLEE_SLOT, NullValue());
|
||||
setFixedSlot(ENV_CHAIN_SLOT, NullValue());
|
||||
setFixedSlot(ARGS_OBJ_SLOT, NullValue());
|
||||
setFixedSlot(STACK_STORAGE_SLOT, NullValue());
|
||||
setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
|
||||
setFixedSlot(RESUME_INDEX_SLOT, NullValue());
|
||||
}
|
||||
|
||||
|
@ -173,8 +176,8 @@ class AbstractGeneratorObject : public NativeObject {
|
|||
static size_t offsetOfResumeIndexSlot() {
|
||||
return getFixedSlotOffset(RESUME_INDEX_SLOT);
|
||||
}
|
||||
static size_t offsetOfStackStorageSlot() {
|
||||
return getFixedSlotOffset(STACK_STORAGE_SLOT);
|
||||
static size_t offsetOfExpressionStackSlot() {
|
||||
return getFixedSlotOffset(EXPRESSION_STACK_SLOT);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -209,6 +212,8 @@ bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
|
|||
AbstractGeneratorObject* GetGeneratorObjectForFrame(JSContext* cx,
|
||||
AbstractFramePtr frame);
|
||||
|
||||
void SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame);
|
||||
|
||||
inline GeneratorResumeKind IntToResumeKind(int32_t value) {
|
||||
MOZ_ASSERT(uint32_t(value) <= uint32_t(GeneratorResumeKind::Return));
|
||||
return static_cast<GeneratorResumeKind>(value);
|
||||
|
|
|
@ -1274,8 +1274,7 @@ bool js::HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame,
|
|||
if (cx->isClosingGenerator()) {
|
||||
cx->clearPendingException();
|
||||
ok = true;
|
||||
auto* genObj = GetGeneratorObjectForFrame(cx, frame);
|
||||
genObj->setClosed();
|
||||
SetGeneratorClosed(cx, frame);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@ -4266,8 +4265,8 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
ReservedRooted<JSObject*> obj(&rootObject0, ®S.sp[-1].toObject());
|
||||
POP_RETURN_VALUE();
|
||||
MOZ_ASSERT(REGS.stackDepth() == 0);
|
||||
if (!AbstractGeneratorObject::suspend(cx, obj, REGS.fp(), REGS.pc,
|
||||
script->nfixed())) {
|
||||
if (!AbstractGeneratorObject::initialSuspend(cx, obj, REGS.fp(),
|
||||
REGS.pc)) {
|
||||
goto error;
|
||||
}
|
||||
goto successful_return_continuation;
|
||||
|
@ -4278,9 +4277,9 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
MOZ_ASSERT(REGS.fp()->isFunctionFrame());
|
||||
ReservedRooted<JSObject*> obj(&rootObject0, ®S.sp[-1].toObject());
|
||||
if (!AbstractGeneratorObject::suspend(
|
||||
cx, obj, REGS.fp(), REGS.pc,
|
||||
script->nfixed() + REGS.stackDepth() - 2)) {
|
||||
if (!AbstractGeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc,
|
||||
REGS.spForStackDepth(0),
|
||||
REGS.stackDepth() - 2)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
|
@ -514,10 +514,6 @@ class JSFunction : public js::NativeObject {
|
|||
return asyncKind() == js::FunctionAsyncKind::AsyncFunction;
|
||||
}
|
||||
|
||||
bool isGeneratorOrAsync() const {
|
||||
return isGenerator() || isAsync();
|
||||
}
|
||||
|
||||
void initScript(js::BaseScript* script) {
|
||||
MOZ_ASSERT_IF(script, realm() == script->realm());
|
||||
MOZ_ASSERT(isInterpreted());
|
||||
|
|
|
@ -4513,8 +4513,6 @@ js::UniquePtr<ImmutableScriptData> ImmutableScriptData::new_(
|
|||
mozilla::Span<const ScopeNote> scopeNotes,
|
||||
mozilla::Span<const TryNote> tryNotes) {
|
||||
MOZ_RELEASE_ASSERT(code.Length() <= frontend::MaxBytecodeLength);
|
||||
MOZ_ASSERT_IF(!resumeOffsets.IsEmpty(),
|
||||
nfixed <= GeneratorObject::FixedSlotLimit);
|
||||
|
||||
// There are 1-4 copies of SrcNoteType::Null appended after the source
|
||||
// notes. These are a combination of sentinel and padding values.
|
||||
|
|
|
@ -229,48 +229,6 @@ inline void NativeObject::initDenseElements(const Value* src, uint32_t count) {
|
|||
elementsRangePostWriteBarrier(0, count);
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
inline bool NativeObject::initDenseElementsFromRange(JSContext* cx, Iter begin,
|
||||
Iter end) {
|
||||
// This method populates the elements of a particular Array that's an
|
||||
// internal implementation detail of GeneratorObject. Failing any of the
|
||||
// following means the Array has escaped and/or been mistreated.
|
||||
MOZ_ASSERT(isExtensible());
|
||||
MOZ_ASSERT(!isIndexed());
|
||||
MOZ_ASSERT(is<ArrayObject>());
|
||||
MOZ_ASSERT(as<ArrayObject>().lengthIsWritable());
|
||||
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
||||
MOZ_ASSERT(!denseElementsAreFrozen());
|
||||
MOZ_ASSERT(getElementsHeader()->numShiftedElements() == 0);
|
||||
|
||||
MOZ_ASSERT(getDenseInitializedLength() == 0);
|
||||
|
||||
auto size = end - begin;
|
||||
uint32_t count = uint32_t(size);
|
||||
MOZ_ASSERT(count == size);
|
||||
MOZ_ASSERT(count <= uint32_t(INT32_MAX));
|
||||
if (count > getDenseCapacity()) {
|
||||
if (!growElements(cx, count)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HeapSlot* sp = elements_;
|
||||
size_t slot = 0;
|
||||
for (; begin != end; sp++, begin++) {
|
||||
Value v = *begin;
|
||||
#ifdef DEBUG
|
||||
checkStoredValue(v);
|
||||
#endif
|
||||
sp->init(this, HeapSlot::Element, slot++, v);
|
||||
}
|
||||
MOZ_ASSERT(slot == count);
|
||||
|
||||
getElementsHeader()->initializedLength = count;
|
||||
as<ArrayObject>().setLengthInt32(count);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool NativeObject::tryShiftDenseElements(uint32_t count) {
|
||||
MOZ_ASSERT(isExtensible());
|
||||
|
||||
|
|
|
@ -1408,24 +1408,9 @@ class NativeObject : public JSObject {
|
|||
|
||||
inline void copyDenseElements(uint32_t dstStart, const Value* src,
|
||||
uint32_t count);
|
||||
|
||||
inline void initDenseElements(const Value* src, uint32_t count);
|
||||
inline void initDenseElements(JSContext* cx, NativeObject* src,
|
||||
uint32_t srcStart, uint32_t count);
|
||||
|
||||
// Store the Values in the range [begin, end) as elements of this array.
|
||||
//
|
||||
// Preconditions: This must be a boring ArrayObject with dense initialized
|
||||
// length 0: no shifted elements, no frozen elements, no fixed "length", not
|
||||
// indexed, not inextensible, not copy-on-write. Existing capacity is
|
||||
// optional.
|
||||
//
|
||||
// This runs write barriers but does not update types. `end - begin` must
|
||||
// return the size of the range, which must be >= 0 and fit in an int32_t.
|
||||
template <typename Iter>
|
||||
inline MOZ_MUST_USE bool initDenseElementsFromRange(JSContext* cx, Iter begin,
|
||||
Iter end);
|
||||
|
||||
inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart,
|
||||
uint32_t count);
|
||||
inline void reverseDenseElementsNoPreBarrier(uint32_t length);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
#include "builtin/Array.h" // js::NewDenseEmptyArray
|
||||
#include "jit/BaselineFrame.h"
|
||||
#include "jit/RematerializedFrame.h"
|
||||
#include "js/Debug.h"
|
||||
|
@ -187,19 +186,6 @@ inline void InterpreterFrame::unsetIsDebuggee() {
|
|||
flags_ &= ~DEBUGGEE;
|
||||
}
|
||||
|
||||
inline bool InterpreterFrame::saveGeneratorSlots(JSContext* cx, unsigned nslots,
|
||||
ArrayObject* dest) const {
|
||||
return dest->initDenseElementsFromRange(cx, slots(), slots() + nslots);
|
||||
}
|
||||
|
||||
inline void InterpreterFrame::restoreGeneratorSlots(ArrayObject* src) {
|
||||
MOZ_ASSERT(script()->nfixed() <= src->length());
|
||||
MOZ_ASSERT(src->length() <= script()->nslots());
|
||||
MOZ_ASSERT(src->getDenseInitializedLength() == src->length());
|
||||
const Value* srcElements = src->getDenseElements();
|
||||
mozilla::PodCopy(slots(), srcElements, src->length());
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
inline void InterpreterStack::purge(JSRuntime* rt) {
|
||||
|
@ -675,16 +661,6 @@ inline bool AbstractFramePtr::isGeneratorFrame() const {
|
|||
return s->isGenerator() || s->isAsync();
|
||||
}
|
||||
|
||||
inline bool AbstractFramePtr::saveGeneratorSlots(JSContext* cx, unsigned nslots,
|
||||
ArrayObject* dest) const {
|
||||
MOZ_ASSERT(isGeneratorFrame());
|
||||
if (isInterpreterFrame()) {
|
||||
return asInterpreterFrame()->saveGeneratorSlots(cx, nslots, dest);
|
||||
}
|
||||
MOZ_ASSERT(isBaselineFrame(), "unexpected generator frame in Ion");
|
||||
return asBaselineFrame()->saveGeneratorSlots(cx, nslots, dest);
|
||||
}
|
||||
|
||||
inline Value* AbstractFramePtr::argv() const {
|
||||
if (isInterpreterFrame()) {
|
||||
return asInterpreterFrame()->argv();
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Span.h" // for Span
|
||||
#include "mozilla/Variant.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -214,9 +213,6 @@ class AbstractFramePtr {
|
|||
inline bool isFunctionFrame() const;
|
||||
inline bool isGeneratorFrame() const;
|
||||
|
||||
inline bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
|
||||
ArrayObject* dest) const;
|
||||
|
||||
inline unsigned numActualArgs() const;
|
||||
inline unsigned numFormalArgs() const;
|
||||
|
||||
|
@ -645,14 +641,6 @@ class InterpreterFrame {
|
|||
markReturnValue();
|
||||
}
|
||||
|
||||
// Copy values from this frame into a private Array, owned by the
|
||||
// GeneratorObject, for suspending.
|
||||
MOZ_MUST_USE inline bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
|
||||
ArrayObject* dest) const;
|
||||
|
||||
// Copy values from the Array into this stack frame, for resuming.
|
||||
inline void restoreGeneratorSlots(ArrayObject* src);
|
||||
|
||||
void resumeGeneratorFrame(JSObject* envChain) {
|
||||
MOZ_ASSERT(script()->isGenerator() || script()->isAsync());
|
||||
MOZ_ASSERT(isFunctionFrame());
|
||||
|
|
Загрузка…
Ссылка в новой задаче