Implement asynchronous iterators (ES2018).

This commit is contained in:
Fedor 2020-03-12 20:39:34 +03:00
parent 041acd156d
commit 84d4cb2e76
80 changed files with 3197 additions and 733 deletions

View File

@ -779,7 +779,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 40)
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \

View File

@ -0,0 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function AsyncIteratorIdentity() {
return this;
}

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,8 @@ class PromiseObject : public NativeObject
static PromiseObject* create(JSContext* cx, HandleObject executor,
HandleObject proto = nullptr, bool needsWrapping = false);
static PromiseObject* createSkippingExecutor(JSContext* cx);
static JSObject* unforgeableResolve(JSContext* cx, HandleValue value);
static JSObject* unforgeableReject(JSContext* cx, HandleValue value);
@ -149,6 +151,26 @@ AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise);
MOZ_MUST_USE bool
AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value);
class AsyncGeneratorObject;
MOZ_MUST_USE bool
AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value);
MOZ_MUST_USE bool
AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value, bool done);
MOZ_MUST_USE bool
AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue exception);
MOZ_MUST_USE bool
AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind,
HandleValue completionValue, MutableHandleValue result);
bool
AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind);
/**
* A PromiseTask represents a task that can be dispatched to a helper thread
* (via StartPromiseTask), executed (by implementing PromiseTask::execute()),

View File

@ -2800,11 +2800,11 @@ ASTSerializer::generatorExpression(ParseNode* pn, MutableHandleValue dst)
LOCAL_ASSERT(next->isKind(PNK_SEMI) &&
next->pn_kid->isKind(PNK_YIELD) &&
next->pn_kid->pn_left);
next->pn_kid->pn_kid);
RootedValue body(cx);
return expression(next->pn_kid->pn_left, &body) &&
return expression(next->pn_kid->pn_kid, &body) &&
builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst);
}
@ -3146,7 +3146,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_YIELD_STAR:
{
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
RootedValue arg(cx);
return expression(pn->pn_left, &arg) &&
@ -3155,10 +3155,10 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_YIELD:
{
MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
RootedValue arg(cx);
return optExpression(pn->pn_left, &arg) &&
return optExpression(pn->pn_kid, &arg) &&
builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst);
}
@ -3422,10 +3422,10 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
RootedFunction func(cx, pn->pn_funbox->function());
GeneratorStyle generatorStyle =
pn->pn_funbox->isGenerator()
? (pn->pn_funbox->isLegacyGenerator()
? GeneratorStyle::Legacy
: GeneratorStyle::ES6)
pn->pn_funbox->isStarGenerator()
? GeneratorStyle::ES6
: pn->pn_funbox->isLegacyGenerator()
? GeneratorStyle::Legacy
: GeneratorStyle::None;
bool isAsync = pn->pn_funbox->isAsync();
@ -3480,7 +3480,7 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector&
ParseNode* pnstart = pnbody->pn_head;
// Skip over initial yield in generator.
if (pnstart && pnstart->isKind(PNK_YIELD)) {
if (pnstart && pnstart->isKind(PNK_INITIALYIELD)) {
MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD);
pnstart = pnstart->pn_next;
}

View File

@ -110,8 +110,8 @@ resumption value has one of the following forms:
the `new` expression returns the frame's `this` value. Similarly, if
the function is the constructor for a subclass, then a non-object
value may result in a TypeError.
If the frame is a generator or async function, then <i>value</i> must
conform to the iterator protocol: it must be a non-proxy object of the form
If the frame is a generator function, then <i>value</i> must conform to the
iterator protocol: it must be a non-proxy object of the form
<code>{ done: <i>boolean</i>, value: <i>v</i> }</code>, where
both `done` and `value` are ordinary properties.

View File

@ -725,6 +725,19 @@ frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fu
{
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope,
TraceLogger_ParserCompileFunction);
return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd);
}
bool
frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
Maybe<uint32_t> parameterListEnd)
{
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope,
TraceLogger_ParserCompileFunction);
return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd);

View File

@ -84,6 +84,12 @@ CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
JS::SourceBufferHolder& srcBuf,
mozilla::Maybe<uint32_t> parameterListEnd);
MOZ_MUST_USE bool
CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
mozilla::Maybe<uint32_t> parameterListEnd);
MOZ_MUST_USE bool
CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
#define frontend_BytecodeEmitter_h
#include "jscntxt.h"
#include "jsiter.h"
#include "jsopcode.h"
#include "jsscript.h"
@ -98,15 +99,15 @@ struct CGScopeNoteList {
void finish(ScopeNoteArray* array, uint32_t prologueLength);
};
struct CGYieldOffsetList {
struct CGYieldAndAwaitOffsetList {
Vector<uint32_t> list;
uint32_t numYields;
uint32_t numAwaits;
explicit CGYieldOffsetList(ExclusiveContext* cx) : list(cx), numYields(0), numAwaits(0) {}
explicit CGYieldAndAwaitOffsetList(ExclusiveContext* cx) : list(cx), numYields(0), numAwaits(0) {}
MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
size_t length() const { return list.length(); }
void finish(YieldOffsetArray& array, uint32_t prologueLength);
void finish(YieldAndAwaitOffsetArray& array, uint32_t prologueLength);
};
static size_t MaxBytecodeLength = INT32_MAX;
@ -227,9 +228,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter
EmitterScope* varEmitterScope;
NestableControl* innermostNestableControl;
EmitterScope* innermostEmitterScope;
EmitterScope* innermostEmitterScope_;
TDZCheckCache* innermostTDZCheckCache;
#ifdef DEBUG
bool unstableEmitterScope;
friend class AutoCheckUnstableEmitterScope;
#endif
EmitterScope* innermostEmitterScope() const {
MOZ_ASSERT(!unstableEmitterScope);
return innermostEmitterScopeNoCheck();
}
EmitterScope* innermostEmitterScopeNoCheck() const {
return innermostEmitterScope_;
}
CGConstList constList; /* constants to be included with the script */
CGObjectList objectList; /* list of emitted objects */
CGScopeList scopeList; /* list of emitted scopes */
@ -237,10 +252,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
CGScopeNoteList scopeNoteList; /* list of emitted block scope notes */
/*
* For each yield op, map the yield index (stored as bytecode operand) to
* the offset of the next op.
* For each yield or await op, map the yield and await index (stored as
* bytecode operand) to the offset of the next op.
*/
CGYieldOffsetList yieldOffsetList;
CGYieldAndAwaitOffsetList yieldAndAwaitOffsetList;
uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */
@ -318,7 +333,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
EmitterScope* source);
mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(JSAtom* name) {
return locationOfNameBoundInFunctionScope(name, innermostEmitterScope);
return locationOfNameBoundInFunctionScope(name, innermostEmitterScope());
}
void setVarEmitterScope(EmitterScope* emitterScope) {
@ -606,17 +621,29 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitPrepareIteratorResult();
MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
MOZ_MUST_USE bool iteratorResultShape(unsigned* shape);
MOZ_MUST_USE bool emitToIteratorResult(bool done);
MOZ_MUST_USE bool emitGetDotGeneratorInInnermostScope() {
return emitGetDotGeneratorInScope(*innermostEmitterScope());
}
MOZ_MUST_USE bool emitGetDotGeneratorInScope(EmitterScope& currentScope);
MOZ_MUST_USE bool emitInitialYield(ParseNode* pn);
MOZ_MUST_USE bool emitYield(ParseNode* pn);
MOZ_MUST_USE bool emitYieldOp(JSOp op);
MOZ_MUST_USE bool emitYieldStar(ParseNode* iter, ParseNode* gen);
MOZ_MUST_USE bool emitYieldStar(ParseNode* iter);
MOZ_MUST_USE bool emitAwaitInInnermostScope() {
return emitAwaitInScope(*innermostEmitterScope());
}
MOZ_MUST_USE bool emitAwaitInInnermostScope(ParseNode* pn);
MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope);
MOZ_MUST_USE bool emitPropLHS(ParseNode* pn);
MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op);
MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn);
MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow);
MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow,
bool isStarGenerator);
MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName);
@ -703,11 +730,22 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// It will replace that stack value with the corresponding iterator
MOZ_MUST_USE bool emitIterator();
MOZ_MUST_USE bool emitAsyncIterator();
// Pops iterator from the top of the stack. Pushes the result of |.next()|
// onto the stack.
MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
MOZ_MUST_USE bool emitIteratorClose(CompletionKind completionKind = CompletionKind::Normal,
bool allowSelfHosted = false);
MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync,
bool allowSelfHosted = false);
MOZ_MUST_USE bool emitIteratorCloseInScope(EmitterScope& currentScope,
IteratorKind iterKind = IteratorKind::Sync,
CompletionKind completionKind = CompletionKind::Normal,
bool allowSelfHosted = false);
MOZ_MUST_USE bool emitIteratorCloseInInnermostScope(IteratorKind iterKind = IteratorKind::Sync,
CompletionKind completionKind = CompletionKind::Normal,
bool allowSelfHosted = false) {
return emitIteratorCloseInScope(*innermostEmitterScope(), iterKind, completionKind,
allowSelfHosted);
}
template <typename InnerEmitter>
MOZ_MUST_USE bool wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth,
@ -805,6 +843,31 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
};
class MOZ_RAII AutoCheckUnstableEmitterScope {
#ifdef DEBUG
bool prev_;
BytecodeEmitter* bce_;
#endif
public:
AutoCheckUnstableEmitterScope() = delete;
explicit AutoCheckUnstableEmitterScope(BytecodeEmitter* bce)
#ifdef DEBUG
: bce_(bce)
#endif
{
#ifdef DEBUG
prev_ = bce_->unstableEmitterScope;
bce_->unstableEmitterScope = true;
#endif
}
~AutoCheckUnstableEmitterScope() {
#ifdef DEBUG
bce_->unstableEmitterScope = prev_;
#endif
}
};
} /* namespace frontend */
} /* namespace js */

View File

@ -117,9 +117,10 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
// These two aren't statements in the spec, but we sometimes insert them
// in statement lists anyway.
case PNK_INITIALYIELD:
case PNK_YIELD_STAR:
case PNK_YIELD:
MOZ_ASSERT(node->isArity(PN_BINARY));
MOZ_ASSERT(node->isArity(PN_UNARY));
*result = false;
return true;
@ -1775,21 +1776,23 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
case PNK_GENEXP:
return FoldList(cx, pn, parser, inGenexpLambda);
case PNK_INITIALYIELD:
MOZ_ASSERT(pn->isArity(PN_UNARY));
MOZ_ASSERT(pn->pn_kid->isKind(PNK_ASSIGN) &&
pn->pn_kid->pn_left->isKind(PNK_NAME) &&
pn->pn_kid->pn_right->isKind(PNK_GENERATOR));
return true;
case PNK_YIELD_STAR:
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME));
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
MOZ_ASSERT(pn->isArity(PN_UNARY));
return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) ||
(pn->pn_right->isKind(PNK_ASSIGN) &&
pn->pn_right->pn_left->isKind(PNK_NAME) &&
pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
if (!pn->pn_left)
MOZ_ASSERT(pn->isArity(PN_UNARY));
if (!pn->pn_kid)
return true;
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
case PNK_RETURN:
return FoldReturn(cx, pn, parser, inGenexpLambda);

View File

@ -440,20 +440,24 @@ class FullParseHandler
return true;
}
ParseNode* newYieldExpression(uint32_t begin, ParseNode* value, ParseNode* gen,
JSOp op = JSOP_YIELD) {
TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
return new_<BinaryNode>(PNK_YIELD, op, pos, value, gen);
ParseNode* newInitialYieldExpression(uint32_t begin, ParseNode* gen) {
TokenPos pos(begin, begin + 1);
return new_<UnaryNode>(PNK_INITIALYIELD, JSOP_INITIALYIELD, pos, gen);
}
ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value, ParseNode* gen) {
ParseNode* newYieldExpression(uint32_t begin, ParseNode* value) {
TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
return new_<UnaryNode>(PNK_YIELD, JSOP_YIELD, pos, value);
}
ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value) {
TokenPos pos(begin, value->pn_pos.end);
return new_<BinaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen);
return new_<UnaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value);
}
ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value, ParseNode* gen) {
ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value) {
TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
return new_<BinaryNode>(PNK_AWAIT, JSOP_YIELD, pos, value, gen);
return new_<UnaryNode>(PNK_AWAIT, JSOP_AWAIT, pos, value);
}
// Statements
@ -506,8 +510,7 @@ class FullParseHandler
if (!genInit)
return false;
ParseNode* initialYield = newYieldExpression(yieldPos.begin, nullptr, genInit,
JSOP_INITIALYIELD);
ParseNode* initialYield = newInitialYieldExpression(yieldPos.begin, genInit);
if (!initialYield)
return false;

View File

@ -501,24 +501,25 @@ class NameResolver
return false;
break;
case PNK_INITIALYIELD:
MOZ_ASSERT(cur->pn_kid->isKind(PNK_ASSIGN) &&
cur->pn_kid->pn_left->isKind(PNK_NAME) &&
cur->pn_kid->pn_right->isKind(PNK_GENERATOR));
break;
case PNK_YIELD_STAR:
MOZ_ASSERT(cur->isArity(PN_BINARY));
MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME));
if (!resolve(cur->pn_left, prefix))
MOZ_ASSERT(cur->isArity(PN_UNARY));
if (!resolve(cur->pn_kid, prefix))
return false;
break;
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(cur->isArity(PN_BINARY));
if (cur->pn_left) {
if (!resolve(cur->pn_left, prefix))
MOZ_ASSERT(cur->isArity(PN_UNARY));
if (cur->pn_kid) {
if (!resolve(cur->pn_kid, prefix))
return false;
}
MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME) ||
(cur->pn_right->isKind(PNK_ASSIGN) &&
cur->pn_right->pn_left->isKind(PNK_NAME) &&
cur->pn_right->pn_right->isKind(PNK_GENERATOR)));
break;
case PNK_RETURN:

View File

@ -286,22 +286,24 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
return PushResult::Recyclable;
}
// The left half is the expression being yielded. The right half is
// internal goop: a name reference to the invisible '.generator' local
// variable, or an assignment of a PNK_GENERATOR node to the '.generator'
// local, for a synthesized, prepended initial yield. Yum!
// The child is an assignment of a PNK_GENERATOR node to the
// '.generator' local, for a synthesized, prepended initial yield.
case PNK_INITIALYIELD: {
MOZ_ASSERT(pn->isArity(PN_UNARY));
MOZ_ASSERT(pn->pn_kid->isKind(PNK_ASSIGN) &&
pn->pn_kid->pn_left->isKind(PNK_NAME) &&
pn->pn_kid->pn_right->isKind(PNK_GENERATOR));
stack->push(pn->pn_kid);
return PushResult::Recyclable;
}
// The child is the expression being yielded.
case PNK_YIELD_STAR:
case PNK_YIELD:
case PNK_AWAIT: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_right);
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) ||
(pn->pn_right->isKind(PNK_ASSIGN) &&
pn->pn_right->pn_left->isKind(PNK_NAME) &&
pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
if (pn->pn_left)
stack->push(pn->pn_left);
stack->push(pn->pn_right);
MOZ_ASSERT(pn->isArity(PN_UNARY));
if (pn->pn_kid)
stack->push(pn->pn_kid);
return PushResult::Recyclable;
}

View File

@ -83,6 +83,7 @@ class ObjectBox;
F(THROW) \
F(DEBUGGER) \
F(GENERATOR) \
F(INITIALYIELD) \
F(YIELD) \
F(YIELD_STAR) \
F(GENEXP) \
@ -418,8 +419,9 @@ IsTypeofKind(ParseNodeKind kind)
* PNK_LEXICALSCOPE scope pn_u.scope.bindings: scope bindings
* pn_u.scope.body: scope body
* PNK_GENERATOR nullary
* PNK_YIELD, binary pn_left: expr or null; pn_right: generator object
* PNK_YIELD_STAR
* PNK_INITIALYIELD unary pn_kid: generator object
* PNK_YIELD, unary pn_kid: expr or null
* PNK_YIELD_STAR,
* PNK_ARRAYCOMP list pn_count: 1
* pn_head: list of 1 element, which is block
* enclosing for loop(s) and optionally

View File

