Skip to content
This repository has been archived by the owner on Jan 13, 2024. It is now read-only.

Commit

Permalink
Store strictness information during preparse.
Browse files Browse the repository at this point in the history
Summary:
Preparsing properly parses directives when running on functions,
and stores strictness information in the functions' AST node decorations.
That information was not shared with the lazy parse step, which only
stored start/end locations for functions.

Augment the preparsing data to also store strictness information for
these functions and read it back in during the lazy parse.
This also requires the semantic validator to set the strict mode
based on the node strictness instead of reverifying it via scanning
the prologue of the function for directives (in lazy parse mode,
those bodies are empty).

Reviewed By: tmikov

Differential Revision: D25318752

fbshipit-source-id: 5b53f6cae31408dc034d8b0e2f30a929c95d3cc8
  • Loading branch information
avp authored and facebook-github-bot committed Jan 29, 2021
1 parent 4ab90f7 commit c765f74
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 8 deletions.
14 changes: 12 additions & 2 deletions include/hermes/Parser/PreParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,20 @@ struct SMLocInfo : llvh::DenseMapInfo<SMLoc> {
}
};

/// Holds information about a preparsed function that can be used during the
/// lazy parse phase.
struct PreParsedFunctionInfo {
/// The end location of the function.
SMLoc end;

/// Whether or not the function is strict.
bool strictMode;
};

/// Per buffer information from preparsing.
struct PreParsedBufferInfo {
/// Map from function body start to end.
llvh::DenseMap<SMLoc, SMLoc, SMLocInfo> bodyStartToEnd{};
/// Map from function body start to function info.
llvh::DenseMap<SMLoc, PreParsedFunctionInfo, SMLocInfo> functionInfo{};
};

/// Per \p Context information from preparsing.
Expand Down
12 changes: 9 additions & 3 deletions lib/AST/SemanticValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,9 +628,15 @@ void SemanticValidator::visitFunction(

// Note that body might me empty (for lazy functions) or an expression (for
// arrow functions).
if (isa<ESTree::BlockStatementNode>(body)) {
useStrictNode =
scanDirectivePrologue(cast<ESTree::BlockStatementNode>(body)->_body);
if (auto *bodyNode = dyn_cast<ESTree::BlockStatementNode>(body)) {
if (bodyNode->isLazyFunctionBody) {
// If it is a lazy function body, then scanning the directive prologue
// won't accomplish anything. Set the strictness directly via the
// stored strictness (which was populated based on preparsing data).
curFunction()->strictMode = ESTree::isStrict(node->strictness);
} else {
useStrictNode = scanDirectivePrologue(bodyNode->_body);
}
updateNodeStrictness(node);
}

Expand Down
13 changes: 10 additions & 3 deletions lib/Parser/JSParserImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,13 +622,19 @@ Optional<ESTree::BlockStatementNode *> JSParserImpl::parseFunctionBody(
bool parseDirectives) {
if (pass_ == LazyParse && !eagerly) {
auto startLoc = tok_->getStartLoc();
assert(preParsed_->bodyStartToEnd.count(startLoc) == 1);
auto endLoc = preParsed_->bodyStartToEnd[startLoc];
assert(
preParsed_->functionInfo.count(startLoc) == 1 &&
"no function info stored during preparse");
PreParsedFunctionInfo functionInfo = preParsed_->functionInfo[startLoc];
SMLoc endLoc = functionInfo.end;
if ((unsigned)(endLoc.getPointer() - startLoc.getPointer()) >=
context_.getPreemptiveFunctionCompilationThreshold()) {
lexer_.seek(endLoc);
advance(grammarContext);

// Emulate parsing the "use strict" directive in parseBlock.
setStrictMode(functionInfo.strictMode);

auto *body = new (context_) ESTree::BlockStatementNode({});
body->isLazyFunctionBody = true;
body->paramYield = paramYield_;
Expand All @@ -643,7 +649,8 @@ Optional<ESTree::BlockStatementNode *> JSParserImpl::parseFunctionBody(
return None;

if (pass_ == PreParse) {
preParsed_->bodyStartToEnd[(*body)->getStartLoc()] = (*body)->getEndLoc();
preParsed_->functionInfo[(*body)->getStartLoc()] =
PreParsedFunctionInfo{(*body)->getEndLoc(), isStrictMode()};
}

return body;
Expand Down
24 changes: 24 additions & 0 deletions test/hermes/lazy-strict-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: %hermes -non-strict -O0 %s 2>&1 | %FileCheck --match-full-lines %s
// RUN: %hermes -lazy -non-strict -O0 %s 2>&1 | %FileCheck --match-full-lines %s

// foo is never called, but its strictness must be set correctly.
function foo(x) {
"use strict";
}

(function bar() {
try {
// .caller access is forbidden on strict functions.
print(foo.caller);
} catch (e) {
print('caught', e.name);
}
})();
// CHECK: caught TypeError

0 comments on commit c765f74

Please sign in to comment.