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 |