Skip to content

Commit

Permalink
[1.8>1.9] [MERGE #4618 @boingoing] OS#14568840: Remove 'this' binding…
Browse files Browse the repository at this point in the history
… for indirect eval

Merge pull request #4618 from boingoing:RemoveThisBindingIndirectEval

Having a 'this' binding in the indirect eval leads to problems if there is a lambda capturing 'this' in the indirect eval. The lambda would try to load 'this' from a scope slot in the global scope of the indirect eval which asserts.

Seems we can simplify the above by just removing the 'this' binding from the indirect eval. Then we'll simply load 'this' like an ordinary lambda at global scope would.

Fixes:
https://microsoft.visualstudio.com/web/wi.aspx?id=14568840
  • Loading branch information
boingoing committed Feb 3, 2018
2 parents 5c0bed5 + f1d62d7 commit 488faf3
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 20 deletions.
24 changes: 5 additions & 19 deletions lib/Parser/Parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1711,19 +1711,17 @@ ParseNodePtr Parser::CreateSpecialVarDeclIfNeeded(ParseNodePtr pnodeFnc, IdentPt
return nullptr;
}

void Parser::CreateSpecialSymbolDeclarations(ParseNodePtr pnodeFnc, bool isGlobal)
void Parser::CreateSpecialSymbolDeclarations(ParseNodePtr pnodeFnc)
{
// Lambda function cannot have any special bindings.
if (pnodeFnc->sxFnc.IsLambda())
{
return;
}
Assert(!(isGlobal && (this->m_grfscr & fscrEval)));
Assert(!isGlobal || (this->m_grfscr & fscrEvalCode));

bool isTopLevelEventHandler = (this->m_grfscr & fscrImplicitThis || this->m_grfscr & fscrImplicitParents) && !pnodeFnc->sxFnc.IsNested();

// Create a 'this' symbol for indirect eval, non-lambda functions with references to 'this', and all class constructors and top level event hanlders.
// Create a 'this' symbol for non-lambda functions with references to 'this', and all class constructors and top level event hanlders.
ParseNodePtr varDeclNode = CreateSpecialVarDeclIfNeeded(pnodeFnc, wellKnownPropertyPids._this, pnodeFnc->sxFnc.IsClassConstructor() || isTopLevelEventHandler);
if (varDeclNode)
{
Expand All @@ -1735,12 +1733,6 @@ void Parser::CreateSpecialSymbolDeclarations(ParseNodePtr pnodeFnc, bool isGloba
}
}

// Global code cannot have 'new.target' or 'super' bindings.
if (isGlobal)
{
return;
}

// Create a 'new.target' symbol for any ordinary function with a reference and all class constructors.
varDeclNode = CreateSpecialVarDeclIfNeeded(pnodeFnc, wellKnownPropertyPids._newTarget, pnodeFnc->sxFnc.IsClassConstructor());
if (varDeclNode)
Expand Down Expand Up @@ -5760,7 +5752,7 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho
UpdateArgumentsNode(pnodeFnc, argNode);
}

CreateSpecialSymbolDeclarations(pnodeFnc, false);
CreateSpecialSymbolDeclarations(pnodeFnc);

// Restore the lists of scopes that contain function expressions.

Expand Down Expand Up @@ -7082,7 +7074,7 @@ ParseNodePtr Parser::GenerateEmptyConstructor(bool extends)

FinishParseBlock(pnodeInnerBlock);

CreateSpecialSymbolDeclarations(pnodeFnc, false);
CreateSpecialSymbolDeclarations(pnodeFnc);

FinishParseBlock(pnodeBlock);

Expand Down Expand Up @@ -11352,7 +11344,7 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList)
UpdateArgumentsNode(pnodeFnc, argNode);
}

CreateSpecialSymbolDeclarations(pnodeFnc, false);
CreateSpecialSymbolDeclarations(pnodeFnc);

this->FinishParseBlock(pnodeBlock);
if (pnodeFncExprBlock)
Expand Down Expand Up @@ -11762,12 +11754,6 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou
if (tkEOF != m_token.tk)
Error(ERRsyntax);

// We only need to create special symbol bindings for 'this' for indirect eval
if ((this->m_grfscr & fscrEvalCode) && !(this->m_grfscr & fscrEval))
{
CreateSpecialSymbolDeclarations(pnodeProg, true);
}

// Append an EndCode node.
AddToNodeList(&pnodeProg->sxFnc.pnodeBody, &lastNodeRef,
CreateNodeWithScanner<knopEndCode>());
Expand Down
2 changes: 1 addition & 1 deletion lib/Parser/Parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ class Parser
void FinishParseFncExprScope(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncExprScope);

bool IsSpecialName(IdentPtr pid);
void CreateSpecialSymbolDeclarations(ParseNodePtr pnodeFnc, bool isGlobal);
void CreateSpecialSymbolDeclarations(ParseNodePtr pnodeFnc);
ParseNodePtr ReferenceSpecialName(IdentPtr pid, charcount_t ichMin = 0, charcount_t ichLim = 0, bool createNode = false);
ParseNodePtr CreateSpecialVarDeclIfNeeded(ParseNodePtr pnodeFnc, IdentPtr pid, bool forceCreate = false);

Expand Down
13 changes: 13 additions & 0 deletions test/Basics/SpecialSymbolCapture.js
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,19 @@ var tests = [
assert.throws(() => WScript.LoadScript(`(class classExpr {}())`), TypeError, "Class expression called at global scope", "Class constructor cannot be called without the new keyword");
assert.throws(() => WScript.LoadScript(`(() => (class classExpr {}()))()`), TypeError, "Class expression called in global lambda", "Class constructor cannot be called without the new keyword");
}
},
{
name: "Indirect eval should not create a 'this' binding",
body: function() {
WScript.LoadScript(`
this.eval("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval called off of this capturing this'))()");
this['eval']("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval called from a property index capturing this'))()");
var _eval = 'eval';
this[_eval]("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval called from a property index capturing this'))()");
_eval = eval;
_eval("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval capturing this'))()");
`);
}
}
]

Expand Down

0 comments on commit 488faf3

Please sign in to comment.