Skip to content

Commit 24928c5

Browse files
committed
[1.8>1.9] [MERGE #4628 @boingoing] Defer parse treats yield as keyword
Merge pull request #4628 from boingoing:DeferParseYieldToken When we're defer parsing a function, we construct an enclosing function context to use as a parent function. That dummy parent function does not initialize the function flags, though, so they're random. When we're parsing a deferred lambda function, we look at those flags in the dummy parent function to see if it was a generator function to know if we should treat yield as an identifier or not. Because the flags are random, we sometimes throw a syntax error here thinking yield should be treated as a keyword. If we throw a syntax error upon defer-parse, we will hit an assert. Fix by storing a bit of state in the ScopeInfo which indicates if the enclosing function is a generator or is an async function. Fixes: https://microsoft.visualstudio.com/web/wi.aspx?id=14502906
2 parents 77a31ac + 7443cf6 commit 24928c5

File tree

5 files changed

+78
-2
lines changed

5 files changed

+78
-2
lines changed

Diff for: lib/Parser/Parse.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -11611,6 +11611,10 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou
1161111611
m_currentNodeFunc->sxFnc.SetStrictMode(!!this->m_fUseStrictMode);
1161211612

1161311613
this->RestoreScopeInfo(scopeInfo);
11614+
11615+
m_currentNodeFunc->sxFnc.ClearFlags();
11616+
m_currentNodeFunc->sxFnc.SetIsGenerator(scopeInfo->IsGeneratorFunctionBody());
11617+
m_currentNodeFunc->sxFnc.SetIsAsync(scopeInfo->IsAsyncFunctionBody());
1161411618
}
1161511619
}
1161611620

Diff for: lib/Runtime/ByteCode/ScopeInfo.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ namespace Js
7171
scopeInfo->mustInstantiate = scope->GetMustInstantiate();
7272
scopeInfo->isCached = (scope->GetFunc()->GetBodyScope() == scope) && scope->GetFunc()->GetHasCachedScope();
7373
scopeInfo->hasLocalInClosure = scope->GetHasOwnLocalInClosure();
74-
74+
75+
if (scope->GetScopeType() == ScopeType_FunctionBody)
76+
{
77+
scopeInfo->isGeneratorFunctionBody = scope->GetFunc()->byteCodeFunction->GetFunctionInfo()->IsGenerator();
78+
scopeInfo->isAsyncFunctionBody = scope->GetFunc()->byteCodeFunction->GetFunctionInfo()->IsAsync();
79+
}
7580

7681
TRACE_BYTECODE(_u("\nSave ScopeInfo: %s #symbols: %d %s\n"),
7782
scope->GetFunc()->name, count,

Diff for: lib/Runtime/ByteCode/ScopeInfo.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ namespace Js {
4545
Field(BYTE) isCached : 1; // indicates that local vars and functions are cached across invocations
4646
Field(BYTE) areNamesCached : 1;
4747
Field(BYTE) hasLocalInClosure : 1;
48+
Field(BYTE) isGeneratorFunctionBody : 1;
49+
Field(BYTE) isAsyncFunctionBody : 1;
4850

4951
FieldNoBarrier(Scope *) scope;
5052
Field(::ScopeType) scopeType;
@@ -54,7 +56,7 @@ namespace Js {
5456

5557
private:
5658
ScopeInfo(FunctionInfo * function, int symbolCount)
57-
: functionInfo(function), /*funcExprScopeInfo(nullptr), paramScopeInfo(nullptr),*/ symbolCount(symbolCount), parent(nullptr), scope(nullptr), areNamesCached(false), hasLocalInClosure(false)/*, parentOnly(false)*/
59+
: functionInfo(function), /*funcExprScopeInfo(nullptr), paramScopeInfo(nullptr),*/ symbolCount(symbolCount), parent(nullptr), scope(nullptr), areNamesCached(false), hasLocalInClosure(false), isGeneratorFunctionBody(false), isAsyncFunctionBody(false)/*, parentOnly(false)*/
5860
{
5961
}
6062

@@ -259,6 +261,16 @@ namespace Js {
259261
return hasLocalInClosure;
260262
}
261263

264+
bool IsGeneratorFunctionBody() const
265+
{
266+
return this->isGeneratorFunctionBody;
267+
}
268+
269+
bool IsAsyncFunctionBody() const
270+
{
271+
return this->isAsyncFunctionBody;
272+
}
273+
262274
static void SaveEnclosingScopeInfo(ByteCodeGenerator* byteCodeGenerator, /*FuncInfo* parentFunc,*/ FuncInfo* func);
263275

264276
void EnsurePidTracking(ScriptContext* scriptContext);

Diff for: test/es6/DeferParseLambda.js

+48
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,54 @@ var tests = [
110110
`);
111111
}
112112
},
113+
{
114+
name: "Global functions using 'yield' as identifier",
115+
body: function () {
116+
WScript.LoadScript(`
117+
var a = async (yield) => { yield };
118+
assert.isTrue(a() instanceof Promise, "Async lambda with yield as a formal parameter name");
119+
120+
function b(yield) {
121+
return yield;
122+
}
123+
assert.areEqual('b', b('b'), "Function with yield as a formal parameter name");
124+
125+
var c = async (yield) => yield;
126+
assert.isTrue(c() instanceof Promise, "Async lambda with yield as a formal parameter name and compact body");
127+
128+
async function d(yield) {
129+
return yield;
130+
}
131+
assert.isTrue(d() instanceof Promise, "Async lambda with yield as a formal parameter name and compact body");
132+
`);
133+
}
134+
},
135+
{
136+
name: "Nested functions using 'yield' as identifier",
137+
body: function () {
138+
var a = async (yield) => { yield };
139+
assert.isTrue(a() instanceof Promise, "Async lambda with yield as a formal parameter name");
140+
141+
function b(yield) {
142+
return yield;
143+
}
144+
assert.areEqual('b', b('b'), "Function with yield as a formal parameter name");
145+
146+
var c = async (yield) => yield;
147+
assert.isTrue(c() instanceof Promise, "Async lambda with yield as a formal parameter name and compact body");
148+
149+
async function d(yield) {
150+
return yield;
151+
}
152+
assert.isTrue(d() instanceof Promise, "Async lambda with yield as a formal parameter name and compact body");
153+
154+
var e = async (a = yield) => { yield };
155+
assert.isTrue(e() instanceof Promise, "Async lambda with yield in a default argument");
156+
157+
var f = async (a = yield) => yield;
158+
assert.isTrue(f() instanceof Promise, "Async lambda with compact body and yield in a default argument");
159+
}
160+
},
113161
]
114162

115163
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

Diff for: test/es6/rlexe.xml

+7
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,13 @@
831831
<tags>exclude_arm</tags>
832832
</default>
833833
</test>
834+
<test>
835+
<default>
836+
<files>generators-syntax.js</files>
837+
<compile-flags>-ES6Generators -ES6Classes -ES6DefaultArgs -force:deferparse -args summary -endargs</compile-flags>
838+
<tags>exclude_arm</tags>
839+
</default>
840+
</test>
834841
<test>
835842
<default>
836843
<files>generators-deferparse.js</files>

0 commit comments

Comments
 (0)