@ -2474,10 +2474,8 @@ Parser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /* = false
}
static YieldHandling
GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
GetYieldHandling(GeneratorKind generatorKind)
{
if (asyncKind == AsyncFunction)
return YieldIsName;
if (generatorKind == NotGenerator)
return YieldIsName;
return YieldIsKeyword;
@ -2541,7 +2539,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
return null();
funpc.setIsStandaloneFunctionBody();
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
parameterListEnd, /* isStandaloneFunction = */ true))
@ -2699,7 +2697,7 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
switch (pc->generatorKind()) {
case NotGenerator:
MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
MOZ_ASSERT_IF(!pc->isAsync(), pc->lastYieldOffset == startYieldOffset);
break;
case LegacyGenerator:
@ -2715,12 +2713,12 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
break;
case StarGenerator:
MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow);
MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
MOZ_ASSERT(kind != Arrow);
MOZ_ASSERT(type == StatementListBody);
break;
}
if (pc->isGenerator()) {
if (pc->needsDotGeneratorName()) {
MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
if (!declareDotGeneratorName())
return null();
@ -2761,9 +2759,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
#endif
switch (kind) {
case Expression:
flags = (generatorKind == NotGenerator
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
? JSFunction::INTERPRETED_LAMBDA
: JSFunction::INTERPRETED_LAMBDA_GENERATOR);
: JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC);
break;
case Arrow:
flags = JSFunction::INTERPRETED_LAMBDA_ARROW;
@ -2771,9 +2769,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
break;
case Method:
MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
flags = (generatorKind == NotGenerator
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
? JSFunction::INTERPRETED_METHOD
: JSFunction::INTERPRETED_METHOD_GENERATOR);
: JSFunction::INTERPRETED_METHOD_GENERATOR_OR_ASYNC);
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
break;
case ClassConstructor:
@ -2799,9 +2797,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
}
#endif
flags = (generatorKind == NotGenerator
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
? JSFunction::INTERPRETED_NORMAL
: JSFunction::INTERPRETED_GENERATOR);
: JSFunction::INTERPRETED_GENERATOR_OR_ASYNC);
}
// We store the async wrapper in a slot for later access.
@ -3324,7 +3322,6 @@ Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, Node pn, InHand
bool tryAnnexB /* = false */)
{
MOZ_ASSERT_IF(kind == Statement, funName);
MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator);
// When fully parsing a LazyScript, we do not fully reparse its inner
// functions, which are also lazy. Instead, their free variables and
@ -3336,7 +3333,7 @@ Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, Node pn, InHand
}
RootedObject proto(context);
if (generatorKind == StarGenerator) {
if (generatorKind == StarGenerator || asyncKind == AsyncFunction) {
// If we are off the main thread, the generator meta-objects have
// already been created by js::StartOffThreadParseScript, so cx will not
// be necessary.
@ -3408,7 +3405,7 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
// parse to avoid the overhead of a lazy syntax-only parse. Although
// the prediction may be incorrect, IIFEs are common enough that it
// pays off for lots of code.
if (pn->isLikelyIIFE() && generatorKind == NotGenerator)
if (pn->isLikelyIIFE() && generatorKind == NotGenerator && asyncKind == SyncFunction)
break;
Parser<SyntaxParseHandler>* parser = handler.syntaxParser;
@ -3584,7 +3581,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier))
return null();
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
FunctionSyntaxKind syntaxKind = Statement;
if (fun->isClassConstructor())
syntaxKind = ClassConstructor;
@ -3665,7 +3662,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
return false;
uint32_t openedPos = 0;
if (tt != TOK_LC) {
if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
if (funbox->isStarGenerator() || kind == Method ||
kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
IsConstructorKind(kind)) {
error(JSMSG_CURLY_BEFORE_BODY);
@ -3695,7 +3692,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
// whether the arrow function is enclosed in a generator function or not.
// Whereas the |yield| in the function body is always parsed as a name.
// The same goes when parsing |await| in arrow functions.
YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind());
YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
Node body;
{
AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync());
@ -3786,12 +3783,8 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa
if (!tokenStream.getToken(&tt))
return null();
GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
GeneratorKind generatorKind = NotGenerator;
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
error(JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
@ -3817,7 +3810,7 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa
MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
if (!pc->sc()->strict() && generatorKind == NotGenerator) {
if (!pc->sc()->strict() && generatorKind == NotGenerator && asyncKind == SyncFunction) {
// In sloppy mode, try Annex B.3.3 semantics. If making an
// additional 'var' binding of the same name does not throw an
// early error, do so. This 'var' binding would be assigned
@ -3841,7 +3834,7 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa
if (!pn)
return null();
YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
return functionDefinition(toStringStart, pn, InAllowed, newYieldHandling,
name, Statement, generatorKind, asyncKind, tryAnnexB);
}
@ -3854,22 +3847,18 @@ Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction inv
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
GeneratorKind generatorKind = NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
error(JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
}
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
RootedPropertyName name(context);
if (TokenKindIsPossibleIdentifier(tt)) {
@ -5971,6 +5960,7 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
template <class ParseHandler>
bool
Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
IteratorKind iterKind,
ParseNodeKind* forHeadKind,
Node* forInitialPart,
Maybe<ParseContext::Scope>& forLoopLexicalScope,
@ -6061,6 +6051,11 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
if (!matchInOrOf(&isForIn, &isForOf))
return false;
if (iterKind == IteratorKind::Async && !isForOf) {
error(JSMSG_FOR_AWAIT_NOT_OF);
return false;
}
// If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled
// the init expression; the caller handles the rest. Allow the Operand
// modifier when regetting: Operand must be used to examine the ';' in
@ -6134,6 +6129,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
ParseContext::Statement stmt(pc, StatementKind::ForLoop);
bool isForEach = false;
IteratorKind iterKind = IteratorKind::Sync;
unsigned iflags = 0;
if (allowsForEachIn()) {
@ -6148,6 +6144,17 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
}
}
if (pc->isAsync()) {
bool matched;
if (!tokenStream.matchToken(&matched, TOK_AWAIT))
return null();
if (matched) {
iflags |= JSITER_FORAWAITOF;
iterKind = IteratorKind::Async;
}
}
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
// PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type.
@ -6185,7 +6192,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
//
// In either case the subsequent token can be consistently accessed using
// TokenStream::None semantics.
if (!forHeadStart(yieldHandling, &headKind, &startNode, forLoopLexicalScope,
if (!forHeadStart(yieldHandling, iterKind, &headKind, &startNode, forLoopLexicalScope,
&iteratedExpr))
{
return null();
@ -6549,29 +6556,6 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
return pn;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::newYieldExpression(uint32_t begin, typename ParseHandler::Node expr,
bool isYieldStar)
{
Node generator = newDotGeneratorName();
if (!generator)
return null();
if (isYieldStar)
return handler.newYieldStarExpression(begin, expr, generator);
return handler.newYieldExpression(begin, expr, generator);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::newAwaitExpression(uint32_t begin, typename ParseHandler::Node expr)
{
Node generator = newDotGeneratorName();
if (!generator)
return null();
return handler.newAwaitExpression(begin, expr, generator);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::yieldExpression(InHandling inHandling)
@ -6620,7 +6604,9 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
if (!exprNode)
return null();
}
return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR);
if (kind == PNK_YIELD_STAR)
return handler.newYieldStarExpression(begin, exprNode);
return handler.newYieldExpression(begin, exprNode);
}
case NotGenerator:
@ -6697,7 +6683,7 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
return null();
}
return newYieldExpression(begin, exprNode);
return handler.newYieldExpression(begin, exprNode);
}
}
@ -8187,7 +8173,6 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
uint32_t toStringStart = pos().begin;
tokenStream.ungetToken();
GeneratorKind generatorKind = NotGenerator;
FunctionAsyncKind asyncKind = SyncFunction;
if (next == TOK_ASYNC) {
@ -8200,7 +8185,6 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (nextSameLine == TOK_ARROW) {
tokenStream.ungetToken();
} else {
generatorKind = StarGenerator;
asyncKind = AsyncFunction;
}
}
@ -8210,7 +8194,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
return null();
Node arrowFunc = functionDefinition(toStringStart, pn, inHandling, yieldHandling, nullptr,
Arrow, generatorKind, asyncKind);
Arrow, NotGenerator, asyncKind);
if (!arrowFunc)
return null();
@ -8454,7 +8438,7 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
if (!kid)
return null();
pc->lastAwaitOffset = begin;
return newAwaitExpression(begin, kid);
return handler.newAwaitExpression(begin, kid);
}
}
@ -8712,7 +8696,7 @@ Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
return handler.newArrayPush(begin, bodyExpr);
MOZ_ASSERT(comprehensionKind == StarGenerator);
Node yieldExpr = newYieldExpression(begin, bodyExpr);
Node yieldExpr = handler.newYieldExpression(begin, bodyExpr);
if (!yieldExpr)
return null();
yieldExpr = handler.parenthesize(yieldExpr);
@ -9494,11 +9478,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling,
bool isGenerator = false;
bool isAsync = false;
if (ltok == TOK_MUL) {
isGenerator = true;
if (!tokenStream.getToken(&ltok))
return null();
}
if (ltok == TOK_ASYNC) {
// AsyncMethod[Yield, Await]:
@ -9526,9 +9505,10 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling,
}
}
if (isAsync && isGenerator) {
error(JSMSG_ASYNC_GENERATOR);
return null();
if (ltok == TOK_MUL) {
isGenerator = true;
if (!tokenStream.getToken(&ltok))
return null();
}
propAtom.set(nullptr);
@ -9950,8 +9930,7 @@ Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType prop
MOZ_CRASH("Parser: methodDefinition: unexpected property type");
}
GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod ||
propType == PropertyType::AsyncMethod)
GeneratorKind generatorKind = propType == PropertyType::GeneratorMethod
? StarGenerator
: NotGenerator;
@ -9959,7 +9938,7 @@ Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType prop
? AsyncFunction
: SyncFunction;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
Node pn = handler.newFunctionExpression();
if (!pn)

View File

@ -12,6 +12,7 @@
#include "mozilla/Array.h"
#include "mozilla/Maybe.h"
#include "jsiter.h"
#include "jspubtd.h"
#include "frontend/BytecodeCompiler.h"
@ -496,10 +497,6 @@ class ParseContext : public Nestable<ParseContext>
return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator;
}
bool isGenerator() const {
return generatorKind() != NotGenerator;
}
bool isLegacyGenerator() const {
return generatorKind() == LegacyGenerator;
}
@ -512,6 +509,10 @@ class ParseContext : public Nestable<ParseContext>
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
}
bool needsDotGeneratorName() const {
return isStarGenerator() || isLegacyGenerator() || isAsync();
}
FunctionAsyncKind asyncKind() const {
return isAsync() ? AsyncFunction : SyncFunction;
}
@ -818,7 +819,9 @@ class ParserBase : public StrictModeGetter
// whether it's prohibited due to strictness, JS version, or occurrence
// inside a star generator.
bool yieldExpressionsSupported() {
return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
return (versionNumber() >= JSVERSION_1_7 && !pc->isAsync()) ||
pc->isStarGenerator() ||
pc->isLegacyGenerator();
}
virtual bool strictMode() { return pc->sc()->strict(); }
@ -1108,8 +1111,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
inline Node newName(PropertyName* name);
inline Node newName(PropertyName* name, TokenPos pos);
inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
inline Node newAwaitExpression(uint32_t begin, Node expr);
inline bool abortIfSyntaxParser();
@ -1196,6 +1197,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
Node forStatement(YieldHandling yieldHandling);
bool forHeadStart(YieldHandling yieldHandling,
IteratorKind iterKind,
ParseNodeKind* forHeadKind,
Node* forInitialPart,
mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,

View File

@ -520,7 +520,9 @@ class FunctionBox : public ObjectBox, public SharedContext
return hasExtensibleScope() ||
needsHomeObject() ||
isDerivedClassConstructor() ||
isGenerator();
isStarGenerator() ||
isLegacyGenerator() ||
isAsync();
}
bool hasExtraBodyVarScope() const {
@ -531,7 +533,7 @@ class FunctionBox : public ObjectBox, public SharedContext
bool needsExtraBodyVarEnvironmentRegardlessOfBindings() const {
MOZ_ASSERT(hasParameterExprs);
return hasExtensibleScope() || isGenerator();
return hasExtensibleScope() || needsDotGeneratorName();
}
bool isLikelyConstructorWrapper() const {
@ -539,10 +541,21 @@ class FunctionBox : public ObjectBox, public SharedContext
}
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
FunctionAsyncKind asyncKind() const { return AsyncKindFromBits(asyncKindBits_); }
bool needsFinalYield() const {
return isStarGenerator() || isLegacyGenerator() || isAsync();
}
bool needsDotGeneratorName() const {
return isStarGenerator() || isLegacyGenerator() || isAsync();
}
bool needsIteratorResult() const {
return isStarGenerator();
}
bool isAsync() const { return asyncKind() == AsyncFunction; }
bool isArrow() const { return function()->isArrow(); }
@ -560,7 +573,7 @@ class FunctionBox : public ObjectBox, public SharedContext
// A generator kind can be set at initialization, or when "yield" is
// first seen. In both cases the transition can only happen from
// NotGenerator.
MOZ_ASSERT(!isGenerator());
MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator());
generatorKindBits_ = GeneratorKindAsBits(kind);
}
@ -655,7 +668,11 @@ SharedContext::asModuleContext()
inline bool
SharedContext::allBindingsClosedOver()
{
return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator());
return bindingsAccessedDynamically() ||
(isFunctionBox() &&
(asFunctionBox()->isStarGenerator() ||
asFunctionBox()->isLegacyGenerator() ||
asFunctionBox()->isAsync()));
}
} // namespace frontend

View File

@ -301,9 +301,9 @@ class SyntaxParseHandler
MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; }
MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
Node newAwaitExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
// Statements

View File

