| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "src/ast.h" | |
| 6 #include "src/messages.h" | |
| 7 #include "src/parameter-initializer-rewriter.h" | |
| 8 #include "src/parser.h" | |
| 9 | |
| 10 namespace v8 { | |
| 11 | |
| 12 namespace internal { | |
| 13 | |
| 14 | |
| 15 void Parser::PatternRewriter::DeclareAndInitializeVariables( | |
| 16 Block* block, const DeclarationDescriptor* declaration_descriptor, | |
| 17 const DeclarationParsingResult::Declaration* declaration, | |
| 18 ZoneList<const AstRawString*>* names, bool* ok) { | |
| 19 PatternRewriter rewriter; | |
| 20 | |
| 21 rewriter.pattern_ = declaration->pattern; | |
| 22 rewriter.initializer_position_ = declaration->initializer_position; | |
| 23 rewriter.block_ = block; | |
| 24 rewriter.descriptor_ = declaration_descriptor; | |
| 25 rewriter.names_ = names; | |
| 26 rewriter.ok_ = ok; | |
| 27 | |
| 28 rewriter.RecurseIntoSubpattern(rewriter.pattern_, declaration->initializer); | |
| 29 } | |
| 30 | |
| 31 | |
| 32 void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { | |
| 33 Expression* value = current_value_; | |
| 34 descriptor_->scope->RemoveUnresolved(pattern); | |
| 35 | |
| 36 // Declare variable. | |
| 37 // Note that we *always* must treat the initial value via a separate init | |
| 38 // assignment for variables and constants because the value must be assigned | |
| 39 // when the variable is encountered in the source. But the variable/constant | |
| 40 // is declared (and set to 'undefined') upon entering the function within | |
| 41 // which the variable or constant is declared. Only function variables have | |
| 42 // an initial value in the declaration (because they are initialized upon | |
| 43 // entering the function). | |
| 44 // | |
| 45 // If we have a legacy const declaration, in an inner scope, the proxy | |
| 46 // is always bound to the declared variable (independent of possibly | |
| 47 // surrounding 'with' statements). | |
| 48 // For let/const declarations in harmony mode, we can also immediately | |
| 49 // pre-resolve the proxy because it resides in the same scope as the | |
| 50 // declaration. | |
| 51 Parser* parser = descriptor_->parser; | |
| 52 const AstRawString* name = pattern->raw_name(); | |
| 53 VariableProxy* proxy = parser->NewUnresolved(name, descriptor_->mode); | |
| 54 Declaration* declaration = factory()->NewVariableDeclaration( | |
| 55 proxy, descriptor_->mode, descriptor_->scope, | |
| 56 descriptor_->declaration_pos); | |
| 57 Variable* var = parser->Declare(declaration, descriptor_->declaration_kind, | |
| 58 descriptor_->mode != VAR, ok_, | |
| 59 descriptor_->hoist_scope); | |
| 60 if (!*ok_) return; | |
| 61 DCHECK_NOT_NULL(var); | |
| 62 DCHECK(!proxy->is_resolved() || proxy->var() == var); | |
| 63 var->set_initializer_position(initializer_position_); | |
| 64 | |
| 65 DCHECK(initializer_position_ != RelocInfo::kNoPosition); | |
| 66 | |
| 67 if (descriptor_->declaration_scope->num_var_or_const() > | |
| 68 kMaxNumFunctionLocals) { | |
| 69 parser->ReportMessage(MessageTemplate::kTooManyVariables); | |
| 70 *ok_ = false; | |
| 71 return; | |
| 72 } | |
| 73 if (names_) { | |
| 74 names_->Add(name, zone()); | |
| 75 } | |
| 76 | |
| 77 // Initialize variables if needed. A | |
| 78 // declaration of the form: | |
| 79 // | |
| 80 // var v = x; | |
| 81 // | |
| 82 // is syntactic sugar for: | |
| 83 // | |
| 84 // var v; v = x; | |
| 85 // | |
| 86 // In particular, we need to re-lookup 'v' (in scope_, not | |
| 87 // declaration_scope) as it may be a different 'v' than the 'v' in the | |
| 88 // declaration (e.g., if we are inside a 'with' statement or 'catch' | |
| 89 // block). | |
| 90 // | |
| 91 // However, note that const declarations are different! A const | |
| 92 // declaration of the form: | |
| 93 // | |
| 94 // const c = x; | |
| 95 // | |
| 96 // is *not* syntactic sugar for: | |
| 97 // | |
| 98 // const c; c = x; | |
| 99 // | |
| 100 // The "variable" c initialized to x is the same as the declared | |
| 101 // one - there is no re-lookup (see the last parameter of the | |
| 102 // Declare() call above). | |
| 103 Scope* initialization_scope = descriptor_->is_const | |
| 104 ? descriptor_->declaration_scope | |
| 105 : descriptor_->scope; | |
| 106 | |
| 107 | |
| 108 // Global variable declarations must be compiled in a specific | |
| 109 // way. When the script containing the global variable declaration | |
| 110 // is entered, the global variable must be declared, so that if it | |
| 111 // doesn't exist (on the global object itself, see ES5 errata) it | |
| 112 // gets created with an initial undefined value. This is handled | |
| 113 // by the declarations part of the function representing the | |
| 114 // top-level global code; see Runtime::DeclareGlobalVariable. If | |
| 115 // it already exists (in the object or in a prototype), it is | |
| 116 // *not* touched until the variable declaration statement is | |
| 117 // executed. | |
| 118 // | |
| 119 // Executing the variable declaration statement will always | |
| 120 // guarantee to give the global object an own property. | |
| 121 // This way, global variable declarations can shadow | |
| 122 // properties in the prototype chain, but only after the variable | |
| 123 // declaration statement has been executed. This is important in | |
| 124 // browsers where the global object (window) has lots of | |
| 125 // properties defined in prototype objects. | |
| 126 if (initialization_scope->is_script_scope() && | |
| 127 !IsLexicalVariableMode(descriptor_->mode)) { | |
| 128 // Compute the arguments for the runtime | |
| 129 // call.test-parsing/InitializedDeclarationsInStrictForOfError | |
| 130 ZoneList<Expression*>* arguments = | |
| 131 new (zone()) ZoneList<Expression*>(3, zone()); | |
| 132 // We have at least 1 parameter. | |
| 133 arguments->Add( | |
| 134 factory()->NewStringLiteral(name, descriptor_->declaration_pos), | |
| 135 zone()); | |
| 136 CallRuntime* initialize; | |
| 137 | |
| 138 if (descriptor_->is_const) { | |
| 139 arguments->Add(value, zone()); | |
| 140 value = NULL; // zap the value to avoid the unnecessary assignment | |
| 141 | |
| 142 // Construct the call to Runtime_InitializeConstGlobal | |
| 143 // and add it to the initialization statement block. | |
| 144 // Note that the function does different things depending on | |
| 145 // the number of arguments (1 or 2). | |
| 146 initialize = | |
| 147 factory()->NewCallRuntime(Runtime::kInitializeConstGlobal, arguments, | |
| 148 descriptor_->initialization_pos); | |
| 149 } else { | |
| 150 // Add language mode. | |
| 151 // We may want to pass singleton to avoid Literal allocations. | |
| 152 LanguageMode language_mode = initialization_scope->language_mode(); | |
| 153 arguments->Add(factory()->NewNumberLiteral(language_mode, | |
| 154 descriptor_->declaration_pos), | |
| 155 zone()); | |
| 156 | |
| 157 // Be careful not to assign a value to the global variable if | |
| 158 // we're in a with. The initialization value should not | |
| 159 // necessarily be stored in the global object in that case, | |
| 160 // which is why we need to generate a separate assignment node. | |
| 161 if (value != NULL && !descriptor_->scope->inside_with()) { | |
| 162 arguments->Add(value, zone()); | |
| 163 value = NULL; // zap the value to avoid the unnecessary assignment | |
| 164 // Construct the call to Runtime_InitializeVarGlobal | |
| 165 // and add it to the initialization statement block. | |
| 166 initialize = | |
| 167 factory()->NewCallRuntime(Runtime::kInitializeVarGlobal, arguments, | |
| 168 descriptor_->declaration_pos); | |
| 169 } else { | |
| 170 initialize = NULL; | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 if (initialize != NULL) { | |
| 175 block_->statements()->Add( | |
| 176 factory()->NewExpressionStatement(initialize, RelocInfo::kNoPosition), | |
| 177 zone()); | |
| 178 } | |
| 179 } else if (value != nullptr && (descriptor_->mode == CONST_LEGACY || | |
| 180 IsLexicalVariableMode(descriptor_->mode))) { | |
| 181 // Constant initializations always assign to the declared constant which | |
| 182 // is always at the function scope level. This is only relevant for | |
| 183 // dynamically looked-up variables and constants (the | |
| 184 // start context for constant lookups is always the function context, | |
| 185 // while it is the top context for var declared variables). Sigh... | |
| 186 // For 'let' and 'const' declared variables in harmony mode the | |
| 187 // initialization also always assigns to the declared variable. | |
| 188 DCHECK_NOT_NULL(proxy); | |
| 189 DCHECK_NOT_NULL(proxy->var()); | |
| 190 DCHECK_NOT_NULL(value); | |
| 191 Assignment* assignment = factory()->NewAssignment( | |
| 192 Token::INIT, proxy, value, descriptor_->initialization_pos); | |
| 193 block_->statements()->Add( | |
| 194 factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), | |
| 195 zone()); | |
| 196 value = NULL; | |
| 197 } | |
| 198 | |
| 199 // Add an assignment node to the initialization statement block if we still | |
| 200 // have a pending initialization value. | |
| 201 if (value != NULL) { | |
| 202 DCHECK(descriptor_->mode == VAR); | |
| 203 // 'var' initializations are simply assignments (with all the consequences | |
| 204 // if they are inside a 'with' statement - they may change a 'with' object | |
| 205 // property). | |
| 206 VariableProxy* proxy = initialization_scope->NewUnresolved(factory(), name); | |
| 207 Assignment* assignment = factory()->NewAssignment( | |
| 208 Token::INIT, proxy, value, descriptor_->initialization_pos); | |
| 209 block_->statements()->Add( | |
| 210 factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), | |
| 211 zone()); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 | |
| 216 Variable* Parser::PatternRewriter::CreateTempVar(Expression* value) { | |
| 217 auto temp = descriptor_->parser->scope_->NewTemporary( | |
| 218 ast_value_factory()->empty_string()); | |
| 219 if (value != nullptr) { | |
| 220 auto assignment = factory()->NewAssignment( | |
| 221 Token::ASSIGN, factory()->NewVariableProxy(temp), value, | |
| 222 RelocInfo::kNoPosition); | |
| 223 | |
| 224 block_->statements()->Add( | |
| 225 factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), | |
| 226 zone()); | |
| 227 } | |
| 228 return temp; | |
| 229 } | |
| 230 | |
| 231 | |
| 232 void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern) { | |
| 233 auto temp = CreateTempVar(current_value_); | |
| 234 | |
| 235 block_->statements()->Add(descriptor_->parser->BuildAssertIsCoercible(temp), | |
| 236 zone()); | |
| 237 | |
| 238 for (ObjectLiteralProperty* property : *pattern->properties()) { | |
| 239 RecurseIntoSubpattern( | |
| 240 property->value(), | |
| 241 factory()->NewProperty(factory()->NewVariableProxy(temp), | |
| 242 property->key(), RelocInfo::kNoPosition)); | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 | |
| 247 void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) { | |
| 248 auto temp = CreateTempVar(current_value_); | |
| 249 | |
| 250 block_->statements()->Add(descriptor_->parser->BuildAssertIsCoercible(temp), | |
| 251 zone()); | |
| 252 | |
| 253 auto iterator = CreateTempVar(descriptor_->parser->GetIterator( | |
| 254 factory()->NewVariableProxy(temp), factory())); | |
| 255 auto done = CreateTempVar( | |
| 256 factory()->NewBooleanLiteral(false, RelocInfo::kNoPosition)); | |
| 257 auto result = CreateTempVar(); | |
| 258 auto v = CreateTempVar(); | |
| 259 | |
| 260 Spread* spread = nullptr; | |
| 261 for (Expression* value : *node->values()) { | |
| 262 if (value->IsSpread()) { | |
| 263 spread = value->AsSpread(); | |
| 264 break; | |
| 265 } | |
| 266 | |
| 267 // if (!done) { | |
| 268 // result = IteratorNext(iterator); | |
| 269 // v = (done = result.done) ? undefined : result.value; | |
| 270 // } | |
| 271 auto next_block = | |
| 272 factory()->NewBlock(nullptr, 2, true, RelocInfo::kNoPosition); | |
| 273 next_block->statements()->Add( | |
| 274 factory()->NewExpressionStatement( | |
| 275 descriptor_->parser->BuildIteratorNextResult( | |
| 276 factory()->NewVariableProxy(iterator), result, | |
| 277 RelocInfo::kNoPosition), | |
| 278 RelocInfo::kNoPosition), | |
| 279 zone()); | |
| 280 | |
| 281 auto assign_to_done = factory()->NewAssignment( | |
| 282 Token::ASSIGN, factory()->NewVariableProxy(done), | |
| 283 factory()->NewProperty( | |
| 284 factory()->NewVariableProxy(result), | |
| 285 factory()->NewStringLiteral(ast_value_factory()->done_string(), | |
| 286 RelocInfo::kNoPosition), | |
| 287 RelocInfo::kNoPosition), | |
| 288 RelocInfo::kNoPosition); | |
| 289 auto next_value = factory()->NewConditional( | |
| 290 assign_to_done, factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), | |
| 291 factory()->NewProperty( | |
| 292 factory()->NewVariableProxy(result), | |
| 293 factory()->NewStringLiteral(ast_value_factory()->value_string(), | |
| 294 RelocInfo::kNoPosition), | |
| 295 RelocInfo::kNoPosition), | |
| 296 RelocInfo::kNoPosition); | |
| 297 next_block->statements()->Add( | |
| 298 factory()->NewExpressionStatement( | |
| 299 factory()->NewAssignment(Token::ASSIGN, | |
| 300 factory()->NewVariableProxy(v), next_value, | |
| 301 RelocInfo::kNoPosition), | |
| 302 RelocInfo::kNoPosition), | |
| 303 zone()); | |
| 304 | |
| 305 auto if_statement = factory()->NewIfStatement( | |
| 306 factory()->NewUnaryOperation(Token::NOT, | |
| 307 factory()->NewVariableProxy(done), | |
| 308 RelocInfo::kNoPosition), | |
| 309 next_block, factory()->NewEmptyStatement(RelocInfo::kNoPosition), | |
| 310 RelocInfo::kNoPosition); | |
| 311 block_->statements()->Add(if_statement, zone()); | |
| 312 | |
| 313 if (!(value->IsLiteral() && value->AsLiteral()->raw_value()->IsTheHole())) { | |
| 314 RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 if (spread != nullptr) { | |
| 319 // array = []; | |
| 320 // if (!done) %concat_iterable_to_array(array, iterator); | |
| 321 auto empty_exprs = new (zone()) ZoneList<Expression*>(0, zone()); | |
| 322 auto array = CreateTempVar(factory()->NewArrayLiteral( | |
| 323 empty_exprs, | |
| 324 // Reuse pattern's literal index - it is unused since there is no | |
| 325 // actual literal allocated. | |
| 326 node->literal_index(), is_strong(descriptor_->parser->language_mode()), | |
| 327 RelocInfo::kNoPosition)); | |
| 328 | |
| 329 auto arguments = new (zone()) ZoneList<Expression*>(2, zone()); | |
| 330 arguments->Add(factory()->NewVariableProxy(array), zone()); | |
| 331 arguments->Add(factory()->NewVariableProxy(iterator), zone()); | |
| 332 auto spread_into_array_call = | |
| 333 factory()->NewCallRuntime(Context::CONCAT_ITERABLE_TO_ARRAY_INDEX, | |
| 334 arguments, RelocInfo::kNoPosition); | |
| 335 | |
| 336 auto if_statement = factory()->NewIfStatement( | |
| 337 factory()->NewUnaryOperation(Token::NOT, | |
| 338 factory()->NewVariableProxy(done), | |
| 339 RelocInfo::kNoPosition), | |
| 340 factory()->NewExpressionStatement(spread_into_array_call, | |
| 341 RelocInfo::kNoPosition), | |
| 342 factory()->NewEmptyStatement(RelocInfo::kNoPosition), | |
| 343 RelocInfo::kNoPosition); | |
| 344 block_->statements()->Add(if_statement, zone()); | |
| 345 | |
| 346 RecurseIntoSubpattern(spread->expression(), | |
| 347 factory()->NewVariableProxy(array)); | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 | |
| 352 void Parser::PatternRewriter::VisitAssignment(Assignment* node) { | |
| 353 // let {<pattern> = <init>} = <value> | |
| 354 // becomes | |
| 355 // temp = <value>; | |
| 356 // <pattern> = temp === undefined ? <init> : temp; | |
| 357 DCHECK(node->op() == Token::ASSIGN); | |
| 358 auto temp = CreateTempVar(current_value_); | |
| 359 Expression* is_undefined = factory()->NewCompareOperation( | |
| 360 Token::EQ_STRICT, factory()->NewVariableProxy(temp), | |
| 361 factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), | |
| 362 RelocInfo::kNoPosition); | |
| 363 Expression* initializer = node->value(); | |
| 364 if (descriptor_->declaration_kind == DeclarationDescriptor::PARAMETER && | |
| 365 descriptor_->scope->is_arrow_scope()) { | |
| 366 // TODO(adamk): Only call this if necessary. | |
| 367 RewriteParameterInitializerScope( | |
| 368 descriptor_->parser->stack_limit(), initializer, | |
| 369 descriptor_->scope->outer_scope(), descriptor_->scope); | |
| 370 } | |
| 371 Expression* value = factory()->NewConditional( | |
| 372 is_undefined, initializer, factory()->NewVariableProxy(temp), | |
| 373 RelocInfo::kNoPosition); | |
| 374 RecurseIntoSubpattern(node->target(), value); | |
| 375 } | |
| 376 | |
| 377 | |
| 378 // =============== UNREACHABLE ============================= | |
| 379 | |
| 380 void Parser::PatternRewriter::Visit(AstNode* node) { UNREACHABLE(); } | |
| 381 | |
| 382 #define NOT_A_PATTERN(Node) \ | |
| 383 void Parser::PatternRewriter::Visit##Node(v8::internal::Node*) { \ | |
| 384 UNREACHABLE(); \ | |
| 385 } | |
| 386 | |
| 387 NOT_A_PATTERN(BinaryOperation) | |
| 388 NOT_A_PATTERN(Block) | |
| 389 NOT_A_PATTERN(BreakStatement) | |
| 390 NOT_A_PATTERN(Call) | |
| 391 NOT_A_PATTERN(CallNew) | |
| 392 NOT_A_PATTERN(CallRuntime) | |
| 393 NOT_A_PATTERN(CaseClause) | |
| 394 NOT_A_PATTERN(ClassLiteral) | |
| 395 NOT_A_PATTERN(CompareOperation) | |
| 396 NOT_A_PATTERN(Conditional) | |
| 397 NOT_A_PATTERN(ContinueStatement) | |
| 398 NOT_A_PATTERN(CountOperation) | |
| 399 NOT_A_PATTERN(DebuggerStatement) | |
| 400 NOT_A_PATTERN(DoExpression) | |
| 401 NOT_A_PATTERN(DoWhileStatement) | |
| 402 NOT_A_PATTERN(EmptyStatement) | |
| 403 NOT_A_PATTERN(EmptyParentheses) | |
| 404 NOT_A_PATTERN(ExportDeclaration) | |
| 405 NOT_A_PATTERN(ExpressionStatement) | |
| 406 NOT_A_PATTERN(ForInStatement) | |
| 407 NOT_A_PATTERN(ForOfStatement) | |
| 408 NOT_A_PATTERN(ForStatement) | |
| 409 NOT_A_PATTERN(FunctionDeclaration) | |
| 410 NOT_A_PATTERN(FunctionLiteral) | |
| 411 NOT_A_PATTERN(IfStatement) | |
| 412 NOT_A_PATTERN(ImportDeclaration) | |
| 413 NOT_A_PATTERN(Literal) | |
| 414 NOT_A_PATTERN(NativeFunctionLiteral) | |
| 415 NOT_A_PATTERN(Property) | |
| 416 NOT_A_PATTERN(RegExpLiteral) | |
| 417 NOT_A_PATTERN(ReturnStatement) | |
| 418 NOT_A_PATTERN(SloppyBlockFunctionStatement) | |
| 419 NOT_A_PATTERN(Spread) | |
| 420 NOT_A_PATTERN(SuperPropertyReference) | |
| 421 NOT_A_PATTERN(SuperCallReference) | |
| 422 NOT_A_PATTERN(SwitchStatement) | |
| 423 NOT_A_PATTERN(ThisFunction) | |
| 424 NOT_A_PATTERN(Throw) | |
| 425 NOT_A_PATTERN(TryCatchStatement) | |
| 426 NOT_A_PATTERN(TryFinallyStatement) | |
| 427 NOT_A_PATTERN(UnaryOperation) | |
| 428 NOT_A_PATTERN(VariableDeclaration) | |
| 429 NOT_A_PATTERN(WhileStatement) | |
| 430 NOT_A_PATTERN(WithStatement) | |
| 431 NOT_A_PATTERN(Yield) | |
| 432 | |
| 433 #undef NOT_A_PATTERN | |
| 434 } // namespace internal | |
| 435 } // namespace v8 | |
| OLD | NEW |