Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 1 | // Copyright 2014 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/compiler/ast-graph-builder.h" |
| 6 | |
| 7 | #include "src/compiler.h" |
| 8 | #include "src/compiler/control-builders.h" |
| 9 | #include "src/compiler/machine-operator.h" |
| 10 | #include "src/compiler/node-properties.h" |
| 11 | #include "src/compiler/node-properties-inl.h" |
| 12 | #include "src/full-codegen.h" |
| 13 | #include "src/parser.h" |
| 14 | #include "src/scopes.h" |
| 15 | |
| 16 | namespace v8 { |
| 17 | namespace internal { |
| 18 | namespace compiler { |
| 19 | |
| 20 | AstGraphBuilder::AstGraphBuilder(CompilationInfo* info, JSGraph* jsgraph) |
| 21 | : StructuredGraphBuilder(jsgraph->graph(), jsgraph->common()), |
| 22 | info_(info), |
| 23 | jsgraph_(jsgraph), |
| 24 | globals_(0, info->zone()), |
| 25 | breakable_(NULL), |
| 26 | execution_context_(NULL) { |
| 27 | InitializeAstVisitor(info->zone()); |
| 28 | } |
| 29 | |
| 30 | |
| 31 | Node* AstGraphBuilder::GetFunctionClosure() { |
| 32 | if (!function_closure_.is_set()) { |
| 33 | // Parameter -1 is special for the function closure |
| 34 | const Operator* op = common()->Parameter(-1); |
| 35 | Node* node = NewNode(op, graph()->start()); |
| 36 | function_closure_.set(node); |
| 37 | } |
| 38 | return function_closure_.get(); |
| 39 | } |
| 40 | |
| 41 | |
| 42 | Node* AstGraphBuilder::GetFunctionContext() { |
| 43 | if (!function_context_.is_set()) { |
| 44 | // Parameter (arity + 1) is special for the outer context of the function |
| 45 | const Operator* op = common()->Parameter(info()->num_parameters() + 1); |
| 46 | Node* node = NewNode(op, graph()->start()); |
| 47 | function_context_.set(node); |
| 48 | } |
| 49 | return function_context_.get(); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | bool AstGraphBuilder::CreateGraph() { |
| 54 | Scope* scope = info()->scope(); |
| 55 | DCHECK(graph() != NULL); |
| 56 | |
| 57 | // Set up the basic structure of the graph. |
| 58 | int parameter_count = info()->num_parameters(); |
| 59 | graph()->SetStart(graph()->NewNode(common()->Start(parameter_count))); |
| 60 | |
| 61 | // Initialize the top-level environment. |
| 62 | Environment env(this, scope, graph()->start()); |
| 63 | set_environment(&env); |
| 64 | |
| 65 | // Build node to initialize local function context. |
| 66 | Node* closure = GetFunctionClosure(); |
| 67 | Node* outer = GetFunctionContext(); |
| 68 | Node* inner = BuildLocalFunctionContext(outer, closure); |
| 69 | |
| 70 | // Push top-level function scope for the function body. |
| 71 | ContextScope top_context(this, scope, inner); |
| 72 | |
| 73 | // Build the arguments object if it is used. |
| 74 | BuildArgumentsObject(scope->arguments()); |
| 75 | |
| 76 | // Emit tracing call if requested to do so. |
| 77 | if (FLAG_trace) { |
| 78 | NewNode(javascript()->Runtime(Runtime::kTraceEnter, 0)); |
| 79 | } |
| 80 | |
| 81 | // Visit implicit declaration of the function name. |
| 82 | if (scope->is_function_scope() && scope->function() != NULL) { |
| 83 | VisitVariableDeclaration(scope->function()); |
| 84 | } |
| 85 | |
| 86 | // Visit declarations within the function scope. |
| 87 | VisitDeclarations(scope->declarations()); |
| 88 | |
| 89 | // TODO(mstarzinger): This should do an inlined stack check. |
| 90 | Node* node = NewNode(javascript()->Runtime(Runtime::kStackGuard, 0)); |
| 91 | PrepareFrameState(node, BailoutId::FunctionEntry()); |
| 92 | |
| 93 | // Visit statements in the function body. |
| 94 | VisitStatements(info()->function()->body()); |
| 95 | if (HasStackOverflow()) return false; |
| 96 | |
| 97 | // Emit tracing call if requested to do so. |
| 98 | if (FLAG_trace) { |
| 99 | // TODO(mstarzinger): Only traces implicit return. |
| 100 | Node* return_value = jsgraph()->UndefinedConstant(); |
| 101 | NewNode(javascript()->Runtime(Runtime::kTraceExit, 1), return_value); |
| 102 | } |
| 103 | |
| 104 | // Return 'undefined' in case we can fall off the end. |
| 105 | Node* control = NewNode(common()->Return(), jsgraph()->UndefinedConstant()); |
| 106 | UpdateControlDependencyToLeaveFunction(control); |
| 107 | |
| 108 | // Finish the basic structure of the graph. |
| 109 | environment()->UpdateControlDependency(exit_control()); |
| 110 | graph()->SetEnd(NewNode(common()->End())); |
| 111 | |
| 112 | return true; |
| 113 | } |
| 114 | |
| 115 | |
| 116 | // Left-hand side can only be a property, a global or a variable slot. |
| 117 | enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
| 118 | |
| 119 | |
| 120 | // Determine the left-hand side kind of an assignment. |
| 121 | static LhsKind DetermineLhsKind(Expression* expr) { |
| 122 | Property* property = expr->AsProperty(); |
| 123 | DCHECK(expr->IsValidReferenceExpression()); |
| 124 | LhsKind lhs_kind = |
| 125 | (property == NULL) ? VARIABLE : (property->key()->IsPropertyName()) |
| 126 | ? NAMED_PROPERTY |
| 127 | : KEYED_PROPERTY; |
| 128 | return lhs_kind; |
| 129 | } |
| 130 | |
| 131 | |
| 132 | // Helper to find an existing shared function info in the baseline code for the |
| 133 | // given function literal. Used to canonicalize SharedFunctionInfo objects. |
| 134 | static Handle<SharedFunctionInfo> SearchSharedFunctionInfo( |
| 135 | Code* unoptimized_code, FunctionLiteral* expr) { |
| 136 | int start_position = expr->start_position(); |
| 137 | for (RelocIterator it(unoptimized_code); !it.done(); it.next()) { |
| 138 | RelocInfo* rinfo = it.rinfo(); |
| 139 | if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue; |
| 140 | Object* obj = rinfo->target_object(); |
| 141 | if (obj->IsSharedFunctionInfo()) { |
| 142 | SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj); |
| 143 | if (shared->start_position() == start_position) { |
| 144 | return Handle<SharedFunctionInfo>(shared); |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | return Handle<SharedFunctionInfo>(); |
| 149 | } |
| 150 | |
| 151 | |
| 152 | StructuredGraphBuilder::Environment* AstGraphBuilder::CopyEnvironment( |
| 153 | StructuredGraphBuilder::Environment* env) { |
| 154 | return new (zone()) Environment(*reinterpret_cast<Environment*>(env)); |
| 155 | } |
| 156 | |
| 157 | |
| 158 | AstGraphBuilder::Environment::Environment(AstGraphBuilder* builder, |
| 159 | Scope* scope, |
| 160 | Node* control_dependency) |
| 161 | : StructuredGraphBuilder::Environment(builder, control_dependency), |
| 162 | parameters_count_(scope->num_parameters() + 1), |
| 163 | locals_count_(scope->num_stack_slots()), |
| 164 | parameters_node_(NULL), |
| 165 | locals_node_(NULL), |
| 166 | stack_node_(NULL) { |
| 167 | DCHECK_EQ(scope->num_parameters() + 1, parameters_count()); |
| 168 | |
| 169 | // Bind the receiver variable. |
| 170 | Node* receiver = builder->graph()->NewNode(common()->Parameter(0), |
| 171 | builder->graph()->start()); |
| 172 | values()->push_back(receiver); |
| 173 | |
| 174 | // Bind all parameter variables. The parameter indices are shifted by 1 |
| 175 | // (receiver is parameter index -1 but environment index 0). |
| 176 | for (int i = 0; i < scope->num_parameters(); ++i) { |
| 177 | Node* parameter = builder->graph()->NewNode(common()->Parameter(i + 1), |
| 178 | builder->graph()->start()); |
| 179 | values()->push_back(parameter); |
| 180 | } |
| 181 | |
| 182 | // Bind all local variables to undefined. |
| 183 | Node* undefined_constant = builder->jsgraph()->UndefinedConstant(); |
| 184 | values()->insert(values()->end(), locals_count(), undefined_constant); |
| 185 | } |
| 186 | |
| 187 | |
| 188 | AstGraphBuilder::Environment::Environment(const Environment& copy) |
| 189 | : StructuredGraphBuilder::Environment( |
| 190 | static_cast<StructuredGraphBuilder::Environment>(copy)), |
| 191 | parameters_count_(copy.parameters_count_), |
| 192 | locals_count_(copy.locals_count_), |
| 193 | parameters_node_(copy.parameters_node_), |
| 194 | locals_node_(copy.locals_node_), |
| 195 | stack_node_(copy.stack_node_) {} |
| 196 | |
| 197 | |
| 198 | void AstGraphBuilder::Environment::UpdateStateValues(Node** state_values, |
| 199 | int offset, int count) { |
| 200 | bool should_update = false; |
| 201 | Node** env_values = (count == 0) ? NULL : &values()->at(offset); |
| 202 | if (*state_values == NULL || (*state_values)->InputCount() != count) { |
| 203 | should_update = true; |
| 204 | } else { |
| 205 | DCHECK(static_cast<size_t>(offset + count) <= values()->size()); |
| 206 | for (int i = 0; i < count; i++) { |
| 207 | if ((*state_values)->InputAt(i) != env_values[i]) { |
| 208 | should_update = true; |
| 209 | break; |
| 210 | } |
| 211 | } |
| 212 | } |
| 213 | if (should_update) { |
| 214 | const Operator* op = common()->StateValues(count); |
| 215 | (*state_values) = graph()->NewNode(op, count, env_values); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | |
| 220 | Node* AstGraphBuilder::Environment::Checkpoint( |
| 221 | BailoutId ast_id, OutputFrameStateCombine combine) { |
| 222 | UpdateStateValues(¶meters_node_, 0, parameters_count()); |
| 223 | UpdateStateValues(&locals_node_, parameters_count(), locals_count()); |
| 224 | UpdateStateValues(&stack_node_, parameters_count() + locals_count(), |
| 225 | stack_height()); |
| 226 | |
| 227 | const Operator* op = common()->FrameState(JS_FRAME, ast_id, combine); |
| 228 | |
| 229 | return graph()->NewNode(op, parameters_node_, locals_node_, stack_node_, |
| 230 | GetContext(), |
| 231 | builder()->jsgraph()->UndefinedConstant()); |
| 232 | } |
| 233 | |
| 234 | |
| 235 | AstGraphBuilder::AstContext::AstContext(AstGraphBuilder* own, |
| 236 | Expression::Context kind) |
| 237 | : kind_(kind), owner_(own), outer_(own->ast_context()) { |
| 238 | owner()->set_ast_context(this); // Push. |
| 239 | #ifdef DEBUG |
| 240 | original_height_ = environment()->stack_height(); |
| 241 | #endif |
| 242 | } |
| 243 | |
| 244 | |
| 245 | AstGraphBuilder::AstContext::~AstContext() { |
| 246 | owner()->set_ast_context(outer_); // Pop. |
| 247 | } |
| 248 | |
| 249 | |
| 250 | AstGraphBuilder::AstEffectContext::~AstEffectContext() { |
| 251 | DCHECK(environment()->stack_height() == original_height_); |
| 252 | } |
| 253 | |
| 254 | |
| 255 | AstGraphBuilder::AstValueContext::~AstValueContext() { |
| 256 | DCHECK(environment()->stack_height() == original_height_ + 1); |
| 257 | } |
| 258 | |
| 259 | |
| 260 | AstGraphBuilder::AstTestContext::~AstTestContext() { |
| 261 | DCHECK(environment()->stack_height() == original_height_ + 1); |
| 262 | } |
| 263 | |
| 264 | |
| 265 | void AstGraphBuilder::AstEffectContext::ProduceValue(Node* value) { |
| 266 | // The value is ignored. |
| 267 | } |
| 268 | |
| 269 | |
| 270 | void AstGraphBuilder::AstValueContext::ProduceValue(Node* value) { |
| 271 | environment()->Push(value); |
| 272 | } |
| 273 | |
| 274 | |
| 275 | void AstGraphBuilder::AstTestContext::ProduceValue(Node* value) { |
| 276 | environment()->Push(owner()->BuildToBoolean(value)); |
| 277 | } |
| 278 | |
| 279 | |
| 280 | Node* AstGraphBuilder::AstEffectContext::ConsumeValue() { return NULL; } |
| 281 | |
| 282 | |
| 283 | Node* AstGraphBuilder::AstValueContext::ConsumeValue() { |
| 284 | return environment()->Pop(); |
| 285 | } |
| 286 | |
| 287 | |
| 288 | Node* AstGraphBuilder::AstTestContext::ConsumeValue() { |
| 289 | return environment()->Pop(); |
| 290 | } |
| 291 | |
| 292 | |
| 293 | AstGraphBuilder::BreakableScope* AstGraphBuilder::BreakableScope::FindBreakable( |
| 294 | BreakableStatement* target) { |
| 295 | BreakableScope* current = this; |
| 296 | while (current != NULL && current->target_ != target) { |
| 297 | owner_->environment()->Drop(current->drop_extra_); |
| 298 | current = current->next_; |
| 299 | } |
| 300 | DCHECK(current != NULL); // Always found (unless stack is malformed). |
| 301 | return current; |
| 302 | } |
| 303 | |
| 304 | |
| 305 | void AstGraphBuilder::BreakableScope::BreakTarget(BreakableStatement* stmt) { |
| 306 | FindBreakable(stmt)->control_->Break(); |
| 307 | } |
| 308 | |
| 309 | |
| 310 | void AstGraphBuilder::BreakableScope::ContinueTarget(BreakableStatement* stmt) { |
| 311 | FindBreakable(stmt)->control_->Continue(); |
| 312 | } |
| 313 | |
| 314 | |
| 315 | void AstGraphBuilder::VisitForValueOrNull(Expression* expr) { |
| 316 | if (expr == NULL) { |
| 317 | return environment()->Push(jsgraph()->NullConstant()); |
| 318 | } |
| 319 | VisitForValue(expr); |
| 320 | } |
| 321 | |
| 322 | |
| 323 | void AstGraphBuilder::VisitForValues(ZoneList<Expression*>* exprs) { |
| 324 | for (int i = 0; i < exprs->length(); ++i) { |
| 325 | VisitForValue(exprs->at(i)); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | |
| 330 | void AstGraphBuilder::VisitForValue(Expression* expr) { |
| 331 | AstValueContext for_value(this); |
| 332 | if (!HasStackOverflow()) { |
| 333 | expr->Accept(this); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | |
| 338 | void AstGraphBuilder::VisitForEffect(Expression* expr) { |
| 339 | AstEffectContext for_effect(this); |
| 340 | if (!HasStackOverflow()) { |
| 341 | expr->Accept(this); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | |
| 346 | void AstGraphBuilder::VisitForTest(Expression* expr) { |
| 347 | AstTestContext for_condition(this); |
| 348 | if (!HasStackOverflow()) { |
| 349 | expr->Accept(this); |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | |
| 354 | void AstGraphBuilder::VisitVariableDeclaration(VariableDeclaration* decl) { |
| 355 | Variable* variable = decl->proxy()->var(); |
| 356 | VariableMode mode = decl->mode(); |
| 357 | bool hole_init = mode == CONST || mode == CONST_LEGACY || mode == LET; |
| 358 | switch (variable->location()) { |
| 359 | case Variable::UNALLOCATED: { |
| 360 | Handle<Oddball> value = variable->binding_needs_init() |
| 361 | ? isolate()->factory()->the_hole_value() |
| 362 | : isolate()->factory()->undefined_value(); |
| 363 | globals()->Add(variable->name(), zone()); |
| 364 | globals()->Add(value, zone()); |
| 365 | break; |
| 366 | } |
| 367 | case Variable::PARAMETER: |
| 368 | case Variable::LOCAL: |
| 369 | if (hole_init) { |
| 370 | Node* value = jsgraph()->TheHoleConstant(); |
| 371 | environment()->Bind(variable, value); |
| 372 | } |
| 373 | break; |
| 374 | case Variable::CONTEXT: |
| 375 | if (hole_init) { |
| 376 | Node* value = jsgraph()->TheHoleConstant(); |
| 377 | const Operator* op = javascript()->StoreContext(0, variable->index()); |
| 378 | NewNode(op, current_context(), value); |
| 379 | } |
| 380 | break; |
| 381 | case Variable::LOOKUP: |
| 382 | UNIMPLEMENTED(); |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | |
| 387 | void AstGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* decl) { |
| 388 | Variable* variable = decl->proxy()->var(); |
| 389 | switch (variable->location()) { |
| 390 | case Variable::UNALLOCATED: { |
| 391 | Handle<SharedFunctionInfo> function = |
| 392 | Compiler::BuildFunctionInfo(decl->fun(), info()->script(), info()); |
| 393 | // Check for stack-overflow exception. |
| 394 | if (function.is_null()) return SetStackOverflow(); |
| 395 | globals()->Add(variable->name(), zone()); |
| 396 | globals()->Add(function, zone()); |
| 397 | break; |
| 398 | } |
| 399 | case Variable::PARAMETER: |
| 400 | case Variable::LOCAL: { |
| 401 | VisitForValue(decl->fun()); |
| 402 | Node* value = environment()->Pop(); |
| 403 | environment()->Bind(variable, value); |
| 404 | break; |
| 405 | } |
| 406 | case Variable::CONTEXT: { |
| 407 | VisitForValue(decl->fun()); |
| 408 | Node* value = environment()->Pop(); |
| 409 | const Operator* op = javascript()->StoreContext(0, variable->index()); |
| 410 | NewNode(op, current_context(), value); |
| 411 | break; |
| 412 | } |
| 413 | case Variable::LOOKUP: |
| 414 | UNIMPLEMENTED(); |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | |
| 419 | void AstGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* decl) { |
| 420 | UNREACHABLE(); |
| 421 | } |
| 422 | |
| 423 | |
| 424 | void AstGraphBuilder::VisitImportDeclaration(ImportDeclaration* decl) { |
| 425 | UNREACHABLE(); |
| 426 | } |
| 427 | |
| 428 | |
| 429 | void AstGraphBuilder::VisitExportDeclaration(ExportDeclaration* decl) { |
| 430 | UNREACHABLE(); |
| 431 | } |
| 432 | |
| 433 | |
| 434 | void AstGraphBuilder::VisitModuleLiteral(ModuleLiteral* modl) { UNREACHABLE(); } |
| 435 | |
| 436 | |
| 437 | void AstGraphBuilder::VisitModuleVariable(ModuleVariable* modl) { |
| 438 | UNREACHABLE(); |
| 439 | } |
| 440 | |
| 441 | |
| 442 | void AstGraphBuilder::VisitModulePath(ModulePath* modl) { UNREACHABLE(); } |
| 443 | |
| 444 | |
| 445 | void AstGraphBuilder::VisitModuleUrl(ModuleUrl* modl) { UNREACHABLE(); } |
| 446 | |
| 447 | |
| 448 | void AstGraphBuilder::VisitBlock(Block* stmt) { |
| 449 | BlockBuilder block(this); |
| 450 | BreakableScope scope(this, stmt, &block, 0); |
| 451 | if (stmt->labels() != NULL) block.BeginBlock(); |
| 452 | if (stmt->scope() == NULL) { |
| 453 | // Visit statements in the same scope, no declarations. |
| 454 | VisitStatements(stmt->statements()); |
| 455 | } else { |
| 456 | const Operator* op = javascript()->CreateBlockContext(); |
| 457 | Node* scope_info = jsgraph()->Constant(stmt->scope()->GetScopeInfo()); |
| 458 | Node* context = NewNode(op, scope_info, GetFunctionClosure()); |
| 459 | ContextScope scope(this, stmt->scope(), context); |
| 460 | |
| 461 | // Visit declarations and statements in a block scope. |
| 462 | VisitDeclarations(stmt->scope()->declarations()); |
| 463 | VisitStatements(stmt->statements()); |
| 464 | } |
| 465 | if (stmt->labels() != NULL) block.EndBlock(); |
| 466 | } |
| 467 | |
| 468 | |
| 469 | void AstGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) { |
| 470 | UNREACHABLE(); |
| 471 | } |
| 472 | |
| 473 | |
| 474 | void AstGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { |
| 475 | VisitForEffect(stmt->expression()); |
| 476 | } |
| 477 | |
| 478 | |
| 479 | void AstGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) { |
| 480 | // Do nothing. |
| 481 | } |
| 482 | |
| 483 | |
| 484 | void AstGraphBuilder::VisitIfStatement(IfStatement* stmt) { |
| 485 | IfBuilder compare_if(this); |
| 486 | VisitForTest(stmt->condition()); |
| 487 | Node* condition = environment()->Pop(); |
| 488 | compare_if.If(condition); |
| 489 | compare_if.Then(); |
| 490 | Visit(stmt->then_statement()); |
| 491 | compare_if.Else(); |
| 492 | Visit(stmt->else_statement()); |
| 493 | compare_if.End(); |
| 494 | } |
| 495 | |
| 496 | |
| 497 | void AstGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) { |
| 498 | StructuredGraphBuilder::Environment* env = environment()->CopyAsUnreachable(); |
| 499 | breakable()->ContinueTarget(stmt->target()); |
| 500 | set_environment(env); |
| 501 | } |
| 502 | |
| 503 | |
| 504 | void AstGraphBuilder::VisitBreakStatement(BreakStatement* stmt) { |
| 505 | StructuredGraphBuilder::Environment* env = environment()->CopyAsUnreachable(); |
| 506 | breakable()->BreakTarget(stmt->target()); |
| 507 | set_environment(env); |
| 508 | } |
| 509 | |
| 510 | |
| 511 | void AstGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
| 512 | VisitForValue(stmt->expression()); |
| 513 | Node* result = environment()->Pop(); |
| 514 | Node* control = NewNode(common()->Return(), result); |
| 515 | UpdateControlDependencyToLeaveFunction(control); |
| 516 | } |
| 517 | |
| 518 | |
| 519 | void AstGraphBuilder::VisitWithStatement(WithStatement* stmt) { |
| 520 | VisitForValue(stmt->expression()); |
| 521 | Node* value = environment()->Pop(); |
| 522 | const Operator* op = javascript()->CreateWithContext(); |
| 523 | Node* context = NewNode(op, value, GetFunctionClosure()); |
| 524 | ContextScope scope(this, stmt->scope(), context); |
| 525 | Visit(stmt->statement()); |
| 526 | } |
| 527 | |
| 528 | |
| 529 | void AstGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
| 530 | ZoneList<CaseClause*>* clauses = stmt->cases(); |
| 531 | SwitchBuilder compare_switch(this, clauses->length()); |
| 532 | BreakableScope scope(this, stmt, &compare_switch, 0); |
| 533 | compare_switch.BeginSwitch(); |
| 534 | int default_index = -1; |
| 535 | |
| 536 | // Keep the switch value on the stack until a case matches. |
| 537 | VisitForValue(stmt->tag()); |
| 538 | Node* tag = environment()->Top(); |
| 539 | |
| 540 | // Iterate over all cases and create nodes for label comparison. |
| 541 | for (int i = 0; i < clauses->length(); i++) { |
| 542 | CaseClause* clause = clauses->at(i); |
| 543 | |
| 544 | // The default is not a test, remember index. |
| 545 | if (clause->is_default()) { |
| 546 | default_index = i; |
| 547 | continue; |
| 548 | } |
| 549 | |
| 550 | // Create nodes to perform label comparison as if via '==='. The switch |
| 551 | // value is still on the operand stack while the label is evaluated. |
| 552 | VisitForValue(clause->label()); |
| 553 | Node* label = environment()->Pop(); |
| 554 | const Operator* op = javascript()->StrictEqual(); |
| 555 | Node* condition = NewNode(op, tag, label); |
| 556 | compare_switch.BeginLabel(i, condition); |
| 557 | |
| 558 | // Discard the switch value at label match. |
| 559 | environment()->Pop(); |
| 560 | compare_switch.EndLabel(); |
| 561 | } |
| 562 | |
| 563 | // Discard the switch value and mark the default case. |
| 564 | environment()->Pop(); |
| 565 | if (default_index >= 0) { |
| 566 | compare_switch.DefaultAt(default_index); |
| 567 | } |
| 568 | |
| 569 | // Iterate over all cases and create nodes for case bodies. |
| 570 | for (int i = 0; i < clauses->length(); i++) { |
| 571 | CaseClause* clause = clauses->at(i); |
| 572 | compare_switch.BeginCase(i); |
| 573 | VisitStatements(clause->statements()); |
| 574 | compare_switch.EndCase(); |
| 575 | } |
| 576 | |
| 577 | compare_switch.EndSwitch(); |
| 578 | } |
| 579 | |
| 580 | |
| 581 | void AstGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) { |
| 582 | LoopBuilder while_loop(this); |
| 583 | while_loop.BeginLoop(); |
| 584 | VisitIterationBody(stmt, &while_loop, 0); |
| 585 | while_loop.EndBody(); |
| 586 | VisitForTest(stmt->cond()); |
| 587 | Node* condition = environment()->Pop(); |
| 588 | while_loop.BreakUnless(condition); |
| 589 | while_loop.EndLoop(); |
| 590 | } |
| 591 | |
| 592 | |
| 593 | void AstGraphBuilder::VisitWhileStatement(WhileStatement* stmt) { |
| 594 | LoopBuilder while_loop(this); |
| 595 | while_loop.BeginLoop(); |
| 596 | VisitForTest(stmt->cond()); |
| 597 | Node* condition = environment()->Pop(); |
| 598 | while_loop.BreakUnless(condition); |
| 599 | VisitIterationBody(stmt, &while_loop, 0); |
| 600 | while_loop.EndBody(); |
| 601 | while_loop.EndLoop(); |
| 602 | } |
| 603 | |
| 604 | |
| 605 | void AstGraphBuilder::VisitForStatement(ForStatement* stmt) { |
| 606 | LoopBuilder for_loop(this); |
| 607 | VisitIfNotNull(stmt->init()); |
| 608 | for_loop.BeginLoop(); |
| 609 | if (stmt->cond() != NULL) { |
| 610 | VisitForTest(stmt->cond()); |
| 611 | Node* condition = environment()->Pop(); |
| 612 | for_loop.BreakUnless(condition); |
| 613 | } |
| 614 | VisitIterationBody(stmt, &for_loop, 0); |
| 615 | for_loop.EndBody(); |
| 616 | VisitIfNotNull(stmt->next()); |
| 617 | for_loop.EndLoop(); |
| 618 | } |
| 619 | |
| 620 | |
| 621 | // TODO(dcarney): this is a big function. Try to clean up some. |
| 622 | void AstGraphBuilder::VisitForInStatement(ForInStatement* stmt) { |
| 623 | VisitForValue(stmt->subject()); |
| 624 | Node* obj = environment()->Pop(); |
| 625 | // Check for undefined or null before entering loop. |
| 626 | IfBuilder is_undefined(this); |
| 627 | Node* is_undefined_cond = |
| 628 | NewNode(javascript()->StrictEqual(), obj, jsgraph()->UndefinedConstant()); |
| 629 | is_undefined.If(is_undefined_cond); |
| 630 | is_undefined.Then(); |
| 631 | is_undefined.Else(); |
| 632 | { |
| 633 | IfBuilder is_null(this); |
| 634 | Node* is_null_cond = |
| 635 | NewNode(javascript()->StrictEqual(), obj, jsgraph()->NullConstant()); |
| 636 | is_null.If(is_null_cond); |
| 637 | is_null.Then(); |
| 638 | is_null.Else(); |
| 639 | // Convert object to jsobject. |
| 640 | // PrepareForBailoutForId(stmt->PrepareId(), TOS_REG); |
| 641 | obj = NewNode(javascript()->ToObject(), obj); |
| 642 | environment()->Push(obj); |
| 643 | // TODO(dcarney): should do a fast enum cache check here to skip runtime. |
| 644 | environment()->Push(obj); |
| 645 | Node* cache_type = ProcessArguments( |
| 646 | javascript()->Runtime(Runtime::kGetPropertyNamesFast, 1), 1); |
| 647 | // TODO(dcarney): these next runtime calls should be removed in favour of |
| 648 | // a few simplified instructions. |
| 649 | environment()->Push(obj); |
| 650 | environment()->Push(cache_type); |
| 651 | Node* cache_pair = |
| 652 | ProcessArguments(javascript()->Runtime(Runtime::kForInInit, 2), 2); |
| 653 | // cache_type may have been replaced. |
| 654 | Node* cache_array = NewNode(common()->Projection(0), cache_pair); |
| 655 | cache_type = NewNode(common()->Projection(1), cache_pair); |
| 656 | environment()->Push(cache_type); |
| 657 | environment()->Push(cache_array); |
| 658 | Node* cache_length = ProcessArguments( |
| 659 | javascript()->Runtime(Runtime::kForInCacheArrayLength, 2), 2); |
| 660 | { |
| 661 | // TODO(dcarney): this check is actually supposed to be for the |
| 662 | // empty enum case only. |
| 663 | IfBuilder have_no_properties(this); |
| 664 | Node* empty_array_cond = NewNode(javascript()->StrictEqual(), |
| 665 | cache_length, jsgraph()->ZeroConstant()); |
| 666 | have_no_properties.If(empty_array_cond); |
| 667 | have_no_properties.Then(); |
| 668 | // Pop obj and skip loop. |
| 669 | environment()->Pop(); |
| 670 | have_no_properties.Else(); |
| 671 | { |
| 672 | // Construct the rest of the environment. |
| 673 | environment()->Push(cache_type); |
| 674 | environment()->Push(cache_array); |
| 675 | environment()->Push(cache_length); |
| 676 | environment()->Push(jsgraph()->ZeroConstant()); |
| 677 | // PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS); |
| 678 | LoopBuilder for_loop(this); |
| 679 | for_loop.BeginLoop(); |
| 680 | // Check loop termination condition. |
| 681 | Node* index = environment()->Peek(0); |
| 682 | Node* exit_cond = |
| 683 | NewNode(javascript()->LessThan(), index, cache_length); |
| 684 | // TODO(jarin): provide real bailout id. |
| 685 | PrepareFrameState(exit_cond, BailoutId::None()); |
| 686 | for_loop.BreakUnless(exit_cond); |
| 687 | // TODO(dcarney): this runtime call should be a handful of |
| 688 | // simplified instructions that |
| 689 | // basically produce |
| 690 | // value = array[index] |
| 691 | environment()->Push(obj); |
| 692 | environment()->Push(cache_array); |
| 693 | environment()->Push(cache_type); |
| 694 | environment()->Push(index); |
| 695 | Node* pair = |
| 696 | ProcessArguments(javascript()->Runtime(Runtime::kForInNext, 4), 4); |
| 697 | Node* value = NewNode(common()->Projection(0), pair); |
| 698 | Node* should_filter = NewNode(common()->Projection(1), pair); |
| 699 | environment()->Push(value); |
| 700 | { |
| 701 | // Test if FILTER_KEY needs to be called. |
| 702 | IfBuilder test_should_filter(this); |
| 703 | Node* should_filter_cond = |
| 704 | NewNode(javascript()->StrictEqual(), should_filter, |
| 705 | jsgraph()->TrueConstant()); |
| 706 | test_should_filter.If(should_filter_cond); |
| 707 | test_should_filter.Then(); |
| 708 | value = environment()->Pop(); |
| 709 | Node* builtins = BuildLoadBuiltinsObject(); |
| 710 | Node* function = BuildLoadObjectField( |
| 711 | builtins, |
| 712 | JSBuiltinsObject::OffsetOfFunctionWithId(Builtins::FILTER_KEY)); |
| 713 | // Callee. |
| 714 | environment()->Push(function); |
| 715 | // Receiver. |
| 716 | environment()->Push(obj); |
| 717 | // Args. |
| 718 | environment()->Push(value); |
| 719 | // result is either the string key or Smi(0) indicating the property |
| 720 | // is gone. |
| 721 | Node* res = ProcessArguments( |
| 722 | javascript()->Call(3, NO_CALL_FUNCTION_FLAGS), 3); |
| 723 | // TODO(jarin): provide real bailout id. |
| 724 | PrepareFrameState(res, BailoutId::None()); |
| 725 | Node* property_missing = NewNode(javascript()->StrictEqual(), res, |
| 726 | jsgraph()->ZeroConstant()); |
| 727 | { |
| 728 | IfBuilder is_property_missing(this); |
| 729 | is_property_missing.If(property_missing); |
| 730 | is_property_missing.Then(); |
| 731 | // Inc counter and continue. |
| 732 | Node* index_inc = |
| 733 | NewNode(javascript()->Add(), index, jsgraph()->OneConstant()); |
| 734 | // TODO(jarin): provide real bailout id. |
| 735 | PrepareFrameState(index_inc, BailoutId::None()); |
| 736 | environment()->Poke(0, index_inc); |
| 737 | for_loop.Continue(); |
| 738 | is_property_missing.Else(); |
| 739 | is_property_missing.End(); |
| 740 | } |
| 741 | // Replace 'value' in environment. |
| 742 | environment()->Push(res); |
| 743 | test_should_filter.Else(); |
| 744 | test_should_filter.End(); |
| 745 | } |
| 746 | value = environment()->Pop(); |
| 747 | // Bind value and do loop body. |
| 748 | VisitForInAssignment(stmt->each(), value); |
| 749 | VisitIterationBody(stmt, &for_loop, 5); |
| 750 | for_loop.EndBody(); |
| 751 | // Inc counter and continue. |
| 752 | Node* index_inc = |
| 753 | NewNode(javascript()->Add(), index, jsgraph()->OneConstant()); |
| 754 | // TODO(jarin): provide real bailout id. |
| 755 | PrepareFrameState(index_inc, BailoutId::None()); |
| 756 | environment()->Poke(0, index_inc); |
| 757 | for_loop.EndLoop(); |
| 758 | environment()->Drop(5); |
| 759 | // PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); |
| 760 | } |
| 761 | have_no_properties.End(); |
| 762 | } |
| 763 | is_null.End(); |
| 764 | } |
| 765 | is_undefined.End(); |
| 766 | } |
| 767 | |
| 768 | |
| 769 | void AstGraphBuilder::VisitForOfStatement(ForOfStatement* stmt) { |
| 770 | VisitForValue(stmt->subject()); |
| 771 | environment()->Pop(); |
| 772 | // TODO(turbofan): create and use loop builder. |
| 773 | } |
| 774 | |
| 775 | |
| 776 | void AstGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) { |
| 777 | UNREACHABLE(); |
| 778 | } |
| 779 | |
| 780 | |
| 781 | void AstGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
| 782 | UNREACHABLE(); |
| 783 | } |
| 784 | |
| 785 | |
| 786 | void AstGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { |
| 787 | // TODO(turbofan): Do we really need a separate reloc-info for this? |
| 788 | Node* node = NewNode(javascript()->Runtime(Runtime::kDebugBreak, 0)); |
| 789 | PrepareFrameState(node, stmt->DebugBreakId()); |
| 790 | } |
| 791 | |
| 792 | |
| 793 | void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { |
| 794 | Node* context = current_context(); |
| 795 | |
| 796 | // Build a new shared function info if we cannot find one in the baseline |
| 797 | // code. We also have a stack overflow if the recursive compilation did. |
| 798 | Handle<SharedFunctionInfo> shared_info = |
| 799 | SearchSharedFunctionInfo(info()->shared_info()->code(), expr); |
| 800 | if (shared_info.is_null()) { |
| 801 | shared_info = Compiler::BuildFunctionInfo(expr, info()->script(), info()); |
| 802 | CHECK(!shared_info.is_null()); // TODO(mstarzinger): Set stack overflow? |
| 803 | } |
| 804 | |
| 805 | // Create node to instantiate a new closure. |
| 806 | Node* info = jsgraph()->Constant(shared_info); |
| 807 | Node* pretenure = expr->pretenure() ? jsgraph()->TrueConstant() |
| 808 | : jsgraph()->FalseConstant(); |
| 809 | const Operator* op = javascript()->Runtime(Runtime::kNewClosure, 3); |
| 810 | Node* value = NewNode(op, context, info, pretenure); |
| 811 | ast_context()->ProduceValue(value); |
| 812 | } |
| 813 | |
| 814 | |
| 815 | void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) { |
| 816 | // TODO(arv): Implement. |
| 817 | UNREACHABLE(); |
| 818 | } |
| 819 | |
| 820 | |
| 821 | void AstGraphBuilder::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) { |
| 822 | UNREACHABLE(); |
| 823 | } |
| 824 | |
| 825 | |
| 826 | void AstGraphBuilder::VisitConditional(Conditional* expr) { |
| 827 | IfBuilder compare_if(this); |
| 828 | VisitForTest(expr->condition()); |
| 829 | Node* condition = environment()->Pop(); |
| 830 | compare_if.If(condition); |
| 831 | compare_if.Then(); |
| 832 | Visit(expr->then_expression()); |
| 833 | compare_if.Else(); |
| 834 | Visit(expr->else_expression()); |
| 835 | compare_if.End(); |
| 836 | ast_context()->ReplaceValue(); |
| 837 | } |
| 838 | |
| 839 | |
| 840 | void AstGraphBuilder::VisitVariableProxy(VariableProxy* expr) { |
| 841 | Node* value = BuildVariableLoad(expr->var(), expr->id()); |
| 842 | ast_context()->ProduceValue(value); |
| 843 | } |
| 844 | |
| 845 | |
| 846 | void AstGraphBuilder::VisitLiteral(Literal* expr) { |
| 847 | Node* value = jsgraph()->Constant(expr->value()); |
| 848 | ast_context()->ProduceValue(value); |
| 849 | } |
| 850 | |
| 851 | |
| 852 | void AstGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { |
| 853 | Node* closure = GetFunctionClosure(); |
| 854 | |
| 855 | // Create node to materialize a regular expression literal. |
| 856 | Node* literals_array = |
| 857 | BuildLoadObjectField(closure, JSFunction::kLiteralsOffset); |
| 858 | Node* literal_index = jsgraph()->Constant(expr->literal_index()); |
| 859 | Node* pattern = jsgraph()->Constant(expr->pattern()); |
| 860 | Node* flags = jsgraph()->Constant(expr->flags()); |
| 861 | const Operator* op = |
| 862 | javascript()->Runtime(Runtime::kMaterializeRegExpLiteral, 4); |
| 863 | Node* literal = NewNode(op, literals_array, literal_index, pattern, flags); |
| 864 | ast_context()->ProduceValue(literal); |
| 865 | } |
| 866 | |
| 867 | |
| 868 | void AstGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { |
| 869 | Node* closure = GetFunctionClosure(); |
| 870 | |
| 871 | // Create node to deep-copy the literal boilerplate. |
| 872 | expr->BuildConstantProperties(isolate()); |
| 873 | Node* literals_array = |
| 874 | BuildLoadObjectField(closure, JSFunction::kLiteralsOffset); |
| 875 | Node* literal_index = jsgraph()->Constant(expr->literal_index()); |
| 876 | Node* constants = jsgraph()->Constant(expr->constant_properties()); |
| 877 | Node* flags = jsgraph()->Constant(expr->ComputeFlags()); |
| 878 | const Operator* op = javascript()->Runtime(Runtime::kCreateObjectLiteral, 4); |
| 879 | Node* literal = NewNode(op, literals_array, literal_index, constants, flags); |
| 880 | |
| 881 | // The object is expected on the operand stack during computation of the |
| 882 | // property values and is the value of the entire expression. |
| 883 | environment()->Push(literal); |
| 884 | |
| 885 | // Mark all computed expressions that are bound to a key that is shadowed by |
| 886 | // a later occurrence of the same key. For the marked expressions, no store |
| 887 | // code is emitted. |
| 888 | expr->CalculateEmitStore(zone()); |
| 889 | |
| 890 | // Create nodes to store computed values into the literal. |
| 891 | AccessorTable accessor_table(zone()); |
| 892 | for (int i = 0; i < expr->properties()->length(); i++) { |
| 893 | ObjectLiteral::Property* property = expr->properties()->at(i); |
| 894 | if (property->IsCompileTimeValue()) continue; |
| 895 | |
| 896 | Literal* key = property->key(); |
| 897 | switch (property->kind()) { |
| 898 | case ObjectLiteral::Property::CONSTANT: |
| 899 | UNREACHABLE(); |
| 900 | case ObjectLiteral::Property::MATERIALIZED_LITERAL: |
| 901 | DCHECK(!CompileTimeValue::IsCompileTimeValue(property->value())); |
| 902 | // Fall through. |
| 903 | case ObjectLiteral::Property::COMPUTED: { |
| 904 | // It is safe to use [[Put]] here because the boilerplate already |
| 905 | // contains computed properties with an uninitialized value. |
| 906 | if (key->value()->IsInternalizedString()) { |
| 907 | if (property->emit_store()) { |
| 908 | VisitForValue(property->value()); |
| 909 | Node* value = environment()->Pop(); |
| 910 | Unique<Name> name = MakeUnique(key->AsPropertyName()); |
| 911 | Node* store = NewNode(javascript()->StoreNamed(strict_mode(), name), |
| 912 | literal, value); |
| 913 | PrepareFrameState(store, key->id()); |
| 914 | } else { |
| 915 | VisitForEffect(property->value()); |
| 916 | } |
| 917 | break; |
| 918 | } |
| 919 | environment()->Push(literal); // Duplicate receiver. |
| 920 | VisitForValue(property->key()); |
| 921 | VisitForValue(property->value()); |
| 922 | Node* value = environment()->Pop(); |
| 923 | Node* key = environment()->Pop(); |
| 924 | Node* receiver = environment()->Pop(); |
| 925 | if (property->emit_store()) { |
| 926 | Node* strict = jsgraph()->Constant(SLOPPY); |
| 927 | const Operator* op = javascript()->Runtime(Runtime::kSetProperty, 4); |
| 928 | NewNode(op, receiver, key, value, strict); |
| 929 | } |
| 930 | break; |
| 931 | } |
| 932 | case ObjectLiteral::Property::PROTOTYPE: { |
| 933 | environment()->Push(literal); // Duplicate receiver. |
| 934 | VisitForValue(property->value()); |
| 935 | Node* value = environment()->Pop(); |
| 936 | Node* receiver = environment()->Pop(); |
| 937 | if (property->emit_store()) { |
| 938 | const Operator* op = javascript()->Runtime(Runtime::kSetPrototype, 2); |
| 939 | NewNode(op, receiver, value); |
| 940 | } |
| 941 | break; |
| 942 | } |
| 943 | case ObjectLiteral::Property::GETTER: |
| 944 | accessor_table.lookup(key)->second->getter = property->value(); |
| 945 | break; |
| 946 | case ObjectLiteral::Property::SETTER: |
| 947 | accessor_table.lookup(key)->second->setter = property->value(); |
| 948 | break; |
| 949 | } |
| 950 | } |
| 951 | |
| 952 | // Create nodes to define accessors, using only a single call to the runtime |
| 953 | // for each pair of corresponding getters and setters. |
| 954 | for (AccessorTable::Iterator it = accessor_table.begin(); |
| 955 | it != accessor_table.end(); ++it) { |
| 956 | VisitForValue(it->first); |
| 957 | VisitForValueOrNull(it->second->getter); |
| 958 | VisitForValueOrNull(it->second->setter); |
| 959 | Node* setter = environment()->Pop(); |
| 960 | Node* getter = environment()->Pop(); |
| 961 | Node* name = environment()->Pop(); |
| 962 | Node* attr = jsgraph()->Constant(NONE); |
| 963 | const Operator* op = |
| 964 | javascript()->Runtime(Runtime::kDefineAccessorPropertyUnchecked, 5); |
| 965 | Node* call = NewNode(op, literal, name, getter, setter, attr); |
| 966 | PrepareFrameState(call, it->first->id()); |
| 967 | } |
| 968 | |
| 969 | // Transform literals that contain functions to fast properties. |
| 970 | if (expr->has_function()) { |
| 971 | const Operator* op = javascript()->Runtime(Runtime::kToFastProperties, 1); |
| 972 | NewNode(op, literal); |
| 973 | } |
| 974 | |
| 975 | ast_context()->ProduceValue(environment()->Pop()); |
| 976 | } |
| 977 | |
| 978 | |
| 979 | void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
| 980 | Node* closure = GetFunctionClosure(); |
| 981 | |
| 982 | // Create node to deep-copy the literal boilerplate. |
| 983 | expr->BuildConstantElements(isolate()); |
| 984 | Node* literals_array = |
| 985 | BuildLoadObjectField(closure, JSFunction::kLiteralsOffset); |
| 986 | Node* literal_index = jsgraph()->Constant(expr->literal_index()); |
| 987 | Node* constants = jsgraph()->Constant(expr->constant_elements()); |
| 988 | Node* flags = jsgraph()->Constant(expr->ComputeFlags()); |
| 989 | const Operator* op = javascript()->Runtime(Runtime::kCreateArrayLiteral, 4); |
| 990 | Node* literal = NewNode(op, literals_array, literal_index, constants, flags); |
| 991 | |
| 992 | // The array and the literal index are both expected on the operand stack |
| 993 | // during computation of the element values. |
| 994 | environment()->Push(literal); |
| 995 | environment()->Push(literal_index); |
| 996 | |
| 997 | // Create nodes to evaluate all the non-constant subexpressions and to store |
| 998 | // them into the newly cloned array. |
| 999 | for (int i = 0; i < expr->values()->length(); i++) { |
| 1000 | Expression* subexpr = expr->values()->at(i); |
| 1001 | if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; |
| 1002 | |
| 1003 | VisitForValue(subexpr); |
| 1004 | Node* value = environment()->Pop(); |
| 1005 | Node* index = jsgraph()->Constant(i); |
| 1006 | Node* store = NewNode(javascript()->StoreProperty(strict_mode()), literal, |
| 1007 | index, value); |
| 1008 | PrepareFrameState(store, expr->GetIdForElement(i)); |
| 1009 | } |
| 1010 | |
| 1011 | environment()->Pop(); // Array literal index. |
| 1012 | ast_context()->ProduceValue(environment()->Pop()); |
| 1013 | } |
| 1014 | |
| 1015 | |
| 1016 | void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value) { |
| 1017 | DCHECK(expr->IsValidReferenceExpression()); |
| 1018 | |
| 1019 | // Left-hand side can only be a property, a global or a variable slot. |
| 1020 | Property* property = expr->AsProperty(); |
| 1021 | LhsKind assign_type = DetermineLhsKind(expr); |
| 1022 | |
| 1023 | // Evaluate LHS expression and store the value. |
| 1024 | switch (assign_type) { |
| 1025 | case VARIABLE: { |
| 1026 | Variable* var = expr->AsVariableProxy()->var(); |
| 1027 | // TODO(jarin) Fill in the correct bailout id. |
| 1028 | BuildVariableAssignment(var, value, Token::ASSIGN, BailoutId::None()); |
| 1029 | break; |
| 1030 | } |
| 1031 | case NAMED_PROPERTY: { |
| 1032 | environment()->Push(value); |
| 1033 | VisitForValue(property->obj()); |
| 1034 | Node* object = environment()->Pop(); |
| 1035 | value = environment()->Pop(); |
| 1036 | Unique<Name> name = |
| 1037 | MakeUnique(property->key()->AsLiteral()->AsPropertyName()); |
| 1038 | Node* store = |
| 1039 | NewNode(javascript()->StoreNamed(strict_mode(), name), object, value); |
| 1040 | // TODO(jarin) Fill in the correct bailout id. |
| 1041 | PrepareFrameState(store, BailoutId::None()); |
| 1042 | break; |
| 1043 | } |
| 1044 | case KEYED_PROPERTY: { |
| 1045 | environment()->Push(value); |
| 1046 | VisitForValue(property->obj()); |
| 1047 | VisitForValue(property->key()); |
| 1048 | Node* key = environment()->Pop(); |
| 1049 | Node* object = environment()->Pop(); |
| 1050 | value = environment()->Pop(); |
| 1051 | Node* store = NewNode(javascript()->StoreProperty(strict_mode()), object, |
| 1052 | key, value); |
| 1053 | // TODO(jarin) Fill in the correct bailout id. |
| 1054 | PrepareFrameState(store, BailoutId::None()); |
| 1055 | break; |
| 1056 | } |
| 1057 | } |
| 1058 | } |
| 1059 | |
| 1060 | |
| 1061 | void AstGraphBuilder::VisitAssignment(Assignment* expr) { |
| 1062 | DCHECK(expr->target()->IsValidReferenceExpression()); |
| 1063 | |
| 1064 | // Left-hand side can only be a property, a global or a variable slot. |
| 1065 | Property* property = expr->target()->AsProperty(); |
| 1066 | LhsKind assign_type = DetermineLhsKind(expr->target()); |
| 1067 | |
| 1068 | // Evaluate LHS expression. |
| 1069 | switch (assign_type) { |
| 1070 | case VARIABLE: |
| 1071 | // Nothing to do here. |
| 1072 | break; |
| 1073 | case NAMED_PROPERTY: |
| 1074 | VisitForValue(property->obj()); |
| 1075 | break; |
| 1076 | case KEYED_PROPERTY: { |
| 1077 | VisitForValue(property->obj()); |
| 1078 | VisitForValue(property->key()); |
| 1079 | break; |
| 1080 | } |
| 1081 | } |
| 1082 | |
| 1083 | // Evaluate the value and potentially handle compound assignments by loading |
| 1084 | // the left-hand side value and performing a binary operation. |
| 1085 | if (expr->is_compound()) { |
| 1086 | Node* old_value = NULL; |
| 1087 | switch (assign_type) { |
| 1088 | case VARIABLE: { |
| 1089 | Variable* variable = expr->target()->AsVariableProxy()->var(); |
| 1090 | old_value = BuildVariableLoad(variable, expr->target()->id()); |
| 1091 | break; |
| 1092 | } |
| 1093 | case NAMED_PROPERTY: { |
| 1094 | Node* object = environment()->Top(); |
| 1095 | Unique<Name> name = |
| 1096 | MakeUnique(property->key()->AsLiteral()->AsPropertyName()); |
| 1097 | old_value = NewNode(javascript()->LoadNamed(name), object); |
| 1098 | PrepareFrameState(old_value, property->LoadId(), kPushOutput); |
| 1099 | break; |
| 1100 | } |
| 1101 | case KEYED_PROPERTY: { |
| 1102 | Node* key = environment()->Top(); |
| 1103 | Node* object = environment()->Peek(1); |
| 1104 | old_value = NewNode(javascript()->LoadProperty(), object, key); |
| 1105 | PrepareFrameState(old_value, property->LoadId(), kPushOutput); |
| 1106 | break; |
| 1107 | } |
| 1108 | } |
| 1109 | environment()->Push(old_value); |
| 1110 | VisitForValue(expr->value()); |
| 1111 | Node* right = environment()->Pop(); |
| 1112 | Node* left = environment()->Pop(); |
| 1113 | Node* value = BuildBinaryOp(left, right, expr->binary_op()); |
| 1114 | PrepareFrameState(value, expr->binary_operation()->id(), kPushOutput); |
| 1115 | environment()->Push(value); |
| 1116 | } else { |
| 1117 | VisitForValue(expr->value()); |
| 1118 | } |
| 1119 | |
| 1120 | // Store the value. |
| 1121 | Node* value = environment()->Pop(); |
| 1122 | switch (assign_type) { |
| 1123 | case VARIABLE: { |
| 1124 | Variable* variable = expr->target()->AsVariableProxy()->var(); |
| 1125 | BuildVariableAssignment(variable, value, expr->op(), |
| 1126 | expr->AssignmentId()); |
| 1127 | break; |
| 1128 | } |
| 1129 | case NAMED_PROPERTY: { |
| 1130 | Node* object = environment()->Pop(); |
| 1131 | Unique<Name> name = |
| 1132 | MakeUnique(property->key()->AsLiteral()->AsPropertyName()); |
| 1133 | Node* store = |
| 1134 | NewNode(javascript()->StoreNamed(strict_mode(), name), object, value); |
| 1135 | PrepareFrameState(store, expr->AssignmentId()); |
| 1136 | break; |
| 1137 | } |
| 1138 | case KEYED_PROPERTY: { |
| 1139 | Node* key = environment()->Pop(); |
| 1140 | Node* object = environment()->Pop(); |
| 1141 | Node* store = NewNode(javascript()->StoreProperty(strict_mode()), object, |
| 1142 | key, value); |
| 1143 | PrepareFrameState(store, expr->AssignmentId()); |
| 1144 | break; |
| 1145 | } |
| 1146 | } |
| 1147 | |
| 1148 | ast_context()->ProduceValue(value); |
| 1149 | } |
| 1150 | |
| 1151 | |
| 1152 | void AstGraphBuilder::VisitYield(Yield* expr) { |
| 1153 | VisitForValue(expr->generator_object()); |
| 1154 | VisitForValue(expr->expression()); |
| 1155 | environment()->Pop(); |
| 1156 | environment()->Pop(); |
| 1157 | // TODO(turbofan): VisitYield |
| 1158 | ast_context()->ProduceValue(jsgraph()->UndefinedConstant()); |
| 1159 | } |
| 1160 | |
| 1161 | |
| 1162 | void AstGraphBuilder::VisitThrow(Throw* expr) { |
| 1163 | VisitForValue(expr->exception()); |
| 1164 | Node* exception = environment()->Pop(); |
| 1165 | const Operator* op = javascript()->Runtime(Runtime::kThrow, 1); |
| 1166 | Node* value = NewNode(op, exception); |
| 1167 | ast_context()->ProduceValue(value); |
| 1168 | } |
| 1169 | |
| 1170 | |
| 1171 | void AstGraphBuilder::VisitProperty(Property* expr) { |
| 1172 | Node* value; |
| 1173 | if (expr->key()->IsPropertyName()) { |
| 1174 | VisitForValue(expr->obj()); |
| 1175 | Node* object = environment()->Pop(); |
| 1176 | Unique<Name> name = MakeUnique(expr->key()->AsLiteral()->AsPropertyName()); |
| 1177 | value = NewNode(javascript()->LoadNamed(name), object); |
| 1178 | } else { |
| 1179 | VisitForValue(expr->obj()); |
| 1180 | VisitForValue(expr->key()); |
| 1181 | Node* key = environment()->Pop(); |
| 1182 | Node* object = environment()->Pop(); |
| 1183 | value = NewNode(javascript()->LoadProperty(), object, key); |
| 1184 | } |
| 1185 | PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
| 1186 | ast_context()->ProduceValue(value); |
| 1187 | } |
| 1188 | |
| 1189 | |
| 1190 | void AstGraphBuilder::VisitCall(Call* expr) { |
| 1191 | Expression* callee = expr->expression(); |
| 1192 | Call::CallType call_type = expr->GetCallType(isolate()); |
| 1193 | |
| 1194 | // Prepare the callee and the receiver to the function call. This depends on |
| 1195 | // the semantics of the underlying call type. |
| 1196 | CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS; |
| 1197 | Node* receiver_value = NULL; |
| 1198 | Node* callee_value = NULL; |
| 1199 | bool possibly_eval = false; |
| 1200 | switch (call_type) { |
| 1201 | case Call::GLOBAL_CALL: { |
| 1202 | Variable* variable = callee->AsVariableProxy()->var(); |
| 1203 | callee_value = BuildVariableLoad(variable, expr->expression()->id()); |
| 1204 | receiver_value = jsgraph()->UndefinedConstant(); |
| 1205 | break; |
| 1206 | } |
| 1207 | case Call::LOOKUP_SLOT_CALL: { |
| 1208 | Variable* variable = callee->AsVariableProxy()->var(); |
| 1209 | DCHECK(variable->location() == Variable::LOOKUP); |
| 1210 | Node* name = jsgraph()->Constant(variable->name()); |
| 1211 | const Operator* op = javascript()->Runtime(Runtime::kLoadLookupSlot, 2); |
| 1212 | Node* pair = NewNode(op, current_context(), name); |
| 1213 | callee_value = NewNode(common()->Projection(0), pair); |
| 1214 | receiver_value = NewNode(common()->Projection(1), pair); |
| 1215 | break; |
| 1216 | } |
| 1217 | case Call::PROPERTY_CALL: { |
| 1218 | Property* property = callee->AsProperty(); |
| 1219 | VisitForValue(property->obj()); |
| 1220 | Node* object = environment()->Top(); |
| 1221 | if (property->key()->IsPropertyName()) { |
| 1222 | Unique<Name> name = |
| 1223 | MakeUnique(property->key()->AsLiteral()->AsPropertyName()); |
| 1224 | callee_value = NewNode(javascript()->LoadNamed(name), object); |
| 1225 | } else { |
| 1226 | VisitForValue(property->key()); |
| 1227 | Node* key = environment()->Pop(); |
| 1228 | callee_value = NewNode(javascript()->LoadProperty(), object, key); |
| 1229 | } |
| 1230 | PrepareFrameState(callee_value, property->LoadId(), kPushOutput); |
| 1231 | receiver_value = environment()->Pop(); |
| 1232 | // Note that a PROPERTY_CALL requires the receiver to be wrapped into an |
| 1233 | // object for sloppy callees. This could also be modeled explicitly here, |
| 1234 | // thereby obsoleting the need for a flag to the call operator. |
| 1235 | flags = CALL_AS_METHOD; |
| 1236 | break; |
| 1237 | } |
| 1238 | case Call::POSSIBLY_EVAL_CALL: |
| 1239 | possibly_eval = true; |
| 1240 | // Fall through. |
| 1241 | case Call::OTHER_CALL: |
| 1242 | VisitForValue(callee); |
| 1243 | callee_value = environment()->Pop(); |
| 1244 | receiver_value = jsgraph()->UndefinedConstant(); |
| 1245 | break; |
| 1246 | } |
| 1247 | |
| 1248 | // The callee and the receiver both have to be pushed onto the operand stack |
| 1249 | // before arguments are being evaluated. |
| 1250 | environment()->Push(callee_value); |
| 1251 | environment()->Push(receiver_value); |
| 1252 | |
| 1253 | // Evaluate all arguments to the function call, |
| 1254 | ZoneList<Expression*>* args = expr->arguments(); |
| 1255 | VisitForValues(args); |
| 1256 | |
| 1257 | // Resolve callee and receiver for a potential direct eval call. This block |
| 1258 | // will mutate the callee and receiver values pushed onto the environment. |
| 1259 | if (possibly_eval && args->length() > 0) { |
| 1260 | int arg_count = args->length(); |
| 1261 | |
| 1262 | // Extract callee and source string from the environment. |
| 1263 | Node* callee = environment()->Peek(arg_count + 1); |
| 1264 | Node* source = environment()->Peek(arg_count - 1); |
| 1265 | |
| 1266 | // Create node to ask for help resolving potential eval call. This will |
| 1267 | // provide a fully resolved callee and the corresponding receiver. |
| 1268 | Node* function = GetFunctionClosure(); |
| 1269 | Node* receiver = environment()->Lookup(info()->scope()->receiver()); |
| 1270 | Node* strict = jsgraph()->Constant(strict_mode()); |
| 1271 | Node* position = jsgraph()->Constant(info()->scope()->start_position()); |
| 1272 | const Operator* op = |
| 1273 | javascript()->Runtime(Runtime::kResolvePossiblyDirectEval, 6); |
| 1274 | Node* pair = |
| 1275 | NewNode(op, callee, source, function, receiver, strict, position); |
| 1276 | Node* new_callee = NewNode(common()->Projection(0), pair); |
| 1277 | Node* new_receiver = NewNode(common()->Projection(1), pair); |
| 1278 | |
| 1279 | // Patch callee and receiver on the environment. |
| 1280 | environment()->Poke(arg_count + 1, new_callee); |
| 1281 | environment()->Poke(arg_count + 0, new_receiver); |
| 1282 | } |
| 1283 | |
| 1284 | // Create node to perform the function call. |
| 1285 | const Operator* call = javascript()->Call(args->length() + 2, flags); |
| 1286 | Node* value = ProcessArguments(call, args->length() + 2); |
| 1287 | PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
| 1288 | ast_context()->ProduceValue(value); |
| 1289 | } |
| 1290 | |
| 1291 | |
| 1292 | void AstGraphBuilder::VisitCallNew(CallNew* expr) { |
| 1293 | VisitForValue(expr->expression()); |
| 1294 | |
| 1295 | // Evaluate all arguments to the construct call. |
| 1296 | ZoneList<Expression*>* args = expr->arguments(); |
| 1297 | VisitForValues(args); |
| 1298 | |
| 1299 | // Create node to perform the construct call. |
| 1300 | const Operator* call = javascript()->CallNew(args->length() + 1); |
| 1301 | Node* value = ProcessArguments(call, args->length() + 1); |
| 1302 | PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
| 1303 | ast_context()->ProduceValue(value); |
| 1304 | } |
| 1305 | |
| 1306 | |
| 1307 | void AstGraphBuilder::VisitCallJSRuntime(CallRuntime* expr) { |
| 1308 | Handle<String> name = expr->name(); |
| 1309 | |
| 1310 | // The callee and the receiver both have to be pushed onto the operand stack |
| 1311 | // before arguments are being evaluated. |
| 1312 | CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS; |
| 1313 | Node* receiver_value = BuildLoadBuiltinsObject(); |
| 1314 | Unique<String> unique = MakeUnique(name); |
| 1315 | Node* callee_value = NewNode(javascript()->LoadNamed(unique), receiver_value); |
| 1316 | // TODO(jarin): Find/create a bailout id to deoptimize to (crankshaft |
| 1317 | // refuses to optimize functions with jsruntime calls). |
| 1318 | PrepareFrameState(callee_value, BailoutId::None(), kPushOutput); |
| 1319 | environment()->Push(callee_value); |
| 1320 | environment()->Push(receiver_value); |
| 1321 | |
| 1322 | // Evaluate all arguments to the JS runtime call. |
| 1323 | ZoneList<Expression*>* args = expr->arguments(); |
| 1324 | VisitForValues(args); |
| 1325 | |
| 1326 | // Create node to perform the JS runtime call. |
| 1327 | const Operator* call = javascript()->Call(args->length() + 2, flags); |
| 1328 | Node* value = ProcessArguments(call, args->length() + 2); |
| 1329 | PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
| 1330 | ast_context()->ProduceValue(value); |
| 1331 | } |
| 1332 | |
| 1333 | |
| 1334 | void AstGraphBuilder::VisitCallRuntime(CallRuntime* expr) { |
| 1335 | const Runtime::Function* function = expr->function(); |
| 1336 | |
| 1337 | // Handle calls to runtime functions implemented in JavaScript separately as |
| 1338 | // the call follows JavaScript ABI and the callee is statically unknown. |
| 1339 | if (expr->is_jsruntime()) { |
| 1340 | DCHECK(function == NULL && expr->name()->length() > 0); |
| 1341 | return VisitCallJSRuntime(expr); |
| 1342 | } |
| 1343 | |
| 1344 | // Evaluate all arguments to the runtime call. |
| 1345 | ZoneList<Expression*>* args = expr->arguments(); |
| 1346 | VisitForValues(args); |
| 1347 | |
| 1348 | // Create node to perform the runtime call. |
| 1349 | Runtime::FunctionId functionId = function->function_id; |
| 1350 | const Operator* call = javascript()->Runtime(functionId, args->length()); |
| 1351 | Node* value = ProcessArguments(call, args->length()); |
| 1352 | PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
| 1353 | ast_context()->ProduceValue(value); |
| 1354 | } |
| 1355 | |
| 1356 | |
| 1357 | void AstGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { |
| 1358 | switch (expr->op()) { |
| 1359 | case Token::DELETE: |
| 1360 | return VisitDelete(expr); |
| 1361 | case Token::VOID: |
| 1362 | return VisitVoid(expr); |
| 1363 | case Token::TYPEOF: |
| 1364 | return VisitTypeof(expr); |
| 1365 | case Token::NOT: |
| 1366 | return VisitNot(expr); |
| 1367 | default: |
| 1368 | UNREACHABLE(); |
| 1369 | } |
| 1370 | } |
| 1371 | |
| 1372 | |
| 1373 | void AstGraphBuilder::VisitCountOperation(CountOperation* expr) { |
| 1374 | DCHECK(expr->expression()->IsValidReferenceExpression()); |
| 1375 | |
| 1376 | // Left-hand side can only be a property, a global or a variable slot. |
| 1377 | Property* property = expr->expression()->AsProperty(); |
| 1378 | LhsKind assign_type = DetermineLhsKind(expr->expression()); |
| 1379 | |
| 1380 | // Reserve space for result of postfix operation. |
| 1381 | bool is_postfix = expr->is_postfix() && !ast_context()->IsEffect(); |
| 1382 | if (is_postfix) environment()->Push(jsgraph()->UndefinedConstant()); |
| 1383 | |
| 1384 | // Evaluate LHS expression and get old value. |
| 1385 | Node* old_value = NULL; |
| 1386 | int stack_depth = -1; |
| 1387 | switch (assign_type) { |
| 1388 | case VARIABLE: { |
| 1389 | Variable* variable = expr->expression()->AsVariableProxy()->var(); |
| 1390 | old_value = BuildVariableLoad(variable, expr->expression()->id()); |
| 1391 | stack_depth = 0; |
| 1392 | break; |
| 1393 | } |
| 1394 | case NAMED_PROPERTY: { |
| 1395 | VisitForValue(property->obj()); |
| 1396 | Node* object = environment()->Top(); |
| 1397 | Unique<Name> name = |
| 1398 | MakeUnique(property->key()->AsLiteral()->AsPropertyName()); |
| 1399 | old_value = NewNode(javascript()->LoadNamed(name), object); |
| 1400 | PrepareFrameState(old_value, property->LoadId(), kPushOutput); |
| 1401 | stack_depth = 1; |
| 1402 | break; |
| 1403 | } |
| 1404 | case KEYED_PROPERTY: { |
| 1405 | VisitForValue(property->obj()); |
| 1406 | VisitForValue(property->key()); |
| 1407 | Node* key = environment()->Top(); |
| 1408 | Node* object = environment()->Peek(1); |
| 1409 | old_value = NewNode(javascript()->LoadProperty(), object, key); |
| 1410 | PrepareFrameState(old_value, property->LoadId(), kPushOutput); |
| 1411 | stack_depth = 2; |
| 1412 | break; |
| 1413 | } |
| 1414 | } |
| 1415 | |
| 1416 | // Convert old value into a number. |
| 1417 | old_value = NewNode(javascript()->ToNumber(), old_value); |
| 1418 | |
| 1419 | // Save result for postfix expressions at correct stack depth. |
| 1420 | if (is_postfix) environment()->Poke(stack_depth, old_value); |
| 1421 | |
| 1422 | // Create node to perform +1/-1 operation. |
| 1423 | Node* value = |
| 1424 | BuildBinaryOp(old_value, jsgraph()->OneConstant(), expr->binary_op()); |
| 1425 | // TODO(jarin) Insert proper bailout id here (will need to change |
| 1426 | // full code generator). |
| 1427 | PrepareFrameState(value, BailoutId::None()); |
| 1428 | |
| 1429 | // Store the value. |
| 1430 | switch (assign_type) { |
| 1431 | case VARIABLE: { |
| 1432 | Variable* variable = expr->expression()->AsVariableProxy()->var(); |
| 1433 | environment()->Push(value); |
| 1434 | BuildVariableAssignment(variable, value, expr->op(), |
| 1435 | expr->AssignmentId()); |
| 1436 | environment()->Pop(); |
| 1437 | break; |
| 1438 | } |
| 1439 | case NAMED_PROPERTY: { |
| 1440 | Node* object = environment()->Pop(); |
| 1441 | Unique<Name> name = |
| 1442 | MakeUnique(property->key()->AsLiteral()->AsPropertyName()); |
| 1443 | Node* store = |
| 1444 | NewNode(javascript()->StoreNamed(strict_mode(), name), object, value); |
| 1445 | environment()->Push(value); |
| 1446 | PrepareFrameState(store, expr->AssignmentId()); |
| 1447 | environment()->Pop(); |
| 1448 | break; |
| 1449 | } |
| 1450 | case KEYED_PROPERTY: { |
| 1451 | Node* key = environment()->Pop(); |
| 1452 | Node* object = environment()->Pop(); |
| 1453 | Node* store = NewNode(javascript()->StoreProperty(strict_mode()), object, |
| 1454 | key, value); |
| 1455 | environment()->Push(value); |
| 1456 | PrepareFrameState(store, expr->AssignmentId()); |
| 1457 | environment()->Pop(); |
| 1458 | break; |
| 1459 | } |
| 1460 | } |
| 1461 | |
| 1462 | // Restore old value for postfix expressions. |
| 1463 | if (is_postfix) value = environment()->Pop(); |
| 1464 | |
| 1465 | ast_context()->ProduceValue(value); |
| 1466 | } |
| 1467 | |
| 1468 | |
| 1469 | void AstGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) { |
| 1470 | switch (expr->op()) { |
| 1471 | case Token::COMMA: |
| 1472 | return VisitComma(expr); |
| 1473 | case Token::OR: |
| 1474 | case Token::AND: |
| 1475 | return VisitLogicalExpression(expr); |
| 1476 | default: { |
| 1477 | VisitForValue(expr->left()); |
| 1478 | VisitForValue(expr->right()); |
| 1479 | Node* right = environment()->Pop(); |
| 1480 | Node* left = environment()->Pop(); |
| 1481 | Node* value = BuildBinaryOp(left, right, expr->op()); |
| 1482 | PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
| 1483 | ast_context()->ProduceValue(value); |
| 1484 | } |
| 1485 | } |
| 1486 | } |
| 1487 | |
| 1488 | |
| 1489 | void AstGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
| 1490 | const Operator* op; |
| 1491 | switch (expr->op()) { |
| 1492 | case Token::EQ: |
| 1493 | op = javascript()->Equal(); |
| 1494 | break; |
| 1495 | case Token::NE: |
| 1496 | op = javascript()->NotEqual(); |
| 1497 | break; |
| 1498 | case Token::EQ_STRICT: |
| 1499 | op = javascript()->StrictEqual(); |
| 1500 | break; |
| 1501 | case Token::NE_STRICT: |
| 1502 | op = javascript()->StrictNotEqual(); |
| 1503 | break; |
| 1504 | case Token::LT: |
| 1505 | op = javascript()->LessThan(); |
| 1506 | break; |
| 1507 | case Token::GT: |
| 1508 | op = javascript()->GreaterThan(); |
| 1509 | break; |
| 1510 | case Token::LTE: |
| 1511 | op = javascript()->LessThanOrEqual(); |
| 1512 | break; |
| 1513 | case Token::GTE: |
| 1514 | op = javascript()->GreaterThanOrEqual(); |
| 1515 | break; |
| 1516 | case Token::INSTANCEOF: |
| 1517 | op = javascript()->InstanceOf(); |
| 1518 | break; |
| 1519 | case Token::IN: |
| 1520 | op = javascript()->HasProperty(); |
| 1521 | break; |
| 1522 | default: |
| 1523 | op = NULL; |
| 1524 | UNREACHABLE(); |
| 1525 | } |
| 1526 | VisitForValue(expr->left()); |
| 1527 | VisitForValue(expr->right()); |
| 1528 | Node* right = environment()->Pop(); |
| 1529 | Node* left = environment()->Pop(); |
| 1530 | Node* value = NewNode(op, left, right); |
| 1531 | PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
| 1532 | ast_context()->ProduceValue(value); |
| 1533 | } |
| 1534 | |
| 1535 | |
| 1536 | void AstGraphBuilder::VisitThisFunction(ThisFunction* expr) { |
| 1537 | Node* value = GetFunctionClosure(); |
| 1538 | ast_context()->ProduceValue(value); |
| 1539 | } |
| 1540 | |
| 1541 | |
| 1542 | void AstGraphBuilder::VisitSuperReference(SuperReference* expr) { |
| 1543 | UNREACHABLE(); |
| 1544 | } |
| 1545 | |
| 1546 | |
| 1547 | void AstGraphBuilder::VisitCaseClause(CaseClause* expr) { UNREACHABLE(); } |
| 1548 | |
| 1549 | |
| 1550 | void AstGraphBuilder::VisitDeclarations(ZoneList<Declaration*>* declarations) { |
| 1551 | DCHECK(globals()->is_empty()); |
| 1552 | AstVisitor::VisitDeclarations(declarations); |
| 1553 | if (globals()->is_empty()) return; |
| 1554 | Handle<FixedArray> data = |
| 1555 | isolate()->factory()->NewFixedArray(globals()->length(), TENURED); |
| 1556 | for (int i = 0; i < globals()->length(); ++i) data->set(i, *globals()->at(i)); |
| 1557 | int encoded_flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) | |
| 1558 | DeclareGlobalsNativeFlag::encode(info()->is_native()) | |
| 1559 | DeclareGlobalsStrictMode::encode(strict_mode()); |
| 1560 | Node* flags = jsgraph()->Constant(encoded_flags); |
| 1561 | Node* pairs = jsgraph()->Constant(data); |
| 1562 | const Operator* op = javascript()->Runtime(Runtime::kDeclareGlobals, 3); |
| 1563 | NewNode(op, current_context(), pairs, flags); |
| 1564 | globals()->Rewind(0); |
| 1565 | } |
| 1566 | |
| 1567 | |
| 1568 | void AstGraphBuilder::VisitIfNotNull(Statement* stmt) { |
| 1569 | if (stmt == NULL) return; |
| 1570 | Visit(stmt); |
| 1571 | } |
| 1572 | |
| 1573 | |
| 1574 | void AstGraphBuilder::VisitIterationBody(IterationStatement* stmt, |
| 1575 | LoopBuilder* loop, int drop_extra) { |
| 1576 | BreakableScope scope(this, stmt, loop, drop_extra); |
| 1577 | Visit(stmt->body()); |
| 1578 | } |
| 1579 | |
| 1580 | |
| 1581 | void AstGraphBuilder::VisitDelete(UnaryOperation* expr) { |
| 1582 | Node* value; |
| 1583 | if (expr->expression()->IsVariableProxy()) { |
| 1584 | // Delete of an unqualified identifier is only allowed in classic mode but |
| 1585 | // deleting "this" is allowed in all language modes. |
| 1586 | Variable* variable = expr->expression()->AsVariableProxy()->var(); |
| 1587 | DCHECK(strict_mode() == SLOPPY || variable->is_this()); |
| 1588 | value = BuildVariableDelete(variable); |
| 1589 | } else if (expr->expression()->IsProperty()) { |
| 1590 | Property* property = expr->expression()->AsProperty(); |
| 1591 | VisitForValue(property->obj()); |
| 1592 | VisitForValue(property->key()); |
| 1593 | Node* key = environment()->Pop(); |
| 1594 | Node* object = environment()->Pop(); |
| 1595 | value = NewNode(javascript()->DeleteProperty(strict_mode()), object, key); |
| 1596 | } else { |
| 1597 | VisitForEffect(expr->expression()); |
| 1598 | value = jsgraph()->TrueConstant(); |
| 1599 | } |
| 1600 | ast_context()->ProduceValue(value); |
| 1601 | } |
| 1602 | |
| 1603 | |
| 1604 | void AstGraphBuilder::VisitVoid(UnaryOperation* expr) { |
| 1605 | VisitForEffect(expr->expression()); |
| 1606 | Node* value = jsgraph()->UndefinedConstant(); |
| 1607 | ast_context()->ProduceValue(value); |
| 1608 | } |
| 1609 | |
| 1610 | |
| 1611 | void AstGraphBuilder::VisitTypeof(UnaryOperation* expr) { |
| 1612 | Node* operand; |
| 1613 | if (expr->expression()->IsVariableProxy()) { |
| 1614 | // Typeof does not throw a reference error on global variables, hence we |
| 1615 | // perform a non-contextual load in case the operand is a variable proxy. |
| 1616 | Variable* variable = expr->expression()->AsVariableProxy()->var(); |
| 1617 | operand = |
| 1618 | BuildVariableLoad(variable, expr->expression()->id(), NOT_CONTEXTUAL); |
| 1619 | } else { |
| 1620 | VisitForValue(expr->expression()); |
| 1621 | operand = environment()->Pop(); |
| 1622 | } |
| 1623 | Node* value = NewNode(javascript()->TypeOf(), operand); |
| 1624 | ast_context()->ProduceValue(value); |
| 1625 | } |
| 1626 | |
| 1627 | |
| 1628 | void AstGraphBuilder::VisitNot(UnaryOperation* expr) { |
| 1629 | VisitForValue(expr->expression()); |
| 1630 | Node* operand = environment()->Pop(); |
| 1631 | // TODO(mstarzinger): Possible optimization when we are in effect context. |
| 1632 | Node* value = NewNode(javascript()->UnaryNot(), operand); |
| 1633 | ast_context()->ProduceValue(value); |
| 1634 | } |
| 1635 | |
| 1636 | |
| 1637 | void AstGraphBuilder::VisitComma(BinaryOperation* expr) { |
| 1638 | VisitForEffect(expr->left()); |
| 1639 | Visit(expr->right()); |
| 1640 | ast_context()->ReplaceValue(); |
| 1641 | } |
| 1642 | |
| 1643 | |
| 1644 | void AstGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) { |
| 1645 | bool is_logical_and = expr->op() == Token::AND; |
| 1646 | IfBuilder compare_if(this); |
| 1647 | VisitForValue(expr->left()); |
| 1648 | Node* condition = environment()->Top(); |
| 1649 | compare_if.If(BuildToBoolean(condition)); |
| 1650 | compare_if.Then(); |
| 1651 | if (is_logical_and) { |
| 1652 | environment()->Pop(); |
| 1653 | Visit(expr->right()); |
| 1654 | } else if (ast_context()->IsEffect()) { |
| 1655 | environment()->Pop(); |
| 1656 | } |
| 1657 | compare_if.Else(); |
| 1658 | if (!is_logical_and) { |
| 1659 | environment()->Pop(); |
| 1660 | Visit(expr->right()); |
| 1661 | } else if (ast_context()->IsEffect()) { |
| 1662 | environment()->Pop(); |
| 1663 | } |
| 1664 | compare_if.End(); |
| 1665 | ast_context()->ReplaceValue(); |
| 1666 | } |
| 1667 | |
| 1668 | |
| 1669 | Node* AstGraphBuilder::ProcessArguments(const Operator* op, int arity) { |
| 1670 | DCHECK(environment()->stack_height() >= arity); |
| 1671 | Node** all = info()->zone()->NewArray<Node*>(arity); |
| 1672 | for (int i = arity - 1; i >= 0; --i) { |
| 1673 | all[i] = environment()->Pop(); |
| 1674 | } |
| 1675 | Node* value = NewNode(op, arity, all); |
| 1676 | return value; |
| 1677 | } |
| 1678 | |
| 1679 | |
| 1680 | Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context, Node* closure) { |
| 1681 | int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; |
| 1682 | if (heap_slots <= 0) return context; |
| 1683 | set_current_context(context); |
| 1684 | |
| 1685 | // Allocate a new local context. |
| 1686 | const Operator* op = javascript()->CreateFunctionContext(); |
| 1687 | Node* local_context = NewNode(op, closure); |
| 1688 | set_current_context(local_context); |
| 1689 | |
| 1690 | // Copy parameters into context if necessary. |
| 1691 | int num_parameters = info()->scope()->num_parameters(); |
| 1692 | for (int i = 0; i < num_parameters; i++) { |
| 1693 | Variable* variable = info()->scope()->parameter(i); |
| 1694 | if (!variable->IsContextSlot()) continue; |
| 1695 | // Temporary parameter node. The parameter indices are shifted by 1 |
| 1696 | // (receiver is parameter index -1 but environment index 0). |
| 1697 | Node* parameter = NewNode(common()->Parameter(i + 1), graph()->start()); |
| 1698 | // Context variable (at bottom of the context chain). |
| 1699 | DCHECK_EQ(0, info()->scope()->ContextChainLength(variable->scope())); |
| 1700 | const Operator* op = javascript()->StoreContext(0, variable->index()); |
| 1701 | NewNode(op, local_context, parameter); |
| 1702 | } |
| 1703 | |
| 1704 | return local_context; |
| 1705 | } |
| 1706 | |
| 1707 | |
| 1708 | Node* AstGraphBuilder::BuildArgumentsObject(Variable* arguments) { |
| 1709 | if (arguments == NULL) return NULL; |
| 1710 | |
| 1711 | // Allocate and initialize a new arguments object. |
| 1712 | Node* callee = GetFunctionClosure(); |
| 1713 | const Operator* op = javascript()->Runtime(Runtime::kNewArguments, 1); |
| 1714 | Node* object = NewNode(op, callee); |
| 1715 | |
| 1716 | // Assign the object to the arguments variable. |
| 1717 | DCHECK(arguments->IsContextSlot() || arguments->IsStackAllocated()); |
| 1718 | // This should never lazy deopt, so it is fine to send invalid bailout id. |
| 1719 | BuildVariableAssignment(arguments, object, Token::ASSIGN, BailoutId::None()); |
| 1720 | |
| 1721 | return object; |
| 1722 | } |
| 1723 | |
| 1724 | |
| 1725 | Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole, |
| 1726 | Node* not_hole) { |
| 1727 | IfBuilder hole_check(this); |
| 1728 | Node* the_hole = jsgraph()->TheHoleConstant(); |
| 1729 | Node* check = NewNode(javascript()->StrictEqual(), value, the_hole); |
| 1730 | hole_check.If(check); |
| 1731 | hole_check.Then(); |
| 1732 | environment()->Push(for_hole); |
| 1733 | hole_check.Else(); |
| 1734 | environment()->Push(not_hole); |
| 1735 | hole_check.End(); |
| 1736 | return environment()->Pop(); |
| 1737 | } |
| 1738 | |
| 1739 | |
| 1740 | Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable, |
| 1741 | Node* not_hole) { |
| 1742 | IfBuilder hole_check(this); |
| 1743 | Node* the_hole = jsgraph()->TheHoleConstant(); |
| 1744 | Node* check = NewNode(javascript()->StrictEqual(), value, the_hole); |
| 1745 | hole_check.If(check); |
| 1746 | hole_check.Then(); |
| 1747 | environment()->Push(BuildThrowReferenceError(variable)); |
| 1748 | hole_check.Else(); |
| 1749 | environment()->Push(not_hole); |
| 1750 | hole_check.End(); |
| 1751 | return environment()->Pop(); |
| 1752 | } |
| 1753 | |
| 1754 | |
| 1755 | Node* AstGraphBuilder::BuildVariableLoad(Variable* variable, |
| 1756 | BailoutId bailout_id, |
| 1757 | ContextualMode contextual_mode) { |
| 1758 | Node* the_hole = jsgraph()->TheHoleConstant(); |
| 1759 | VariableMode mode = variable->mode(); |
| 1760 | switch (variable->location()) { |
| 1761 | case Variable::UNALLOCATED: { |
| 1762 | // Global var, const, or let variable. |
| 1763 | Node* global = BuildLoadGlobalObject(); |
| 1764 | Unique<Name> name = MakeUnique(variable->name()); |
| 1765 | const Operator* op = javascript()->LoadNamed(name, contextual_mode); |
| 1766 | Node* node = NewNode(op, global); |
| 1767 | PrepareFrameState(node, bailout_id, kPushOutput); |
| 1768 | return node; |
| 1769 | } |
| 1770 | case Variable::PARAMETER: |
| 1771 | case Variable::LOCAL: { |
| 1772 | // Local var, const, or let variable. |
| 1773 | Node* value = environment()->Lookup(variable); |
| 1774 | if (mode == CONST_LEGACY) { |
| 1775 | // Perform check for uninitialized legacy const variables. |
| 1776 | if (value->op() == the_hole->op()) { |
| 1777 | value = jsgraph()->UndefinedConstant(); |
| 1778 | } else if (value->opcode() == IrOpcode::kPhi) { |
| 1779 | Node* undefined = jsgraph()->UndefinedConstant(); |
| 1780 | value = BuildHoleCheckSilent(value, undefined, value); |
| 1781 | } |
| 1782 | } else if (mode == LET || mode == CONST) { |
| 1783 | // Perform check for uninitialized let/const variables. |
| 1784 | if (value->op() == the_hole->op()) { |
| 1785 | value = BuildThrowReferenceError(variable); |
| 1786 | } else if (value->opcode() == IrOpcode::kPhi) { |
| 1787 | value = BuildHoleCheckThrow(value, variable, value); |
| 1788 | } |
| 1789 | } |
| 1790 | return value; |
| 1791 | } |
| 1792 | case Variable::CONTEXT: { |
| 1793 | // Context variable (potentially up the context chain). |
| 1794 | int depth = current_scope()->ContextChainLength(variable->scope()); |
| 1795 | bool immutable = variable->maybe_assigned() == kNotAssigned; |
| 1796 | const Operator* op = |
| 1797 | javascript()->LoadContext(depth, variable->index(), immutable); |
| 1798 | Node* value = NewNode(op, current_context()); |
| 1799 | // TODO(titzer): initialization checks are redundant for already |
| 1800 | // initialized immutable context loads, but only specialization knows. |
| 1801 | // Maybe specializer should be a parameter to the graph builder? |
| 1802 | if (mode == CONST_LEGACY) { |
| 1803 | // Perform check for uninitialized legacy const variables. |
| 1804 | Node* undefined = jsgraph()->UndefinedConstant(); |
| 1805 | value = BuildHoleCheckSilent(value, undefined, value); |
| 1806 | } else if (mode == LET || mode == CONST) { |
| 1807 | // Perform check for uninitialized let/const variables. |
| 1808 | value = BuildHoleCheckThrow(value, variable, value); |
| 1809 | } |
| 1810 | return value; |
| 1811 | } |
| 1812 | case Variable::LOOKUP: { |
| 1813 | // Dynamic lookup of context variable (anywhere in the chain). |
| 1814 | Node* name = jsgraph()->Constant(variable->name()); |
| 1815 | Runtime::FunctionId function_id = |
| 1816 | (contextual_mode == CONTEXTUAL) |
| 1817 | ? Runtime::kLoadLookupSlot |
| 1818 | : Runtime::kLoadLookupSlotNoReferenceError; |
| 1819 | const Operator* op = javascript()->Runtime(function_id, 2); |
| 1820 | Node* pair = NewNode(op, current_context(), name); |
| 1821 | return NewNode(common()->Projection(0), pair); |
| 1822 | } |
| 1823 | } |
| 1824 | UNREACHABLE(); |
| 1825 | return NULL; |
| 1826 | } |
| 1827 | |
| 1828 | |
| 1829 | Node* AstGraphBuilder::BuildVariableDelete(Variable* variable) { |
| 1830 | switch (variable->location()) { |
| 1831 | case Variable::UNALLOCATED: { |
| 1832 | // Global var, const, or let variable. |
| 1833 | Node* global = BuildLoadGlobalObject(); |
| 1834 | Node* name = jsgraph()->Constant(variable->name()); |
| 1835 | const Operator* op = javascript()->DeleteProperty(strict_mode()); |
| 1836 | return NewNode(op, global, name); |
| 1837 | } |
| 1838 | case Variable::PARAMETER: |
| 1839 | case Variable::LOCAL: |
| 1840 | case Variable::CONTEXT: |
| 1841 | // Local var, const, or let variable or context variable. |
| 1842 | return variable->is_this() ? jsgraph()->TrueConstant() |
| 1843 | : jsgraph()->FalseConstant(); |
| 1844 | case Variable::LOOKUP: { |
| 1845 | // Dynamic lookup of context variable (anywhere in the chain). |
| 1846 | Node* name = jsgraph()->Constant(variable->name()); |
| 1847 | const Operator* op = javascript()->Runtime(Runtime::kDeleteLookupSlot, 2); |
| 1848 | return NewNode(op, current_context(), name); |
| 1849 | } |
| 1850 | } |
| 1851 | UNREACHABLE(); |
| 1852 | return NULL; |
| 1853 | } |
| 1854 | |
| 1855 | |
| 1856 | Node* AstGraphBuilder::BuildVariableAssignment(Variable* variable, Node* value, |
| 1857 | Token::Value op, |
| 1858 | BailoutId bailout_id) { |
| 1859 | Node* the_hole = jsgraph()->TheHoleConstant(); |
| 1860 | VariableMode mode = variable->mode(); |
| 1861 | switch (variable->location()) { |
| 1862 | case Variable::UNALLOCATED: { |
| 1863 | // Global var, const, or let variable. |
| 1864 | Node* global = BuildLoadGlobalObject(); |
| 1865 | Unique<Name> name = MakeUnique(variable->name()); |
| 1866 | const Operator* op = javascript()->StoreNamed(strict_mode(), name); |
| 1867 | Node* store = NewNode(op, global, value); |
| 1868 | PrepareFrameState(store, bailout_id); |
| 1869 | return store; |
| 1870 | } |
| 1871 | case Variable::PARAMETER: |
| 1872 | case Variable::LOCAL: |
| 1873 | // Local var, const, or let variable. |
| 1874 | if (mode == CONST_LEGACY && op == Token::INIT_CONST_LEGACY) { |
| 1875 | // Perform an initialization check for legacy const variables. |
| 1876 | Node* current = environment()->Lookup(variable); |
| 1877 | if (current->op() != the_hole->op()) { |
| 1878 | value = BuildHoleCheckSilent(current, value, current); |
| 1879 | } |
| 1880 | } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) { |
| 1881 | // Non-initializing assignments to legacy const is ignored. |
| 1882 | return value; |
| 1883 | } else if (mode == LET && op != Token::INIT_LET) { |
| 1884 | // Perform an initialization check for let declared variables. |
| 1885 | // Also note that the dynamic hole-check is only done to ensure that |
| 1886 | // this does not break in the presence of do-expressions within the |
| 1887 | // temporal dead zone of a let declared variable. |
| 1888 | Node* current = environment()->Lookup(variable); |
| 1889 | if (current->op() == the_hole->op()) { |
| 1890 | value = BuildThrowReferenceError(variable); |
| 1891 | } else if (value->opcode() == IrOpcode::kPhi) { |
| 1892 | value = BuildHoleCheckThrow(current, variable, value); |
| 1893 | } |
| 1894 | } else if (mode == CONST && op != Token::INIT_CONST) { |
| 1895 | // All assignments to const variables are early errors. |
| 1896 | UNREACHABLE(); |
| 1897 | } |
| 1898 | environment()->Bind(variable, value); |
| 1899 | return value; |
| 1900 | case Variable::CONTEXT: { |
| 1901 | // Context variable (potentially up the context chain). |
| 1902 | int depth = current_scope()->ContextChainLength(variable->scope()); |
| 1903 | if (mode == CONST_LEGACY && op == Token::INIT_CONST_LEGACY) { |
| 1904 | // Perform an initialization check for legacy const variables. |
| 1905 | const Operator* op = |
| 1906 | javascript()->LoadContext(depth, variable->index(), false); |
| 1907 | Node* current = NewNode(op, current_context()); |
| 1908 | value = BuildHoleCheckSilent(current, value, current); |
| 1909 | } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) { |
| 1910 | // Non-initializing assignments to legacy const is ignored. |
| 1911 | return value; |
| 1912 | } else if (mode == LET && op != Token::INIT_LET) { |
| 1913 | // Perform an initialization check for let declared variables. |
| 1914 | const Operator* op = |
| 1915 | javascript()->LoadContext(depth, variable->index(), false); |
| 1916 | Node* current = NewNode(op, current_context()); |
| 1917 | value = BuildHoleCheckThrow(current, variable, value); |
| 1918 | } else if (mode == CONST && op != Token::INIT_CONST) { |
| 1919 | // All assignments to const variables are early errors. |
| 1920 | UNREACHABLE(); |
| 1921 | } |
| 1922 | const Operator* op = javascript()->StoreContext(depth, variable->index()); |
| 1923 | return NewNode(op, current_context(), value); |
| 1924 | } |
| 1925 | case Variable::LOOKUP: { |
| 1926 | // Dynamic lookup of context variable (anywhere in the chain). |
| 1927 | Node* name = jsgraph()->Constant(variable->name()); |
| 1928 | Node* strict = jsgraph()->Constant(strict_mode()); |
| 1929 | // TODO(mstarzinger): Use Runtime::kInitializeLegacyConstLookupSlot for |
| 1930 | // initializations of const declarations. |
| 1931 | const Operator* op = javascript()->Runtime(Runtime::kStoreLookupSlot, 4); |
| 1932 | return NewNode(op, value, current_context(), name, strict); |
| 1933 | } |
| 1934 | } |
| 1935 | UNREACHABLE(); |
| 1936 | return NULL; |
| 1937 | } |
| 1938 | |
| 1939 | |
| 1940 | Node* AstGraphBuilder::BuildLoadObjectField(Node* object, int offset) { |
| 1941 | // TODO(sigurds) Use simplified load here once it is ready. |
| 1942 | Node* field_load = NewNode(jsgraph()->machine()->Load(kMachAnyTagged), object, |
| 1943 | jsgraph()->Int32Constant(offset - kHeapObjectTag)); |
| 1944 | return field_load; |
| 1945 | } |
| 1946 | |
| 1947 | |
| 1948 | Node* AstGraphBuilder::BuildLoadBuiltinsObject() { |
| 1949 | Node* global = BuildLoadGlobalObject(); |
| 1950 | Node* builtins = |
| 1951 | BuildLoadObjectField(global, JSGlobalObject::kBuiltinsOffset); |
| 1952 | return builtins; |
| 1953 | } |
| 1954 | |
| 1955 | |
| 1956 | Node* AstGraphBuilder::BuildLoadGlobalObject() { |
| 1957 | Node* context = GetFunctionContext(); |
| 1958 | const Operator* load_op = |
| 1959 | javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true); |
| 1960 | return NewNode(load_op, context); |
| 1961 | } |
| 1962 | |
| 1963 | |
| 1964 | Node* AstGraphBuilder::BuildToBoolean(Node* value) { |
| 1965 | // TODO(mstarzinger): Possible optimization is to NOP for boolean values. |
| 1966 | return NewNode(javascript()->ToBoolean(), value); |
| 1967 | } |
| 1968 | |
| 1969 | |
| 1970 | Node* AstGraphBuilder::BuildThrowReferenceError(Variable* variable) { |
| 1971 | // TODO(mstarzinger): Should be unified with the VisitThrow implementation. |
| 1972 | Node* variable_name = jsgraph()->Constant(variable->name()); |
| 1973 | const Operator* op = javascript()->Runtime(Runtime::kThrowReferenceError, 1); |
| 1974 | return NewNode(op, variable_name); |
| 1975 | } |
| 1976 | |
| 1977 | |
| 1978 | Node* AstGraphBuilder::BuildBinaryOp(Node* left, Node* right, Token::Value op) { |
| 1979 | const Operator* js_op; |
| 1980 | switch (op) { |
| 1981 | case Token::BIT_OR: |
| 1982 | js_op = javascript()->BitwiseOr(); |
| 1983 | break; |
| 1984 | case Token::BIT_AND: |
| 1985 | js_op = javascript()->BitwiseAnd(); |
| 1986 | break; |
| 1987 | case Token::BIT_XOR: |
| 1988 | js_op = javascript()->BitwiseXor(); |
| 1989 | break; |
| 1990 | case Token::SHL: |
| 1991 | js_op = javascript()->ShiftLeft(); |
| 1992 | break; |
| 1993 | case Token::SAR: |
| 1994 | js_op = javascript()->ShiftRight(); |
| 1995 | break; |
| 1996 | case Token::SHR: |
| 1997 | js_op = javascript()->ShiftRightLogical(); |
| 1998 | break; |
| 1999 | case Token::ADD: |
| 2000 | js_op = javascript()->Add(); |
| 2001 | break; |
| 2002 | case Token::SUB: |
| 2003 | js_op = javascript()->Subtract(); |
| 2004 | break; |
| 2005 | case Token::MUL: |
| 2006 | js_op = javascript()->Multiply(); |
| 2007 | break; |
| 2008 | case Token::DIV: |
| 2009 | js_op = javascript()->Divide(); |
| 2010 | break; |
| 2011 | case Token::MOD: |
| 2012 | js_op = javascript()->Modulus(); |
| 2013 | break; |
| 2014 | default: |
| 2015 | UNREACHABLE(); |
| 2016 | js_op = NULL; |
| 2017 | } |
| 2018 | return NewNode(js_op, left, right); |
| 2019 | } |
| 2020 | |
| 2021 | |
| 2022 | void AstGraphBuilder::PrepareFrameState(Node* node, BailoutId ast_id, |
| 2023 | OutputFrameStateCombine combine) { |
| 2024 | if (OperatorProperties::HasFrameStateInput(node->op())) { |
| 2025 | DCHECK(NodeProperties::GetFrameStateInput(node)->opcode() == |
| 2026 | IrOpcode::kDead); |
| 2027 | NodeProperties::ReplaceFrameStateInput( |
| 2028 | node, environment()->Checkpoint(ast_id, combine)); |
| 2029 | } |
| 2030 | } |
| 2031 | |
| 2032 | } |
| 2033 | } |
| 2034 | } // namespace v8::internal::compiler |