OLD | NEW |
| (Empty) |
1 // Copyright 2009 the V8 project authors. All rights reserved. | |
2 // Redistribution and use in source and binary forms, with or without | |
3 // modification, are permitted provided that the following conditions are | |
4 // met: | |
5 // | |
6 // * Redistributions of source code must retain the above copyright | |
7 // notice, this list of conditions and the following disclaimer. | |
8 // * Redistributions in binary form must reproduce the above | |
9 // copyright notice, this list of conditions and the following | |
10 // disclaimer in the documentation and/or other materials provided | |
11 // with the distribution. | |
12 // * Neither the name of Google Inc. nor the names of its | |
13 // contributors may be used to endorse or promote products derived | |
14 // from this software without specific prior written permission. | |
15 // | |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 #include "v8.h" | |
29 | |
30 #include "codegen-inl.h" | |
31 #include "compiler.h" | |
32 #include "fast-codegen.h" | |
33 #include "stub-cache.h" | |
34 #include "debug.h" | |
35 | |
36 namespace v8 { | |
37 namespace internal { | |
38 | |
39 #define BAILOUT(reason) \ | |
40 do { \ | |
41 if (FLAG_trace_bailout) { \ | |
42 PrintF("%s\n", reason); \ | |
43 } \ | |
44 has_supported_syntax_ = false; \ | |
45 return; \ | |
46 } while (false) | |
47 | |
48 | |
49 #define CHECK_BAILOUT \ | |
50 do { \ | |
51 if (!has_supported_syntax_) return; \ | |
52 } while (false) | |
53 | |
54 | |
55 void FullCodeGenSyntaxChecker::Check(FunctionLiteral* fun) { | |
56 Scope* scope = fun->scope(); | |
57 | |
58 if (scope->num_heap_slots() > 0) { | |
59 // We support functions with a local context if they do not have | |
60 // parameters that need to be copied into the context. | |
61 for (int i = 0, len = scope->num_parameters(); i < len; i++) { | |
62 Slot* slot = scope->parameter(i)->slot(); | |
63 if (slot != NULL && slot->type() == Slot::CONTEXT) { | |
64 BAILOUT("Function has context-allocated parameters."); | |
65 } | |
66 } | |
67 } | |
68 | |
69 VisitDeclarations(scope->declarations()); | |
70 CHECK_BAILOUT; | |
71 | |
72 VisitStatements(fun->body()); | |
73 } | |
74 | |
75 | |
76 void FullCodeGenSyntaxChecker::VisitDeclarations( | |
77 ZoneList<Declaration*>* decls) { | |
78 for (int i = 0; i < decls->length(); i++) { | |
79 Visit(decls->at(i)); | |
80 CHECK_BAILOUT; | |
81 } | |
82 } | |
83 | |
84 | |
85 void FullCodeGenSyntaxChecker::VisitStatements(ZoneList<Statement*>* stmts) { | |
86 for (int i = 0, len = stmts->length(); i < len; i++) { | |
87 Visit(stmts->at(i)); | |
88 CHECK_BAILOUT; | |
89 } | |
90 } | |
91 | |
92 | |
93 void FullCodeGenSyntaxChecker::VisitDeclaration(Declaration* decl) { | |
94 Property* prop = decl->proxy()->AsProperty(); | |
95 if (prop != NULL) { | |
96 Visit(prop->obj()); | |
97 Visit(prop->key()); | |
98 } | |
99 | |
100 if (decl->fun() != NULL) { | |
101 Visit(decl->fun()); | |
102 } | |
103 } | |
104 | |
105 | |
106 void FullCodeGenSyntaxChecker::VisitBlock(Block* stmt) { | |
107 VisitStatements(stmt->statements()); | |
108 } | |
109 | |
110 | |
111 void FullCodeGenSyntaxChecker::VisitExpressionStatement( | |
112 ExpressionStatement* stmt) { | |
113 Visit(stmt->expression()); | |
114 } | |
115 | |
116 | |
117 void FullCodeGenSyntaxChecker::VisitEmptyStatement(EmptyStatement* stmt) { | |
118 // Supported. | |
119 } | |
120 | |
121 | |
122 void FullCodeGenSyntaxChecker::VisitIfStatement(IfStatement* stmt) { | |
123 Visit(stmt->condition()); | |
124 CHECK_BAILOUT; | |
125 Visit(stmt->then_statement()); | |
126 CHECK_BAILOUT; | |
127 Visit(stmt->else_statement()); | |
128 } | |
129 | |
130 | |
131 void FullCodeGenSyntaxChecker::VisitContinueStatement(ContinueStatement* stmt) { | |
132 // Supported. | |
133 } | |
134 | |
135 | |
136 void FullCodeGenSyntaxChecker::VisitBreakStatement(BreakStatement* stmt) {} | |
137 | |
138 | |
139 void FullCodeGenSyntaxChecker::VisitReturnStatement(ReturnStatement* stmt) { | |
140 Visit(stmt->expression()); | |
141 } | |
142 | |
143 | |
144 void FullCodeGenSyntaxChecker::VisitWithEnterStatement( | |
145 WithEnterStatement* stmt) { | |
146 Visit(stmt->expression()); | |
147 } | |
148 | |
149 | |
150 void FullCodeGenSyntaxChecker::VisitWithExitStatement(WithExitStatement* stmt) { | |
151 // Supported. | |
152 } | |
153 | |
154 | |
155 void FullCodeGenSyntaxChecker::VisitSwitchStatement(SwitchStatement* stmt) { | |
156 BAILOUT("SwitchStatement"); | |
157 } | |
158 | |
159 | |
160 void FullCodeGenSyntaxChecker::VisitDoWhileStatement(DoWhileStatement* stmt) { | |
161 Visit(stmt->cond()); | |
162 CHECK_BAILOUT; | |
163 Visit(stmt->body()); | |
164 } | |
165 | |
166 | |
167 void FullCodeGenSyntaxChecker::VisitWhileStatement(WhileStatement* stmt) { | |
168 Visit(stmt->cond()); | |
169 CHECK_BAILOUT; | |
170 Visit(stmt->body()); | |
171 } | |
172 | |
173 | |
174 void FullCodeGenSyntaxChecker::VisitForStatement(ForStatement* stmt) { | |
175 BAILOUT("ForStatement"); | |
176 } | |
177 | |
178 | |
179 void FullCodeGenSyntaxChecker::VisitForInStatement(ForInStatement* stmt) { | |
180 BAILOUT("ForInStatement"); | |
181 } | |
182 | |
183 | |
184 void FullCodeGenSyntaxChecker::VisitTryCatchStatement(TryCatchStatement* stmt) { | |
185 Visit(stmt->try_block()); | |
186 CHECK_BAILOUT; | |
187 Visit(stmt->catch_block()); | |
188 } | |
189 | |
190 | |
191 void FullCodeGenSyntaxChecker::VisitTryFinallyStatement( | |
192 TryFinallyStatement* stmt) { | |
193 Visit(stmt->try_block()); | |
194 CHECK_BAILOUT; | |
195 Visit(stmt->finally_block()); | |
196 } | |
197 | |
198 | |
199 void FullCodeGenSyntaxChecker::VisitDebuggerStatement( | |
200 DebuggerStatement* stmt) { | |
201 // Supported. | |
202 } | |
203 | |
204 | |
205 void FullCodeGenSyntaxChecker::VisitFunctionLiteral(FunctionLiteral* expr) { | |
206 // Supported. | |
207 } | |
208 | |
209 | |
210 void FullCodeGenSyntaxChecker::VisitFunctionBoilerplateLiteral( | |
211 FunctionBoilerplateLiteral* expr) { | |
212 BAILOUT("FunctionBoilerplateLiteral"); | |
213 } | |
214 | |
215 | |
216 void FullCodeGenSyntaxChecker::VisitConditional(Conditional* expr) { | |
217 Visit(expr->condition()); | |
218 CHECK_BAILOUT; | |
219 Visit(expr->then_expression()); | |
220 CHECK_BAILOUT; | |
221 Visit(expr->else_expression()); | |
222 } | |
223 | |
224 | |
225 void FullCodeGenSyntaxChecker::VisitSlot(Slot* expr) { | |
226 UNREACHABLE(); | |
227 } | |
228 | |
229 | |
230 void FullCodeGenSyntaxChecker::VisitVariableProxy(VariableProxy* expr) { | |
231 Variable* var = expr->var(); | |
232 if (!var->is_global()) { | |
233 Slot* slot = var->slot(); | |
234 if (slot != NULL) { | |
235 Slot::Type type = slot->type(); | |
236 // When LOOKUP slots are enabled, some currently dead code | |
237 // implementing unary typeof will become live. | |
238 if (type == Slot::LOOKUP) { | |
239 BAILOUT("Lookup slot"); | |
240 } | |
241 } else { | |
242 // If not global or a slot, it is a parameter rewritten to an explicit | |
243 // property reference on the (shadow) arguments object. | |
244 #ifdef DEBUG | |
245 Property* property = var->AsProperty(); | |
246 ASSERT_NOT_NULL(property); | |
247 Variable* object = property->obj()->AsVariableProxy()->AsVariable(); | |
248 ASSERT_NOT_NULL(object); | |
249 ASSERT_NOT_NULL(object->slot()); | |
250 ASSERT_NOT_NULL(property->key()->AsLiteral()); | |
251 ASSERT(property->key()->AsLiteral()->handle()->IsSmi()); | |
252 #endif | |
253 } | |
254 } | |
255 } | |
256 | |
257 | |
258 void FullCodeGenSyntaxChecker::VisitLiteral(Literal* expr) { | |
259 // Supported. | |
260 } | |
261 | |
262 | |
263 void FullCodeGenSyntaxChecker::VisitRegExpLiteral(RegExpLiteral* expr) { | |
264 // Supported. | |
265 } | |
266 | |
267 | |
268 void FullCodeGenSyntaxChecker::VisitObjectLiteral(ObjectLiteral* expr) { | |
269 ZoneList<ObjectLiteral::Property*>* properties = expr->properties(); | |
270 | |
271 for (int i = 0, len = properties->length(); i < len; i++) { | |
272 ObjectLiteral::Property* property = properties->at(i); | |
273 if (property->IsCompileTimeValue()) continue; | |
274 Visit(property->key()); | |
275 CHECK_BAILOUT; | |
276 Visit(property->value()); | |
277 CHECK_BAILOUT; | |
278 } | |
279 } | |
280 | |
281 | |
282 void FullCodeGenSyntaxChecker::VisitArrayLiteral(ArrayLiteral* expr) { | |
283 ZoneList<Expression*>* subexprs = expr->values(); | |
284 for (int i = 0, len = subexprs->length(); i < len; i++) { | |
285 Expression* subexpr = subexprs->at(i); | |
286 if (subexpr->AsLiteral() != NULL) continue; | |
287 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; | |
288 Visit(subexpr); | |
289 CHECK_BAILOUT; | |
290 } | |
291 } | |
292 | |
293 | |
294 void FullCodeGenSyntaxChecker::VisitCatchExtensionObject( | |
295 CatchExtensionObject* expr) { | |
296 Visit(expr->key()); | |
297 CHECK_BAILOUT; | |
298 Visit(expr->value()); | |
299 } | |
300 | |
301 | |
302 void FullCodeGenSyntaxChecker::VisitAssignment(Assignment* expr) { | |
303 // We support plain non-compound assignments to properties, parameters and | |
304 // non-context (stack-allocated) locals, and global variables. | |
305 Token::Value op = expr->op(); | |
306 if (op == Token::INIT_CONST) BAILOUT("initialize constant"); | |
307 | |
308 Variable* var = expr->target()->AsVariableProxy()->AsVariable(); | |
309 Property* prop = expr->target()->AsProperty(); | |
310 ASSERT(var == NULL || prop == NULL); | |
311 if (var != NULL) { | |
312 if (var->mode() == Variable::CONST) { | |
313 BAILOUT("Assignment to const"); | |
314 } | |
315 // All global variables are supported. | |
316 if (!var->is_global()) { | |
317 ASSERT(var->slot() != NULL); | |
318 Slot::Type type = var->slot()->type(); | |
319 if (type == Slot::LOOKUP) { | |
320 BAILOUT("Lookup slot"); | |
321 } | |
322 } | |
323 } else if (prop != NULL) { | |
324 Visit(prop->obj()); | |
325 CHECK_BAILOUT; | |
326 Visit(prop->key()); | |
327 CHECK_BAILOUT; | |
328 } else { | |
329 // This is a throw reference error. | |
330 BAILOUT("non-variable/non-property assignment"); | |
331 } | |
332 | |
333 Visit(expr->value()); | |
334 } | |
335 | |
336 | |
337 void FullCodeGenSyntaxChecker::VisitThrow(Throw* expr) { | |
338 Visit(expr->exception()); | |
339 } | |
340 | |
341 | |
342 void FullCodeGenSyntaxChecker::VisitProperty(Property* expr) { | |
343 Visit(expr->obj()); | |
344 CHECK_BAILOUT; | |
345 Visit(expr->key()); | |
346 } | |
347 | |
348 | |
349 void FullCodeGenSyntaxChecker::VisitCall(Call* expr) { | |
350 Expression* fun = expr->expression(); | |
351 ZoneList<Expression*>* args = expr->arguments(); | |
352 Variable* var = fun->AsVariableProxy()->AsVariable(); | |
353 | |
354 // Check for supported calls | |
355 if (var != NULL && var->is_possibly_eval()) { | |
356 BAILOUT("call to the identifier 'eval'"); | |
357 } else if (var != NULL && !var->is_this() && var->is_global()) { | |
358 // Calls to global variables are supported. | |
359 } else if (var != NULL && var->slot() != NULL && | |
360 var->slot()->type() == Slot::LOOKUP) { | |
361 BAILOUT("call to a lookup slot"); | |
362 } else if (fun->AsProperty() != NULL) { | |
363 Property* prop = fun->AsProperty(); | |
364 Visit(prop->obj()); | |
365 CHECK_BAILOUT; | |
366 Visit(prop->key()); | |
367 CHECK_BAILOUT; | |
368 } else { | |
369 // Otherwise the call is supported if the function expression is. | |
370 Visit(fun); | |
371 } | |
372 // Check all arguments to the call. | |
373 for (int i = 0; i < args->length(); i++) { | |
374 Visit(args->at(i)); | |
375 CHECK_BAILOUT; | |
376 } | |
377 } | |
378 | |
379 | |
380 void FullCodeGenSyntaxChecker::VisitCallNew(CallNew* expr) { | |
381 Visit(expr->expression()); | |
382 CHECK_BAILOUT; | |
383 ZoneList<Expression*>* args = expr->arguments(); | |
384 // Check all arguments to the call | |
385 for (int i = 0; i < args->length(); i++) { | |
386 Visit(args->at(i)); | |
387 CHECK_BAILOUT; | |
388 } | |
389 } | |
390 | |
391 | |
392 void FullCodeGenSyntaxChecker::VisitCallRuntime(CallRuntime* expr) { | |
393 // Check for inline runtime call | |
394 if (expr->name()->Get(0) == '_' && | |
395 CodeGenerator::FindInlineRuntimeLUT(expr->name()) != NULL) { | |
396 BAILOUT("inlined runtime call"); | |
397 } | |
398 // Check all arguments to the call. (Relies on TEMP meaning STACK.) | |
399 for (int i = 0; i < expr->arguments()->length(); i++) { | |
400 Visit(expr->arguments()->at(i)); | |
401 CHECK_BAILOUT; | |
402 } | |
403 } | |
404 | |
405 | |
406 void FullCodeGenSyntaxChecker::VisitUnaryOperation(UnaryOperation* expr) { | |
407 switch (expr->op()) { | |
408 case Token::VOID: | |
409 case Token::NOT: | |
410 case Token::TYPEOF: | |
411 Visit(expr->expression()); | |
412 break; | |
413 case Token::BIT_NOT: | |
414 BAILOUT("UnaryOperation: BIT_NOT"); | |
415 case Token::DELETE: | |
416 BAILOUT("UnaryOperation: DELETE"); | |
417 case Token::ADD: | |
418 BAILOUT("UnaryOperation: ADD"); | |
419 case Token::SUB: | |
420 BAILOUT("UnaryOperation: SUB"); | |
421 default: | |
422 UNREACHABLE(); | |
423 } | |
424 } | |
425 | |
426 | |
427 void FullCodeGenSyntaxChecker::VisitCountOperation(CountOperation* expr) { | |
428 Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); | |
429 Property* prop = expr->expression()->AsProperty(); | |
430 ASSERT(var == NULL || prop == NULL); | |
431 if (var != NULL) { | |
432 // All global variables are supported. | |
433 if (!var->is_global()) { | |
434 ASSERT(var->slot() != NULL); | |
435 Slot::Type type = var->slot()->type(); | |
436 if (type == Slot::LOOKUP) { | |
437 BAILOUT("CountOperation with lookup slot"); | |
438 } | |
439 } | |
440 } else if (prop != NULL) { | |
441 Visit(prop->obj()); | |
442 CHECK_BAILOUT; | |
443 Visit(prop->key()); | |
444 CHECK_BAILOUT; | |
445 } else { | |
446 // This is a throw reference error. | |
447 BAILOUT("CountOperation non-variable/non-property expression"); | |
448 } | |
449 } | |
450 | |
451 | |
452 void FullCodeGenSyntaxChecker::VisitBinaryOperation(BinaryOperation* expr) { | |
453 Visit(expr->left()); | |
454 CHECK_BAILOUT; | |
455 Visit(expr->right()); | |
456 } | |
457 | |
458 | |
459 void FullCodeGenSyntaxChecker::VisitCompareOperation(CompareOperation* expr) { | |
460 Visit(expr->left()); | |
461 CHECK_BAILOUT; | |
462 Visit(expr->right()); | |
463 } | |
464 | |
465 | |
466 void FullCodeGenSyntaxChecker::VisitThisFunction(ThisFunction* expr) { | |
467 // Supported. | |
468 } | |
469 | |
470 #undef BAILOUT | |
471 #undef CHECK_BAILOUT | |
472 | |
473 | |
474 #define __ ACCESS_MASM(masm()) | |
475 | |
476 Handle<Code> FullCodeGenerator::MakeCode(FunctionLiteral* fun, | |
477 Handle<Script> script, | |
478 bool is_eval) { | |
479 CodeGenerator::MakeCodePrologue(fun); | |
480 const int kInitialBufferSize = 4 * KB; | |
481 MacroAssembler masm(NULL, kInitialBufferSize); | |
482 FullCodeGenerator cgen(&masm, script, is_eval); | |
483 cgen.Generate(fun); | |
484 if (cgen.HasStackOverflow()) { | |
485 ASSERT(!Top::has_pending_exception()); | |
486 return Handle<Code>::null(); | |
487 } | |
488 Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP); | |
489 return CodeGenerator::MakeCodeEpilogue(fun, &masm, flags, script); | |
490 } | |
491 | |
492 | |
493 int FullCodeGenerator::SlotOffset(Slot* slot) { | |
494 ASSERT(slot != NULL); | |
495 // Offset is negative because higher indexes are at lower addresses. | |
496 int offset = -slot->index() * kPointerSize; | |
497 // Adjust by a (parameter or local) base offset. | |
498 switch (slot->type()) { | |
499 case Slot::PARAMETER: | |
500 offset += (function_->scope()->num_parameters() + 1) * kPointerSize; | |
501 break; | |
502 case Slot::LOCAL: | |
503 offset += JavaScriptFrameConstants::kLocal0Offset; | |
504 break; | |
505 case Slot::CONTEXT: | |
506 case Slot::LOOKUP: | |
507 UNREACHABLE(); | |
508 } | |
509 return offset; | |
510 } | |
511 | |
512 | |
513 void FullCodeGenerator::VisitDeclarations( | |
514 ZoneList<Declaration*>* declarations) { | |
515 int length = declarations->length(); | |
516 int globals = 0; | |
517 for (int i = 0; i < length; i++) { | |
518 Declaration* decl = declarations->at(i); | |
519 Variable* var = decl->proxy()->var(); | |
520 Slot* slot = var->slot(); | |
521 | |
522 // If it was not possible to allocate the variable at compile | |
523 // time, we need to "declare" it at runtime to make sure it | |
524 // actually exists in the local context. | |
525 if ((slot != NULL && slot->type() == Slot::LOOKUP) || !var->is_global()) { | |
526 VisitDeclaration(decl); | |
527 } else { | |
528 // Count global variables and functions for later processing | |
529 globals++; | |
530 } | |
531 } | |
532 | |
533 // Compute array of global variable and function declarations. | |
534 // Do nothing in case of no declared global functions or variables. | |
535 if (globals > 0) { | |
536 Handle<FixedArray> array = Factory::NewFixedArray(2 * globals, TENURED); | |
537 for (int j = 0, i = 0; i < length; i++) { | |
538 Declaration* decl = declarations->at(i); | |
539 Variable* var = decl->proxy()->var(); | |
540 Slot* slot = var->slot(); | |
541 | |
542 if ((slot == NULL || slot->type() != Slot::LOOKUP) && var->is_global()) { | |
543 array->set(j++, *(var->name())); | |
544 if (decl->fun() == NULL) { | |
545 if (var->mode() == Variable::CONST) { | |
546 // In case this is const property use the hole. | |
547 array->set_the_hole(j++); | |
548 } else { | |
549 array->set_undefined(j++); | |
550 } | |
551 } else { | |
552 Handle<JSFunction> function = | |
553 Compiler::BuildBoilerplate(decl->fun(), script_, this); | |
554 // Check for stack-overflow exception. | |
555 if (HasStackOverflow()) return; | |
556 array->set(j++, *function); | |
557 } | |
558 } | |
559 } | |
560 // Invoke the platform-dependent code generator to do the actual | |
561 // declaration the global variables and functions. | |
562 DeclareGlobals(array); | |
563 } | |
564 } | |
565 | |
566 | |
567 void FullCodeGenerator::SetFunctionPosition(FunctionLiteral* fun) { | |
568 if (FLAG_debug_info) { | |
569 CodeGenerator::RecordPositions(masm_, fun->start_position()); | |
570 } | |
571 } | |
572 | |
573 | |
574 void FullCodeGenerator::SetReturnPosition(FunctionLiteral* fun) { | |
575 if (FLAG_debug_info) { | |
576 CodeGenerator::RecordPositions(masm_, fun->end_position()); | |
577 } | |
578 } | |
579 | |
580 | |
581 void FullCodeGenerator::SetStatementPosition(Statement* stmt) { | |
582 if (FLAG_debug_info) { | |
583 CodeGenerator::RecordPositions(masm_, stmt->statement_pos()); | |
584 } | |
585 } | |
586 | |
587 | |
588 void FullCodeGenerator::SetStatementPosition(int pos) { | |
589 if (FLAG_debug_info) { | |
590 CodeGenerator::RecordPositions(masm_, pos); | |
591 } | |
592 } | |
593 | |
594 | |
595 void FullCodeGenerator::SetSourcePosition(int pos) { | |
596 if (FLAG_debug_info && pos != RelocInfo::kNoPosition) { | |
597 masm_->RecordPosition(pos); | |
598 } | |
599 } | |
600 | |
601 | |
602 void FullCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) { | |
603 Label eval_right, done; | |
604 | |
605 // Set up the appropriate context for the left subexpression based | |
606 // on the operation and our own context. Initially assume we can | |
607 // inherit both true and false labels from our context. | |
608 if (expr->op() == Token::OR) { | |
609 switch (context_) { | |
610 case Expression::kUninitialized: | |
611 UNREACHABLE(); | |
612 case Expression::kEffect: | |
613 VisitForControl(expr->left(), &done, &eval_right); | |
614 break; | |
615 case Expression::kValue: | |
616 VisitForValueControl(expr->left(), | |
617 location_, | |
618 &done, | |
619 &eval_right); | |
620 break; | |
621 case Expression::kTest: | |
622 VisitForControl(expr->left(), true_label_, &eval_right); | |
623 break; | |
624 case Expression::kValueTest: | |
625 VisitForValueControl(expr->left(), | |
626 location_, | |
627 true_label_, | |
628 &eval_right); | |
629 break; | |
630 case Expression::kTestValue: | |
631 VisitForControl(expr->left(), true_label_, &eval_right); | |
632 break; | |
633 } | |
634 } else { | |
635 ASSERT_EQ(Token::AND, expr->op()); | |
636 switch (context_) { | |
637 case Expression::kUninitialized: | |
638 UNREACHABLE(); | |
639 case Expression::kEffect: | |
640 VisitForControl(expr->left(), &eval_right, &done); | |
641 break; | |
642 case Expression::kValue: | |
643 VisitForControlValue(expr->left(), | |
644 location_, | |
645 &eval_right, | |
646 &done); | |
647 break; | |
648 case Expression::kTest: | |
649 VisitForControl(expr->left(), &eval_right, false_label_); | |
650 break; | |
651 case Expression::kValueTest: | |
652 VisitForControl(expr->left(), &eval_right, false_label_); | |
653 break; | |
654 case Expression::kTestValue: | |
655 VisitForControlValue(expr->left(), | |
656 location_, | |
657 &eval_right, | |
658 false_label_); | |
659 break; | |
660 } | |
661 } | |
662 | |
663 __ bind(&eval_right); | |
664 Visit(expr->right()); | |
665 | |
666 __ bind(&done); | |
667 } | |
668 | |
669 | |
670 void FullCodeGenerator::VisitBlock(Block* stmt) { | |
671 Comment cmnt(masm_, "[ Block"); | |
672 Breakable nested_statement(this, stmt); | |
673 SetStatementPosition(stmt); | |
674 VisitStatements(stmt->statements()); | |
675 __ bind(nested_statement.break_target()); | |
676 } | |
677 | |
678 | |
679 void FullCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { | |
680 Comment cmnt(masm_, "[ ExpressionStatement"); | |
681 SetStatementPosition(stmt); | |
682 VisitForEffect(stmt->expression()); | |
683 } | |
684 | |
685 | |
686 void FullCodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) { | |
687 Comment cmnt(masm_, "[ EmptyStatement"); | |
688 SetStatementPosition(stmt); | |
689 } | |
690 | |
691 | |
692 void FullCodeGenerator::VisitIfStatement(IfStatement* stmt) { | |
693 Comment cmnt(masm_, "[ IfStatement"); | |
694 SetStatementPosition(stmt); | |
695 Label then_part, else_part, done; | |
696 | |
697 // Do not worry about optimizing for empty then or else bodies. | |
698 VisitForControl(stmt->condition(), &then_part, &else_part); | |
699 | |
700 __ bind(&then_part); | |
701 Visit(stmt->then_statement()); | |
702 __ jmp(&done); | |
703 | |
704 __ bind(&else_part); | |
705 Visit(stmt->else_statement()); | |
706 | |
707 __ bind(&done); | |
708 } | |
709 | |
710 | |
711 void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { | |
712 Comment cmnt(masm_, "[ ContinueStatement"); | |
713 SetStatementPosition(stmt); | |
714 NestedStatement* current = nesting_stack_; | |
715 int stack_depth = 0; | |
716 while (!current->IsContinueTarget(stmt->target())) { | |
717 stack_depth = current->Exit(stack_depth); | |
718 current = current->outer(); | |
719 } | |
720 __ Drop(stack_depth); | |
721 | |
722 Iteration* loop = current->AsIteration(); | |
723 __ jmp(loop->continue_target()); | |
724 } | |
725 | |
726 | |
727 void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { | |
728 Comment cmnt(masm_, "[ BreakStatement"); | |
729 SetStatementPosition(stmt); | |
730 NestedStatement* current = nesting_stack_; | |
731 int stack_depth = 0; | |
732 while (!current->IsBreakTarget(stmt->target())) { | |
733 stack_depth = current->Exit(stack_depth); | |
734 current = current->outer(); | |
735 } | |
736 __ Drop(stack_depth); | |
737 | |
738 Breakable* target = current->AsBreakable(); | |
739 __ jmp(target->break_target()); | |
740 } | |
741 | |
742 | |
743 void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { | |
744 Comment cmnt(masm_, "[ ReturnStatement"); | |
745 SetStatementPosition(stmt); | |
746 Expression* expr = stmt->expression(); | |
747 VisitForValue(expr, kAccumulator); | |
748 | |
749 // Exit all nested statements. | |
750 NestedStatement* current = nesting_stack_; | |
751 int stack_depth = 0; | |
752 while (current != NULL) { | |
753 stack_depth = current->Exit(stack_depth); | |
754 current = current->outer(); | |
755 } | |
756 __ Drop(stack_depth); | |
757 | |
758 EmitReturnSequence(stmt->statement_pos()); | |
759 } | |
760 | |
761 | |
762 void FullCodeGenerator::VisitWithEnterStatement(WithEnterStatement* stmt) { | |
763 Comment cmnt(masm_, "[ WithEnterStatement"); | |
764 SetStatementPosition(stmt); | |
765 | |
766 VisitForValue(stmt->expression(), kStack); | |
767 if (stmt->is_catch_block()) { | |
768 __ CallRuntime(Runtime::kPushCatchContext, 1); | |
769 } else { | |
770 __ CallRuntime(Runtime::kPushContext, 1); | |
771 } | |
772 // Both runtime calls return the new context in both the context and the | |
773 // result registers. | |
774 | |
775 // Update local stack frame context field. | |
776 StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); | |
777 } | |
778 | |
779 | |
780 void FullCodeGenerator::VisitWithExitStatement(WithExitStatement* stmt) { | |
781 Comment cmnt(masm_, "[ WithExitStatement"); | |
782 SetStatementPosition(stmt); | |
783 | |
784 // Pop context. | |
785 LoadContextField(context_register(), Context::PREVIOUS_INDEX); | |
786 // Update local stack frame context field. | |
787 StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); | |
788 } | |
789 | |
790 | |
791 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { | |
792 UNREACHABLE(); | |
793 } | |
794 | |
795 | |
796 void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { | |
797 Comment cmnt(masm_, "[ DoWhileStatement"); | |
798 SetStatementPosition(stmt); | |
799 Label body, stack_limit_hit, stack_check_success; | |
800 | |
801 Iteration loop_statement(this, stmt); | |
802 increment_loop_depth(); | |
803 | |
804 __ bind(&body); | |
805 Visit(stmt->body()); | |
806 | |
807 // Check stack before looping. | |
808 __ StackLimitCheck(&stack_limit_hit); | |
809 __ bind(&stack_check_success); | |
810 | |
811 __ bind(loop_statement.continue_target()); | |
812 SetStatementPosition(stmt->condition_position()); | |
813 VisitForControl(stmt->cond(), &body, loop_statement.break_target()); | |
814 | |
815 __ bind(&stack_limit_hit); | |
816 StackCheckStub stack_stub; | |
817 __ CallStub(&stack_stub); | |
818 __ jmp(&stack_check_success); | |
819 | |
820 __ bind(loop_statement.break_target()); | |
821 | |
822 decrement_loop_depth(); | |
823 } | |
824 | |
825 | |
826 void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { | |
827 Comment cmnt(masm_, "[ WhileStatement"); | |
828 SetStatementPosition(stmt); | |
829 Label body, stack_limit_hit, stack_check_success; | |
830 | |
831 Iteration loop_statement(this, stmt); | |
832 increment_loop_depth(); | |
833 | |
834 // Emit the test at the bottom of the loop. | |
835 __ jmp(loop_statement.continue_target()); | |
836 | |
837 __ bind(&body); | |
838 Visit(stmt->body()); | |
839 | |
840 __ bind(loop_statement.continue_target()); | |
841 // Check stack before looping. | |
842 __ StackLimitCheck(&stack_limit_hit); | |
843 __ bind(&stack_check_success); | |
844 | |
845 VisitForControl(stmt->cond(), &body, loop_statement.break_target()); | |
846 | |
847 __ bind(&stack_limit_hit); | |
848 StackCheckStub stack_stub; | |
849 __ CallStub(&stack_stub); | |
850 __ jmp(&stack_check_success); | |
851 | |
852 __ bind(loop_statement.break_target()); | |
853 decrement_loop_depth(); | |
854 } | |
855 | |
856 | |
857 void FullCodeGenerator::VisitForStatement(ForStatement* stmt) { | |
858 UNREACHABLE(); | |
859 } | |
860 | |
861 | |
862 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { | |
863 UNREACHABLE(); | |
864 } | |
865 | |
866 | |
867 void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { | |
868 Comment cmnt(masm_, "[ TryCatchStatement"); | |
869 SetStatementPosition(stmt); | |
870 // The try block adds a handler to the exception handler chain | |
871 // before entering, and removes it again when exiting normally. | |
872 // If an exception is thrown during execution of the try block, | |
873 // control is passed to the handler, which also consumes the handler. | |
874 // At this point, the exception is in a register, and store it in | |
875 // the temporary local variable (prints as ".catch-var") before | |
876 // executing the catch block. The catch block has been rewritten | |
877 // to introduce a new scope to bind the catch variable and to remove | |
878 // that scope again afterwards. | |
879 | |
880 Label try_handler_setup, catch_entry, done; | |
881 __ Call(&try_handler_setup); | |
882 // Try handler code, exception in result register. | |
883 | |
884 // Store exception in local .catch variable before executing catch block. | |
885 { | |
886 // The catch variable is *always* a variable proxy for a local variable. | |
887 Variable* catch_var = stmt->catch_var()->AsVariableProxy()->AsVariable(); | |
888 ASSERT_NOT_NULL(catch_var); | |
889 Slot* variable_slot = catch_var->slot(); | |
890 ASSERT_NOT_NULL(variable_slot); | |
891 ASSERT_EQ(Slot::LOCAL, variable_slot->type()); | |
892 StoreToFrameField(SlotOffset(variable_slot), result_register()); | |
893 } | |
894 | |
895 Visit(stmt->catch_block()); | |
896 __ jmp(&done); | |
897 | |
898 // Try block code. Sets up the exception handler chain. | |
899 __ bind(&try_handler_setup); | |
900 { | |
901 TryCatch try_block(this, &catch_entry); | |
902 __ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER); | |
903 Visit(stmt->try_block()); | |
904 __ PopTryHandler(); | |
905 } | |
906 __ bind(&done); | |
907 } | |
908 | |
909 | |
910 void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { | |
911 Comment cmnt(masm_, "[ TryFinallyStatement"); | |
912 SetStatementPosition(stmt); | |
913 // Try finally is compiled by setting up a try-handler on the stack while | |
914 // executing the try body, and removing it again afterwards. | |
915 // | |
916 // The try-finally construct can enter the finally block in three ways: | |
917 // 1. By exiting the try-block normally. This removes the try-handler and | |
918 // calls the finally block code before continuing. | |
919 // 2. By exiting the try-block with a function-local control flow transfer | |
920 // (break/continue/return). The site of the, e.g., break removes the | |
921 // try handler and calls the finally block code before continuing | |
922 // its outward control transfer. | |
923 // 3. by exiting the try-block with a thrown exception. | |
924 // This can happen in nested function calls. It traverses the try-handler | |
925 // chain and consumes the try-handler entry before jumping to the | |
926 // handler code. The handler code then calls the finally-block before | |
927 // rethrowing the exception. | |
928 // | |
929 // The finally block must assume a return address on top of the stack | |
930 // (or in the link register on ARM chips) and a value (return value or | |
931 // exception) in the result register (rax/eax/r0), both of which must | |
932 // be preserved. The return address isn't GC-safe, so it should be | |
933 // cooked before GC. | |
934 Label finally_entry; | |
935 Label try_handler_setup; | |
936 | |
937 // Setup the try-handler chain. Use a call to | |
938 // Jump to try-handler setup and try-block code. Use call to put try-handler | |
939 // address on stack. | |
940 __ Call(&try_handler_setup); | |
941 // Try handler code. Return address of call is pushed on handler stack. | |
942 { | |
943 // This code is only executed during stack-handler traversal when an | |
944 // exception is thrown. The execption is in the result register, which | |
945 // is retained by the finally block. | |
946 // Call the finally block and then rethrow the exception. | |
947 __ Call(&finally_entry); | |
948 __ push(result_register()); | |
949 __ CallRuntime(Runtime::kReThrow, 1); | |
950 } | |
951 | |
952 __ bind(&finally_entry); | |
953 { | |
954 // Finally block implementation. | |
955 Finally finally_block(this); | |
956 EnterFinallyBlock(); | |
957 Visit(stmt->finally_block()); | |
958 ExitFinallyBlock(); // Return to the calling code. | |
959 } | |
960 | |
961 __ bind(&try_handler_setup); | |
962 { | |
963 // Setup try handler (stack pointer registers). | |
964 TryFinally try_block(this, &finally_entry); | |
965 __ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER); | |
966 Visit(stmt->try_block()); | |
967 __ PopTryHandler(); | |
968 } | |
969 // Execute the finally block on the way out. | |
970 __ Call(&finally_entry); | |
971 } | |
972 | |
973 | |
974 void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { | |
975 #ifdef ENABLE_DEBUGGER_SUPPORT | |
976 Comment cmnt(masm_, "[ DebuggerStatement"); | |
977 SetStatementPosition(stmt); | |
978 __ CallRuntime(Runtime::kDebugBreak, 0); | |
979 // Ignore the return value. | |
980 #endif | |
981 } | |
982 | |
983 | |
984 void FullCodeGenerator::VisitFunctionBoilerplateLiteral( | |
985 FunctionBoilerplateLiteral* expr) { | |
986 UNREACHABLE(); | |
987 } | |
988 | |
989 | |
990 void FullCodeGenerator::VisitConditional(Conditional* expr) { | |
991 Comment cmnt(masm_, "[ Conditional"); | |
992 Label true_case, false_case, done; | |
993 VisitForControl(expr->condition(), &true_case, &false_case); | |
994 | |
995 __ bind(&true_case); | |
996 Visit(expr->then_expression()); | |
997 // If control flow falls through Visit, jump to done. | |
998 if (context_ == Expression::kEffect || context_ == Expression::kValue) { | |
999 __ jmp(&done); | |
1000 } | |
1001 | |
1002 __ bind(&false_case); | |
1003 Visit(expr->else_expression()); | |
1004 // If control flow falls through Visit, merge it with true case here. | |
1005 if (context_ == Expression::kEffect || context_ == Expression::kValue) { | |
1006 __ bind(&done); | |
1007 } | |
1008 } | |
1009 | |
1010 | |
1011 void FullCodeGenerator::VisitSlot(Slot* expr) { | |
1012 // Slots do not appear directly in the AST. | |
1013 UNREACHABLE(); | |
1014 } | |
1015 | |
1016 | |
1017 void FullCodeGenerator::VisitLiteral(Literal* expr) { | |
1018 Comment cmnt(masm_, "[ Literal"); | |
1019 Apply(context_, expr); | |
1020 } | |
1021 | |
1022 | |
1023 void FullCodeGenerator::VisitAssignment(Assignment* expr) { | |
1024 Comment cmnt(masm_, "[ Assignment"); | |
1025 // Left-hand side can only be a property, a global or a (parameter or local) | |
1026 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. | |
1027 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; | |
1028 LhsKind assign_type = VARIABLE; | |
1029 Property* prop = expr->target()->AsProperty(); | |
1030 if (prop != NULL) { | |
1031 assign_type = | |
1032 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; | |
1033 } | |
1034 | |
1035 // Evaluate LHS expression. | |
1036 switch (assign_type) { | |
1037 case VARIABLE: | |
1038 // Nothing to do here. | |
1039 break; | |
1040 case NAMED_PROPERTY: | |
1041 VisitForValue(prop->obj(), kStack); | |
1042 break; | |
1043 case KEYED_PROPERTY: | |
1044 VisitForValue(prop->obj(), kStack); | |
1045 VisitForValue(prop->key(), kStack); | |
1046 break; | |
1047 } | |
1048 | |
1049 // If we have a compound assignment: Get value of LHS expression and | |
1050 // store in on top of the stack. | |
1051 if (expr->is_compound()) { | |
1052 Location saved_location = location_; | |
1053 location_ = kStack; | |
1054 switch (assign_type) { | |
1055 case VARIABLE: | |
1056 EmitVariableLoad(expr->target()->AsVariableProxy()->var(), | |
1057 Expression::kValue); | |
1058 break; | |
1059 case NAMED_PROPERTY: | |
1060 EmitNamedPropertyLoad(prop); | |
1061 __ push(result_register()); | |
1062 break; | |
1063 case KEYED_PROPERTY: | |
1064 EmitKeyedPropertyLoad(prop); | |
1065 __ push(result_register()); | |
1066 break; | |
1067 } | |
1068 location_ = saved_location; | |
1069 } | |
1070 | |
1071 // Evaluate RHS expression. | |
1072 Expression* rhs = expr->value(); | |
1073 VisitForValue(rhs, kAccumulator); | |
1074 | |
1075 // If we have a compount assignment: Apply operator. | |
1076 if (expr->is_compound()) { | |
1077 Location saved_location = location_; | |
1078 location_ = kAccumulator; | |
1079 EmitBinaryOp(expr->binary_op(), Expression::kValue); | |
1080 location_ = saved_location; | |
1081 } | |
1082 | |
1083 // Record source position before possible IC call. | |
1084 SetSourcePosition(expr->position()); | |
1085 | |
1086 // Store the value. | |
1087 switch (assign_type) { | |
1088 case VARIABLE: | |
1089 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), | |
1090 context_); | |
1091 break; | |
1092 case NAMED_PROPERTY: | |
1093 EmitNamedPropertyAssignment(expr); | |
1094 break; | |
1095 case KEYED_PROPERTY: | |
1096 EmitKeyedPropertyAssignment(expr); | |
1097 break; | |
1098 } | |
1099 } | |
1100 | |
1101 | |
1102 void FullCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { | |
1103 // Call runtime routine to allocate the catch extension object and | |
1104 // assign the exception value to the catch variable. | |
1105 Comment cmnt(masm_, "[ CatchExtensionObject"); | |
1106 VisitForValue(expr->key(), kStack); | |
1107 VisitForValue(expr->value(), kStack); | |
1108 // Create catch extension object. | |
1109 __ CallRuntime(Runtime::kCreateCatchExtensionObject, 2); | |
1110 Apply(context_, result_register()); | |
1111 } | |
1112 | |
1113 | |
1114 void FullCodeGenerator::VisitThrow(Throw* expr) { | |
1115 Comment cmnt(masm_, "[ Throw"); | |
1116 VisitForValue(expr->exception(), kStack); | |
1117 __ CallRuntime(Runtime::kThrow, 1); | |
1118 // Never returns here. | |
1119 } | |
1120 | |
1121 | |
1122 int FullCodeGenerator::TryFinally::Exit(int stack_depth) { | |
1123 // The macros used here must preserve the result register. | |
1124 __ Drop(stack_depth); | |
1125 __ PopTryHandler(); | |
1126 __ Call(finally_entry_); | |
1127 return 0; | |
1128 } | |
1129 | |
1130 | |
1131 int FullCodeGenerator::TryCatch::Exit(int stack_depth) { | |
1132 // The macros used here must preserve the result register. | |
1133 __ Drop(stack_depth); | |
1134 __ PopTryHandler(); | |
1135 return 0; | |
1136 } | |
1137 | |
1138 | |
1139 #undef __ | |
1140 | |
1141 | |
1142 } } // namespace v8::internal | |
OLD | NEW |