/src/hermes/lib/AST/SemanticValidator.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "SemanticValidator.h" |
9 | | |
10 | | #include "hermes/AST/ESTree.h" |
11 | | #include "hermes/Regex/RegexSerialization.h" |
12 | | |
13 | | #include "llvh/ADT/ScopeExit.h" |
14 | | #include "llvh/ADT/SmallSet.h" |
15 | | #include "llvh/Support/SaveAndRestore.h" |
16 | | |
17 | | using llvh::cast_or_null; |
18 | | using llvh::dyn_cast; |
19 | | using llvh::isa; |
20 | | using llvh::SaveAndRestore; |
21 | | |
22 | | namespace hermes { |
23 | | namespace sem { |
24 | | |
25 | | //===----------------------------------------------------------------------===// |
26 | | // Keywords |
27 | | |
28 | | Keywords::Keywords(Context &astContext) |
29 | | : identArguments( |
30 | 154 | astContext.getIdentifier("arguments").getUnderlyingPointer()), |
31 | 154 | identEval(astContext.getIdentifier("eval").getUnderlyingPointer()), |
32 | 154 | identDelete(astContext.getIdentifier("delete").getUnderlyingPointer()), |
33 | 154 | identThis(astContext.getIdentifier("this").getUnderlyingPointer()), |
34 | | identUseStrict( |
35 | 154 | astContext.getIdentifier("use strict").getUnderlyingPointer()), |
36 | | identShowSource( |
37 | 154 | astContext.getIdentifier("show source").getUnderlyingPointer()), |
38 | | identHideSource( |
39 | 154 | astContext.getIdentifier("hide source").getUnderlyingPointer()), |
40 | | identSensitive( |
41 | 154 | astContext.getIdentifier("sensitive").getUnderlyingPointer()), |
42 | 154 | identVar(astContext.getIdentifier("var").getUnderlyingPointer()), |
43 | 154 | identLet(astContext.getIdentifier("let").getUnderlyingPointer()), |
44 | 154 | identConst(astContext.getIdentifier("const").getUnderlyingPointer()), |
45 | 154 | identPlus(astContext.getIdentifier("+").getUnderlyingPointer()), |
46 | 154 | identMinus(astContext.getIdentifier("-").getUnderlyingPointer()), |
47 | 154 | identAssign(astContext.getIdentifier("=").getUnderlyingPointer()) {} |
48 | | |
49 | | //===----------------------------------------------------------------------===// |
50 | | // SemanticValidator |
51 | | |
52 | | SemanticValidator::SemanticValidator( |
53 | | Context &astContext, |
54 | | sem::SemContext &semCtx, |
55 | | bool compile) |
56 | 154 | : astContext_(astContext), |
57 | 154 | sm_(astContext.getSourceErrorManager()), |
58 | 154 | bufferMessages_{&sm_}, |
59 | 154 | semCtx_(semCtx), |
60 | 154 | initialErrorCount_(sm_.getErrorCount()), |
61 | 154 | kw_(astContext), |
62 | 154 | compile_(compile) {} |
63 | | |
64 | 154 | bool SemanticValidator::doIt(Node *rootNode) { |
65 | 154 | visitESTreeNode(*this, rootNode); |
66 | 154 | return sm_.getErrorCount() == initialErrorCount_; |
67 | 154 | } |
68 | | |
69 | 0 | bool SemanticValidator::doFunction(Node *function, bool strict) { |
70 | | // Create a wrapper context since a function always assumes there is an |
71 | | // existing context. |
72 | 0 | FunctionContext wrapperContext(this, strict, nullptr, nullptr); |
73 | |
|
74 | 0 | assert( |
75 | 0 | wrapperContext.scopedClosures == nullptr && |
76 | 0 | "current context doesnt have a body, so it shouldn't have closures."); |
77 | | |
78 | | // Create a dummy closures array that will contain the closure for \p |
79 | | // function. |
80 | 0 | FunctionInfo::BlockClosures dummyClosures; |
81 | 0 | wrapperContext.scopedClosures = &dummyClosures; |
82 | 0 | FunctionInfo::BlockDecls dummyDecls; |
83 | 0 | wrapperContext.scopedDecls = &dummyDecls; |
84 | |
|
85 | 0 | visitESTreeNode(*this, function); |
86 | 0 | return sm_.getErrorCount() == initialErrorCount_; |
87 | 0 | } |
88 | | |
89 | 154 | void SemanticValidator::visit(ProgramNode *node) { |
90 | 154 | FunctionContext newFuncCtx{this, astContext_.isStrictMode(), node, node}; |
91 | | |
92 | 154 | scanDirectivePrologue(node->_body); |
93 | 154 | setDirectiveDerivedInfo(node); |
94 | | |
95 | 154 | visitESTreeChildren(*this, node); |
96 | 154 | } |
97 | | |
98 | 69 | void SemanticValidator::visit(VariableDeclaratorNode *varDecl, Node *parent) { |
99 | 69 | auto *declaration = cast<VariableDeclarationNode>(parent); |
100 | | |
101 | 69 | FunctionInfo::VarDecl::Kind declKind; |
102 | 69 | if (declaration->_kind == kw_.identLet) |
103 | 67 | declKind = FunctionInfo::VarDecl::Kind::Let; |
104 | 2 | else if (declaration->_kind == kw_.identConst) |
105 | 0 | declKind = FunctionInfo::VarDecl::Kind::Const; |
106 | 2 | else { |
107 | 2 | assert(declaration->_kind == kw_.identVar); |
108 | 2 | declKind = FunctionInfo::VarDecl::Kind::Var; |
109 | 2 | } |
110 | | |
111 | 69 | validateDeclarationNames( |
112 | 69 | declKind, |
113 | 69 | varDecl->_id, |
114 | 69 | curFunction()->varDecls, |
115 | 69 | curFunction()->scopedDecls); |
116 | 69 | visitESTreeChildren(*this, varDecl); |
117 | 69 | } |
118 | | |
119 | 0 | void SemanticValidator::visit(MetaPropertyNode *metaProp) { |
120 | 0 | auto *meta = cast<IdentifierNode>(metaProp->_meta); |
121 | 0 | auto *property = cast<IdentifierNode>(metaProp->_property); |
122 | |
|
123 | 0 | if (meta->_name->str() == "new" && property->_name->str() == "target") { |
124 | 0 | if (curFunction()->isGlobalScope()) { |
125 | | // ES9.0 15.1.1: |
126 | | // It is a Syntax Error if StatementList Contains NewTarget unless the |
127 | | // source code containing NewTarget is eval code that is being processed |
128 | | // by a direct eval. |
129 | | // Hermes does not support local eval, so we assume that this is not |
130 | | // inside a local eval call. |
131 | 0 | sm_.error(metaProp->getSourceRange(), "'new.target' not in a function"); |
132 | 0 | } |
133 | 0 | return; |
134 | 0 | } |
135 | | |
136 | 0 | if (meta->_name->str() == "import" && property->_name->str() == "meta") { |
137 | 0 | if (compile_) { |
138 | 0 | sm_.error( |
139 | 0 | metaProp->getSourceRange(), "'import.meta' is currently unsupported"); |
140 | 0 | } |
141 | 0 | return; |
142 | 0 | } |
143 | | |
144 | 0 | sm_.error( |
145 | 0 | metaProp->getSourceRange(), |
146 | 0 | "invalid meta property " + meta->_name->str() + "." + |
147 | 0 | property->_name->str()); |
148 | 0 | } |
149 | | |
150 | 521k | void SemanticValidator::visit(IdentifierNode *identifier) { |
151 | 521k | if (identifier->_name == kw_.identEval && !astContext_.getEnableEval()) |
152 | 0 | sm_.error(identifier->getSourceRange(), "'eval' is disabled"); |
153 | | |
154 | 521k | if (identifier->_name == kw_.identArguments) { |
155 | 15 | if (forbidArguments_) |
156 | 0 | sm_.error(identifier->getSourceRange(), "invalid use of 'arguments'"); |
157 | 15 | curFunction()->semInfo->usesArguments = true; |
158 | 15 | } |
159 | 521k | } |
160 | | |
161 | | /// Process a function declaration by creating a new FunctionContext. |
162 | 0 | void SemanticValidator::visit(FunctionDeclarationNode *funcDecl) { |
163 | 0 | curFunction()->addHoistingCandidate(funcDecl); |
164 | 0 | visitFunction(funcDecl, funcDecl->_id, funcDecl->_params, funcDecl->_body); |
165 | 0 | } |
166 | | |
167 | | /// Process a function expression by creating a new FunctionContext. |
168 | 80 | void SemanticValidator::visit(FunctionExpressionNode *funcExpr) { |
169 | 80 | visitFunction(funcExpr, funcExpr->_id, funcExpr->_params, funcExpr->_body); |
170 | 80 | } |
171 | | |
172 | 23.0k | void SemanticValidator::visit(ArrowFunctionExpressionNode *arrowFunc) { |
173 | | // Convert expression functions to a full-body to simplify IRGen. |
174 | 23.0k | if (compile_ && arrowFunc->_expression) { |
175 | 23.0k | auto *retStmt = new (astContext_) ReturnStatementNode(arrowFunc->_body); |
176 | 23.0k | retStmt->copyLocationFrom(arrowFunc->_body); |
177 | | |
178 | 23.0k | ESTree::NodeList stmtList; |
179 | 23.0k | stmtList.push_back(*retStmt); |
180 | | |
181 | 23.0k | auto *blockStmt = new (astContext_) BlockStatementNode(std::move(stmtList)); |
182 | 23.0k | blockStmt->copyLocationFrom(arrowFunc->_body); |
183 | | |
184 | 23.0k | arrowFunc->_body = blockStmt; |
185 | 23.0k | arrowFunc->_expression = false; |
186 | 23.0k | } |
187 | | |
188 | 23.0k | visitFunction(arrowFunc, nullptr, arrowFunc->_params, arrowFunc->_body); |
189 | | |
190 | 23.0k | curFunction()->semInfo->containsArrowFunctions = true; |
191 | 23.0k | curFunction()->semInfo->containsArrowFunctionsUsingArguments = |
192 | 23.0k | curFunction()->semInfo->containsArrowFunctionsUsingArguments || |
193 | 23.0k | arrowFunc->getSemInfo()->containsArrowFunctionsUsingArguments || |
194 | 23.0k | arrowFunc->getSemInfo()->usesArguments; |
195 | 23.0k | } |
196 | | |
197 | | #if HERMES_PARSE_FLOW |
198 | | /// Process a component declaration by creating a new FunctionContext. |
199 | 0 | void SemanticValidator::visit(ComponentDeclarationNode *componentDecl) { |
200 | 0 | visitFunction( |
201 | 0 | componentDecl, |
202 | 0 | componentDecl->_id, |
203 | 0 | componentDecl->_params, |
204 | 0 | componentDecl->_body); |
205 | 0 | } |
206 | | /// Process a hook declaration by creating a new FunctionContext. |
207 | 0 | void SemanticValidator::visit(HookDeclarationNode *hookDecl) { |
208 | 0 | visitFunction(hookDecl, hookDecl->_id, hookDecl->_params, hookDecl->_body); |
209 | 0 | } |
210 | | #endif |
211 | | |
212 | | /// Ensure that the left side of for-in is an l-value. |
213 | 9 | void SemanticValidator::visit(ForInStatementNode *forIn) { |
214 | 9 | visitForInOf(forIn, forIn->_left); |
215 | 9 | } |
216 | 1 | void SemanticValidator::visit(ForOfStatementNode *forOf) { |
217 | 1 | if (compile_ && forOf->_await) |
218 | 0 | sm_.error( |
219 | 0 | forOf->getSourceRange(), |
220 | 0 | "for await..of loops are currently unsupported"); |
221 | | |
222 | 1 | visitForInOf(forOf, forOf->_left); |
223 | 1 | } |
224 | | |
225 | 10 | void SemanticValidator::visitForInOf(LoopStatementNode *loopNode, Node *left) { |
226 | 10 | loopNode->setLabelIndex(curFunction()->allocateLabel()); |
227 | | |
228 | 10 | SaveAndRestore<LoopStatementNode *> saveLoop( |
229 | 10 | curFunction()->activeLoop, loopNode); |
230 | 10 | SaveAndRestore<StatementNode *> saveSwitch( |
231 | 10 | curFunction()->activeSwitchOrLoop, loopNode); |
232 | | |
233 | 10 | if (auto *VD = dyn_cast<VariableDeclarationNode>(left)) { |
234 | 0 | assert( |
235 | 0 | VD->_declarations.size() == 1 && |
236 | 0 | "for-in/for-of must have a single binding"); |
237 | | |
238 | 0 | auto *declarator = |
239 | 0 | cast<ESTree::VariableDeclaratorNode>(&VD->_declarations.front()); |
240 | |
|
241 | 0 | if (declarator->_init) { |
242 | 0 | if (isa<ESTree::PatternNode>(declarator->_id)) { |
243 | 0 | sm_.error( |
244 | 0 | declarator->_init->getSourceRange(), |
245 | 0 | "destructuring declaration cannot be initialized in for-in/for-of loop"); |
246 | 0 | } else if (!(isa<ForInStatementNode>(loopNode) && |
247 | 0 | !curFunction()->strictMode && VD->_kind == kw_.identVar)) { |
248 | 0 | sm_.error( |
249 | 0 | declarator->_init->getSourceRange(), |
250 | 0 | "for-in/for-of variable declaration may not be initialized"); |
251 | 0 | } |
252 | 0 | } |
253 | 10 | } else { |
254 | 10 | validateAssignmentTarget(left); |
255 | 10 | } |
256 | 10 | visitESTreeChildren(*this, loopNode); |
257 | 10 | } |
258 | | |
259 | 83.7k | void SemanticValidator::visit(BinaryExpressionNode *bin) { |
260 | | // Handle nested +/- non-recursively. |
261 | 83.7k | if (bin->_operator == kw_.identPlus || bin->_operator == kw_.identMinus) { |
262 | 17.2k | auto list = linearizeLeft(bin, {"+", "-"}); |
263 | 17.2k | if (list.size() > MAX_NESTED_BINARY) { |
264 | 3 | recursionDepthExceeded(bin); |
265 | 3 | return; |
266 | 3 | } |
267 | | |
268 | 17.2k | visitESTreeNode(*this, list[0]->_left, list[0]); |
269 | 93.3k | for (auto *e : list) { |
270 | 93.3k | visitESTreeNode(*this, e->_right, e); |
271 | 93.3k | } |
272 | 17.2k | return; |
273 | 17.2k | } |
274 | | |
275 | 66.4k | visitESTreeChildren(*this, bin); |
276 | 66.4k | } |
277 | | |
278 | | /// Ensure that the left side of assgnments is an l-value. |
279 | 3.82k | void SemanticValidator::visit(AssignmentExpressionNode *assignment) { |
280 | | // Handle nested "=" non-recursively. |
281 | 3.82k | if (assignment->_operator == kw_.identAssign) { |
282 | 3.78k | auto list = linearizeRight(assignment, {"="}); |
283 | 3.78k | if (list.size() > MAX_NESTED_ASSIGNMENTS) { |
284 | 0 | recursionDepthExceeded(assignment); |
285 | 0 | return; |
286 | 0 | } |
287 | | |
288 | 4.46k | for (auto *e : list) { |
289 | 4.46k | validateAssignmentTarget(e->_left); |
290 | 4.46k | visitESTreeNode(*this, e->_left, e); |
291 | 4.46k | } |
292 | 3.78k | visitESTreeNode(*this, list.back()->_right, list.back()); |
293 | 3.78k | return; |
294 | 3.78k | } |
295 | | |
296 | 44 | validateAssignmentTarget(assignment->_left); |
297 | 44 | visitESTreeChildren(*this, assignment); |
298 | 44 | } |
299 | | |
300 | | /// Ensure that the operand of ++/-- is an l-value. |
301 | 17 | void SemanticValidator::visit(UpdateExpressionNode *update) { |
302 | | // Check if the left-hand side is valid. |
303 | 17 | if (!isLValue(update->_argument)) { |
304 | 0 | sm_.error( |
305 | 0 | update->_argument->getSourceRange(), |
306 | 0 | "invalid operand in update operation"); |
307 | 0 | } |
308 | 17 | visitESTreeChildren(*this, update); |
309 | 17 | } |
310 | | |
311 | | /// Declare named labels, checking for duplicates, etc. |
312 | 66.5k | void SemanticValidator::visit(LabeledStatementNode *labelStmt) { |
313 | 66.5k | auto id = cast<IdentifierNode>(labelStmt->_label); |
314 | | |
315 | 66.5k | labelStmt->setLabelIndex(curFunction()->allocateLabel()); |
316 | | |
317 | | // Determine the target statement. We need to check if it directly encloses |
318 | | // a loop or another label enclosing a loop. |
319 | 66.5k | StatementNode *targetStmt = labelStmt; |
320 | 66.5k | { |
321 | 66.5k | LabeledStatementNode *curStmt = labelStmt; |
322 | 67.4k | do { |
323 | 67.4k | if (auto *LS = dyn_cast<LoopStatementNode>(curStmt->_body)) { |
324 | 0 | targetStmt = LS; |
325 | 0 | break; |
326 | 0 | } |
327 | 67.4k | } while ((curStmt = dyn_cast<LabeledStatementNode>(curStmt->_body))); |
328 | 66.5k | } |
329 | | |
330 | | // Define the new label, checking for a previous definition. |
331 | 66.5k | auto insertRes = |
332 | 66.5k | curFunction()->labelMap.insert({id->_name, {id, targetStmt}}); |
333 | 66.5k | if (!insertRes.second) { |
334 | 1 | sm_.error( |
335 | 1 | id->getSourceRange(), |
336 | 1 | llvh::Twine("label '") + id->_name->str() + "' is already defined"); |
337 | 1 | sm_.note( |
338 | 1 | insertRes.first->second.declarationNode->getSourceRange(), |
339 | 1 | "previous definition"); |
340 | 1 | } |
341 | | // Auto-erase the label on exit, if we inserted it. |
342 | 66.5k | const auto &deleter = llvh::make_scope_exit( |
343 | 66.5k | [this, inserted = insertRes.second, name = id->_name]() { |
344 | 66.5k | if (inserted) |
345 | 66.5k | curFunction()->labelMap.erase(name); |
346 | 66.5k | }); |
347 | 66.5k | (void)deleter; |
348 | | |
349 | 66.5k | visitESTreeChildren(*this, labelStmt); |
350 | 66.5k | } |
351 | | |
352 | | /// Check RegExp syntax. |
353 | 1.82k | void SemanticValidator::visit(RegExpLiteralNode *regexp) { |
354 | 1.82k | llvh::StringRef regexpError; |
355 | 1.82k | if (compile_) { |
356 | 1.82k | if (auto compiled = CompiledRegExp::tryCompile( |
357 | 1.82k | regexp->_pattern->str(), regexp->_flags->str(), ®expError)) { |
358 | 1.82k | astContext_.addCompiledRegExp( |
359 | 1.82k | regexp->_pattern, regexp->_flags, std::move(*compiled)); |
360 | 1.82k | } else { |
361 | 2 | sm_.error( |
362 | 2 | regexp->getSourceRange(), |
363 | 2 | "Invalid regular expression: " + Twine(regexpError)); |
364 | 2 | } |
365 | 1.82k | } |
366 | 1.82k | visitESTreeChildren(*this, regexp); |
367 | 1.82k | } |
368 | | |
369 | 0 | void SemanticValidator::validateCatchClause(const Node *catchClause) { |
370 | | // The catch clause is optional, so bail early if it doesn't exist. |
371 | 0 | if (!catchClause) { |
372 | 0 | return; |
373 | 0 | } |
374 | 0 | auto *castedCatch = llvh::dyn_cast<ESTree::CatchClauseNode>(catchClause); |
375 | 0 | if (!castedCatch) { |
376 | 0 | return; |
377 | 0 | } |
378 | | // Bail early if there is no identifier in the parameter of the catch. |
379 | 0 | if (!castedCatch->_param || |
380 | 0 | !llvh::isa<ESTree::IdentifierNode>(castedCatch->_param)) { |
381 | 0 | return; |
382 | 0 | } |
383 | 0 | auto *idNode = cast<ESTree::IdentifierNode>(castedCatch->_param); |
384 | 0 | if (!isValidDeclarationName(idNode)) { |
385 | 0 | sm_.error( |
386 | 0 | idNode->getSourceRange(), |
387 | 0 | "cannot bind to " + idNode->_name->str() + |
388 | 0 | " in the catch clause in strict mode"); |
389 | 0 | } |
390 | 0 | } |
391 | | |
392 | 0 | void SemanticValidator::visit(TryStatementNode *tryStatement) { |
393 | 0 | if (curFunction()->strictMode) { |
394 | 0 | validateCatchClause(tryStatement->_handler); |
395 | 0 | } |
396 | | // The catch parameter cannot bind to 'eval' or 'arguments' in strict mode. |
397 | | // A try statement with both catch and finally handlers is technically |
398 | | // two nested try statements. Transform: |
399 | | // |
400 | | // try { |
401 | | // tryBody; |
402 | | // } catch { |
403 | | // catchBody; |
404 | | // } finally { |
405 | | // finallyBody; |
406 | | // } |
407 | | // |
408 | | // into |
409 | | // |
410 | | // try { |
411 | | // try { |
412 | | // tryBody; |
413 | | // } catch { |
414 | | // catchBody; |
415 | | // } |
416 | | // } finally { |
417 | | // finallyBody; |
418 | | // } |
419 | 0 | if (compile_ && tryStatement->_handler && tryStatement->_finalizer) { |
420 | 0 | auto *nestedTry = new (astContext_) |
421 | 0 | TryStatementNode(tryStatement->_block, tryStatement->_handler, nullptr); |
422 | 0 | nestedTry->copyLocationFrom(tryStatement); |
423 | 0 | nestedTry->setEndLoc(nestedTry->_handler->getEndLoc()); |
424 | |
|
425 | 0 | ESTree::NodeList stmtList; |
426 | 0 | stmtList.push_back(*nestedTry); |
427 | 0 | tryStatement->_block = |
428 | 0 | new (astContext_) BlockStatementNode(std::move(stmtList)); |
429 | 0 | tryStatement->_block->copyLocationFrom(nestedTry); |
430 | 0 | tryStatement->_handler = nullptr; |
431 | 0 | } |
432 | |
|
433 | 0 | visitESTreeNode(*this, tryStatement->_block, tryStatement); |
434 | 0 | if (!blockScopingEnabled()) { |
435 | 0 | visitESTreeNode(*this, tryStatement->_handler, tryStatement); |
436 | 0 | } else { |
437 | 0 | visitTryHandler(tryStatement); |
438 | 0 | } |
439 | |
|
440 | 0 | visitESTreeNode(*this, tryStatement->_finalizer, tryStatement); |
441 | 0 | } |
442 | | |
443 | 0 | void SemanticValidator::visitTryHandler(TryStatementNode *tryStatement) { |
444 | 0 | if (auto *handler = |
445 | 0 | llvh::dyn_cast_or_null<CatchClauseNode>(tryStatement->_handler)) { |
446 | 0 | auto *param = llvh::dyn_cast_or_null<IdentifierNode>(handler->_param); |
447 | |
|
448 | 0 | BlockContext blockScope(this, curFunction(), handler); |
449 | |
|
450 | 0 | if (auto *block = llvh::dyn_cast<BlockStatementNode>(handler->_body)) { |
451 | 0 | for (auto &stmt : block->_body) { |
452 | 0 | visitESTreeNode(*this, &stmt, block); |
453 | 0 | } |
454 | 0 | } else { |
455 | 0 | visitESTreeNode(*this, tryStatement->_handler, tryStatement); |
456 | 0 | } |
457 | |
|
458 | 0 | blockScope.ensureScopedNamesAreUnique( |
459 | 0 | BlockContext::IsFunctionBody::No, param); |
460 | | |
461 | | // Delay adding the catch param until now to prevent Syntax Errors if the |
462 | | // handler has a var that with the same ID as the catch param (as specified |
463 | | // in ES2023 B.3.4). |
464 | 0 | validateDeclarationNames( |
465 | 0 | FunctionInfo::VarDecl::Kind::Let, |
466 | 0 | param, |
467 | 0 | curFunction()->varDecls, |
468 | 0 | curFunction()->scopedDecls); |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | 0 | void SemanticValidator::visit(BlockStatementNode *block) { |
473 | 0 | BlockContext blockScope(this, curFunction(), block); |
474 | 0 | visitESTreeChildren(*this, block); |
475 | |
|
476 | 0 | blockScope.ensureScopedNamesAreUnique(BlockContext::IsFunctionBody::No); |
477 | 0 | } |
478 | | |
479 | 0 | void SemanticValidator::visit(DoWhileStatementNode *loop) { |
480 | 0 | loop->setLabelIndex(curFunction()->allocateLabel()); |
481 | |
|
482 | 0 | SaveAndRestore<LoopStatementNode *> saveLoop(curFunction()->activeLoop, loop); |
483 | 0 | SaveAndRestore<StatementNode *> saveSwitch( |
484 | 0 | curFunction()->activeSwitchOrLoop, loop); |
485 | |
|
486 | 0 | visitESTreeChildren(*this, loop); |
487 | 0 | } |
488 | 0 | void SemanticValidator::visit(ForStatementNode *loop) { |
489 | 0 | loop->setLabelIndex(curFunction()->allocateLabel()); |
490 | |
|
491 | 0 | SaveAndRestore<LoopStatementNode *> saveLoop(curFunction()->activeLoop, loop); |
492 | 0 | SaveAndRestore<StatementNode *> saveSwitch( |
493 | 0 | curFunction()->activeSwitchOrLoop, loop); |
494 | |
|
495 | 0 | visitESTreeChildren(*this, loop); |
496 | 0 | } |
497 | 0 | void SemanticValidator::visit(WhileStatementNode *loop) { |
498 | 0 | loop->setLabelIndex(curFunction()->allocateLabel()); |
499 | |
|
500 | 0 | SaveAndRestore<LoopStatementNode *> saveLoop(curFunction()->activeLoop, loop); |
501 | 0 | SaveAndRestore<StatementNode *> saveSwitch( |
502 | 0 | curFunction()->activeSwitchOrLoop, loop); |
503 | |
|
504 | 0 | visitESTreeChildren(*this, loop); |
505 | 0 | } |
506 | 0 | void SemanticValidator::visit(SwitchStatementNode *switchStmt) { |
507 | 0 | switchStmt->setLabelIndex(curFunction()->allocateLabel()); |
508 | |
|
509 | 0 | BlockContext switchContext(this, curFunction(), switchStmt); |
510 | |
|
511 | 0 | SaveAndRestore<StatementNode *> saveSwitch( |
512 | 0 | curFunction()->activeSwitchOrLoop, switchStmt); |
513 | |
|
514 | 0 | visitESTreeChildren(*this, switchStmt); |
515 | |
|
516 | 0 | switchContext.ensureScopedNamesAreUnique(BlockContext::IsFunctionBody::No); |
517 | 0 | } |
518 | | |
519 | 0 | void SemanticValidator::visit(BreakStatementNode *breakStmt) { |
520 | 0 | if (auto id = cast_or_null<IdentifierNode>(breakStmt->_label)) { |
521 | | // A labeled break. |
522 | | // Find the label in the label map. |
523 | 0 | auto labelIt = curFunction()->labelMap.find(id->_name); |
524 | 0 | if (labelIt != curFunction()->labelMap.end()) { |
525 | 0 | auto labelIndex = getLabelDecorationBase(labelIt->second.targetStatement) |
526 | 0 | ->getLabelIndex(); |
527 | 0 | breakStmt->setLabelIndex(labelIndex); |
528 | 0 | } else { |
529 | 0 | sm_.error( |
530 | 0 | id->getSourceRange(), |
531 | 0 | Twine("label '") + id->_name->str() + "' is not defined"); |
532 | 0 | } |
533 | 0 | } else { |
534 | | // Anonymous break. |
535 | 0 | if (curFunction()->activeSwitchOrLoop) { |
536 | 0 | auto labelIndex = |
537 | 0 | getLabelDecorationBase(curFunction()->activeSwitchOrLoop) |
538 | 0 | ->getLabelIndex(); |
539 | 0 | breakStmt->setLabelIndex(labelIndex); |
540 | 0 | } else { |
541 | 0 | sm_.error( |
542 | 0 | breakStmt->getSourceRange(), "'break' not within a loop or a switch"); |
543 | 0 | } |
544 | 0 | } |
545 | |
|
546 | 0 | visitESTreeChildren(*this, breakStmt); |
547 | 0 | } |
548 | | |
549 | 0 | void SemanticValidator::visit(ContinueStatementNode *continueStmt) { |
550 | 0 | if (auto id = cast_or_null<IdentifierNode>(continueStmt->_label)) { |
551 | | // A labeled continue. |
552 | | // Find the label in the label map. |
553 | 0 | auto labelIt = curFunction()->labelMap.find(id->_name); |
554 | 0 | if (labelIt != curFunction()->labelMap.end()) { |
555 | 0 | if (isa<LoopStatementNode>(labelIt->second.targetStatement)) { |
556 | 0 | auto labelIndex = |
557 | 0 | getLabelDecorationBase(labelIt->second.targetStatement) |
558 | 0 | ->getLabelIndex(); |
559 | 0 | continueStmt->setLabelIndex(labelIndex); |
560 | 0 | } else { |
561 | 0 | sm_.error( |
562 | 0 | id->getSourceRange(), |
563 | 0 | llvh::Twine("continue label '") + id->_name->str() + |
564 | 0 | "' is not a loop label"); |
565 | 0 | sm_.note( |
566 | 0 | labelIt->second.declarationNode->getSourceRange(), |
567 | 0 | "label defined here"); |
568 | 0 | } |
569 | 0 | } else { |
570 | 0 | sm_.error( |
571 | 0 | id->getSourceRange(), |
572 | 0 | Twine("label '") + id->_name->str() + "' is not defined"); |
573 | 0 | } |
574 | 0 | } else { |
575 | | // Anonymous continue. |
576 | 0 | if (curFunction()->activeLoop) { |
577 | 0 | auto labelIndex = curFunction()->activeLoop->getLabelIndex(); |
578 | 0 | continueStmt->setLabelIndex(labelIndex); |
579 | 0 | } else { |
580 | 0 | sm_.error(continueStmt->getSourceRange(), "'continue' not within a loop"); |
581 | 0 | } |
582 | 0 | } |
583 | 0 | visitESTreeChildren(*this, continueStmt); |
584 | 0 | } |
585 | | |
586 | 23.0k | void SemanticValidator::visit(ReturnStatementNode *returnStmt) { |
587 | 23.0k | if (curFunction()->isGlobalScope() && |
588 | 0 | !astContext_.allowReturnOutsideFunction()) |
589 | 0 | sm_.error(returnStmt->getSourceRange(), "'return' not in a function"); |
590 | 23.0k | visitESTreeChildren(*this, returnStmt); |
591 | 23.0k | } |
592 | | |
593 | 0 | void SemanticValidator::visit(YieldExpressionNode *yieldExpr) { |
594 | 0 | if (curFunction()->isGlobalScope() || |
595 | 0 | (curFunction()->node && !ESTree::isGenerator(curFunction()->node))) |
596 | 0 | sm_.error( |
597 | 0 | yieldExpr->getSourceRange(), "'yield' not in a generator function"); |
598 | |
|
599 | 0 | if (isFormalParams_) { |
600 | | // For generators functions (the only time YieldExpression is parsed): |
601 | | // It is a Syntax Error if UniqueFormalParameters Contains YieldExpression |
602 | | // is true. |
603 | 0 | sm_.error( |
604 | 0 | yieldExpr->getSourceRange(), |
605 | 0 | "'yield' not allowed in a formal parameter"); |
606 | 0 | } |
607 | |
|
608 | 0 | visitESTreeChildren(*this, yieldExpr); |
609 | 0 | } |
610 | | |
611 | 0 | void SemanticValidator::visit(AwaitExpressionNode *awaitExpr) { |
612 | 0 | if (forbidAwaitExpression_) |
613 | 0 | sm_.error(awaitExpr->getSourceRange(), "'await' not in an async function"); |
614 | |
|
615 | 0 | visitESTreeChildren(*this, awaitExpr); |
616 | 0 | } |
617 | | |
618 | 93.6k | void SemanticValidator::visit(UnaryExpressionNode *unaryExpr) { |
619 | | // Check for unqualified delete in strict mode. |
620 | 93.6k | if (unaryExpr->_operator == kw_.identDelete) { |
621 | 84.9k | if (curFunction()->strictMode && |
622 | 0 | isa<IdentifierNode>(unaryExpr->_argument)) { |
623 | 0 | sm_.error( |
624 | 0 | unaryExpr->getSourceRange(), |
625 | 0 | "'delete' of a variable is not allowed in strict mode"); |
626 | 0 | } |
627 | 84.9k | } |
628 | 93.6k | visitESTreeChildren(*this, unaryExpr); |
629 | 93.6k | } |
630 | | |
631 | 825 | void SemanticValidator::visit(ArrayPatternNode *AP) { |
632 | 825 | visitESTreeChildren(*this, AP); |
633 | 825 | } |
634 | | |
635 | 2 | void SemanticValidator::visit(SpreadElementNode *S, Node *parent) { |
636 | 2 | if (!isa<ESTree::ObjectExpressionNode>(parent) && |
637 | 2 | !isa<ESTree::ArrayExpressionNode>(parent) && |
638 | 0 | !isa<ESTree::CallExpressionNode>(parent) && |
639 | 0 | !isa<ESTree::OptionalCallExpressionNode>(parent) && |
640 | 0 | !isa<ESTree::NewExpressionNode>(parent)) |
641 | 0 | sm_.error(S->getSourceRange(), "spread operator is not supported"); |
642 | 2 | visitESTreeChildren(*this, S); |
643 | 2 | } |
644 | | |
645 | 0 | void SemanticValidator::visit(ClassExpressionNode *node) { |
646 | 0 | SaveAndRestore<bool> oldStrictMode{curFunction()->strictMode, true}; |
647 | 0 | visitESTreeChildren(*this, node); |
648 | 0 | } |
649 | | |
650 | 0 | void SemanticValidator::visit(ClassDeclarationNode *node) { |
651 | 0 | SaveAndRestore<bool> oldStrictMode{curFunction()->strictMode, true}; |
652 | 0 | visitESTreeChildren(*this, node); |
653 | 0 | } |
654 | | |
655 | 0 | void SemanticValidator::visit(PrivateNameNode *node) { |
656 | 0 | if (compile_) |
657 | 0 | sm_.error(node->getSourceRange(), "private properties are not supported"); |
658 | 0 | visitESTreeChildren(*this, node); |
659 | 0 | } |
660 | | |
661 | 0 | void SemanticValidator::visit(ClassPrivatePropertyNode *node) { |
662 | 0 | if (compile_) |
663 | 0 | sm_.error(node->getSourceRange(), "private properties are not supported"); |
664 | 0 | visitESTreeNode(*this, node->_key); |
665 | 0 | { |
666 | 0 | SaveAndRestore<bool> oldForbidAwait{forbidAwaitExpression_, true}; |
667 | | // ES14.0 15.7.1 |
668 | | // It is a Syntax Error if Initializer is present and ContainsArguments of |
669 | | // Initializer is true. |
670 | 0 | SaveAndRestore<bool> oldForbidArguments{forbidArguments_, true}; |
671 | 0 | visitESTreeNode(*this, node->_value); |
672 | 0 | } |
673 | 0 | } |
674 | | |
675 | 0 | void SemanticValidator::visit(ClassPropertyNode *node) { |
676 | 0 | visitESTreeNode(*this, node->_key); |
677 | 0 | { |
678 | 0 | SaveAndRestore<bool> oldForbidAwait{forbidAwaitExpression_, true}; |
679 | | // ES14.0 15.7.1 |
680 | | // It is a Syntax Error if Initializer is present and ContainsArguments of |
681 | | // Initializer is true. |
682 | 0 | SaveAndRestore<bool> oldForbidArguments{forbidArguments_, true}; |
683 | 0 | visitESTreeNode(*this, node->_value); |
684 | 0 | } |
685 | 0 | } |
686 | | |
687 | 0 | void SemanticValidator::visit(StaticBlockNode *node) { |
688 | 0 | if (compile_) |
689 | 0 | sm_.error(node->getSourceRange(), "class static blocks are not supported"); |
690 | | // ES14.0 15.7.1 |
691 | | // It is a Syntax Error if ClassStaticBlockStatementList Contains await is |
692 | | // true. |
693 | 0 | llvh::SaveAndRestore<bool> oldForbidAwait{forbidAwaitExpression_, true}; |
694 | 0 | visitESTreeChildren(*this, node); |
695 | 0 | } |
696 | | |
697 | 0 | void SemanticValidator::visit(ImportDeclarationNode *importDecl) { |
698 | | // Like variable declarations, imported names must be hoisted. |
699 | 0 | if (!astContext_.getTransformCJSModules()) { |
700 | 0 | sm_.error( |
701 | 0 | importDecl->getSourceRange(), |
702 | 0 | "'import' statement requires module mode"); |
703 | 0 | } |
704 | |
|
705 | 0 | if (compile_ && !importDecl->_assertions.empty()) { |
706 | 0 | sm_.error( |
707 | 0 | importDecl->getSourceRange(), "import assertions are not supported"); |
708 | 0 | } |
709 | |
|
710 | 0 | curFunction()->semInfo->imports.push_back(importDecl); |
711 | 0 | visitESTreeChildren(*this, importDecl); |
712 | 0 | } |
713 | | |
714 | 0 | void SemanticValidator::visit(ImportDefaultSpecifierNode *importDecl) { |
715 | | // import defaultProperty from 'file.js'; |
716 | 0 | validateDeclarationNames( |
717 | 0 | FunctionInfo::VarDecl::Kind::Var, |
718 | 0 | importDecl->_local, |
719 | 0 | curFunction()->varDecls, |
720 | 0 | curFunction()->scopedDecls); |
721 | 0 | visitESTreeChildren(*this, importDecl); |
722 | 0 | } |
723 | | |
724 | 0 | void SemanticValidator::visit(ImportNamespaceSpecifierNode *importDecl) { |
725 | | // import * as File from 'file.js'; |
726 | 0 | validateDeclarationNames( |
727 | 0 | FunctionInfo::VarDecl::Kind::Var, |
728 | 0 | importDecl->_local, |
729 | 0 | curFunction()->varDecls, |
730 | 0 | curFunction()->scopedDecls); |
731 | 0 | visitESTreeChildren(*this, importDecl); |
732 | 0 | } |
733 | | |
734 | 0 | void SemanticValidator::visit(ImportSpecifierNode *importDecl) { |
735 | | // import {x as y} as File from 'file.js'; |
736 | | // import {x} as File from 'file.js'; |
737 | 0 | validateDeclarationNames( |
738 | 0 | FunctionInfo::VarDecl::Kind::Var, |
739 | 0 | importDecl->_local, |
740 | 0 | curFunction()->varDecls, |
741 | 0 | curFunction()->scopedDecls); |
742 | 0 | visitESTreeChildren(*this, importDecl); |
743 | 0 | } |
744 | | |
745 | 0 | void SemanticValidator::visit(ExportNamedDeclarationNode *exportDecl) { |
746 | 0 | if (!astContext_.getTransformCJSModules()) { |
747 | 0 | sm_.error( |
748 | 0 | exportDecl->getSourceRange(), |
749 | 0 | "'export' statement requires module mode"); |
750 | 0 | } |
751 | |
|
752 | 0 | visitESTreeChildren(*this, exportDecl); |
753 | 0 | } |
754 | | |
755 | 0 | void SemanticValidator::visit(ExportDefaultDeclarationNode *exportDecl) { |
756 | 0 | if (!astContext_.getTransformCJSModules() && |
757 | 0 | !astContext_.getTransformCJSModules()) { |
758 | 0 | sm_.error( |
759 | 0 | exportDecl->getSourceRange(), |
760 | 0 | "'export' statement requires module mode"); |
761 | 0 | } |
762 | |
|
763 | 0 | if (auto *funcDecl = |
764 | 0 | dyn_cast<ESTree::FunctionDeclarationNode>(exportDecl->_declaration)) { |
765 | 0 | if (compile_ && !funcDecl->_id) { |
766 | | // If the default function declaration has no name, then change it to a |
767 | | // FunctionExpression node for cleaner IRGen. |
768 | 0 | auto *funcExpr = new (astContext_) ESTree::FunctionExpressionNode( |
769 | 0 | funcDecl->_id, |
770 | 0 | std::move(funcDecl->_params), |
771 | 0 | funcDecl->_body, |
772 | 0 | funcDecl->_typeParameters, |
773 | 0 | funcDecl->_returnType, |
774 | 0 | funcDecl->_predicate, |
775 | 0 | funcDecl->_generator, |
776 | 0 | /* async */ false); |
777 | 0 | funcExpr->strictness = funcDecl->strictness; |
778 | 0 | funcExpr->copyLocationFrom(funcDecl); |
779 | |
|
780 | 0 | exportDecl->_declaration = funcExpr; |
781 | 0 | } |
782 | 0 | } |
783 | |
|
784 | 0 | visitESTreeChildren(*this, exportDecl); |
785 | 0 | } |
786 | | |
787 | 0 | void SemanticValidator::visit(ExportAllDeclarationNode *exportDecl) { |
788 | 0 | if (!astContext_.getTransformCJSModules()) { |
789 | 0 | sm_.error( |
790 | 0 | exportDecl->getSourceRange(), |
791 | 0 | "'export' statement requires CommonJS module mode"); |
792 | 0 | } |
793 | 0 | visitESTreeChildren(*this, exportDecl); |
794 | 0 | } |
795 | | |
796 | 0 | void SemanticValidator::visit(CoverEmptyArgsNode *CEA) { |
797 | 0 | sm_.error(CEA->getSourceRange(), "invalid empty parentheses '( )'"); |
798 | 0 | } |
799 | | |
800 | 0 | void SemanticValidator::visit(CoverTrailingCommaNode *CTC) { |
801 | 0 | sm_.error(CTC->getSourceRange(), "expression expected after ','"); |
802 | 0 | } |
803 | | |
804 | 0 | void SemanticValidator::visit(CoverInitializerNode *CI) { |
805 | 0 | sm_.error(CI->getStartLoc(), "':' expected in property initialization"); |
806 | 0 | } |
807 | | |
808 | 0 | void SemanticValidator::visit(CoverRestElementNode *R) { |
809 | 0 | sm_.error(R->getSourceRange(), "'...' not allowed in this context"); |
810 | 0 | } |
811 | | |
812 | | #if HERMES_PARSE_FLOW |
813 | 0 | void SemanticValidator::visit(CoverTypedIdentifierNode *CTI) { |
814 | 0 | sm_.error(CTI->getSourceRange(), "typecast not allowed in this context"); |
815 | 0 | } |
816 | | #endif |
817 | | |
818 | | void SemanticValidator::visitFunction( |
819 | | FunctionLikeNode *node, |
820 | | Node *id, |
821 | | NodeList ¶ms, |
822 | 23.1k | Node *body) { |
823 | 23.1k | FunctionContext newFuncCtx{ |
824 | 23.1k | this, |
825 | 23.1k | haveActiveContext() && curFunction()->strictMode, |
826 | 23.1k | node, |
827 | 23.1k | body, |
828 | 23.1k | haveActiveContext() ? curFunction()->sourceVisibility |
829 | 23.1k | : SourceVisibility::Default}; |
830 | | |
831 | 23.1k | if (compile_ && ESTree::isAsync(node) && ESTree::isGenerator(node)) { |
832 | 0 | sm_.error(node->getSourceRange(), "async generators are unsupported"); |
833 | 0 | } |
834 | | |
835 | | // It is a Syntax Error if UniqueFormalParameters Contains YieldExpression |
836 | | // is true. |
837 | | // NOTE: isFormalParams_ is reset to false on encountering a new function, |
838 | | // because the semantics for "x Contains y" always return `false` when "x" is |
839 | | // a function definition. |
840 | 23.1k | llvh::SaveAndRestore<bool> oldIsFormalParamsFn{isFormalParams_, false}; |
841 | | |
842 | 23.1k | Node *useStrictNode = nullptr; |
843 | | |
844 | | // Note that body might me empty (for lazy functions) or an expression (for |
845 | | // arrow functions). |
846 | 23.1k | if (auto *bodyNode = dyn_cast<ESTree::BlockStatementNode>(body)) { |
847 | 23.1k | if (bodyNode->isLazyFunctionBody) { |
848 | | // If it is a lazy function body, then the directive nodes in the body are |
849 | | // fabricated without location, so don't set useStrictNode. |
850 | 80 | scanDirectivePrologue(bodyNode->_body); |
851 | 23.0k | } else { |
852 | 23.0k | useStrictNode = scanDirectivePrologue(bodyNode->_body); |
853 | 23.0k | } |
854 | 23.1k | setDirectiveDerivedInfo(node); |
855 | 23.1k | } |
856 | | |
857 | 23.1k | if (id) |
858 | 0 | validateDeclarationNames( |
859 | 0 | FunctionInfo::VarDecl::Kind::Var, id, nullptr, nullptr); |
860 | | |
861 | 23.1k | #if HERMES_PARSE_FLOW |
862 | 23.1k | if (astContext_.getParseFlow() && !params.empty()) { |
863 | | // Skip 'this' parameter annotation, and error if it's an arrow parameter, |
864 | | // because arrow functions inherit 'this'. |
865 | 0 | if (auto *ident = dyn_cast<ESTree::IdentifierNode>(¶ms.front())) { |
866 | 0 | if (ident->_name == kw_.identThis) { |
867 | 0 | if (isa<ArrowFunctionExpressionNode>(node)) { |
868 | 0 | sm_.error( |
869 | 0 | ident->getSourceRange(), "'this' not allowed as parameter name"); |
870 | 0 | } |
871 | 0 | if (compile_) { |
872 | | // Delete the node because it cannot be compiled. |
873 | 0 | params.erase(params.begin()); |
874 | 0 | } |
875 | 0 | } |
876 | 0 | } |
877 | 0 | } |
878 | 23.1k | #endif |
879 | | |
880 | 23.1k | for (auto ¶m : params) { |
881 | 23.0k | #if HERMES_PARSE_FLOW |
882 | 23.0k | if (isa<ComponentParameterNode>(param)) { |
883 | 0 | validateDeclarationNames( |
884 | 0 | FunctionInfo::VarDecl::Kind::Var, |
885 | 0 | dyn_cast<ComponentParameterNode>(¶m)->_local, |
886 | 0 | &newFuncCtx.semInfo->paramNames, |
887 | 0 | nullptr); |
888 | 0 | continue; |
889 | 0 | } |
890 | 23.0k | #endif |
891 | 23.0k | validateDeclarationNames( |
892 | 23.0k | FunctionInfo::VarDecl::Kind::Var, |
893 | 23.0k | ¶m, |
894 | 23.0k | &newFuncCtx.semInfo->paramNames, |
895 | 23.0k | nullptr); |
896 | 23.0k | } |
897 | | |
898 | 23.1k | bool simpleParameterList = ESTree::hasSimpleParams(node); |
899 | 23.1k | if (!simpleParameterList && useStrictNode) { |
900 | 0 | sm_.error( |
901 | 0 | useStrictNode->getSourceRange(), |
902 | 0 | "'use strict' not allowed inside function with non-simple parameter list"); |
903 | 0 | } |
904 | | |
905 | | // Check repeated parameter names when they are supposed to be unique. |
906 | 23.1k | if (!simpleParameterList || curFunction()->strictMode || |
907 | 23.1k | isa<ArrowFunctionExpressionNode>(node)) { |
908 | 23.0k | llvh::SmallSet<NodeLabel, 8> paramNameSet; |
909 | 23.0k | for (const auto &curIdNode : newFuncCtx.semInfo->paramNames) { |
910 | 23.0k | auto insert_result = paramNameSet.insert(curIdNode.identifier->_name); |
911 | 23.0k | if (insert_result.second == false) { |
912 | 0 | sm_.error( |
913 | 0 | curIdNode.identifier->getSourceRange(), |
914 | 0 | "cannot declare two parameters with the same name '" + |
915 | 0 | curIdNode.identifier->_name->str() + "'"); |
916 | 0 | } |
917 | 23.0k | } |
918 | 23.0k | } |
919 | | |
920 | | // 'await' forbidden outside async functions. |
921 | 23.1k | llvh::SaveAndRestore<bool> oldForbidAwait{ |
922 | 23.1k | forbidAwaitExpression_, !ESTree::isAsync(node)}; |
923 | | // Forbidden-ness of 'arguments' passes through arrow functions because they |
924 | | // use the same 'arguments'. |
925 | 23.1k | llvh::SaveAndRestore<bool> oldForbidArguments{ |
926 | 23.1k | forbidArguments_, |
927 | 23.1k | llvh::isa<ESTree::ArrowFunctionExpressionNode>(node) ? forbidArguments_ |
928 | 23.1k | : false}; |
929 | | |
930 | 23.1k | visitParamsAndBody(node); |
931 | 23.1k | } |
932 | | |
933 | 23.1k | void SemanticValidator::visitParamsAndBody(FunctionLikeNode *node) { |
934 | 23.1k | switch (node->getKind()) { |
935 | 80 | case NodeKind::FunctionExpression: { |
936 | 80 | auto *fe = cast<ESTree::FunctionExpressionNode>(node); |
937 | 80 | visitESTreeNode(*this, fe->_id, fe); |
938 | 80 | for (auto ¶m : fe->_params) { |
939 | 0 | llvh::SaveAndRestore<bool> oldIsFormalParams{isFormalParams_, true}; |
940 | 0 | visitESTreeNode(*this, ¶m, fe); |
941 | 0 | } |
942 | 80 | visitBody(fe->_body, fe); |
943 | 80 | break; |
944 | 0 | } |
945 | 23.0k | case NodeKind::ArrowFunctionExpression: { |
946 | 23.0k | auto *fe = cast<ESTree::ArrowFunctionExpressionNode>(node); |
947 | 23.0k | visitESTreeNode(*this, fe->_id, fe); |
948 | 23.0k | for (auto ¶m : fe->_params) { |
949 | 23.0k | llvh::SaveAndRestore<bool> oldIsFormalParams{isFormalParams_, true}; |
950 | 23.0k | visitESTreeNode(*this, ¶m, fe); |
951 | 23.0k | } |
952 | 23.0k | visitBody(fe->_body, fe); |
953 | 23.0k | break; |
954 | 0 | } |
955 | 0 | case NodeKind::FunctionDeclaration: { |
956 | 0 | auto *fe = cast<ESTree::FunctionDeclarationNode>(node); |
957 | 0 | visitESTreeNode(*this, fe->_id, fe); |
958 | 0 | for (auto ¶m : fe->_params) { |
959 | 0 | llvh::SaveAndRestore<bool> oldIsFormalParams{isFormalParams_, true}; |
960 | 0 | visitESTreeNode(*this, ¶m, fe); |
961 | 0 | } |
962 | 0 | visitBody(fe->_body, fe); |
963 | 0 | visitESTreeNode(*this, fe->_returnType, fe); |
964 | 0 | break; |
965 | 0 | } |
966 | 0 | #if HERMES_PARSE_FLOW |
967 | 0 | case NodeKind::ComponentDeclaration: { |
968 | 0 | auto *fe = cast<ESTree::ComponentDeclarationNode>(node); |
969 | 0 | visitESTreeNode(*this, fe->_id, fe); |
970 | 0 | for (auto ¶m : fe->_params) { |
971 | 0 | llvh::SaveAndRestore<bool> oldIsFormalParams{isFormalParams_, true}; |
972 | 0 | visitESTreeNode(*this, ¶m, fe); |
973 | 0 | } |
974 | 0 | visitBody(fe->_body, fe); |
975 | 0 | visitESTreeNode(*this, fe->_rendersType, fe); |
976 | 0 | break; |
977 | 0 | } |
978 | 0 | case NodeKind::HookDeclaration: { |
979 | 0 | auto *fe = cast<ESTree::HookDeclarationNode>(node); |
980 | 0 | visitESTreeNode(*this, fe->_id, fe); |
981 | 0 | for (auto ¶m : fe->_params) { |
982 | 0 | llvh::SaveAndRestore<bool> oldIsFormalParams{isFormalParams_, true}; |
983 | 0 | visitESTreeNode(*this, ¶m, fe); |
984 | 0 | } |
985 | 0 | visitBody(fe->_body, fe); |
986 | 0 | visitESTreeNode(*this, fe->_returnType, fe); |
987 | 0 | break; |
988 | 0 | } |
989 | 0 | #endif |
990 | 0 | default: |
991 | 0 | visitESTreeChildren(*this, node); |
992 | 23.1k | } |
993 | 23.1k | } |
994 | | |
995 | 23.1k | void SemanticValidator::visitBody(Node *body, FunctionLikeNode *func) { |
996 | 23.1k | if (auto *block = dyn_cast<ESTree::BlockStatementNode>(body)) { |
997 | | // Avoid creating yet another block scope for function declarations like |
998 | | // |
999 | | // (function func() { ... }) |
1000 | | // ^ BlockStatementNode |
1001 | | // |
1002 | | // In those cases, the scope for the BlockStatementNode is the same as |
1003 | | // func's. |
1004 | 23.1k | for (auto &stmt : block->_body) { |
1005 | 23.0k | visitESTreeNode(*this, &stmt, block); |
1006 | 23.0k | } |
1007 | 23.1k | return; |
1008 | 23.1k | } |
1009 | | |
1010 | 0 | visitESTreeNode(*this, body, func); |
1011 | 0 | } |
1012 | | |
1013 | | void SemanticValidator::tryOverrideSourceVisibility( |
1014 | 0 | SourceVisibility newSourceVisibility) { |
1015 | 0 | if (newSourceVisibility > curFunction()->sourceVisibility) { |
1016 | 0 | curFunction()->sourceVisibility = newSourceVisibility; |
1017 | 0 | } |
1018 | 0 | } |
1019 | | |
1020 | 23.2k | Node *SemanticValidator::scanDirectivePrologue(NodeList &body) { |
1021 | 23.2k | Node *result = nullptr; |
1022 | 23.2k | for (auto &nodeRef : body) { |
1023 | 23.1k | auto *exprSt = dyn_cast<ESTree::ExpressionStatementNode>(&nodeRef); |
1024 | 23.1k | if (!exprSt || !exprSt->_directive) |
1025 | 23.1k | break; |
1026 | | |
1027 | 0 | auto *directive = exprSt->_directive; |
1028 | |
|
1029 | 0 | if (directive == kw_.identUseStrict) { |
1030 | 0 | curFunction()->strictMode = true; |
1031 | 0 | if (!result) |
1032 | 0 | result = &nodeRef; |
1033 | 0 | } |
1034 | 0 | if (directive == kw_.identShowSource) { |
1035 | 0 | tryOverrideSourceVisibility(SourceVisibility::ShowSource); |
1036 | 0 | } |
1037 | 0 | if (directive == kw_.identHideSource) { |
1038 | 0 | tryOverrideSourceVisibility(SourceVisibility::HideSource); |
1039 | 0 | } |
1040 | 0 | if (directive == kw_.identSensitive) { |
1041 | 0 | tryOverrideSourceVisibility(SourceVisibility::Sensitive); |
1042 | 0 | } |
1043 | 0 | } |
1044 | | |
1045 | 23.2k | return result; |
1046 | 23.2k | } |
1047 | | |
1048 | 5.22k | bool SemanticValidator::isLValue(const Node *node) const { |
1049 | 5.22k | if (isa<MemberExpressionNode>(node)) |
1050 | 118 | return true; |
1051 | 5.10k | if (!isa<IdentifierNode>(node)) |
1052 | 715 | return false; |
1053 | | |
1054 | 4.38k | auto *idNode = cast<IdentifierNode>(node); |
1055 | | |
1056 | | /// 'arguments' cannot be modified in strict mode, but we also don't |
1057 | | /// support modifying it in non-strict mode yet. |
1058 | 4.38k | if (idNode->_name == kw_.identArguments) |
1059 | 0 | return false; |
1060 | | |
1061 | | // 'eval' cannot be used as a variable in strict mode. If it is disabled we |
1062 | | // we don't report an error because it will be reported separately. |
1063 | 4.38k | if (idNode->_name == kw_.identEval && curFunction()->strictMode && |
1064 | 0 | astContext_.getEnableEval()) |
1065 | 0 | return false; |
1066 | | |
1067 | 4.38k | return true; |
1068 | 4.38k | } |
1069 | | |
1070 | | bool SemanticValidator::isValidDeclarationName( |
1071 | 23.1k | const IdentifierNode *idNode) const { |
1072 | | // 'arguments' cannot be redeclared in strict mode. |
1073 | 23.1k | if (idNode->_name == kw_.identArguments && curFunction()->strictMode) |
1074 | 0 | return false; |
1075 | | |
1076 | | // 'eval' cannot be redeclared in strict mode. If it is disabled we |
1077 | | // we don't report an error because it will be reported separately. |
1078 | 23.1k | if (idNode->_name == kw_.identEval && curFunction()->strictMode && |
1079 | 0 | astContext_.getEnableEval()) |
1080 | 0 | return false; |
1081 | | |
1082 | 23.1k | return true; |
1083 | 23.1k | } |
1084 | | |
1085 | | void SemanticValidator::validateDeclarationNames( |
1086 | | FunctionInfo::VarDecl::Kind declKind, |
1087 | | Node *node, |
1088 | | FunctionInfo::BlockDecls *varIdents, |
1089 | 23.2k | FunctionInfo::BlockDecls *scopedIdents) { |
1090 | 23.2k | assert( |
1091 | 23.2k | (varIdents || !scopedIdents) && |
1092 | 23.2k | "Variable scope must always be provided if a scoped scope is provided."); |
1093 | | // The identifier is sometimes optional, in which case it is valid. |
1094 | 23.2k | if (!node) |
1095 | 0 | return; |
1096 | | |
1097 | 23.2k | if (auto *idNode = dyn_cast<IdentifierNode>(node)) { |
1098 | 23.1k | if (!blockScopingEnabled()) { |
1099 | | // Block scoping is disabled, so short-circuit both BlockDecls to use the |
1100 | | // var environment. |
1101 | 23.1k | scopedIdents = varIdents; |
1102 | 23.1k | } |
1103 | 23.1k | if (varIdents) { |
1104 | 23.1k | if (declKind == FunctionInfo::VarDecl::Kind::Var) { |
1105 | 23.0k | varIdents->emplace_back(FunctionInfo::VarDecl{declKind, idNode}); |
1106 | 23.0k | } else { |
1107 | 67 | assert( |
1108 | 67 | scopedIdents && |
1109 | 67 | "const/let declaration, but no scopedIdents array."); |
1110 | 67 | scopedIdents->emplace_back(FunctionInfo::VarDecl{declKind, idNode}); |
1111 | 67 | } |
1112 | 23.1k | } |
1113 | 23.1k | if (!isValidDeclarationName(idNode)) { |
1114 | 0 | sm_.error( |
1115 | 0 | node->getSourceRange(), |
1116 | 0 | "cannot declare '" + cast<IdentifierNode>(node)->_name->str() + "'"); |
1117 | 0 | } |
1118 | | |
1119 | 23.1k | if (declKind != FunctionInfo::VarDecl::Kind::Var && |
1120 | 67 | idNode->_name == kw_.identLet) { |
1121 | | // ES9.0 13.3.1.1 |
1122 | | // LexicalDeclaration : LetOrConst BindingList |
1123 | | // It is a Syntax Error if the BoundNames of BindingList |
1124 | | // contains "let". |
1125 | 0 | sm_.error( |
1126 | 0 | node->getSourceRange(), |
1127 | 0 | "'let' is disallowed as a lexically bound name"); |
1128 | 0 | } |
1129 | | |
1130 | 23.1k | return; |
1131 | 23.1k | } |
1132 | | |
1133 | 118 | if (isa<EmptyNode>(node)) |
1134 | 0 | return; |
1135 | | |
1136 | 118 | if (auto *assign = dyn_cast<AssignmentPatternNode>(node)) |
1137 | 0 | return validateDeclarationNames( |
1138 | 0 | declKind, assign->_left, varIdents, scopedIdents); |
1139 | | |
1140 | 118 | if (auto *array = dyn_cast<ArrayPatternNode>(node)) { |
1141 | 118 | for (auto &elem : array->_elements) { |
1142 | 116 | validateDeclarationNames(declKind, &elem, varIdents, scopedIdents); |
1143 | 116 | } |
1144 | 118 | return; |
1145 | 118 | } |
1146 | | |
1147 | 0 | if (auto *restElem = dyn_cast<RestElementNode>(node)) { |
1148 | 0 | return validateDeclarationNames( |
1149 | 0 | declKind, restElem->_argument, varIdents, scopedIdents); |
1150 | 0 | } |
1151 | | |
1152 | 0 | if (auto *obj = dyn_cast<ObjectPatternNode>(node)) { |
1153 | 0 | for (auto &propNode : obj->_properties) { |
1154 | 0 | if (auto *prop = dyn_cast<PropertyNode>(&propNode)) { |
1155 | 0 | validateDeclarationNames( |
1156 | 0 | declKind, prop->_value, varIdents, scopedIdents); |
1157 | 0 | } else { |
1158 | 0 | auto *rest = cast<RestElementNode>(&propNode); |
1159 | 0 | validateDeclarationNames( |
1160 | 0 | declKind, rest->_argument, varIdents, scopedIdents); |
1161 | 0 | } |
1162 | 0 | } |
1163 | 0 | return; |
1164 | 0 | } |
1165 | | |
1166 | 0 | sm_.error(node->getSourceRange(), "invalid destructuring target"); |
1167 | 0 | } |
1168 | | |
1169 | 5.31k | void SemanticValidator::validateAssignmentTarget(const Node *node) { |
1170 | 5.31k | if (isa<EmptyNode>(node) || isLValue(node)) { |
1171 | 4.60k | return; |
1172 | 4.60k | } |
1173 | | |
1174 | 715 | if (auto *assign = dyn_cast<AssignmentPatternNode>(node)) { |
1175 | 8 | return validateAssignmentTarget(assign->_left); |
1176 | 8 | } |
1177 | | |
1178 | 707 | if (auto *APN = dyn_cast<ArrayPatternNode>(node)) { |
1179 | 789 | for (auto &elem : APN->_elements) { |
1180 | 789 | validateAssignmentTarget(&elem); |
1181 | 789 | } |
1182 | 707 | return; |
1183 | 707 | } |
1184 | | |
1185 | 0 | if (auto *RP = dyn_cast<RestElementNode>(node)) { |
1186 | 0 | return validateAssignmentTarget(RP->_argument); |
1187 | 0 | } |
1188 | | |
1189 | 0 | if (auto *obj = dyn_cast<ObjectPatternNode>(node)) { |
1190 | 0 | for (auto &propNode : obj->_properties) { |
1191 | 0 | if (auto *prop = dyn_cast<PropertyNode>(&propNode)) { |
1192 | 0 | assert( |
1193 | 0 | prop->_kind->str() == "init" && |
1194 | 0 | "getters and setters must have been reported by the parser"); |
1195 | 0 | validateAssignmentTarget(prop->_value); |
1196 | 0 | } else { |
1197 | 0 | auto *rest = cast<RestElementNode>(&propNode); |
1198 | 0 | validateAssignmentTarget(rest->_argument); |
1199 | 0 | } |
1200 | 0 | } |
1201 | 0 | return; |
1202 | 0 | } |
1203 | | |
1204 | 0 | sm_.error(node->getSourceRange(), "invalid assignment left-hand side"); |
1205 | 0 | } |
1206 | | |
1207 | 23.2k | void SemanticValidator::setDirectiveDerivedInfo(FunctionLikeNode *node) { |
1208 | 23.2k | node->strictness = ESTree::makeStrictness(curFunction()->strictMode); |
1209 | 23.2k | node->sourceVisibility = curFunction()->sourceVisibility; |
1210 | 23.2k | } |
1211 | | |
1212 | | LabelDecorationBase *SemanticValidator::getLabelDecorationBase( |
1213 | 0 | StatementNode *node) { |
1214 | 0 | if (auto *LS = dyn_cast<LoopStatementNode>(node)) |
1215 | 0 | return LS; |
1216 | 0 | if (auto *SS = dyn_cast<SwitchStatementNode>(node)) |
1217 | 0 | return SS; |
1218 | 0 | if (auto *BS = dyn_cast<BreakStatementNode>(node)) |
1219 | 0 | return BS; |
1220 | 0 | if (auto *CS = dyn_cast<ContinueStatementNode>(node)) |
1221 | 0 | return CS; |
1222 | 0 | if (auto *LabS = dyn_cast<LabeledStatementNode>(node)) |
1223 | 0 | return LabS; |
1224 | 0 | llvm_unreachable("invalid node type"); |
1225 | 0 | return nullptr; |
1226 | 0 | } |
1227 | | |
1228 | 4 | void SemanticValidator::recursionDepthExceeded(Node *n) { |
1229 | 4 | sm_.error( |
1230 | 4 | n->getEndLoc(), "Too many nested expressions/statements/declarations"); |
1231 | 4 | } |
1232 | | |
1233 | | void SemanticValidator::reportRedeclaredIdentifier( |
1234 | | const IdentifierNode &id1, |
1235 | 0 | const IdentifierNode &id2) { |
1236 | | // The redeclared ID is the one that appears later in the source code. |
1237 | 0 | const IdentifierNode &redeclaredId = id1.getSourceRange().Start.getPointer() < |
1238 | 0 | id2.getSourceRange().Start.getPointer() |
1239 | 0 | ? id2 |
1240 | 0 | : id1; |
1241 | | // The original ID is the one that appears first in the source code. |
1242 | 0 | const IdentifierNode &originalId = &redeclaredId == &id2 ? id1 : id2; |
1243 | |
|
1244 | 0 | sm_.error( |
1245 | 0 | redeclaredId.getSourceRange(), |
1246 | 0 | "Identifier '" + redeclaredId._name->str() + |
1247 | 0 | "' has already been declared"); |
1248 | 0 | sm_.note( |
1249 | 0 | originalId.getSourceRange(), |
1250 | 0 | "'" + redeclaredId._name->str() + "' previously defined here."); |
1251 | 0 | return; |
1252 | 0 | } |
1253 | | |
1254 | | //===----------------------------------------------------------------------===// |
1255 | | // BlockContext |
1256 | | |
1257 | | /// Helper function that ensures the given \p ptr is not nullptr. If it is, |
1258 | | /// \p ptr will be initialized to a new \p T. \return \p ptr. |
1259 | | template <typename T> |
1260 | 46.5k | std::unique_ptr<T> &initializeIfNull(std::unique_ptr<T> &ptr) { |
1261 | 46.5k | if (!ptr) { |
1262 | 46.5k | ptr.reset(new T); |
1263 | 46.5k | } |
1264 | 46.5k | return ptr; |
1265 | 46.5k | } std::__1::unique_ptr<llvh::SmallVector<hermes::sem::FunctionInfo::VarDecl, 4u>, std::__1::default_delete<llvh::SmallVector<hermes::sem::FunctionInfo::VarDecl, 4u> > >& hermes::sem::initializeIfNull<llvh::SmallVector<hermes::sem::FunctionInfo::VarDecl, 4u> >(std::__1::unique_ptr<llvh::SmallVector<hermes::sem::FunctionInfo::VarDecl, 4u>, std::__1::default_delete<llvh::SmallVector<hermes::sem::FunctionInfo::VarDecl, 4u> > >&) Line | Count | Source | 1260 | 23.2k | std::unique_ptr<T> &initializeIfNull(std::unique_ptr<T> &ptr) { | 1261 | 23.2k | if (!ptr) { | 1262 | 23.2k | ptr.reset(new T); | 1263 | 23.2k | } | 1264 | 23.2k | return ptr; | 1265 | 23.2k | } |
std::__1::unique_ptr<llvh::SmallVector<hermes::ESTree::FunctionDeclarationNode*, 2u>, std::__1::default_delete<llvh::SmallVector<hermes::ESTree::FunctionDeclarationNode*, 2u> > >& hermes::sem::initializeIfNull<llvh::SmallVector<hermes::ESTree::FunctionDeclarationNode*, 2u> >(std::__1::unique_ptr<llvh::SmallVector<hermes::ESTree::FunctionDeclarationNode*, 2u>, std::__1::default_delete<llvh::SmallVector<hermes::ESTree::FunctionDeclarationNode*, 2u> > >&) Line | Count | Source | 1260 | 23.2k | std::unique_ptr<T> &initializeIfNull(std::unique_ptr<T> &ptr) { | 1261 | 23.2k | if (!ptr) { | 1262 | 23.2k | ptr.reset(new T); | 1263 | 23.2k | } | 1264 | 23.2k | return ptr; | 1265 | 23.2k | } |
|
1266 | | |
1267 | | BlockContext::BlockContext( |
1268 | | SemanticValidator *validator, |
1269 | | FunctionContext *curFunction, |
1270 | | Node *nextScopeNode) |
1271 | 23.2k | : validator_(validator), |
1272 | 23.2k | curFunction_(curFunction), |
1273 | 23.2k | previousScopedDecls_(curFunction_->scopedDecls), |
1274 | 23.2k | previousScopedClosures_(curFunction_->scopedClosures), |
1275 | 23.2k | varDeclaredBegin_(curFunction_->semInfo->varScoped.size()) { |
1276 | 23.2k | if (nextScopeNode) { |
1277 | | // nextScopeNode is nullptr for lazy compilation's wrapper context -- in |
1278 | | // which case there is no need for setting up the block environment. |
1279 | | |
1280 | 23.2k | if (!validator->blockScopingEnabled()) { |
1281 | | // Block scope is disabled, so short-circuit nextScopeNode to be the |
1282 | | // function body (or the Program node if the semantic validator is |
1283 | | // currently traversing the global scope). This means all declarations |
1284 | | // will be placed in the top-level function/global scope. |
1285 | 23.2k | nextScopeNode = curFunction->body; |
1286 | 23.2k | } |
1287 | | |
1288 | | // Now set up curFunction_'s scopedDecls and scopedClosures pointer. Note |
1289 | | // that this will either create new containers (when block scoping is |
1290 | | // enabled), or reuse the existing containers for the function's top-level |
1291 | | // scope |
1292 | 23.2k | curFunction_->scopedDecls = |
1293 | 23.2k | initializeIfNull(curFunction_->semInfo->lexicallyScoped[nextScopeNode]) |
1294 | 23.2k | .get(); |
1295 | 23.2k | curFunction_->scopedClosures = |
1296 | 23.2k | initializeIfNull(curFunction_->semInfo->closures[nextScopeNode]).get(); |
1297 | 23.2k | } |
1298 | 23.2k | } |
1299 | | |
1300 | 23.2k | BlockContext::~BlockContext() { |
1301 | 23.2k | curFunction_->scopedDecls = previousScopedDecls_; |
1302 | 23.2k | curFunction_->scopedClosures = previousScopedClosures_; |
1303 | 23.2k | } |
1304 | | |
1305 | 0 | void BlockContext::stopHoisting(IdentifierNode *id) { |
1306 | | // The hoisting candidates are stored in a map of name to list of all known |
1307 | | // functions with that name. Therefore, if name is in hoistingCandidates_, |
1308 | | // clearing the list is all that's needed to stop function hoisting. Note |
1309 | | // that the code __clears__ the list instead of removing it from the map, thus |
1310 | | // preserving any memory allocated by the list (so, in case the same |
1311 | | // identifier appears again the compiler won't incur in (possibly) heap |
1312 | | // allocating memory). |
1313 | 0 | auto it = curFunction_->hoistingCandidates_.find(id->_name); |
1314 | 0 | if (it != curFunction_->hoistingCandidates_.end()) { |
1315 | 0 | curFunction_->hoistingCandidates_.erase(it); |
1316 | 0 | } |
1317 | 0 | } |
1318 | | |
1319 | | void BlockContext::ensureScopedNamesAreUnique( |
1320 | | IsFunctionBody isFunctionBody, |
1321 | 23.2k | IdentifierNode *catchParam) { |
1322 | 23.2k | if (!validator_->blockScopingEnabled()) { |
1323 | 23.2k | return; |
1324 | 23.2k | } |
1325 | | |
1326 | 0 | if (isFunctionBody == IsFunctionBody::Yes) { |
1327 | 0 | if (!curFunction_->scopedClosures) { |
1328 | | // This happens during semantic validation for lazy compilation. |
1329 | 0 | return; |
1330 | 0 | } |
1331 | 0 | } |
1332 | | |
1333 | 0 | llvh::SmallDenseMap<UniqueString *, IdentifierNode *, 8> |
1334 | 0 | lexicallyDeclaredNames; |
1335 | | |
1336 | | // It is a Syntax Error if the LexicallyDeclaredNames of StatementList |
1337 | | // contains any duplicate entries. LexicallyDeclaredNames includes let/const |
1338 | | // declarations. Function identifiers are not considered |
1339 | | // LexicallyDeclaredNames in function scopes. |
1340 | 0 | if (isFunctionBody != IsFunctionBody::Yes) { |
1341 | | // Keep track of all scopedClosures that are not regular functions (i.e., |
1342 | | // generators and async functions). |
1343 | 0 | llvh::SmallDenseSet<UniqueString *, 8> generatorOrAsync; |
1344 | 0 | for (FunctionDeclarationNode *scopedClosure : |
1345 | 0 | *curFunction_->scopedClosures) { |
1346 | 0 | auto *funName = |
1347 | 0 | llvh::dyn_cast_or_null<IdentifierNode>(scopedClosure->_id); |
1348 | |
|
1349 | 0 | if (!funName) { |
1350 | | // Anonymous function names are always unique. |
1351 | 0 | assert( |
1352 | 0 | !scopedClosure->_id && |
1353 | 0 | "FunctionLikeNode's _id is not an IdentifierNode"); |
1354 | 0 | continue; |
1355 | 0 | } |
1356 | | |
1357 | | // ES2023 B.3.2.4 requires that duplicate identifiers within |
1358 | | // FunctionDeclarations is not an error in non-strict mode. Thus, keep |
1359 | | // track of the closures that are not regular function declarations so the |
1360 | | // error can be reported. N.B.: adding the name here ensures that, should |
1361 | | // the check for duplicate below fail, an error would still be reported |
1362 | | // even if all previous instances of the duplicate symbol were |
1363 | | // FunctionDeclarations and scopedClosure was not. |
1364 | 0 | if (ESTree::isAsync(scopedClosure) || scopedClosure->_generator) { |
1365 | 0 | generatorOrAsync.insert(funName->_name); |
1366 | 0 | } |
1367 | |
|
1368 | 0 | auto res = lexicallyDeclaredNames.insert( |
1369 | 0 | std::make_pair(funName->_name, funName)); |
1370 | 0 | if (!res.second) { |
1371 | | // ES2023 B.3.2.4 Changes to Block Static Semantics: Early Errors |
1372 | | // |
1373 | | // Block: {StatementList} |
1374 | | // It is a Syntax Error if the LexicallyDeclaredNames of |
1375 | | // StatementList contains any duplicate entries, **unless the |
1376 | | // source code matching this production is not strict mode code |
1377 | | // and the duplicate entries are only bound by |
1378 | | // FunctionDeclarations**. |
1379 | 0 | if (ESTree::isStrict(scopedClosure->strictness) || |
1380 | 0 | generatorOrAsync.count(funName->_name)) { |
1381 | 0 | validator_->reportRedeclaredIdentifier(*res.first->second, *funName); |
1382 | 0 | return; |
1383 | 0 | } |
1384 | | |
1385 | | // Keep the ID that was declared earliest in the JS source. This is fine |
1386 | | // since the validation stops on the first duplicate ID found. |
1387 | 0 | if (funName->getSourceRange().Start.getPointer() < |
1388 | 0 | res.first->second->getSourceRange().Start.getPointer()) { |
1389 | 0 | res.first->second = funName; |
1390 | 0 | } |
1391 | 0 | } |
1392 | 0 | } |
1393 | 0 | } |
1394 | | |
1395 | | // Nothing special for const/let declarations -- thus, just iterate over them, |
1396 | | // and complain about duplicates. |
1397 | 0 | for (const FunctionInfo::VarDecl &scopedDecl : *curFunction_->scopedDecls) { |
1398 | | // According to ES2023 B.3.2.1 and ES2023 B.3.2.2 functions should not be |
1399 | | // hoisted if doing so would cause early errors. Therefore, any functions |
1400 | | // that share its ID with scopedDecl cannot possibly be hoisted past this |
1401 | | // scope. |
1402 | 0 | stopHoisting(scopedDecl.identifier); |
1403 | |
|
1404 | 0 | auto res = lexicallyDeclaredNames.insert( |
1405 | 0 | std::make_pair(scopedDecl.identifier->_name, scopedDecl.identifier)); |
1406 | 0 | if (!res.second) { |
1407 | 0 | validator_->reportRedeclaredIdentifier( |
1408 | 0 | *res.first->second, *scopedDecl.identifier); |
1409 | 0 | return; |
1410 | 0 | } |
1411 | 0 | } |
1412 | | |
1413 | 0 | if (catchParam) { |
1414 | 0 | auto it = lexicallyDeclaredNames.find(catchParam->_name); |
1415 | 0 | if (it != lexicallyDeclaredNames.end()) { |
1416 | 0 | validator_->reportRedeclaredIdentifier(*it->second, *catchParam); |
1417 | 0 | return; |
1418 | 0 | } |
1419 | 0 | } |
1420 | | |
1421 | | // Now ensure that the var decls in this scope don't clash with its |
1422 | | // LexicallyDeclaredNames. |
1423 | 0 | for (auto it = curFunction_->semInfo->varScoped.begin() + varDeclaredBegin_, |
1424 | 0 | end = curFunction_->semInfo->varScoped.end(); |
1425 | 0 | it != end; |
1426 | 0 | ++it) { |
1427 | 0 | IdentifierNode *name = it->identifier; |
1428 | 0 | auto pos = lexicallyDeclaredNames.find(name->_name); |
1429 | 0 | if (pos != lexicallyDeclaredNames.end()) { |
1430 | 0 | validator_->reportRedeclaredIdentifier(*pos->second, *name); |
1431 | 0 | return; |
1432 | 0 | } |
1433 | 0 | } |
1434 | 0 | } |
1435 | | |
1436 | | //===----------------------------------------------------------------------===// |
1437 | | // FunctionContext |
1438 | | |
1439 | | FunctionContext::FunctionContext( |
1440 | | SemanticValidator *validator, |
1441 | | bool strictMode, |
1442 | | FunctionLikeNode *node, |
1443 | | Node *body, |
1444 | | SourceVisibility sourceVisibility) |
1445 | 23.2k | : validator_(validator), |
1446 | 23.2k | oldContextValue_(validator->funcCtx_), |
1447 | 23.2k | node(node), |
1448 | 23.2k | body(body), |
1449 | 23.2k | semInfo(validator->semCtx_.createFunction()), |
1450 | 23.2k | varDecls(&semInfo->varScoped), |
1451 | 23.2k | strictMode(strictMode), |
1452 | 23.2k | sourceVisibility(sourceVisibility), |
1453 | 23.2k | functionScope_(validator, this, body) { |
1454 | 23.2k | validator->funcCtx_ = this; |
1455 | | |
1456 | 23.2k | if (node) |
1457 | 23.2k | node->setSemInfo(semInfo); |
1458 | 23.2k | } |
1459 | | |
1460 | 23.2k | FunctionContext::~FunctionContext() { |
1461 | 23.2k | functionScope_.ensureScopedNamesAreUnique(BlockContext::IsFunctionBody::Yes); |
1462 | 23.2k | validator_->funcCtx_ = oldContextValue_; |
1463 | 23.2k | finalizeHoisting(); |
1464 | 23.2k | } |
1465 | | |
1466 | 0 | void FunctionContext::addHoistingCandidate(FunctionDeclarationNode *funDecl) { |
1467 | 0 | auto *funDeclId = llvh::cast<IdentifierNode>(funDecl->_id); |
1468 | |
|
1469 | 0 | if (functionHoistingEnabled()) { |
1470 | 0 | hoistingCandidates_[funDeclId->_name].emplace_back(funDecl); |
1471 | 0 | } |
1472 | |
|
1473 | 0 | scopedClosures->emplace_back(funDecl); |
1474 | 0 | } |
1475 | | |
1476 | 23.2k | void FunctionContext::finalizeHoisting() { |
1477 | 23.2k | assert( |
1478 | 23.2k | (functionHoistingEnabled() || hoistingCandidates_.size() == 0) && |
1479 | 23.2k | "should not have hoisting candidates in strict mode."); |
1480 | | |
1481 | 23.2k | if (node && !ESTree::isStrict(node->strictness)) { |
1482 | | // Hoisting only happens in non-strict mode per ES2023 B.3.2.1 and |
1483 | | // ES2023 B.3.2.2; |
1484 | 23.2k | for (const auto &[name, nodes] : hoistingCandidates_) { |
1485 | 0 | assert( |
1486 | 0 | !nodes.empty() && "empty vectors should be removed by stopHoisting"); |
1487 | | |
1488 | | // Add a new var declaration to this function's varDecls. Only one |
1489 | | // declaration per identifier is needed regardless of how many |
1490 | | // functions with that id are hoisted. |
1491 | 0 | FunctionDeclarationNode *first = nodes[0]; |
1492 | 0 | varDecls->emplace_back( |
1493 | 0 | FunctionInfo::VarDecl::withoutInitializer( |
1494 | 0 | FunctionInfo::VarDecl::Kind::Var, |
1495 | 0 | cast<ESTree::IdentifierNode>(first->_id))); |
1496 | | |
1497 | | // Now mark all functions as hoisted, which prevents creation of the |
1498 | | // binding identifier below. |
1499 | 0 | for (FunctionDeclarationNode *fdn : nodes) { |
1500 | 0 | fdn->getSemInfo()->hoisted = true; |
1501 | 0 | } |
1502 | 0 | } |
1503 | 23.2k | } |
1504 | | |
1505 | | // Now add a scoped var decl (in the proper scope) for all functions that |
1506 | | // weren't hoisted. |
1507 | 23.2k | assert( |
1508 | 23.2k | (validator_->astContext_.getCodeGenerationSettings().enableBlockScoping || |
1509 | 23.2k | semInfo->closures.size() <= 1) && |
1510 | 23.2k | "All closures should be added to the same container when block scoping " |
1511 | 23.2k | "is disabled"); |
1512 | 23.2k | for (auto &[containingNode, containingNodeClosures] : semInfo->closures) { |
1513 | 23.2k | if (containingNodeClosures->empty()) { |
1514 | 23.2k | continue; |
1515 | 23.2k | } |
1516 | | |
1517 | | // For nested functions at the top level of an enclosing function, |
1518 | | // the nested function is added to the enclosing function's |
1519 | | // var list. For nested functions in blocks in an enclosing |
1520 | | // function, the nested function is added to the scope's |
1521 | | // list. For functions in the global scope, the function |
1522 | | // is added to the global scope var list. For functions |
1523 | | // in a scope nested in the global scope, the function is added |
1524 | | // to the scope's list. |
1525 | 0 | FunctionInfo::BlockDecls *decls = |
1526 | 0 | (containingNode == body && functionHoistingEnabled()) |
1527 | 0 | ? &semInfo->varScoped |
1528 | 0 | : semInfo->lexicallyScoped[containingNode].get(); |
1529 | |
|
1530 | 0 | for (auto it = containingNodeClosures->begin(), |
1531 | 0 | end = containingNodeClosures->end(); |
1532 | 0 | it < end; |
1533 | 0 | ++it) { |
1534 | 0 | if (!(*it)->getSemInfo()->hoisted) { |
1535 | 0 | decls->emplace_back( |
1536 | 0 | FunctionInfo::VarDecl::withoutInitializer( |
1537 | 0 | FunctionInfo::VarDecl::Kind::Var, |
1538 | 0 | cast<ESTree::IdentifierNode>((*it)->_id))); |
1539 | 0 | } |
1540 | 0 | } |
1541 | 0 | } |
1542 | 23.2k | } |
1543 | | } // namespace sem |
1544 | | } // namespace hermes |