@ -9,28 +9,6 @@ async function f() {
}
`);
// To continue testing after uncaught exception, remember the exception and
// return normal completeion.
var currentFrame;
var uncaughtException;
dbg.uncaughtExceptionHook = function(e) {
uncaughtException = e;
return {
return: currentFrame.eval("({ done: true, value: 'uncaught' })").return
};
};
function testUncaughtException() {
uncaughtException = undefined;
var val = g.eval(`
var val;
f().then(v => { val = v });
drainJobQueue();
val;
`);
assertEq(val, "uncaught");
assertEq(uncaughtException instanceof TypeError, true);
}
// Just continue
dbg.onExceptionUnwind = function(frame) {
return undefined;
@ -42,83 +20,10 @@ drainJobQueue();
assertEq(exc instanceof ReferenceError, true);
`);
// Should return object.
// Return with resumption value.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: "foo"
};
};
testUncaughtException();
// The object should have `done` property and `value` property.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({})").return
};
};
testUncaughtException();
// The object should have `done` property.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({ value: 10 })").return
};
};
testUncaughtException();
// The object should have `value` property.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({ done: true })").return
};
};
testUncaughtException();
// `done` property should be a boolean value.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({ done: 10, value: 10 })").return
};
};
testUncaughtException();
// `done` property shouldn't be an accessor.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({ get done() { return true; }, value: 10 })").return
};
};
testUncaughtException();
// `value` property shouldn't be an accessor.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({ done: true, get value() { return 10; } })").return
};
};
testUncaughtException();
// The object shouldn't be a Proxy.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("new Proxy({ done: true, value: 10 }, {})").return
};
};
testUncaughtException();
// Correct resumption value.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({ done: true, value: 10 })").return
return: 10
};
};
var val = g.eval(`

View File

@ -25,6 +25,7 @@
#include "jit/VMFunctions.h"
#include "js/UniquePtr.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/EnvironmentObject.h"
#include "vm/Interpreter.h"
#include "vm/TraceLogging.h"
@ -43,7 +44,7 @@ using mozilla::AssertedCast;
BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
: BaselineCompilerSpecific(cx, alloc, script),
yieldOffsets_(cx),
yieldAndAwaitOffsets_(cx),
modifiesArguments_(false)
{
}
@ -210,7 +211,7 @@ BaselineCompiler::compile()
pcMappingIndexEntries.length(),
pcEntries.length(),
bytecodeTypeMapEntries,
yieldOffsets_.length(),
yieldAndAwaitOffsets_.length(),
traceLoggerToggleOffsets_.length()),
JS::DeletePolicy<BaselineScript>(cx->runtime()));
if (!baselineScript) {
@ -275,7 +276,7 @@ BaselineCompiler::compile()
// searches for the sought entry when queries are in linear order.
bytecodeMap[script->nTypeSets()] = 0;
baselineScript->copyYieldEntries(script, yieldOffsets_);
baselineScript->copyYieldAndAwaitEntries(script, yieldAndAwaitOffsets_);
if (compileDebugInstrumentation_)
baselineScript->setHasDebugInstrumentation();
@ -3908,6 +3909,50 @@ BaselineCompiler::emit_JSOP_TOASYNC()
return true;
}
typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
static const VMFunction ToAsyncGenInfo =
FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
bool
BaselineCompiler::emit_JSOP_TOASYNCGEN()
{
frame.syncStack(0);
masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
prepareVMCall();
pushArg(R0.scratchReg());
if (!callVM(ToAsyncGenInfo))
return false;
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
frame.pop();
frame.push(R0);
return true;
}
typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject);
static const VMFunction ToAsyncIterInfo =
FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
bool
BaselineCompiler::emit_JSOP_TOASYNCITER()
{
frame.syncStack(0);
masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
prepareVMCall();
pushArg(R0.scratchReg());
if (!callVM(ToAsyncIterInfo))
return false;
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
frame.pop();
frame.push(R0);
return true;
}
typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue);
static const VMFunction ThrowObjectCoercibleInfo =
FunctionInfo<ThrowObjectCoercibleFn>(ThrowObjectCoercible, "ThrowObjectCoercible");
@ -4174,27 +4219,28 @@ BaselineCompiler::emit_JSOP_GENERATOR()
}
bool
BaselineCompiler::addYieldOffset()
BaselineCompiler::addYieldAndAwaitOffset()
{
MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD);
MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT);
uint32_t yieldIndex = GET_UINT24(pc);
uint32_t yieldAndAwaitIndex = GET_UINT24(pc);
while (yieldIndex >= yieldOffsets_.length()) {
if (!yieldOffsets_.append(0))
while (yieldAndAwaitIndex >= yieldAndAwaitOffsets_.length()) {
if (!yieldAndAwaitOffsets_.append(0))
return false;
}
static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH,
"code below assumes INITIALYIELD and YIELD have same length");
yieldOffsets_[yieldIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH);
static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH &&
JSOP_INITIALYIELD_LENGTH == JSOP_AWAIT_LENGTH,
"code below assumes INITIALYIELD and YIELD and AWAIT have same length");
yieldAndAwaitOffsets_[yieldAndAwaitIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH);
return true;
}
bool
BaselineCompiler::emit_JSOP_INITIALYIELD()
{
if (!addYieldOffset())
if (!addYieldAndAwaitOffset())
return false;
frame.syncStack(0);
@ -4204,7 +4250,8 @@ BaselineCompiler::emit_JSOP_INITIALYIELD()
masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), genObj);
MOZ_ASSERT(GET_UINT24(pc) == 0);
masm.storeValue(Int32Value(0), Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()));
masm.storeValue(Int32Value(0),
Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()));
Register envObj = R0.scratchReg();
Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot());
@ -4233,7 +4280,7 @@ static const VMFunction NormalSuspendInfo =
bool
BaselineCompiler::emit_JSOP_YIELD()
{
if (!addYieldOffset())
if (!addYieldAndAwaitOffset())
return false;
// Store generator in R0.
@ -4250,7 +4297,7 @@ BaselineCompiler::emit_JSOP_YIELD()
// generator is in the closing state, see GeneratorObject::suspend.
masm.storeValue(Int32Value(GET_UINT24(pc)),
Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()));
Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()));
Register envObj = R0.scratchReg();
Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot());
@ -4282,6 +4329,12 @@ BaselineCompiler::emit_JSOP_YIELD()
return emitReturn();
}
bool
BaselineCompiler::emit_JSOP_AWAIT()
{
return emit_JSOP_YIELD();
}
typedef bool (*DebugAfterYieldFn)(JSContext*, BaselineFrame*);
static const VMFunction DebugAfterYieldInfo =
FunctionInfo<DebugAfterYieldFn>(jit::DebugAfterYield, "DebugAfterYield");
@ -4501,16 +4554,17 @@ BaselineCompiler::emit_JSOP_RESUME()
masm.pushValue(retVal);
if (resumeKind == GeneratorObject::NEXT) {
// Determine the resume address based on the yieldIndex and the
// yieldIndex -> native table in the BaselineScript.
// Determine the resume address based on the yieldAndAwaitIndex and the
// yieldAndAwaitIndex -> native table in the BaselineScript.
masm.load32(Address(scratch1, BaselineScript::offsetOfYieldEntriesOffset()), scratch2);
masm.addPtr(scratch2, scratch1);
masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), scratch2);
masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()),
scratch2);
masm.loadPtr(BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))), scratch1);
// Mark as running and jump to the generator's JIT code.
masm.storeValue(Int32Value(GeneratorObject::YIELD_INDEX_RUNNING),
Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()));
masm.storeValue(Int32Value(GeneratorObject::YIELD_AND_AWAIT_INDEX_RUNNING),
Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()));
masm.jump(scratch1);
} else {
MOZ_ASSERT(resumeKind == GeneratorObject::THROW || resumeKind == GeneratorObject::CLOSE);

View File

@ -196,6 +196,8 @@ namespace jit {
_(JSOP_RUNONCE) \
_(JSOP_REST) \
_(JSOP_TOASYNC) \
_(JSOP_TOASYNCGEN) \
_(JSOP_TOASYNCITER) \
_(JSOP_TOID) \
_(JSOP_TOSTRING) \
_(JSOP_TABLESWITCH) \
@ -207,6 +209,7 @@ namespace jit {
_(JSOP_GENERATOR) \
_(JSOP_INITIALYIELD) \
_(JSOP_YIELD) \
_(JSOP_AWAIT) \
_(JSOP_DEBUGAFTERYIELD) \
_(JSOP_FINALYIELDRVAL) \
_(JSOP_RESUME) \
@ -255,9 +258,9 @@ class BaselineCompiler : public BaselineCompilerSpecific
// equivalent positions when debug mode is off.
CodeOffset postDebugPrologueOffset_;
// For each INITIALYIELD or YIELD op, this Vector maps the yield index
// to the bytecode offset of the next op.
Vector<uint32_t> yieldOffsets_;
// For each INITIALYIELD or YIELD or AWAIT op, this Vector maps the yield
// index to the bytecode offset of the next op.
Vector<uint32_t> yieldAndAwaitOffsets_;
// Whether any on stack arguments are modified.
bool modifiesArguments_;
@ -349,7 +352,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry);
MOZ_MUST_USE bool addYieldOffset();
MOZ_MUST_USE bool addYieldAndAwaitOffset();
void getEnvironmentCoordinateObject(Register reg);
Address getEnvironmentCoordinateAddressFromObject(Register objReg, Register reg);

View File

@ -6600,12 +6600,13 @@ ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm
masm.branchTestObjClass(Assembler::NotEqual, genObj, scratch, &StarGeneratorObject::class_,
&returnFalse);
// If the yield index slot holds an int32 value < YIELD_INDEX_CLOSING,
// If the yield index slot holds an int32 value < YIELD_AND_AWAIT_INDEX_CLOSING,
// the generator is suspended.
masm.loadValue(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), argVal);
masm.loadValue(Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()), argVal);
masm.branchTestInt32(Assembler::NotEqual, argVal, &returnFalse);
masm.unboxInt32(argVal, scratch);
masm.branch32(Assembler::AboveOrEqual, scratch, Imm32(StarGeneratorObject::YIELD_INDEX_CLOSING),
masm.branch32(Assembler::AboveOrEqual, scratch,
Imm32(StarGeneratorObject::YIELD_AND_AWAIT_INDEX_CLOSING),
&returnFalse);
masm.moveValue(BooleanValue(true), R0);

View File

@ -760,12 +760,12 @@ BaselineScript::icEntryFromReturnAddress(uint8_t* returnAddr)
}
void
BaselineScript::copyYieldEntries(JSScript* script, Vector<uint32_t>& yieldOffsets)
BaselineScript::copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& yieldAndAwaitOffsets)
{
uint8_t** entries = yieldEntryList();
for (size_t i = 0; i < yieldOffsets.length(); i++) {
uint32_t offset = yieldOffsets[i];
for (size_t i = 0; i < yieldAndAwaitOffsets.length(); i++) {
uint32_t offset = yieldAndAwaitOffsets[i];
entries[i] = nativeCodeForPC(script, script->offsetToPC(offset));
}
}

View File

@ -397,7 +397,7 @@ struct BaselineScript
void copyICEntries(JSScript* script, const BaselineICEntry* entries, MacroAssembler& masm);
void adoptFallbackStubs(FallbackICStubSpace* stubSpace);
void copyYieldEntries(JSScript* script, Vector<uint32_t>& yieldOffsets);
void copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& yieldAndAwaitOffsets);
PCMappingIndexEntry& pcMappingIndexEntry(size_t index);
CompactBufferReader pcMappingReader(size_t indexEntry);

View File

@ -41,6 +41,7 @@
#include "jit/RangeAnalysis.h"
#include "jit/SharedICHelpers.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/MatchPairs.h"
#include "vm/RegExpObject.h"
#include "vm/RegExpStatics.h"
@ -10213,6 +10214,28 @@ CodeGenerator::visitToAsync(LToAsync* lir)
callVM(ToAsyncInfo, lir);
}
typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
static const VMFunction ToAsyncGenInfo =
FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
void
CodeGenerator::visitToAsyncGen(LToAsyncGen* lir)
{
pushArg(ToRegister(lir->unwrapped()));
callVM(ToAsyncGenInfo, lir);
}
typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject);
static const VMFunction ToAsyncIterInfo =
FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
void
CodeGenerator::visitToAsyncIter(LToAsyncIter* lir)
{
pushArg(ToRegister(lir->unwrapped()));
callVM(ToAsyncIterInfo, lir);
}
typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue,
MutableHandleValue);
static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation, "ToIdOperation");

View File

@ -286,6 +286,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitTypeOfV(LTypeOfV* lir);
void visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool);
void visitToAsync(LToAsync* lir);
void visitToAsyncGen(LToAsyncGen* lir);
void visitToAsyncIter(LToAsyncIter* lir);
void visitToIdV(LToIdV* lir);
template<typename T> void emitLoadElementT(LLoadElementT* lir, const T& source);
void visitLoadElementT(LLoadElementT* lir);

View File

@ -2320,7 +2320,9 @@ IonCompile(JSContext* cx, JSScript* script,
static bool
CheckFrame(JSContext* cx, BaselineFrame* frame)
{
MOZ_ASSERT(!frame->script()->isGenerator());
MOZ_ASSERT(!frame->script()->isStarGenerator());
MOZ_ASSERT(!frame->script()->isLegacyGenerator());
MOZ_ASSERT(!frame->script()->isAsync());
MOZ_ASSERT(!frame->isDebuggerEvalFrame());
MOZ_ASSERT(!frame->isEvalFrame());
@ -2351,11 +2353,16 @@ CheckScript(JSContext* cx, JSScript* script, bool osr)
return false;
}
if (script->isGenerator()) {
if (script->isStarGenerator() || script->isLegacyGenerator()) {
TrackAndSpewIonAbort(cx, script, "generator script");
return false;
}
if (script->isAsync()) {
TrackAndSpewIonAbort(cx, script, "async script");
return false;
}
if (script->hasNonSyntacticScope() && !script->functionNonDelazifying()) {
// Support functions with a non-syntactic global scope but not other
// scripts. For global scripts, IonBuilder currently uses the global

View File

@ -4402,8 +4402,14 @@ jit::AnalyzeArgumentsUsage(JSContext* cx, JSScript* scriptArg)
// direct eval is present.
//
// FIXME: Don't build arguments for ES6 generator expressions.
if (scriptArg->isDebuggee() || script->isGenerator() || script->bindingsAccessedDynamically())
if (scriptArg->isDebuggee() ||
script->isStarGenerator() ||
script->isLegacyGenerator() ||
script->isAsync() ||
script->bindingsAccessedDynamically())
{
return true;
}
if (!jit::IsIonEnabled(cx))
return true;

View File

@ -2130,6 +2130,12 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_TOASYNC:
return jsop_toasync();
case JSOP_TOASYNCGEN:
return jsop_toasyncgen();
case JSOP_TOASYNCITER:
return jsop_toasynciter();
case JSOP_TOID:
return jsop_toid();
@ -3351,7 +3357,7 @@ IonBuilder::whileOrForInLoop(jssrcnote* sn)
unsigned stackPhiCount;
if (SN_TYPE(sn) == SRC_FOR_OF)
stackPhiCount = 2;
stackPhiCount = 3;
else if (SN_TYPE(sn) == SRC_FOR_IN)
stackPhiCount = 1;
else
@ -13195,6 +13201,34 @@ IonBuilder::jsop_toasync()
return resumeAfter(ins);
}
bool
IonBuilder::jsop_toasyncgen()
{
MDefinition* unwrapped = current->pop();
MOZ_ASSERT(unwrapped->type() == MIRType::Object);
MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped);
current->add(ins);
current->push(ins);
return resumeAfter(ins);
}
bool
IonBuilder::jsop_toasynciter()
{
MDefinition* unwrapped = current->pop();
MOZ_ASSERT(unwrapped->type() == MIRType::Object);
MToAsyncIter* ins = MToAsyncIter::New(alloc(), unwrapped);
current->add(ins);
current->push(ins);
return resumeAfter(ins);
}
bool
IonBuilder::jsop_toid()
{

View File

@ -762,6 +762,8 @@ class IonBuilder
MOZ_MUST_USE bool jsop_globalthis();
MOZ_MUST_USE bool jsop_typeof();
MOZ_MUST_USE bool jsop_toasync();
MOZ_MUST_USE bool jsop_toasyncgen();
MOZ_MUST_USE bool jsop_toasynciter();
MOZ_MUST_USE bool jsop_toid();
MOZ_MUST_USE bool jsop_iter(uint8_t flags);
MOZ_MUST_USE bool jsop_itermore();

View File

@ -1175,6 +1175,22 @@ LIRGenerator::visitToAsync(MToAsync* ins)
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitToAsyncGen(MToAsyncGen* ins)
{
LToAsyncGen* lir = new(alloc()) LToAsyncGen(useRegisterAtStart(ins->input()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitToAsyncIter(MToAsyncIter* ins)
{
LToAsyncIter* lir = new(alloc()) LToAsyncIter(useRegisterAtStart(ins->input()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitToId(MToId* ins)
{

View File

@ -121,6 +121,8 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitCompare(MCompare* comp);
void visitTypeOf(MTypeOf* ins);
void visitToAsync(MToAsync* ins);
void visitToAsyncGen(MToAsyncGen* ins);
void visitToAsyncIter(MToAsyncIter* ins);
void visitToId(MToId* ins);
void visitBitNot(MBitNot* ins);
void visitBitAnd(MBitAnd* ins);

View File

@ -5784,6 +5784,36 @@ class MToAsync
TRIVIAL_NEW_WRAPPERS
};
class MToAsyncGen
: public MUnaryInstruction,
public SingleObjectPolicy::Data
{
explicit MToAsyncGen(MDefinition* unwrapped)
: MUnaryInstruction(unwrapped)
{
setResultType(MIRType::Object);
}
public:
INSTRUCTION_HEADER(ToAsyncGen)
TRIVIAL_NEW_WRAPPERS
};
class MToAsyncIter
: public MUnaryInstruction,
public SingleObjectPolicy::Data
{
explicit MToAsyncIter(MDefinition* unwrapped)
: MUnaryInstruction(unwrapped)
{
setResultType(MIRType::Object);
}
public:
INSTRUCTION_HEADER(ToAsyncIter)
TRIVIAL_NEW_WRAPPERS
};
class MToId
: public MUnaryInstruction,
public BoxInputsPolicy::Data

View File

@ -79,6 +79,8 @@ namespace jit {
_(BitNot) \
_(TypeOf) \
_(ToAsync) \
_(ToAsyncGen) \
_(ToAsyncIter) \
_(ToId) \
_(BitAnd) \
_(BitOr) \

View File

@ -735,7 +735,7 @@ bool
NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame, jsbytecode* pc,
uint32_t stackDepth)
{
MOZ_ASSERT(*pc == JSOP_YIELD);
MOZ_ASSERT(*pc == JSOP_YIELD || *pc == JSOP_AWAIT);
// Return value is still on the stack.
MOZ_ASSERT(stackDepth >= 1);
@ -816,7 +816,7 @@ GeneratorThrowOrClose(JSContext* cx, BaselineFrame* frame, Handle<GeneratorObjec
// work. This function always returns false, so we're guaranteed to enter
// the exception handler where we will clear the pc.
JSScript* script = frame->script();
uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()];
uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()];
frame->setOverridePc(script->offsetToPC(offset));
MOZ_ALWAYS_TRUE(DebugAfterYield(cx, frame));

View File

@ -1606,6 +1606,32 @@ class LToAsync : public LCallInstructionHelper<1, 1, 0>
}
};
class LToAsyncGen : public LCallInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(ToAsyncGen)
explicit LToAsyncGen(const LAllocation& input) {
setOperand(0, input);
}
const LAllocation* unwrapped() {
return getOperand(0);
}
};
class LToAsyncIter : public LCallInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(ToAsyncIter)
explicit LToAsyncIter(const LAllocation& input) {
setOperand(0, input);
}
const LAllocation* unwrapped() {
return getOperand(0);
}
};
class LToIdV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
{
public:

View File

@ -353,6 +353,8 @@
_(Rest) \
_(TypeOfV) \
_(ToAsync) \
_(ToAsyncGen) \
_(ToAsyncIter) \
_(ToIdV) \
_(Floor) \
_(FloorF) \

View File

@ -185,7 +185,6 @@ MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array compre
MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large")
MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *")
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async")
MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression")
MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
@ -438,7 +437,6 @@ MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not
// Debugger
MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
MSG_DEF(JSMSG_DEBUG_BAD_AWAIT, 0, JSEXN_TYPEERR, "await expression received invalid value")
MSG_DEF(JSMSG_DEBUG_BAD_LINE, 0, JSEXN_TYPEERR, "invalid line number")
MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset")
MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
@ -594,3 +592,9 @@ MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "P
// Iterator
MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of iterator is not callable")
MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
// Async Iteration
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'")
MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")

View File

@ -70,6 +70,7 @@
#include "js/StructuredClone.h"
#include "js/Utility.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/DateObject.h"
#include "vm/Debugger.h"
#include "vm/EnvironmentObject.h"
@ -3591,6 +3592,11 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle
return nullptr;
}
if (IsWrappedAsyncGenerator(fun)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
if (CanReuseScriptForClone(cx->compartment(), fun, env)) {
// If the script is to be reused, either the script can already handle
// non-syntactic scopes, or there is only the standard global lexical

View File

@ -5088,6 +5088,7 @@ GetSymbolDescription(HandleSymbol symbol);
macro(toPrimitive) \
macro(toStringTag) \
macro(unscopables) \
macro(asyncIterator) \
macro(matchAll)
enum class SymbolCode : uint32_t {

View File

@ -32,6 +32,7 @@
#include "vm/ErrorObject.h"
#include "vm/GlobalObject.h"
#include "vm/SavedStacks.h"
#include "vm/SelfHosting.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
@ -1091,3 +1092,19 @@ js::ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& byte
return "<<error converting value to string>>";
return bytes.encodeLatin1(cx, str);
}
bool
js::GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
{
FixedInvokeArgs<1> args(cx);
args[0].set(Int32Value(errorNumber));
return CallSelfHostedFunction(cx, "GetInternalError", NullHandleValue, args, error);
}
bool
js::GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
{
FixedInvokeArgs<1> args(cx);
args[0].set(Int32Value(errorNumber));
return CallSelfHostedFunction(cx, "GetTypeError", NullHandleValue, args, error);
}

View File

@ -131,6 +131,11 @@ class AutoAssertNoPendingException
extern const char*
ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes);
bool
GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
bool
GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
} // namespace js
#endif /* jsexn_h */

View File

@ -948,6 +948,7 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx);
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
#define JSITER_SYMBOLS 0x20 /* also include symbol property keys */
#define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
#define JSITER_FORAWAITOF 0x80 /* for-await-of */
JS_FRIEND_API(bool)
RunningWithTrustedPrincipals(JSContext* cx);

View File

@ -41,6 +41,7 @@
#include "js/CallNonGenericMethod.h"
#include "js/Proxy.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/Debugger.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
@ -131,7 +132,11 @@ IsFunctionInStrictMode(JSFunction* fun)
static bool
IsNewerTypeFunction(JSFunction* fun) {
return fun->isArrow() || fun->isGenerator() || fun->isAsync() || fun->isMethod();
return fun->isArrow() ||
fun->isStarGenerator() ||
fun->isLegacyGenerator() ||
fun->isAsync() ||
fun->isMethod();
}
// Beware: this function can be invoked on *any* function! That includes
@ -308,6 +313,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args)
JSFunction* callerFun = &callerObj->as<JSFunction>();
if (IsWrappedAsyncFunction(callerFun))
callerFun = GetUnwrappedAsyncFunction(callerFun);
else if (IsWrappedAsyncGenerator(callerFun))
callerFun = GetUnwrappedAsyncGenerator(callerFun);
MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
if (callerFun->strict()) {
@ -360,13 +367,15 @@ static const JSPropertySpec function_properties[] = {
static bool
ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId id)
{
MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative());
bool isAsyncGenerator = IsWrappedAsyncGenerator(fun);
MOZ_ASSERT_IF(!isAsyncGenerator, fun->isInterpreted() || fun->isAsmJSNative());
MOZ_ASSERT(id == NameToId(cx->names().prototype));
// Assert that fun is not a compiler-created function object, which
// must never leak to script or embedding code and then be mutated.
// Also assert that fun is not bound, per the ES5 15.3.4.5 ref above.
MOZ_ASSERT(!IsInternalFunctionObject(*fun));
MOZ_ASSERT_IF(!isAsyncGenerator, !IsInternalFunctionObject(*fun));
MOZ_ASSERT(!fun->isBoundFunction());
// Make the prototype object an instance of Object with the same parent as
@ -376,7 +385,9 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId
bool isStarGenerator = fun->isStarGenerator();
Rooted<GlobalObject*> global(cx, &fun->global());
RootedObject objProto(cx);
if (isStarGenerator)
if (isAsyncGenerator)
objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global);
else if (isStarGenerator)
objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
else
objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
@ -392,7 +403,7 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId
// non-enumerable, and writable. However, per the 15 July 2013 ES6 draft,
// section 15.19.3, the .prototype of a generator function does not link
// back with a .constructor.
if (!isStarGenerator) {
if (!isStarGenerator && !isAsyncGenerator) {
RootedValue objVal(cx, ObjectValue(*fun));
if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0))
return false;
@ -442,8 +453,14 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
* - Arrow functions
* - Function.prototype
*/
if (fun->isBuiltin() || (!fun->isConstructor() && !fun->isGenerator()))
return true;
if (!IsWrappedAsyncGenerator(fun)) {
if (fun->isBuiltin())
return true;
if (!fun->isConstructor()) {
if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync())
return true;
}
}
if (!ResolveInterpretedFunctionPrototype(cx, fun, id))
return false;
@ -515,7 +532,7 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
{
enum FirstWordFlag {
HasAtom = 0x1,
IsStarGenerator = 0x2,
HasStarGeneratorProto = 0x2,
IsLazy = 0x4,
HasSingletonType = 0x8
};
@ -544,8 +561,8 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
firstword |= HasAtom;
if (fun->isStarGenerator())
firstword |= IsStarGenerator;
if (fun->isStarGenerator() || fun->isAsync())
firstword |= HasStarGeneratorProto;
if (fun->isInterpretedLazy()) {
// Encode a lazy script.
@ -581,7 +598,7 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
if (mode == XDR_DECODE) {
RootedObject proto(cx);
if (firstword & IsStarGenerator) {
if (firstword & HasStarGeneratorProto) {
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
if (!proto)
return false;
@ -938,6 +955,11 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
return FunctionToString(cx, unwrapped, prettyPrint);
}
if (IsWrappedAsyncGenerator(fun)) {
RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
return FunctionToString(cx, unwrapped, prettyPrint);
}
StringBuffer out(cx);
RootedScript script(cx);
@ -1556,7 +1578,7 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp)
return true;
}
args.rval().setBoolean(fun->isGenerator());
args.rval().setBoolean(fun->isStarGenerator() || fun->isLegacyGenerator());
return true;
}
@ -1588,8 +1610,6 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
bool isStarGenerator = generatorKind == StarGenerator;
bool isAsync = asyncKind == AsyncFunction;
MOZ_ASSERT(generatorKind != LegacyGenerator);
MOZ_ASSERT_IF(isAsync, isStarGenerator);
MOZ_ASSERT_IF(!isStarGenerator, !isAsync);
RootedScript maybeScript(cx);
const char* filename;
@ -1600,10 +1620,14 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
&mutedErrors);
const char* introductionType = "Function";
if (isAsync)
introductionType = "AsyncFunction";
else if (generatorKind != NotGenerator)
if (isAsync) {
if (isStarGenerator)
introductionType = "AsyncGenerator";
else
introductionType = "AsyncFunction";
} else if (generatorKind != NotGenerator) {
introductionType = "GeneratorFunction";
}
const char* introducerFilename = filename;
if (maybeScript && maybeScript->scriptSource()->introducerFilename())
@ -1701,7 +1725,7 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
// Step 4.d, use %Generator% as the fallback prototype.
// Also use %Generator% for the unwrapped function of async functions.
if (!proto && isStarGenerator) {
if (!proto && (isStarGenerator || isAsync)) {
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global);
if (!proto)
return false;
@ -1731,12 +1755,20 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
: SourceBufferHolder::NoOwnership;
bool ok;
SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership);
if (isAsync)
ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd);
else if (isStarGenerator)
ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd);
else
ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd);
if (isAsync) {
if (isStarGenerator) {
ok = frontend::CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf,
parameterListEnd);
} else {
ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf,
parameterListEnd);
}
} else {
if (isStarGenerator)
ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd);
else
ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd);
}
// Step 33.
args.rval().setObject(*fun);
@ -1762,14 +1794,14 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Save the callee before its reset in FunctionConstructor().
// Save the callee before it's reset in FunctionConstructor().
RootedObject newTarget(cx);
if (args.isConstructing())
newTarget = &args.newTarget().toObject();
else
newTarget = &args.callee();
if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction))
if (!FunctionConstructor(cx, args, NotGenerator, AsyncFunction))
return false;
// ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077
@ -1794,6 +1826,40 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp)
return true;
}
bool
js::AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Save the callee before its reset in FunctionConstructor().
RootedObject newTarget(cx);
if (args.isConstructing())
newTarget = &args.newTarget().toObject();
else
newTarget = &args.callee();
if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction))
return false;
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
return false;
if (!proto) {
proto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global());
if (!proto)
return false;
}
RootedFunction unwrapped(cx, &args.rval().toObject().as<JSFunction>());
RootedObject wrapped(cx, WrapAsyncGeneratorWithProto(cx, unwrapped, proto));
if (!wrapped)
return false;
args.rval().setObject(*wrapped);
return true;
}
bool
JSFunction::isBuiltinFunctionConstructor()
{
@ -1965,7 +2031,7 @@ NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind,
gc::AllocKind allocKind, HandleObject proto)
{
RootedObject cloneProto(cx, proto);
if (!proto && fun->isStarGenerator()) {
if (!proto && (fun->isStarGenerator() || fun->isAsync())) {
cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
if (!cloneProto)
return nullptr;

View File

@ -89,15 +89,15 @@ class JSFunction : public js::NativeObject
ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR,
ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA,
INTERPRETED_METHOD = INTERPRETED | METHOD_KIND,
INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND,
INTERPRETED_METHOD_GENERATOR_OR_ASYNC = INTERPRETED | METHOD_KIND,
INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR,
INTERPRETED_GETTER = INTERPRETED | GETTER_KIND,
INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR,
INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND,
INTERPRETED_LAMBDA_GENERATOR = INTERPRETED | LAMBDA,
INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = INTERPRETED | LAMBDA,
INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR,
INTERPRETED_GENERATOR = INTERPRETED,
INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED,
NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA |
@ -146,7 +146,9 @@ class JSFunction : public js::NativeObject
MOZ_ASSERT_IF(nonLazyScript()->funHasExtensibleScope() ||
nonLazyScript()->needsHomeObject() ||
nonLazyScript()->isDerivedClassConstructor() ||
isGenerator(),
isStarGenerator() ||
isLegacyGenerator() ||
isAsync(),
nonLazyScript()->bodyScope()->hasEnvironment());
return nonLazyScript()->bodyScope()->hasEnvironment();
@ -501,8 +503,6 @@ class JSFunction : public js::NativeObject
return js::NotGenerator;
}
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
@ -513,9 +513,9 @@ class JSFunction : public js::NativeObject
bool isAsync() const {
if (isInterpretedLazy())
return lazyScript()->asyncKind() == js::AsyncFunction;
return lazyScript()->isAsync();
if (hasScript())
return nonLazyScript()->asyncKind() == js::AsyncFunction;
return nonLazyScript()->isAsync();
return false;
}
@ -660,6 +660,9 @@ Generator(JSContext* cx, unsigned argc, Value* vp);
extern bool
AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp);
extern bool
AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp);
// Allocate a new function backed by a JSNative. Note that by default this
// creates a singleton object.
extern JSFunction*

View File

@ -916,28 +916,30 @@ js::GetIteratorObject(JSContext* cx, HandleObject obj, uint32_t flags)
return iterator;
}
// ES 2017 draft 7.4.7.
JSObject*
js::CreateItrResultObject(JSContext* cx, HandleValue value, bool done)
js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
{
// FIXME: We can cache the iterator result object shape somewhere.
AssertHeapIsIdle(cx);
// Step 1 (implicit).
RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
if (!proto)
// Step 2.
RootedObject resultObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!resultObj)
return nullptr;
RootedPlainObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto));
if (!obj)
// Step 3.
if (!DefineProperty(cx, resultObj, cx->names().value, value))
return nullptr;
if (!DefineProperty(cx, obj, cx->names().value, value))
// Step 4.
if (!DefineProperty(cx, resultObj, cx->names().done,
done ? TrueHandleValue : FalseHandleValue))
{
return nullptr;
}
RootedValue doneBool(cx, BooleanValue(done));
if (!DefineProperty(cx, obj, cx->names().done, doneBool))
return nullptr;
return obj;
// Step 5.
return resultObj;
}
bool

View File

@ -216,10 +216,10 @@ ThrowStopIteration(JSContext* cx);
/*
* Create an object of the form { value: VALUE, done: DONE }.
* ES6 draft from 2013-09-05, section 25.4.3.4.
* ES 2017 draft 7.4.7.
*/
extern JSObject*
CreateItrResultObject(JSContext* cx, HandleValue value, bool done);
CreateIterResultObject(JSContext* cx, HandleValue value, bool done);
extern JSObject*
InitLegacyIteratorClass(JSContext* cx, HandleObject obj);
@ -227,6 +227,8 @@ InitLegacyIteratorClass(JSContext* cx, HandleObject obj);
extern JSObject*
InitStopIterationClass(JSContext* cx, HandleObject obj);
enum class IteratorKind { Sync, Async };
} /* namespace js */
#endif /* jsiter_h */

View File

@ -655,7 +655,8 @@ IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset);
inline bool
FlowsIntoNext(JSOp op)
{
/* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */
// JSOP_YIELD/JSOP_AWAIT is considered to flow into the next instruction,
// like JSOP_CALL.
switch (op) {
case JSOP_RETRVAL:
case JSOP_RETURN:

View File

@ -402,8 +402,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
ntrynotes = script->trynotes()->length;
if (script->hasScopeNotes())
nscopenotes = script->scopeNotes()->length;
if (script->hasYieldOffsets())
nyieldoffsets = script->yieldOffsets().length();
if (script->hasYieldAndAwaitOffsets())
nyieldoffsets = script->yieldAndAwaitOffsets().length();
nTypeSets = script->nTypeSets();
funLength = script->funLength();
@ -902,7 +902,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
}
for (i = 0; i < nyieldoffsets; ++i) {
uint32_t* offset = &script->yieldOffsets()[i];
uint32_t* offset = &script->yieldAndAwaitOffsets()[i];
if (!xdr->codeUint32(offset))
return false;
}
@ -2456,7 +2456,7 @@ ScriptDataSize(uint32_t nscopes, uint32_t nconsts, uint32_t nobjects,
if (nscopenotes != 0)
size += sizeof(ScopeNoteArray) + nscopenotes * sizeof(ScopeNote);
if (nyieldoffsets != 0)
size += sizeof(YieldOffsetArray) + nyieldoffsets * sizeof(uint32_t);
size += sizeof(YieldAndAwaitOffsetArray) + nyieldoffsets * sizeof(uint32_t);
return size;
}
@ -2558,10 +2558,10 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t nsco
cursor += sizeof(ScopeNoteArray);
}
YieldOffsetArray* yieldOffsets = nullptr;
YieldAndAwaitOffsetArray* yieldAndAwaitOffsets = nullptr;
if (nyieldoffsets != 0) {
yieldOffsets = reinterpret_cast<YieldOffsetArray*>(cursor);
cursor += sizeof(YieldOffsetArray);
yieldAndAwaitOffsets = reinterpret_cast<YieldAndAwaitOffsetArray*>(cursor);
cursor += sizeof(YieldAndAwaitOffsetArray);
}
if (nconsts != 0) {
@ -2602,8 +2602,8 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t nsco
}
if (nyieldoffsets != 0) {
yieldOffsets->init(reinterpret_cast<uint32_t*>(cursor), nyieldoffsets);
size_t vectorSize = nyieldoffsets * sizeof(script->yieldOffsets()[0]);
yieldAndAwaitOffsets->init(reinterpret_cast<uint32_t*>(cursor), nyieldoffsets);
size_t vectorSize = nyieldoffsets * sizeof(script->yieldAndAwaitOffsets()[0]);
#ifdef DEBUG
memset(cursor, 0, vectorSize);
#endif
@ -2623,10 +2623,10 @@ JSScript::initFunctionPrototype(ExclusiveContext* cx, Handle<JSScript*> script,
uint32_t numObjects = 0;
uint32_t numTryNotes = 0;
uint32_t numScopeNotes = 0;
uint32_t numYieldOffsets = 0;
uint32_t numYieldAndAwaitOffsets = 0;
uint32_t numTypeSets = 0;
if (!partiallyInit(cx, script, numScopes, numConsts, numObjects, numTryNotes,
numScopeNotes, numYieldOffsets, numTypeSets))
numScopeNotes, numYieldAndAwaitOffsets, numTypeSets))
{
return false;
}
@ -2739,7 +2739,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
if (!partiallyInit(cx, script,
bce->scopeList.length(), bce->constList.length(), bce->objectList.length,
bce->tryNoteList.length(), bce->scopeNoteList.length(),
bce->yieldOffsetList.length(), bce->typesetCount))
bce->yieldAndAwaitOffsetList.length(), bce->typesetCount))
{
return false;
}
@ -2794,8 +2794,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
// Copy yield offsets last, as the generator kind is set in
// initFromFunctionBox.
if (bce->yieldOffsetList.length() != 0)
bce->yieldOffsetList.finish(script->yieldOffsets(), prologueLength);
if (bce->yieldAndAwaitOffsetList.length() != 0)
bce->yieldAndAwaitOffsetList.finish(script->yieldAndAwaitOffsets(), prologueLength);
#ifdef DEBUG
script->assertValidJumpTargets();
@ -3188,7 +3188,7 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleScope enclosingScope, HandleF
{
/* NB: Keep this in sync with XDRInterpretedFunction. */
RootedObject cloneProto(cx);
if (srcFun->isStarGenerator()) {
if (srcFun->isStarGenerator() || srcFun->isAsync()) {
cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
if (!cloneProto)
return nullptr;
@ -3242,7 +3242,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
uint32_t nscopes = src->scopes()->length;
uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0;
uint32_t nyieldoffsets = src->hasYieldOffsets() ? src->yieldOffsets().length() : 0;
uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0;
/* Script data */
@ -3380,8 +3380,10 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
if (nscopenotes != 0)
dst->scopeNotes()->vector = Rebase<ScopeNote>(dst, src, src->scopeNotes()->vector);
if (nyieldoffsets != 0)
dst->yieldOffsets().vector_ = Rebase<uint32_t>(dst, src, src->yieldOffsets().vector_);
if (nyieldoffsets != 0) {
dst->yieldAndAwaitOffsets().vector_ =
Rebase<uint32_t>(dst, src, src->yieldAndAwaitOffsets().vector_);
}
/*
* Function delazification assumes that their script does not have a
@ -3917,7 +3919,9 @@ JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
if (script->needsArgsObj())
return true;
MOZ_ASSERT(!script->isGenerator());
MOZ_ASSERT(!script->isStarGenerator());
MOZ_ASSERT(!script->isLegacyGenerator());
MOZ_ASSERT(!script->isAsync());
script->needsArgsObj_ = true;

View File

@ -156,7 +156,7 @@ struct ScopeNoteArray {
uint32_t length; // Count of indexed try notes.
};
class YieldOffsetArray {
class YieldAndAwaitOffsetArray {
friend bool
detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
MutableHandle<GCVector<Scope*>> scopes);
@ -1327,13 +1327,12 @@ class JSScript : public js::gc::TenuredCell
js::GeneratorKind generatorKind() const {
return js::GeneratorKindFromBits(generatorKindBits_);
}
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
void setGeneratorKind(js::GeneratorKind kind) {
// A script only gets its generator kind set as part of initialization,
// so it can only transition from not being a generator.
MOZ_ASSERT(!isGenerator());
MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator());
generatorKindBits_ = GeneratorKindAsBits(kind);
}
@ -1341,6 +1340,10 @@ class JSScript : public js::gc::TenuredCell
return isAsync_ ? js::AsyncFunction : js::SyncFunction;
}
bool isAsync() const {
return isAsync_;
}
void setAsyncKind(js::FunctionAsyncKind kind) {
isAsync_ = kind == js::AsyncFunction;
}
@ -1493,7 +1496,8 @@ class JSScript : public js::gc::TenuredCell
bool isRelazifiable() const {
return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ &&
!isGenerator() && !hasBaselineScript() && !hasAnyIonScript() &&
!isStarGenerator() && !isLegacyGenerator() && !isAsync() &&
!hasBaselineScript() && !hasAnyIonScript() &&
!isDefaultClassConstructor() &&
!doNotRelazify_;
}
@ -1695,7 +1699,9 @@ class JSScript : public js::gc::TenuredCell
bool hasObjects() const { return hasArray(OBJECTS); }
bool hasTrynotes() const { return hasArray(TRYNOTES); }
bool hasScopeNotes() const { return hasArray(SCOPENOTES); }
bool hasYieldOffsets() const { return isGenerator(); }
bool hasYieldAndAwaitOffsets() const {
return isStarGenerator() || isLegacyGenerator() || isAsync();
}
#define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0))
@ -1704,7 +1710,9 @@ class JSScript : public js::gc::TenuredCell
size_t objectsOffset() const { return OFF(constsOffset, hasConsts, js::ConstArray); }
size_t trynotesOffset() const { return OFF(objectsOffset, hasObjects, js::ObjectArray); }
size_t scopeNotesOffset() const { return OFF(trynotesOffset, hasTrynotes, js::TryNoteArray); }
size_t yieldOffsetsOffset() const { return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray); }
size_t yieldAndAwaitOffsetsOffset() const {
return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray);
}
#undef OFF
@ -1734,9 +1742,10 @@ class JSScript : public js::gc::TenuredCell
return reinterpret_cast<js::ScopeNoteArray*>(data + scopeNotesOffset());
}
js::YieldOffsetArray& yieldOffsets() {
MOZ_ASSERT(hasYieldOffsets());
return *reinterpret_cast<js::YieldOffsetArray*>(data + yieldOffsetsOffset());
js::YieldAndAwaitOffsetArray& yieldAndAwaitOffsets() {
MOZ_ASSERT(hasYieldAndAwaitOffsets());
return *reinterpret_cast<js::YieldAndAwaitOffsetArray*>(data +
yieldAndAwaitOffsetsOffset());
}
bool hasLoops();
@ -2112,8 +2121,6 @@ class LazyScript : public gc::TenuredCell
GeneratorKind generatorKind() const { return GeneratorKindFromBits(p_.generatorKindBits); }
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
@ -2121,7 +2128,7 @@ class LazyScript : public gc::TenuredCell
void setGeneratorKind(GeneratorKind kind) {
// A script only gets its generator kind set as part of initialization,
// so it can only transition from NotGenerator.
MOZ_ASSERT(!isGenerator());
MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator());
// Legacy generators cannot currently be lazy.
MOZ_ASSERT(kind != LegacyGenerator);
p_.generatorKindBits = GeneratorKindAsBits(kind);
@ -2131,6 +2138,10 @@ class LazyScript : public gc::TenuredCell
return p_.isAsync ? AsyncFunction : SyncFunction;
}
bool isAsync() const {
return p_.isAsync;
}
void setAsyncKind(FunctionAsyncKind kind) {
p_.isAsync = kind == AsyncFunction;
}

View File

@ -305,6 +305,7 @@ UNIFIED_SOURCES += [
'vm/ArgumentsObject.cpp',
'vm/ArrayBufferObject.cpp',
'vm/AsyncFunction.cpp',
'vm/AsyncIteration.cpp',
'vm/Caches.cpp',
'vm/CallNonGenericMethod.cpp',
'vm/CharacterEncoding.cpp',
@ -747,6 +748,7 @@ selfhosted.inputs = [
'builtin/SelfHostingDefines.h',
'builtin/Utilities.js',
'builtin/Array.js',
'builtin/AsyncIteration.js',
'builtin/Classes.js',
'builtin/Date.js',
'builtin/Error.js',
@ -790,3 +792,6 @@ if CONFIG['GNU_CXX']:
# Suppress warnings in third-party code.
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True

View File

@ -85,6 +85,7 @@
#include "threading/Thread.h"
#include "vm/ArgumentsObject.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/Compression.h"
#include "vm/Debugger.h"
#include "vm/HelperThreads.h"
@ -2304,6 +2305,8 @@ ValueToScript(JSContext* cx, HandleValue v, JSFunction** funp = nullptr)
// Get unwrapped async function.
if (IsWrappedAsyncFunction(fun))
fun = GetUnwrappedAsyncFunction(fun);
if (IsWrappedAsyncGenerator(fun))
fun = GetUnwrappedAsyncGenerator(fun);
if (!fun->isInterpreted()) {
JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_SCRIPTS_ONLY);

View File

@ -0,0 +1,76 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function assertOwnDescriptor(object, propertyKey, expected) {
var desc = Object.getOwnPropertyDescriptor(object, propertyKey);
if (desc === undefined) {
assertEq(expected, undefined, "Property shouldn't be present");
return;
}
assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`);
assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`);
if (Object.prototype.hasOwnProperty.call(desc, "value")) {
assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`);
assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`);
} else {
assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`);
assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`);
}
}
async function asyncFunc(){}
var AsyncFunctionPrototype = Object.getPrototypeOf(asyncFunc);
var AsyncFunction = AsyncFunctionPrototype.constructor;
// ES2017, 25.5.2 Properties of the AsyncFunction Constructor
assertEqArray(Object.getOwnPropertyNames(AsyncFunction).sort(), ["length", "name", "prototype"]);
assertEqArray(Object.getOwnPropertySymbols(AsyncFunction), []);
assertOwnDescriptor(AsyncFunction, "length", {
value: 1, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(AsyncFunction, "name", {
value: "AsyncFunction", writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(AsyncFunction, "prototype", {
value: AsyncFunctionPrototype, writable: false, enumerable: false, configurable: false
});
// ES2017, 25.5.3 Properties of the AsyncFunction Prototype Object
assertEqArray(Object.getOwnPropertyNames(AsyncFunctionPrototype).sort(), ["constructor"]);
assertEqArray(Object.getOwnPropertySymbols(AsyncFunctionPrototype), [Symbol.toStringTag]);
assertOwnDescriptor(AsyncFunctionPrototype, "constructor", {
value: AsyncFunction, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(AsyncFunctionPrototype, Symbol.toStringTag, {
value: "AsyncFunction", writable: false, enumerable: false, configurable: true
});
// ES2017, 25.5.4 AsyncFunction Instances
assertEqArray(Object.getOwnPropertyNames(asyncFunc).sort(), ["length", "name"]);
assertEqArray(Object.getOwnPropertySymbols(asyncFunc), []);
assertOwnDescriptor(asyncFunc, "length", {
value: 0, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(asyncFunc, "name", {
value: "asyncFunc", writable: false, enumerable: false, configurable: true
});
if (typeof reportCompare == "function")
reportCompare(true, true);

View File

@ -0,0 +1,111 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function assertOwnDescriptor(object, propertyKey, expected) {
var desc = Object.getOwnPropertyDescriptor(object, propertyKey);
if (desc === undefined) {
assertEq(expected, undefined, "Property shouldn't be present");
return;
}
assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`);
assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`);
if (Object.prototype.hasOwnProperty.call(desc, "value")) {
assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`);
assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`);
} else {
assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`);
assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`);
}
}
function* generator(){}
var GeneratorFunctionPrototype = Object.getPrototypeOf(generator);
var GeneratorFunction = GeneratorFunctionPrototype.constructor;
var GeneratorPrototype = GeneratorFunctionPrototype.prototype;
// ES2017, 25.2.2 Properties of the GeneratorFunction Constructor
assertEqArray(Object.getOwnPropertyNames(GeneratorFunction).sort(), ["length", "name", "prototype"]);
assertEqArray(Object.getOwnPropertySymbols(GeneratorFunction), []);
assertOwnDescriptor(GeneratorFunction, "length", {
value: 1, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorFunction, "name", {
value: "GeneratorFunction", writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorFunction, "prototype", {
value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: false
});
// ES2017, 25.2.3 Properties of the GeneratorFunction Prototype Object
assertEqArray(Object.getOwnPropertyNames(GeneratorFunctionPrototype).sort(), ["constructor", "prototype"]);
assertEqArray(Object.getOwnPropertySymbols(GeneratorFunctionPrototype), [Symbol.toStringTag]);
assertOwnDescriptor(GeneratorFunctionPrototype, "constructor", {
value: GeneratorFunction, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorFunctionPrototype, "prototype", {
value: GeneratorPrototype, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorFunctionPrototype, Symbol.toStringTag, {
value: "GeneratorFunction", writable: false, enumerable: false, configurable: true
});
// ES2017, 25.2.4 GeneratorFunction Instances
assertEqArray(Object.getOwnPropertyNames(generator).sort(), ["length", "name", "prototype"]);
assertEqArray(Object.getOwnPropertySymbols(generator), []);
assertOwnDescriptor(generator, "length", {
value: 0, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(generator, "name", {
value: "generator", writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(generator, "prototype", {
value: generator.prototype, writable: true, enumerable: false, configurable: false
});
// ES2017, 25.3.1 Properties of Generator Prototype
assertEqArray(Object.getOwnPropertyNames(GeneratorPrototype).sort(), ["constructor", "next", "return", "throw"]);
assertEqArray(Object.getOwnPropertySymbols(GeneratorPrototype), [Symbol.toStringTag]);
assertOwnDescriptor(GeneratorPrototype, "constructor", {
value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorPrototype, "next", {
value: GeneratorPrototype.next, writable: true, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorPrototype, "return", {
value: GeneratorPrototype.return, writable: true, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorPrototype, "throw", {
value: GeneratorPrototype.throw, writable: true, enumerable: false, configurable: true
});
assertOwnDescriptor(GeneratorPrototype, Symbol.toStringTag, {
value: "Generator", writable: false, enumerable: false, configurable: true
});
if (typeof reportCompare == "function")
reportCompare(true, true);

View File

@ -5,6 +5,7 @@ if (!this.Promise) {
quit(0);
}
// Resolve Promise with itself by directly calling the "Promise Resolve Function".
let resolve;
let promise = new Promise(function(x) { resolve = x; });
resolve(promise)
@ -20,4 +21,23 @@ drainJobQueue()
assertEq(results.length, 1);
assertEq(results[0], "rejected");
// Resolve Promise with itself when the "Promise Resolve Function" is called
// from (the fast path in) PromiseReactionJob.
results = [];
promise = new Promise(x => { resolve = x; });
let promise2 = promise.then(() => promise2);
promise2.then(() => assertEq(true, false, "not reached"), res => {
assertEq(res instanceof TypeError, true);
results.push("rejected");
});
resolve();
drainJobQueue();
assertEq(results.length, 1);
assertEq(results[0], "rejected");
this.reportCompare && reportCompare(0, 0, "ok");

View File

@ -11,7 +11,8 @@ var names = [
"hasInstance",
"split",
"toPrimitive",
"unscopables"
"unscopables",
"asyncIterator"
];
for (var name of names) {

View File

@ -9,9 +9,6 @@ if (typeof Reflect !== "undefined" && Reflect.parse) {
assertEq(Reflect.parse("async function a() {}").body[0].async, true);
assertEq(Reflect.parse("() => {}").body[0].async, undefined);
// Async generators are not allowed (with regards to spec)
assertThrows(() => Reflect.parse("async function* a() {}"), SyntaxError);
// No line terminator after async
assertEq(Reflect.parse("async\nfunction a(){}").body[0].expression.name, "async");

View File

@ -53,9 +53,8 @@ function asyncFunDecl(id, params, body) {
params: params,
defaults: [],
body: body,
generator: true,
async: true,
style: "es6" });
generator: false,
async: true });
}
function varDecl(decls) {
return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" });
@ -181,9 +180,8 @@ function asyncFunExpr(id, args, body) {
id: id,
params: args,
body: body,
generator: true,
async: true,
style: "es6" });
generator: false,
async: true });
}
function arrowExpr(args, body) {
return Pattern({ type: "ArrowFunctionExpression",
@ -194,10 +192,9 @@ function asyncArrowExpr(isExpression, args, body) {
return Pattern({ type: "ArrowFunctionExpression",
params: args,
body: body,
generator: true,
generator: false,
async: true,
expression: isExpression,
style: "es6" });
expression: isExpression });
}
function metaProperty(meta, property) {

View File

@ -40,8 +40,11 @@ GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global)
proto));
if (!asyncFunction)
return false;
if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto))
if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto,
JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
{
return false;
}
global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto));
@ -109,7 +112,7 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
JSObject*
js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
{
MOZ_ASSERT(unwrapped->isStarGenerator());
MOZ_ASSERT(unwrapped->isAsync());
MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
"%FunctionPrototype% fallback in NewFunctionWithProto().");
@ -171,22 +174,14 @@ AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleV
: cx->names().StarGeneratorThrow;
FixedInvokeArgs<1> args(cx);
args[0].set(valueOrReason);
RootedValue result(cx);
if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result))
RootedValue value(cx);
if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value))
return AsyncFunctionThrown(cx, resultPromise);
RootedObject resultObj(cx, &result.toObject());
RootedValue doneVal(cx);
RootedValue value(cx);
if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
return false;
if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
return false;
if (generatorVal.toObject().as<GeneratorObject>().isAfterAwait())
return AsyncFunctionAwait(cx, resultPromise, value);
if (doneVal.toBoolean())
return AsyncFunctionReturned(cx, resultPromise, value);
return AsyncFunctionAwait(cx, resultPromise, value);
return AsyncFunctionReturned(cx, resultPromise, value);
}
// Async Functions proposal 2.2 steps 3-8.
@ -242,9 +237,3 @@ js::IsWrappedAsyncFunction(JSFunction* fun)
{
return fun->maybeNative() == WrappedAsyncFunction;
}
MOZ_MUST_USE bool
js::CheckAsyncResumptionValue(JSContext* cx, HandleValue v)
{
return CheckStarGeneratorResumptionValue(cx, v);
}

View File

@ -35,9 +35,6 @@ MOZ_MUST_USE bool
AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue reason);
MOZ_MUST_USE bool
CheckAsyncResumptionValue(JSContext* cx, HandleValue v);
} // namespace js
#endif /* vm_AsyncFunction_h */

View File

@ -0,0 +1,644 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "vm/AsyncIteration.h"
#include "jsarray.h"
#include "jscompartment.h"
#include "builtin/Promise.h"
#include "vm/GeneratorObject.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/SelfHosting.h"
#include "jscntxtinlines.h"
#include "jsobjinlines.h"
#include "vm/NativeObject-inl.h"
using namespace js;
using namespace js::gc;
#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody.
static bool
WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction wrapped(cx, &args.callee().as<JSFunction>());
RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT));
RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>());
RootedValue thisValue(cx, args.thisv());
// Step 1.
RootedValue generatorVal(cx);
InvokeArgs args2(cx);
if (!args2.init(cx, argc))
return false;
for (size_t i = 0, len = argc; i < len; i++)
args2[i].set(args[i]);
if (!Call(cx, unwrappedVal, thisValue, args2, &generatorVal))
return false;
// Step 2.
Rooted<AsyncGeneratorObject*> asyncGenObj(
cx, AsyncGeneratorObject::create(cx, wrapped, generatorVal));
if (!asyncGenObj)
return false;
// Step 3 (skipped).
// Done in AsyncGeneratorObject::create and generator.
// Step 4.
args.rval().setObject(*asyncGenObj);
return true;
}
JSObject*
js::WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
{
MOZ_ASSERT(unwrapped->isAsync());
MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
"%FunctionPrototype% fallback in NewFunctionWithProto().");
// Create a new function with AsyncGeneratorPrototype, reusing the name and
// the length of `unwrapped`.
RootedAtom funName(cx, unwrapped->explicitName());
uint16_t length;
if (!JSFunction::getLength(cx, unwrapped, &length))
return nullptr;
RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncGenerator, length,
JSFunction::NATIVE_FUN, nullptr,
funName, proto,
AllocKind::FUNCTION_EXTENDED,
TenuredObject));
if (!wrapped)
return nullptr;
if (unwrapped->hasCompileTimeName())
wrapped->setCompileTimeName(unwrapped->compileTimeName());
// Link them to each other to make GetWrappedAsyncGenerator and
// GetUnwrappedAsyncGenerator work.
unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
return wrapped;
}
JSObject*
js::WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped)
{
RootedObject proto(cx, GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()));
if (!proto)
return nullptr;
return WrapAsyncGeneratorWithProto(cx, unwrapped, proto);
}
bool
js::IsWrappedAsyncGenerator(JSFunction* fun)
{
return fun->maybeNative() == WrappedAsyncGenerator;
}
JSFunction*
js::GetWrappedAsyncGenerator(JSFunction* unwrapped)
{
MOZ_ASSERT(unwrapped->isAsync());
return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
}
JSFunction*
js::GetUnwrappedAsyncGenerator(JSFunction* wrapped)
{
MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped));
JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)
.toObject().as<JSFunction>();
MOZ_ASSERT(unwrapped->isAsync());
return unwrapped;
}
// Async Iteration proposal 4.1.1 Await Fulfilled Functions.
MOZ_MUST_USE bool
js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value)
{
return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value);
}
// Async Iteration proposal 4.1.2 Await Rejected Functions.
MOZ_MUST_USE bool
js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason)
{
return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
}
// Async Iteration proposal 11.4.3.7 step 8.d-e.
MOZ_MUST_USE bool
js::AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value)
{
return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value);
}
// Async Iteration proposal 11.4.3.7 step 8.d-e.
MOZ_MUST_USE bool
js::AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason)
{
return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
}
const Class AsyncFromSyncIteratorObject::class_ = {
"AsyncFromSyncIteratorObject",
JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)
};
// Async Iteration proposal 11.1.3.1.
JSObject*
js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter)
{
// Step 1 (implicit).
// Done in bytecode emitted by emitAsyncIterator.
// Steps 2-4.
return AsyncFromSyncIteratorObject::create(cx, iter);
}
// Async Iteration proposal 11.1.3.1 steps 2-4.
/* static */ JSObject*
AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter)
{
// Step 2.
RootedObject proto(cx, GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(cx,
cx->global()));
if (!proto)
return nullptr;
RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, proto));
if (!obj)
return nullptr;
Handle<AsyncFromSyncIteratorObject*> asyncIter = obj.as<AsyncFromSyncIteratorObject>();
// Step 3.
asyncIter->setIterator(iter);
// Step 4.
return asyncIter;
}
// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next.
static bool
AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
}
// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return.
static bool
AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
}
// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw.
static bool
AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
}
// Async Iteration proposal 11.4.1.2 AsyncGenerator.prototype.next.
static bool
AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Steps 1-3.
return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Normal, args.get(0),
args.rval());
}
// Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return.
static bool
AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Steps 1-3.
return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Return, args.get(0),
args.rval());
}
// Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw.
static bool
AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Steps 1-3.
return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Throw, args.get(0),
args.rval());
}
const Class AsyncGeneratorObject::class_ = {
"AsyncGenerator",
JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots)
};
// ES 2017 draft 9.1.13.
template <typename ProtoGetter>
static JSObject*
OrdinaryCreateFromConstructor(JSContext* cx, HandleFunction fun,
ProtoGetter protoGetter, const Class* clasp)
{
// Step 1 (skipped).
// Step 2.
RootedValue protoVal(cx);
if (!GetProperty(cx, fun, fun, cx->names().prototype, &protoVal))
return nullptr;
RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr);
if (!proto) {
proto = protoGetter(cx, cx->global());
if (!proto)
return nullptr;
}
// Step 3.
return NewNativeObjectWithGivenProto(cx, clasp, proto);
}
/* static */ AsyncGeneratorObject*
AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal)
{
MOZ_ASSERT(generatorVal.isObject());
MOZ_ASSERT(generatorVal.toObject().is<GeneratorObject>());
RootedObject obj(
cx, OrdinaryCreateFromConstructor(cx, asyncGen,
GlobalObject::getOrCreateAsyncGeneratorPrototype,
&class_));
if (!obj)
return nullptr;
Handle<AsyncGeneratorObject*> asyncGenObj = obj.as<AsyncGeneratorObject>();
// Async Iteration proposal 6.4.3.2 AsyncGeneratorStart.
// Step 6.
asyncGenObj->setGenerator(generatorVal);
// Step 7.
asyncGenObj->setSuspendedStart();
// Step 8.
asyncGenObj->clearSingleQueueRequest();
return asyncGenObj;
}
static MOZ_MUST_USE bool
InternalEnqueue(JSContext* cx, HandleArrayObject queue, HandleValue val)
{
uint32_t length;
if (!GetLengthProperty(cx, queue, &length))
return false;
if (length >= MAX_ARRAY_INDEX) {
ReportOutOfMemory(cx);
return false;
}
if (!DefineElement(cx, queue, length, val))
return false;
return SetLengthProperty(cx, queue, length + 1);
}
static MOZ_MUST_USE bool
InternalDequeue(JSContext* cx, HandleArrayObject queue, MutableHandleValue val)
{
uint32_t length;
if (!GetLengthProperty(cx, queue, &length))
return false;
MOZ_ASSERT(length != 0, "Queue should not be empty here");
if (!GetElement(cx, queue, queue, 0, val))
return false;
uint32_t newlength = length - 1;
RootedValue tmp(cx);
for (uint32_t i = 0; i < newlength; i++) {
if (!GetElement(cx, queue, queue, i + 1, &tmp))
return false;
if (!DefineElement(cx, queue, i, tmp))
return false;
}
ObjectOpResult result;
if (!DeleteElement(cx, queue, newlength, result))
return false;
if (!result) {
RootedId id(cx, INT_TO_JSID(newlength));
return result.reportError(cx, queue, id);
}
return SetLengthProperty(cx, queue, newlength);
}
/* static */ MOZ_MUST_USE bool
AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
Handle<AsyncGeneratorRequest*> request)
{
if (asyncGenObj->isSingleQueue()) {
if (asyncGenObj->isSingleQueueEmpty()) {
asyncGenObj->setSingleQueueRequest(request);
return true;
}
RootedArrayObject queue(cx, NewDenseEmptyArray(cx));
if (!queue)
return false;
if (!NewbornArrayPush(cx, queue, ObjectValue(*asyncGenObj->singleQueueRequest())))
return false;
if (!NewbornArrayPush(cx, queue, ObjectValue(*request)))
return false;
asyncGenObj->setQueue(queue);
return true;
}
RootedArrayObject queue(cx, asyncGenObj->queue());
RootedValue requestVal(cx, ObjectValue(*request));
return InternalEnqueue(cx, queue, requestVal);
}
/* static */ AsyncGeneratorRequest*
AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
{
if (asyncGenObj->isSingleQueue()) {
AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest();
asyncGenObj->clearSingleQueueRequest();
return request;
}
RootedArrayObject queue(cx, asyncGenObj->queue());
RootedValue requestVal(cx);
if (!InternalDequeue(cx, queue, &requestVal))
return nullptr;
return &requestVal.toObject().as<AsyncGeneratorRequest>();
}
/* static */ AsyncGeneratorRequest*
AsyncGeneratorObject::peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
{
if (asyncGenObj->isSingleQueue())
return asyncGenObj->singleQueueRequest();
RootedArrayObject queue(cx, asyncGenObj->queue());
RootedValue requestVal(cx);
if (!GetElement(cx, queue, queue, 0, &requestVal))
return nullptr;
return &requestVal.toObject().as<AsyncGeneratorRequest>();
}
const Class AsyncGeneratorRequest::class_ = {
"AsyncGeneratorRequest",
JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)
};
// Async Iteration proposal 11.4.3.1.
/* static */ AsyncGeneratorRequest*
AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_,
HandleValue completionValue_, HandleObject promise_)
{
RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr));
if (!obj)
return nullptr;
Handle<AsyncGeneratorRequest*> request = obj.as<AsyncGeneratorRequest>();
request->setCompletionKind(completionKind_);
request->setCompletionValue(completionValue_);
request->setPromise(promise_);
return request;
}
// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d-g.
static MOZ_MUST_USE bool
AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value)
{
// Step 5.d.
asyncGenObj->setCompleted();
// Step 5.e (done in bytecode).
// Step 5.f.i (implicit).
// Step 5.g.
return AsyncGeneratorResolve(cx, asyncGenObj, value, true);
}
// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d, f.
static MOZ_MUST_USE bool
AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
{
// Step 5.d.
asyncGenObj->setCompleted();
// Not much we can do about uncatchable exceptions, so just bail.
if (!cx->isExceptionPending())
return false;
// Step 5.f.i.
RootedValue value(cx);
if (!GetAndClearException(cx, &value))
return false;
// Step 5.f.ii.
return AsyncGeneratorReject(cx, asyncGenObj, value);
}
// Async Iteration proposal 11.4.3.7 (partially).
// Most steps are done in generator.
static MOZ_MUST_USE bool
AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value)
{
// Step 5 is done in bytecode.
// Step 6.
asyncGenObj->setSuspendedYield();
// Step 9.
return AsyncGeneratorResolve(cx, asyncGenObj, value, false);
}
// Async Iteration proposal 4.1 Await steps 2-9.
// Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g.
// Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext
// steps 12-14, 16-20.
// Execution context switching is handled in generator.
MOZ_MUST_USE bool
js::AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, HandleValue argument)
{
RootedValue generatorVal(cx, asyncGenObj->generatorVal());
// 11.4.3.5 steps 12-14, 16-20.
HandlePropertyName funName = completionKind == CompletionKind::Normal
? cx->names().StarGeneratorNext
: completionKind == CompletionKind::Throw
? cx->names().StarGeneratorThrow
: cx->names().StarGeneratorReturn;
FixedInvokeArgs<1> args(cx);
args[0].set(argument);
RootedValue result(cx);
if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) {
// 11.4.3.2 step 5.d, f.
return AsyncGeneratorThrown(cx, asyncGenObj);
}
// 4.1 steps 2-9.
if (asyncGenObj->generatorObj()->isAfterAwait())
return AsyncGeneratorAwait(cx, asyncGenObj, result);
// The following code corresponds to the following 3 cases:
// * yield
// * yield*
// * return
// For yield and return, property access is done on an internal result
// object and it's not observable.
// For yield*, it's done on a possibly user-provided result object, and
// it's observable.
//
// Note that IteratorComplete steps in 8.2.1 are done in bytecode.
// 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
RootedObject resultObj(cx, &result.toObject());
RootedValue value(cx);
if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
return false;
if (asyncGenObj->generatorObj()->isAfterYield())
return AsyncGeneratorYield(cx, asyncGenObj, value);
// 11.4.3.2 step 5.d-g.
return AsyncGeneratorReturned(cx, asyncGenObj, value);
}
static const JSFunctionSpec async_iterator_proto_methods[] = {
JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
JS_FS_END
};
static const JSFunctionSpec async_from_sync_iter_methods[] = {
JS_FN("next", AsyncFromSyncIteratorNext, 1, 0),
JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0),
JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0),
JS_FS_END
};
static const JSFunctionSpec async_generator_methods[] = {
JS_FN("next", AsyncGeneratorNext, 1, 0),
JS_FN("throw", AsyncGeneratorThrow, 1, 0),
JS_FN("return", AsyncGeneratorReturn, 1, 0),
JS_FS_END
};
/* static */ MOZ_MUST_USE bool
GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
{
if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject())
return true;
// Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%.
RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!asyncIterProto)
return false;
if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods))
return false;
// Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%.
RootedObject asyncFromSyncIterProto(
cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
asyncIterProto));
if (!asyncFromSyncIterProto)
return false;
if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
async_from_sync_iter_methods) ||
!DefineToStringTag(cx, asyncFromSyncIterProto, cx->names().AsyncFromSyncIterator))
{
return false;
}
// Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%.
RootedObject asyncGenProto(
cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
asyncIterProto));
if (!asyncGenProto)
return false;
if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr, async_generator_methods) ||
!DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator))
{
return false;
}
// Async Iteration proposal 11.3.3 %AsyncGenerator%.
RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
if (!asyncGenerator)
return false;
if (!JSObject::setDelegate(cx, asyncGenerator))
return false;
if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto, JSPROP_READONLY,
JSPROP_READONLY) ||
!DefineToStringTag(cx, asyncGenerator, cx->names().AsyncGeneratorFunction))
{
return false;
}
RootedValue function(cx, global->getConstructor(JSProto_Function));
if (!function.toObjectOrNull())
return false;
RootedObject proto(cx, &function.toObject());
RootedAtom name(cx, cx->names().AsyncGeneratorFunction);
// Async Iteration proposal 11.3.2 %AsyncGeneratorFunction%.
RootedObject asyncGenFunction(
cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR,
nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject));
if (!asyncGenFunction)
return false;
if (!LinkConstructorAndPrototype(cx, asyncGenFunction, asyncGenerator,
JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
{
return false;
}
global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto));
global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, ObjectValue(*asyncFromSyncIterProto));
global->setReservedSlot(ASYNC_GENERATOR, ObjectValue(*asyncGenerator));
global->setReservedSlot(ASYNC_GENERATOR_FUNCTION, ObjectValue(*asyncGenFunction));
global->setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*asyncGenProto));
return true;
}

256
js/src/vm/AsyncIteration.h Normal file
View File

@ -0,0 +1,256 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_AsyncIteration_h
#define vm_AsyncIteration_h
#include "jscntxt.h"
#include "jsobj.h"
#include "builtin/Promise.h"
#include "vm/GeneratorObject.h"
namespace js {
// Async generator consists of 2 functions, |wrapped| and |unwrapped|.
// |unwrapped| is a generator function compiled from async generator script,
// |await| behaves just like |yield| there. |unwrapped| isn't exposed to user
// script.
// |wrapped| is a native function that is the value of async generator.
JSObject*
WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto);
JSObject*
WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped);
bool
IsWrappedAsyncGenerator(JSFunction* fun);
JSFunction*
GetWrappedAsyncGenerator(JSFunction* unwrapped);
JSFunction*
GetUnwrappedAsyncGenerator(JSFunction* wrapped);
MOZ_MUST_USE bool
AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value);
MOZ_MUST_USE bool
AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason);
MOZ_MUST_USE bool
AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value);
MOZ_MUST_USE bool
AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason);
class AsyncGeneratorRequest : public NativeObject
{
private:
enum AsyncGeneratorRequestSlots {
Slot_CompletionKind = 0,
Slot_CompletionValue,
Slot_Promise,
Slots,
};
void setCompletionKind(CompletionKind completionKind_) {
setFixedSlot(Slot_CompletionKind,
Int32Value(static_cast<int32_t>(completionKind_)));
}
void setCompletionValue(HandleValue completionValue_) {
setFixedSlot(Slot_CompletionValue, completionValue_);
}
void setPromise(HandleObject promise_) {
setFixedSlot(Slot_Promise, ObjectValue(*promise_));
}
public:
static const Class class_;
static AsyncGeneratorRequest*
create(JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
HandleObject promise);
CompletionKind completionKind() const {
return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32());
}
JS::Value completionValue() const {
return getFixedSlot(Slot_CompletionValue);
}
JSObject* promise() const {
return &getFixedSlot(Slot_Promise).toObject();
}
};
class AsyncGeneratorObject : public NativeObject
{
private:
enum AsyncGeneratorObjectSlots {
Slot_State = 0,
Slot_Generator,
Slot_QueueOrRequest,
Slots
};
enum State {
State_SuspendedStart,
State_SuspendedYield,
State_Executing,
// State_AwaitingYieldReturn corresponds to the case that
// AsyncGenerator#return is called while State_Executing,
// just like the case that AsyncGenerator#return is called
// while State_Completed.
State_AwaitingYieldReturn,
State_AwaitingReturn,
State_Completed
};
State state() const {
return static_cast<State>(getFixedSlot(Slot_State).toInt32());
}
void setState(State state_) {
setFixedSlot(Slot_State, Int32Value(state_));
}
void setGenerator(const Value& value) {
setFixedSlot(Slot_Generator, value);
}
// Queue is implemented in 2 ways. If only one request is queued ever,
// request is stored directly to the slot. Once 2 requests are queued, an
// array is created and requests are pushed into it, and the array is
// stored to the slot.
bool isSingleQueue() const {
return getFixedSlot(Slot_QueueOrRequest).isNull() ||
getFixedSlot(Slot_QueueOrRequest).toObject().is<AsyncGeneratorRequest>();
}
bool isSingleQueueEmpty() const {
return getFixedSlot(Slot_QueueOrRequest).isNull();
}
void setSingleQueueRequest(AsyncGeneratorRequest* request) {
setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request));
}
void clearSingleQueueRequest() {
setFixedSlot(Slot_QueueOrRequest, NullHandleValue);
}
AsyncGeneratorRequest* singleQueueRequest() const {
return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>();
}
ArrayObject* queue() const {
return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ArrayObject>();
}
void setQueue(JSObject* queue_) {
setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_));
}
public:
static const Class class_;
static AsyncGeneratorObject*
create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal);
bool isSuspendedStart() const {
return state() == State_SuspendedStart;
}
bool isSuspendedYield() const {
return state() == State_SuspendedYield;
}
bool isExecuting() const {
return state() == State_Executing;
}
bool isAwaitingYieldReturn() const {
return state() == State_AwaitingYieldReturn;
}
bool isAwaitingReturn() const {
return state() == State_AwaitingReturn;
}
bool isCompleted() const {
return state() == State_Completed;
}
void setSuspendedStart() {
setState(State_SuspendedStart);
}
void setSuspendedYield() {
setState(State_SuspendedYield);
}
void setExecuting() {
setState(State_Executing);
}
void setAwaitingYieldReturn() {
setState(State_AwaitingYieldReturn);
}
void setAwaitingReturn() {
setState(State_AwaitingReturn);
}
void setCompleted() {
setState(State_Completed);
}
JS::Value generatorVal() const {
return getFixedSlot(Slot_Generator);
}
GeneratorObject* generatorObj() const {
return &getFixedSlot(Slot_Generator).toObject().as<GeneratorObject>();
}
static MOZ_MUST_USE bool
enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
Handle<AsyncGeneratorRequest*> request);
static AsyncGeneratorRequest*
dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
static AsyncGeneratorRequest*
peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
bool isQueueEmpty() const {
if (isSingleQueue())
return isSingleQueueEmpty();
return queue()->length() == 0;
}
};
JSObject*
CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter);
class AsyncFromSyncIteratorObject : public NativeObject
{
private:
enum AsyncFromSyncIteratorObjectSlots {
Slot_Iterator = 0,
Slots
};
void setIterator(HandleObject iterator_) {
setFixedSlot(Slot_Iterator, ObjectValue(*iterator_));
}
public:
static const Class class_;
static JSObject*
create(JSContext* cx, HandleObject iter);
JSObject* iterator() const {
return &getFixedSlot(Slot_Iterator).toObject();
}
};
MOZ_MUST_USE bool
AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, HandleValue argument);
} // namespace js
#endif /* vm_AsyncIteration_h */

View File

@ -29,7 +29,10 @@
macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
macro(as, as, "as") \
macro(Async, Async, "Async") \
macro(AsyncFromSyncIterator, AsyncFromSyncIterator, "Async-from-Sync Iterator") \
macro(AsyncFunction, AsyncFunction, "AsyncFunction") \
macro(AsyncGenerator, AsyncGenerator, "AsyncGenerator") \
macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \
macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
macro(async, async, "async") \
macro(await, await, "await") \
@ -311,6 +314,7 @@
macro(star, star, "*") \
macro(starDefaultStar, starDefaultStar, "*default*") \
macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \
macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \
macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \
macro(startTimestamp, startTimestamp, "startTimestamp") \
macro(state, state, "state") \

View File

@ -32,7 +32,6 @@
#include "js/Vector.h"
#include "proxy/ScriptedProxyHandler.h"
#include "vm/ArgumentsObject.h"
#include "vm/AsyncFunction.h"
#include "vm/DebuggerMemory.h"
#include "vm/GeneratorObject.h"
#include "vm/SPSProfiler.h"
@ -1560,16 +1559,11 @@ CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleVa
JSTrapStatus status, MutableHandleValue vp)
{
if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) {
// Don't let a { return: ... } resumption value make a generator or
// async function violate the iterator protocol. The return value from
// Don't let a { return: ... } resumption value make a generator
// function violate the iterator protocol. The return value from
// such a frame must have the form { done: <bool>, value: <anything> }.
RootedFunction callee(cx, frame.callee());
if (callee->isAsync()) {
if (!CheckAsyncResumptionValue(cx, vp)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT);
return false;
}
} else if (callee->isStarGenerator()) {
if (callee->isStarGenerator()) {
if (!CheckStarGeneratorResumptionValue(cx, vp)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD);
return false;
@ -7356,7 +7350,8 @@ DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
/* static */ bool
DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame)
{
return DebuggerFrame::getReferent(frame).script()->isGenerator();
return DebuggerFrame::getReferent(frame).script()->isStarGenerator() ||
DebuggerFrame::getReferent(frame).script()->isLegacyGenerator();
}
/* static */ bool

View File

@ -2448,7 +2448,9 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
MOZ_ASSERT(cx->compartment() == debugEnv->compartment());
// Generators should always have environments.
MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(),
!ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator());
!ei.scope().as<FunctionScope>().canonicalFunction()->isStarGenerator() &&
!ei.scope().as<FunctionScope>().canonicalFunction()->isLegacyGenerator() &&
!ei.scope().as<FunctionScope>().canonicalFunction()->isAsync());
if (!CanUseDebugEnvironmentMaps(cx))
return true;
@ -2594,8 +2596,11 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame)
if (!frame.environmentChain()->is<CallObject>())
return;
if (frame.callee()->isGenerator())
if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() ||
frame.callee()->isAsync())
{
return;
}
CallObject& callobj = frame.environmentChain()->as<CallObject>();
envs->liveEnvs.remove(&callobj);
@ -2726,8 +2731,13 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
if (frame.environmentChain()->compartment() != cx->compartment())
continue;
if (frame.isFunctionFrame() && frame.callee()->isGenerator())
continue;
if (frame.isFunctionFrame()) {
if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() ||
frame.callee()->isAsync())
{
continue;
}
}
if (!frame.isDebuggee())
continue;
@ -2882,7 +2892,8 @@ GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei)
if (ei.scope().is<FunctionScope>()) {
RootedFunction callee(cx, ei.scope().as<FunctionScope>().canonicalFunction());
// Generators should always reify their scopes.
MOZ_ASSERT(!callee->isGenerator());
MOZ_ASSERT(!callee->isStarGenerator() && !callee->isLegacyGenerator() &&
!callee->isAsync());
JS::ExposeObjectToActiveJS(callee);
Rooted<CallObject*> callobj(cx, CallObject::createHollowForDebug(cx, callee));

View File

@ -19,12 +19,13 @@ using namespace js;
JSObject*
GeneratorObject::create(JSContext* cx, AbstractFramePtr frame)
{
MOZ_ASSERT(frame.script()->isGenerator());
MOZ_ASSERT(frame.script()->isStarGenerator() || frame.script()->isLegacyGenerator() ||
frame.script()->isAsync());
MOZ_ASSERT(frame.script()->nfixed() == 0);
Rooted<GlobalObject*> global(cx, cx->global());
RootedNativeObject obj(cx);
if (frame.script()->isStarGenerator()) {
if (frame.script()->isStarGenerator() || frame.script()->isAsync()) {
RootedValue pval(cx);
RootedObject fun(cx, frame.callee());
// FIXME: This would be faster if we could avoid doing a lookup to get
@ -63,10 +64,14 @@ bool
GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc,
Value* vp, unsigned nvalues)
{
MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD);
MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT);
Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
MOZ_ASSERT(!genObj->hasExpressionStack());
MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync());
MOZ_ASSERT_IF(*pc == JSOP_YIELD,
genObj->callee().isStarGenerator() ||
genObj->callee().isLegacyGenerator());
if (*pc == JSOP_YIELD && genObj->isClosing() && genObj->is<LegacyGeneratorObject>()) {
RootedValue val(cx, ObjectValue(*frame.callee()));
@ -74,8 +79,8 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame
return false;
}
uint32_t yieldIndex = GET_UINT24(pc);
genObj->setYieldIndex(yieldIndex);
uint32_t yieldAndAwaitIndex = GET_UINT24(pc);
genObj->setYieldAndAwaitIndex(yieldAndAwaitIndex);
genObj->setEnvironmentChain(*frame.environmentChain());
if (nvalues) {
@ -172,7 +177,7 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation,
}
JSScript* script = callee->nonLazyScript();
uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()];
uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()];
activation.regs().pc = script->offsetToPC(offset);
// Always push on a value, even if we are raising an exception. In the
@ -311,7 +316,8 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto))
return false;
if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) ||
if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto, JSPROP_READONLY,
JSPROP_READONLY) ||
!DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction))
{
return false;
@ -328,8 +334,11 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
SingletonObject));
if (!genFunction)
return false;
if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto,
JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
{
return false;
}
global->setReservedSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
global->setReservedSlot(STAR_GENERATOR_FUNCTION, ObjectValue(*genFunction));
@ -365,3 +374,34 @@ js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v)
return true;
}
bool
GeneratorObject::isAfterYield()
{
return isAfterYieldOrAwait(JSOP_YIELD);
}
bool
GeneratorObject::isAfterAwait()
{
return isAfterYieldOrAwait(JSOP_AWAIT);
}
bool
GeneratorObject::isAfterYieldOrAwait(JSOp op)
{
if (isClosed() || isClosing() || isRunning())
return false;
JSScript* script = callee().nonLazyScript();
jsbytecode* code = script->code();
uint32_t nextOffset = script->yieldAndAwaitOffsets()[yieldAndAwaitIndex()];
if (code[nextOffset] != JSOP_DEBUGAFTERYIELD)
return false;
uint32_t offset = nextOffset - JSOP_YIELD_LENGTH;
MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD ||
code[offset] == JSOP_AWAIT);
return code[offset] == op;
}

View File

@ -21,15 +21,15 @@ class GeneratorObject : public NativeObject
public:
// Magic values stored in the yield index slot when the generator is
// running or closing. See the yield index comment below.
static const int32_t YIELD_INDEX_RUNNING = INT32_MAX;
static const int32_t YIELD_INDEX_CLOSING = INT32_MAX - 1;
static const int32_t YIELD_AND_AWAIT_INDEX_RUNNING = INT32_MAX;
static const int32_t YIELD_AND_AWAIT_INDEX_CLOSING = INT32_MAX - 1;
enum {
CALLEE_SLOT = 0,
ENV_CHAIN_SLOT,
ARGS_OBJ_SLOT,
EXPRESSION_STACK_SLOT,
YIELD_INDEX_SLOT,
YIELD_AND_AWAIT_INDEX_SLOT,
NEWTARGET_SLOT,
RESERVED_SLOTS
};
@ -124,47 +124,48 @@ class GeneratorObject : public NativeObject
// The yield index 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
// generator is closed. If the generator is running, the yield index is
// YIELD_INDEX_RUNNING. If the generator is in that bizarre "closing"
// state, the yield index is YIELD_INDEX_CLOSING.
// YIELD_AND_AWAIT_INDEX_RUNNING. If the generator is in that bizarre
// "closing" state, the yield index is YIELD_AND_AWAIT_INDEX_CLOSING.
//
// If the generator is suspended, it's the yield index (stored as
// JSOP_INITIALYIELD/JSOP_YIELD operand) of the yield instruction that
// suspended the generator. The yield index can be mapped to the bytecode
// offset (interpreter) or to the native code offset (JIT).
// JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield
// instruction that suspended the generator. The yield index can be mapped
// to the bytecode offset (interpreter) or to the native code offset (JIT).
bool isRunning() const {
MOZ_ASSERT(!isClosed());
return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_RUNNING;
return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_RUNNING;
}
bool isClosing() const {
return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_CLOSING;
return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_CLOSING;
}
bool isSuspended() const {
// Note: also update Baseline's IsSuspendedStarGenerator code if this
// changes.
MOZ_ASSERT(!isClosed());
static_assert(YIELD_INDEX_CLOSING < YIELD_INDEX_RUNNING,
"test below should return false for YIELD_INDEX_RUNNING");
return getFixedSlot(YIELD_INDEX_SLOT).toInt32() < YIELD_INDEX_CLOSING;
static_assert(YIELD_AND_AWAIT_INDEX_CLOSING < YIELD_AND_AWAIT_INDEX_RUNNING,
"test below should return false for YIELD_AND_AWAIT_INDEX_RUNNING");
return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() < YIELD_AND_AWAIT_INDEX_CLOSING;
}
void setRunning() {
MOZ_ASSERT(isSuspended());
setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_RUNNING));
setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_RUNNING));
}
void setClosing() {
MOZ_ASSERT(isSuspended());
setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_CLOSING));
setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_CLOSING));
}
void setYieldIndex(uint32_t yieldIndex) {
MOZ_ASSERT_IF(yieldIndex == 0, getFixedSlot(YIELD_INDEX_SLOT).isUndefined());
MOZ_ASSERT_IF(yieldIndex != 0, isRunning() || isClosing());
MOZ_ASSERT(yieldIndex < uint32_t(YIELD_INDEX_CLOSING));
setFixedSlot(YIELD_INDEX_SLOT, Int32Value(yieldIndex));
void setYieldAndAwaitIndex(uint32_t yieldAndAwaitIndex) {
MOZ_ASSERT_IF(yieldAndAwaitIndex == 0,
getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined());
MOZ_ASSERT_IF(yieldAndAwaitIndex != 0, isRunning() || isClosing());
MOZ_ASSERT(yieldAndAwaitIndex < uint32_t(YIELD_AND_AWAIT_INDEX_CLOSING));
setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(yieldAndAwaitIndex));
MOZ_ASSERT(isSuspended());
}
uint32_t yieldIndex() const {
uint32_t yieldAndAwaitIndex() const {
MOZ_ASSERT(isSuspended());
return getFixedSlot(YIELD_INDEX_SLOT).toInt32();
return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32();
}
bool isClosed() const {
return getFixedSlot(CALLEE_SLOT).isNull();
@ -174,10 +175,17 @@ class GeneratorObject : public NativeObject
setFixedSlot(ENV_CHAIN_SLOT, NullValue());
setFixedSlot(ARGS_OBJ_SLOT, NullValue());
setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
setFixedSlot(YIELD_INDEX_SLOT, NullValue());
setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, NullValue());
setFixedSlot(NEWTARGET_SLOT, NullValue());
}
bool isAfterYield();
bool isAfterAwait();
private:
bool isAfterYieldOrAwait(JSOp op);
public:
static size_t offsetOfCalleeSlot() {
return getFixedSlotOffset(CALLEE_SLOT);
}
@ -187,8 +195,8 @@ class GeneratorObject : public NativeObject
static size_t offsetOfArgsObjSlot() {
return getFixedSlotOffset(ARGS_OBJ_SLOT);
}
static size_t offsetOfYieldIndexSlot() {
return getFixedSlotOffset(YIELD_INDEX_SLOT);
static size_t offsetOfYieldAndAwaitIndexSlot() {
return getFixedSlotOffset(YIELD_AND_AWAIT_INDEX_SLOT);
}
static size_t offsetOfExpressionStackSlot() {
return getFixedSlotOffset(EXPRESSION_STACK_SLOT);

View File

@ -586,17 +586,18 @@ GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject*
}
bool
js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_)
js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_,
unsigned prototypeAttrs, unsigned constructorAttrs)
{
RootedObject ctor(cx, ctor_), proto(cx, proto_);
RootedValue protoVal(cx, ObjectValue(*proto));
RootedValue ctorVal(cx, ObjectValue(*ctor));
return DefineProperty(cx, ctor, cx->names().prototype, protoVal,
nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) &&
DefineProperty(cx, proto, cx->names().constructor, ctorVal,
nullptr, nullptr, 0);
return DefineProperty(cx, ctor, cx->names().prototype, protoVal, nullptr, nullptr,
prototypeAttrs) &&
DefineProperty(cx, proto, cx->names().constructor, ctorVal, nullptr, nullptr,
constructorAttrs);
}
bool

View File

@ -100,6 +100,11 @@ class GlobalObject : public NativeObject
STAR_GENERATOR_FUNCTION,
ASYNC_FUNCTION_PROTO,
ASYNC_FUNCTION,
ASYNC_ITERATOR_PROTO,
ASYNC_FROM_SYNC_ITERATOR_PROTO,
ASYNC_GENERATOR,
ASYNC_GENERATOR_FUNCTION,
ASYNC_GENERATOR_PROTO,
MAP_ITERATOR_PROTO,
SET_ITERATOR_PROTO,
COLLATOR_PROTO,
@ -624,6 +629,36 @@ class GlobalObject : public NativeObject
return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction);
}
static NativeObject*
getOrCreateAsyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global)
{
return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO,
initAsyncGenerators));
}
static NativeObject*
getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO,
initAsyncGenerators));
}
static NativeObject*
getOrCreateAsyncGenerator(JSContext* cx, Handle<GlobalObject*> global) {
return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR,
initAsyncGenerators));
}
static JSObject*
getOrCreateAsyncGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateObject(cx, global, ASYNC_GENERATOR_FUNCTION, initAsyncGenerators);
}
static NativeObject*
getOrCreateAsyncGeneratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR_PROTO,
initAsyncGenerators));
}
static JSObject*
getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto);
@ -782,6 +817,8 @@ class GlobalObject : public NativeObject
static bool initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global);
static bool initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global);
// Implemented in builtin/MapObject.cpp.
static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
@ -985,12 +1022,14 @@ GlobalObject::createArrayFromBuffer<uint8_clamped>() const
}
/*
* Define ctor.prototype = proto as non-enumerable, non-configurable, and
* non-writable; define proto.constructor = ctor as non-enumerable but
* configurable and writable.
* Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
* non-configurable, and non-writable; and define proto.constructor = ctor as
* non-enumerable but configurable and writable.
*/
extern bool
LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto);
LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto,
unsigned prototypeAttrs = JSPROP_PERMANENT | JSPROP_READONLY,
unsigned constructorAttrs = 0);
/*
* Define properties and/or functions on any object. Either ps or fs, or both,

View File

@ -39,6 +39,7 @@
#include "jit/Ion.h"
#include "jit/IonAnalysis.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/Debugger.h"
#include "vm/GeneratorObject.h"
#include "vm/Opcodes.h"
@ -1939,9 +1940,6 @@ CASE(EnableInterruptsPseudoOpcode)
CASE(JSOP_NOP)
CASE(JSOP_NOP_DESTRUCTURING)
CASE(JSOP_UNUSED126)
CASE(JSOP_UNUSED192)
CASE(JSOP_UNUSED209)
CASE(JSOP_UNUSED210)
CASE(JSOP_UNUSED211)
CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
CASE(JSOP_UNUSED221)
@ -3585,6 +3583,29 @@ CASE(JSOP_TOASYNC)
}
END_CASE(JSOP_TOASYNC)
CASE(JSOP_TOASYNCGEN)
{
ReservedRooted<JSFunction*> unwrapped(&rootFunction0,
&REGS.sp[-1].toObject().as<JSFunction>());
JSObject* wrapped = WrapAsyncGenerator(cx, unwrapped);
if (!wrapped)
goto error;
REGS.sp[-1].setObject(*wrapped);
}
END_CASE(JSOP_TOASYNCGEN)
CASE(JSOP_TOASYNCITER)
{
ReservedRooted<JSObject*> iter(&rootObject1, &REGS.sp[-1].toObject());
JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter);
if (!asyncIter)
goto error;
REGS.sp[-1].setObject(*asyncIter);
}
END_CASE(JSOP_TOASYNCITER)
CASE(JSOP_SETFUNNAME)
{
MOZ_ASSERT(REGS.stackDepth() >= 2);
@ -3994,6 +4015,7 @@ CASE(JSOP_INITIALYIELD)
}
CASE(JSOP_YIELD)
CASE(JSOP_AWAIT)
{
MOZ_ASSERT(!cx->isExceptionPending());
MOZ_ASSERT(REGS.fp()->isFunctionFrame());
@ -5126,6 +5148,10 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
case CheckIsObjectKind::GetIterator:
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE);
break;
case CheckIsObjectKind::GetAsyncIterator:
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE);
break;
default:
MOZ_CRASH("Unknown kind");
}

View File

@ -561,7 +561,8 @@ enum class CheckIsObjectKind : uint8_t {
IteratorNext,
IteratorReturn,
IteratorThrow,
GetIterator
GetIterator,
GetAsyncIterator
};
bool

View File

@ -184,7 +184,7 @@ ObjectGroup::useSingletonForNewObject(JSContext* cx, JSScript* script, jsbytecod
* Sub2 lets us continue to distinguish the two subclasses and any extra
* properties added to those prototype objects.
*/
if (script->isGenerator())
if (script->isStarGenerator() || script->isLegacyGenerator() || script->isAsync())
return false;
if (JSOp(*pc) != JSOP_NEW)
return false;

View File

@ -1952,7 +1952,15 @@
* Stack: this => this
*/ \
macro(JSOP_CHECKTHISREINIT,191,"checkthisreinit",NULL,1, 1, 1, JOF_BYTE) \
macro(JSOP_UNUSED192, 192,"unused192", NULL, 1, 0, 0, JOF_BYTE) \
/*
* Pops the top of stack value as 'unwrapped', converts it to async
* generator 'wrapped', and pushes 'wrapped' back on the stack.
* Category: Statements
* Type: Generator
* Operands:
* Stack: unwrapped => wrapped
*/ \
macro(JSOP_TOASYNCGEN, 192, "toasyncgen", NULL, 1, 1, 1, JOF_BYTE) \
\
/*
* Pops the top two values on the stack as 'propval' and 'obj', pushes
@ -2055,7 +2063,7 @@
* interpretation.
* Category: Statements
* Type: Generator
* Operands: uint24_t yieldIndex
* Operands: uint24_t yieldAndAwaitIndex
* Stack: generator =>
*/ \
macro(JSOP_INITIALYIELD, 202,"initialyield", NULL, 4, 1, 1, JOF_UINT24) \
@ -2064,7 +2072,7 @@
* returns 'rval1'. Pushes sent value from 'send()' onto the stack.
* Category: Statements
* Type: Generator
* Operands: uint24_t yieldIndex
* Operands: uint24_t yieldAndAwaitIndex
* Stack: rval1, gen => rval2
*/ \
macro(JSOP_YIELD, 203,"yield", NULL, 4, 2, 1, JOF_UINT24) \
@ -2119,8 +2127,24 @@
* Stack: =>
*/ \
macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \
/*
* Pops the generator and the return value 'promise', stops interpretation
* and returns 'promise'. Pushes resolved value onto the stack.
* Category: Statements
* Type: Generator
* Operands: uint24_t yieldAndAwaitIndex
* Stack: promise, gen => resolved
*/ \
macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \
/*
* Pops the iterator from the top of the stack, and create async iterator
* from it and push the async iterator back onto the stack.
* Category: Statements
* Type: Generator
* Operands:
* Stack: iter => asynciter
*/ \
macro(JSOP_TOASYNCITER, 210, "toasynciter", NULL, 1, 1, 1, JOF_BYTE) \
macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \
/*
* Initializes generator frame, creates a generator and pushes it on the

View File

@ -41,7 +41,10 @@ probes::EnterScript(JSContext* cx, JSScript* script, JSFunction* maybeFun,
if (rt->spsProfiler.enabled()) {
if (!rt->spsProfiler.enter(cx, script, maybeFun))
return false;
MOZ_ASSERT_IF(!fp->script()->isGenerator(), !fp->hasPushedSPSFrame());
MOZ_ASSERT_IF(!fp->script()->isStarGenerator() &&
!fp->script()->isLegacyGenerator() &&
!fp->script()->isAsync(),
!fp->hasPushedSPSFrame());
fp->setPushedSPSFrame();
}

View File

@ -27,6 +27,7 @@
#include "builtin/MapObject.h"
#include "builtin/ModuleObject.h"
#include "builtin/Object.h"
#include "builtin/Promise.h"
#include "builtin/Reflect.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/SIMD.h"
@ -2135,6 +2136,105 @@ intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_CreatePendingPromise(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
RootedObject promise(cx, PromiseObject::createSkippingExecutor(cx));
if (!promise)
return false;
args.rval().setObject(*promise);
return true;
}
static bool
intrinsic_CreatePromiseResolvedWith(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, args[0]));
if (!promise)
return false;
args.rval().setObject(*promise);
return true;
}
static bool
intrinsic_CreatePromiseRejectedWith(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedObject promise(cx, PromiseObject::unforgeableReject(cx, args[0]));
if (!promise)
return false;
args.rval().setObject(*promise);
return true;
}
static bool
intrinsic_ResolvePromise(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
if (!PromiseObject::resolve(cx, promise, args[1]))
return false;
args.rval().setUndefined();
return true;
}
static bool
intrinsic_RejectPromise(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
if (!PromiseObject::reject(cx, promise, args[1]))
return false;
args.rval().setUndefined();
return true;
}
static bool
intrinsic_CallOriginalPromiseThen(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 2);
RootedObject promise(cx, &args[0].toObject());
Value val = args[1];
RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
val = args.get(2);
RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
RootedObject resultPromise(cx, JS::CallOriginalPromiseThen(cx, promise, onResolvedObj,
onRejectedObj));
if (!resultPromise)
return false;
args.rval().setObject(*resultPromise);
return true;
}
static bool
intrinsic_AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 2);
RootedObject promise(cx, &args[0].toObject());
Value val = args[1];
RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
val = args.get(2);
RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
bool result = JS::AddPromiseReactions(cx, promise, onResolvedObj, onRejectedObj);
if (!result)
return false;
args.rval().setUndefined();
return true;
}
// The self-hosting global isn't initialized with the normal set of builtins.
// Instead, individual C++-implemented functions that're required by
// self-hosted code are defined as global functions. Accessing these
@ -2536,6 +2636,14 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
JS_FN("CreatePendingPromise", intrinsic_CreatePendingPromise, 0, 0),
JS_FN("CreatePromiseResolvedWith", intrinsic_CreatePromiseResolvedWith, 1, 0),
JS_FN("CreatePromiseRejectedWith", intrinsic_CreatePromiseRejectedWith, 1, 0),
JS_FN("ResolvePromise", intrinsic_ResolvePromise, 2, 0),
JS_FN("RejectPromise", intrinsic_RejectPromise, 2, 0),
JS_FN("AddPromiseReactions", intrinsic_AddPromiseReactions, 3, 0),
JS_FN("CallOriginalPromiseThen", intrinsic_CallOriginalPromiseThen, 3, 0),
JS_FN("IsPromiseObject", intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1, 0),
JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<PromiseObject>>, 2, 0),
JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0),
@ -3005,7 +3113,8 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
return false;
// JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
// aren't any.
MOZ_ASSERT(!sourceFun->isGenerator());
MOZ_ASSERT(!sourceFun->isStarGenerator() && !sourceFun->isLegacyGenerator() &&
!sourceFun->isAsync());
MOZ_ASSERT(targetFun->isExtended());
MOZ_ASSERT(targetFun->isInterpretedLazy());
MOZ_ASSERT(targetFun->isSelfHostedBuiltin());

View File

@ -335,7 +335,7 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs,
HandleFunction callee, HandleValue newTarget,
HandleObject envChain)
{
MOZ_ASSERT(callee->isGenerator());
MOZ_ASSERT(callee->isStarGenerator() || callee->isLegacyGenerator() || callee->isAsync());
RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
InterpreterFrame* prev = regs.fp();
jsbytecode* prevpc = regs.pc;

View File

@ -270,7 +270,9 @@ InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc)
UnwindAllEnvironmentsInFrame(cx, ei);
if (isFunctionFrame()) {
if (!callee().isGenerator() &&
if (!callee().isStarGenerator() &&
!callee().isLegacyGenerator() &&
!callee().isAsync() &&
isConstructing() &&
thisArgument().isObject() &&
returnValue().isPrimitive())

View File

@ -693,7 +693,8 @@ class InterpreterFrame
}
void resumeGeneratorFrame(JSObject* envChain) {
MOZ_ASSERT(script()->isGenerator());
MOZ_ASSERT(script()->isStarGenerator() || script()->isLegacyGenerator() ||
script()->isAsync());
MOZ_ASSERT(isFunctionFrame());
flags_ |= HAS_INITIAL_ENV;
envChain_ = envChain;

View File

@ -8608,9 +8608,12 @@ EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser)
break;
}
if (parser.pc->isGenerator())
if (parser.pc->isStarGenerator() || parser.pc->isLegacyGenerator())
return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
if (parser.pc->isAsync())
return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by async context");
if (parser.pc->isArrowFunction())
return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");