ast.cc revision 3e5fa29ddb82551500b118e9bf37af3966277b70
1// Copyright 2010 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 "ast.h" 31#include "parser.h" 32#include "scopes.h" 33#include "string-stream.h" 34#include "ast-inl.h" 35#include "jump-target-inl.h" 36 37namespace v8 { 38namespace internal { 39 40 41VariableProxySentinel VariableProxySentinel::this_proxy_(true); 42VariableProxySentinel VariableProxySentinel::identifier_proxy_(false); 43ValidLeftHandSideSentinel ValidLeftHandSideSentinel::instance_; 44Property Property::this_property_(VariableProxySentinel::this_proxy(), NULL, 0); 45Call Call::sentinel_(NULL, NULL, 0); 46 47 48// ---------------------------------------------------------------------------- 49// All the Accept member functions for each syntax tree node type. 50 51#define DECL_ACCEPT(type) \ 52 void type::Accept(AstVisitor* v) { v->Visit##type(this); } 53AST_NODE_LIST(DECL_ACCEPT) 54#undef DECL_ACCEPT 55 56 57// ---------------------------------------------------------------------------- 58// Implementation of other node functionality. 59 60Assignment* ExpressionStatement::StatementAsSimpleAssignment() { 61 return (expression()->AsAssignment() != NULL && 62 !expression()->AsAssignment()->is_compound()) 63 ? expression()->AsAssignment() 64 : NULL; 65} 66 67 68CountOperation* ExpressionStatement::StatementAsCountOperation() { 69 return expression()->AsCountOperation(); 70} 71 72 73VariableProxy::VariableProxy(Variable* var) 74 : name_(var->name()), 75 var_(NULL), // Will be set by the call to BindTo. 76 is_this_(var->is_this()), 77 inside_with_(false), 78 is_trivial_(false) { 79 BindTo(var); 80} 81 82 83VariableProxy::VariableProxy(Handle<String> name, 84 bool is_this, 85 bool inside_with) 86 : name_(name), 87 var_(NULL), 88 is_this_(is_this), 89 inside_with_(inside_with), 90 is_trivial_(false) { 91 // names must be canonicalized for fast equality checks 92 ASSERT(name->IsSymbol()); 93} 94 95 96VariableProxy::VariableProxy(bool is_this) 97 : var_(NULL), 98 is_this_(is_this), 99 inside_with_(false), 100 is_trivial_(false) { 101} 102 103 104void VariableProxy::BindTo(Variable* var) { 105 ASSERT(var_ == NULL); // must be bound only once 106 ASSERT(var != NULL); // must bind 107 ASSERT((is_this() && var->is_this()) || name_.is_identical_to(var->name())); 108 // Ideally CONST-ness should match. However, this is very hard to achieve 109 // because we don't know the exact semantics of conflicting (const and 110 // non-const) multiple variable declarations, const vars introduced via 111 // eval() etc. Const-ness and variable declarations are a complete mess 112 // in JS. Sigh... 113 var_ = var; 114 var->set_is_used(true); 115} 116 117 118Token::Value Assignment::binary_op() const { 119 switch (op_) { 120 case Token::ASSIGN_BIT_OR: return Token::BIT_OR; 121 case Token::ASSIGN_BIT_XOR: return Token::BIT_XOR; 122 case Token::ASSIGN_BIT_AND: return Token::BIT_AND; 123 case Token::ASSIGN_SHL: return Token::SHL; 124 case Token::ASSIGN_SAR: return Token::SAR; 125 case Token::ASSIGN_SHR: return Token::SHR; 126 case Token::ASSIGN_ADD: return Token::ADD; 127 case Token::ASSIGN_SUB: return Token::SUB; 128 case Token::ASSIGN_MUL: return Token::MUL; 129 case Token::ASSIGN_DIV: return Token::DIV; 130 case Token::ASSIGN_MOD: return Token::MOD; 131 default: UNREACHABLE(); 132 } 133 return Token::ILLEGAL; 134} 135 136 137bool FunctionLiteral::AllowsLazyCompilation() { 138 return scope()->AllowsLazyCompilation(); 139} 140 141 142ObjectLiteral::Property::Property(Literal* key, Expression* value) { 143 emit_store_ = true; 144 key_ = key; 145 value_ = value; 146 Object* k = *key->handle(); 147 if (k->IsSymbol() && Heap::Proto_symbol()->Equals(String::cast(k))) { 148 kind_ = PROTOTYPE; 149 } else if (value_->AsMaterializedLiteral() != NULL) { 150 kind_ = MATERIALIZED_LITERAL; 151 } else if (value_->AsLiteral() != NULL) { 152 kind_ = CONSTANT; 153 } else { 154 kind_ = COMPUTED; 155 } 156} 157 158 159ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) { 160 emit_store_ = true; 161 key_ = new Literal(value->name()); 162 value_ = value; 163 kind_ = is_getter ? GETTER : SETTER; 164} 165 166 167bool ObjectLiteral::Property::IsCompileTimeValue() { 168 return kind_ == CONSTANT || 169 (kind_ == MATERIALIZED_LITERAL && 170 CompileTimeValue::IsCompileTimeValue(value_)); 171} 172 173 174void ObjectLiteral::Property::set_emit_store(bool emit_store) { 175 emit_store_ = emit_store; 176} 177 178 179bool ObjectLiteral::Property::emit_store() { 180 return emit_store_; 181} 182 183 184bool IsEqualString(void* first, void* second) { 185 Handle<String> h1(reinterpret_cast<String**>(first)); 186 Handle<String> h2(reinterpret_cast<String**>(second)); 187 return (*h1)->Equals(*h2); 188} 189 190bool IsEqualSmi(void* first, void* second) { 191 Handle<Smi> h1(reinterpret_cast<Smi**>(first)); 192 Handle<Smi> h2(reinterpret_cast<Smi**>(second)); 193 return (*h1)->value() == (*h2)->value(); 194} 195 196void ObjectLiteral::CalculateEmitStore() { 197 HashMap properties(&IsEqualString); 198 HashMap elements(&IsEqualSmi); 199 for (int i = this->properties()->length() - 1; i >= 0; i--) { 200 ObjectLiteral::Property* property = this->properties()->at(i); 201 Literal* literal = property->key(); 202 Handle<Object> handle = literal->handle(); 203 204 if (handle->IsNull()) { 205 continue; 206 } 207 208 uint32_t hash; 209 HashMap* table; 210 void* key; 211 uint32_t index; 212 if (handle->IsSymbol()) { 213 Handle<String> name(String::cast(*handle)); 214 ASSERT(!name->AsArrayIndex(&index)); 215 key = name.location(); 216 hash = name->Hash(); 217 table = &properties; 218 } else if (handle->ToArrayIndex(&index)) { 219 key = handle.location(); 220 hash = index; 221 table = &elements; 222 } else { 223 ASSERT(handle->IsNumber()); 224 double num = handle->Number(); 225 char arr[100]; 226 Vector<char> buffer(arr, ARRAY_SIZE(arr)); 227 const char* str = DoubleToCString(num, buffer); 228 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str)); 229 key = name.location(); 230 hash = name->Hash(); 231 table = &properties; 232 } 233 // If the key of a computed property is in the table, do not emit 234 // a store for the property later. 235 if (property->kind() == ObjectLiteral::Property::COMPUTED) { 236 if (table->Lookup(literal, hash, false) != NULL) { 237 property->set_emit_store(false); 238 } 239 } 240 // Add key to the table. 241 table->Lookup(literal, hash, true); 242 } 243} 244 245 246void TargetCollector::AddTarget(BreakTarget* target) { 247 // Add the label to the collector, but discard duplicates. 248 int length = targets_->length(); 249 for (int i = 0; i < length; i++) { 250 if (targets_->at(i) == target) return; 251 } 252 targets_->Add(target); 253} 254 255 256bool Expression::GuaranteedSmiResult() { 257 BinaryOperation* node = AsBinaryOperation(); 258 if (node == NULL) return false; 259 Token::Value op = node->op(); 260 switch (op) { 261 case Token::COMMA: 262 case Token::OR: 263 case Token::AND: 264 case Token::ADD: 265 case Token::SUB: 266 case Token::MUL: 267 case Token::DIV: 268 case Token::MOD: 269 case Token::BIT_XOR: 270 case Token::SHL: 271 return false; 272 break; 273 case Token::BIT_OR: 274 case Token::BIT_AND: { 275 Literal* left = node->left()->AsLiteral(); 276 Literal* right = node->right()->AsLiteral(); 277 if (left != NULL && left->handle()->IsSmi()) { 278 int value = Smi::cast(*left->handle())->value(); 279 if (op == Token::BIT_OR && ((value & 0xc0000000) == 0xc0000000)) { 280 // Result of bitwise or is always a negative Smi. 281 return true; 282 } 283 if (op == Token::BIT_AND && ((value & 0xc0000000) == 0)) { 284 // Result of bitwise and is always a positive Smi. 285 return true; 286 } 287 } 288 if (right != NULL && right->handle()->IsSmi()) { 289 int value = Smi::cast(*right->handle())->value(); 290 if (op == Token::BIT_OR && ((value & 0xc0000000) == 0xc0000000)) { 291 // Result of bitwise or is always a negative Smi. 292 return true; 293 } 294 if (op == Token::BIT_AND && ((value & 0xc0000000) == 0)) { 295 // Result of bitwise and is always a positive Smi. 296 return true; 297 } 298 } 299 return false; 300 break; 301 } 302 case Token::SAR: 303 case Token::SHR: { 304 Literal* right = node->right()->AsLiteral(); 305 if (right != NULL && right->handle()->IsSmi()) { 306 int value = Smi::cast(*right->handle())->value(); 307 if ((value & 0x1F) > 1 || 308 (op == Token::SAR && (value & 0x1F) == 1)) { 309 return true; 310 } 311 } 312 return false; 313 break; 314 } 315 default: 316 UNREACHABLE(); 317 break; 318 } 319 return false; 320} 321 322 323void Expression::CopyAnalysisResultsFrom(Expression* other) { 324 bitfields_ = other->bitfields_; 325 type_ = other->type_; 326} 327 328 329bool UnaryOperation::ResultOverwriteAllowed() { 330 switch (op_) { 331 case Token::BIT_NOT: 332 case Token::SUB: 333 return true; 334 default: 335 return false; 336 } 337} 338 339 340bool BinaryOperation::ResultOverwriteAllowed() { 341 switch (op_) { 342 case Token::COMMA: 343 case Token::OR: 344 case Token::AND: 345 return false; 346 case Token::BIT_OR: 347 case Token::BIT_XOR: 348 case Token::BIT_AND: 349 case Token::SHL: 350 case Token::SAR: 351 case Token::SHR: 352 case Token::ADD: 353 case Token::SUB: 354 case Token::MUL: 355 case Token::DIV: 356 case Token::MOD: 357 return true; 358 default: 359 UNREACHABLE(); 360 } 361 return false; 362} 363 364 365BinaryOperation::BinaryOperation(Assignment* assignment) { 366 ASSERT(assignment->is_compound()); 367 op_ = assignment->binary_op(); 368 left_ = assignment->target(); 369 right_ = assignment->value(); 370 pos_ = assignment->position(); 371 CopyAnalysisResultsFrom(assignment); 372} 373 374 375// ---------------------------------------------------------------------------- 376// Implementation of AstVisitor 377 378bool AstVisitor::CheckStackOverflow() { 379 if (stack_overflow_) return true; 380 StackLimitCheck check; 381 if (!check.HasOverflowed()) return false; 382 return (stack_overflow_ = true); 383} 384 385 386void AstVisitor::VisitDeclarations(ZoneList<Declaration*>* declarations) { 387 for (int i = 0; i < declarations->length(); i++) { 388 Visit(declarations->at(i)); 389 } 390} 391 392 393void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) { 394 for (int i = 0; i < statements->length(); i++) { 395 Visit(statements->at(i)); 396 } 397} 398 399 400void AstVisitor::VisitExpressions(ZoneList<Expression*>* expressions) { 401 for (int i = 0; i < expressions->length(); i++) { 402 // The variable statement visiting code may pass NULL expressions 403 // to this code. Maybe this should be handled by introducing an 404 // undefined expression or literal? Revisit this code if this 405 // changes 406 Expression* expression = expressions->at(i); 407 if (expression != NULL) Visit(expression); 408 } 409} 410 411 412// ---------------------------------------------------------------------------- 413// Regular expressions 414 415#define MAKE_ACCEPT(Name) \ 416 void* RegExp##Name::Accept(RegExpVisitor* visitor, void* data) { \ 417 return visitor->Visit##Name(this, data); \ 418 } 419FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ACCEPT) 420#undef MAKE_ACCEPT 421 422#define MAKE_TYPE_CASE(Name) \ 423 RegExp##Name* RegExpTree::As##Name() { \ 424 return NULL; \ 425 } \ 426 bool RegExpTree::Is##Name() { return false; } 427FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE) 428#undef MAKE_TYPE_CASE 429 430#define MAKE_TYPE_CASE(Name) \ 431 RegExp##Name* RegExp##Name::As##Name() { \ 432 return this; \ 433 } \ 434 bool RegExp##Name::Is##Name() { return true; } 435FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE) 436#undef MAKE_TYPE_CASE 437 438RegExpEmpty RegExpEmpty::kInstance; 439 440 441static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) { 442 Interval result = Interval::Empty(); 443 for (int i = 0; i < children->length(); i++) 444 result = result.Union(children->at(i)->CaptureRegisters()); 445 return result; 446} 447 448 449Interval RegExpAlternative::CaptureRegisters() { 450 return ListCaptureRegisters(nodes()); 451} 452 453 454Interval RegExpDisjunction::CaptureRegisters() { 455 return ListCaptureRegisters(alternatives()); 456} 457 458 459Interval RegExpLookahead::CaptureRegisters() { 460 return body()->CaptureRegisters(); 461} 462 463 464Interval RegExpCapture::CaptureRegisters() { 465 Interval self(StartRegister(index()), EndRegister(index())); 466 return self.Union(body()->CaptureRegisters()); 467} 468 469 470Interval RegExpQuantifier::CaptureRegisters() { 471 return body()->CaptureRegisters(); 472} 473 474 475bool RegExpAssertion::IsAnchoredAtStart() { 476 return type() == RegExpAssertion::START_OF_INPUT; 477} 478 479 480bool RegExpAssertion::IsAnchoredAtEnd() { 481 return type() == RegExpAssertion::END_OF_INPUT; 482} 483 484 485bool RegExpAlternative::IsAnchoredAtStart() { 486 ZoneList<RegExpTree*>* nodes = this->nodes(); 487 for (int i = 0; i < nodes->length(); i++) { 488 RegExpTree* node = nodes->at(i); 489 if (node->IsAnchoredAtStart()) { return true; } 490 if (node->max_match() > 0) { return false; } 491 } 492 return false; 493} 494 495 496bool RegExpAlternative::IsAnchoredAtEnd() { 497 ZoneList<RegExpTree*>* nodes = this->nodes(); 498 for (int i = nodes->length() - 1; i >= 0; i--) { 499 RegExpTree* node = nodes->at(i); 500 if (node->IsAnchoredAtEnd()) { return true; } 501 if (node->max_match() > 0) { return false; } 502 } 503 return false; 504} 505 506 507bool RegExpDisjunction::IsAnchoredAtStart() { 508 ZoneList<RegExpTree*>* alternatives = this->alternatives(); 509 for (int i = 0; i < alternatives->length(); i++) { 510 if (!alternatives->at(i)->IsAnchoredAtStart()) 511 return false; 512 } 513 return true; 514} 515 516 517bool RegExpDisjunction::IsAnchoredAtEnd() { 518 ZoneList<RegExpTree*>* alternatives = this->alternatives(); 519 for (int i = 0; i < alternatives->length(); i++) { 520 if (!alternatives->at(i)->IsAnchoredAtEnd()) 521 return false; 522 } 523 return true; 524} 525 526 527bool RegExpLookahead::IsAnchoredAtStart() { 528 return is_positive() && body()->IsAnchoredAtStart(); 529} 530 531 532bool RegExpCapture::IsAnchoredAtStart() { 533 return body()->IsAnchoredAtStart(); 534} 535 536 537bool RegExpCapture::IsAnchoredAtEnd() { 538 return body()->IsAnchoredAtEnd(); 539} 540 541 542// Convert regular expression trees to a simple sexp representation. 543// This representation should be different from the input grammar 544// in as many cases as possible, to make it more difficult for incorrect 545// parses to look as correct ones which is likely if the input and 546// output formats are alike. 547class RegExpUnparser: public RegExpVisitor { 548 public: 549 RegExpUnparser(); 550 void VisitCharacterRange(CharacterRange that); 551 SmartPointer<const char> ToString() { return stream_.ToCString(); } 552#define MAKE_CASE(Name) virtual void* Visit##Name(RegExp##Name*, void* data); 553 FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE) 554#undef MAKE_CASE 555 private: 556 StringStream* stream() { return &stream_; } 557 HeapStringAllocator alloc_; 558 StringStream stream_; 559}; 560 561 562RegExpUnparser::RegExpUnparser() : stream_(&alloc_) { 563} 564 565 566void* RegExpUnparser::VisitDisjunction(RegExpDisjunction* that, void* data) { 567 stream()->Add("(|"); 568 for (int i = 0; i < that->alternatives()->length(); i++) { 569 stream()->Add(" "); 570 that->alternatives()->at(i)->Accept(this, data); 571 } 572 stream()->Add(")"); 573 return NULL; 574} 575 576 577void* RegExpUnparser::VisitAlternative(RegExpAlternative* that, void* data) { 578 stream()->Add("(:"); 579 for (int i = 0; i < that->nodes()->length(); i++) { 580 stream()->Add(" "); 581 that->nodes()->at(i)->Accept(this, data); 582 } 583 stream()->Add(")"); 584 return NULL; 585} 586 587 588void RegExpUnparser::VisitCharacterRange(CharacterRange that) { 589 stream()->Add("%k", that.from()); 590 if (!that.IsSingleton()) { 591 stream()->Add("-%k", that.to()); 592 } 593} 594 595 596 597void* RegExpUnparser::VisitCharacterClass(RegExpCharacterClass* that, 598 void* data) { 599 if (that->is_negated()) 600 stream()->Add("^"); 601 stream()->Add("["); 602 for (int i = 0; i < that->ranges()->length(); i++) { 603 if (i > 0) stream()->Add(" "); 604 VisitCharacterRange(that->ranges()->at(i)); 605 } 606 stream()->Add("]"); 607 return NULL; 608} 609 610 611void* RegExpUnparser::VisitAssertion(RegExpAssertion* that, void* data) { 612 switch (that->type()) { 613 case RegExpAssertion::START_OF_INPUT: 614 stream()->Add("@^i"); 615 break; 616 case RegExpAssertion::END_OF_INPUT: 617 stream()->Add("@$i"); 618 break; 619 case RegExpAssertion::START_OF_LINE: 620 stream()->Add("@^l"); 621 break; 622 case RegExpAssertion::END_OF_LINE: 623 stream()->Add("@$l"); 624 break; 625 case RegExpAssertion::BOUNDARY: 626 stream()->Add("@b"); 627 break; 628 case RegExpAssertion::NON_BOUNDARY: 629 stream()->Add("@B"); 630 break; 631 } 632 return NULL; 633} 634 635 636void* RegExpUnparser::VisitAtom(RegExpAtom* that, void* data) { 637 stream()->Add("'"); 638 Vector<const uc16> chardata = that->data(); 639 for (int i = 0; i < chardata.length(); i++) { 640 stream()->Add("%k", chardata[i]); 641 } 642 stream()->Add("'"); 643 return NULL; 644} 645 646 647void* RegExpUnparser::VisitText(RegExpText* that, void* data) { 648 if (that->elements()->length() == 1) { 649 that->elements()->at(0).data.u_atom->Accept(this, data); 650 } else { 651 stream()->Add("(!"); 652 for (int i = 0; i < that->elements()->length(); i++) { 653 stream()->Add(" "); 654 that->elements()->at(i).data.u_atom->Accept(this, data); 655 } 656 stream()->Add(")"); 657 } 658 return NULL; 659} 660 661 662void* RegExpUnparser::VisitQuantifier(RegExpQuantifier* that, void* data) { 663 stream()->Add("(# %i ", that->min()); 664 if (that->max() == RegExpTree::kInfinity) { 665 stream()->Add("- "); 666 } else { 667 stream()->Add("%i ", that->max()); 668 } 669 stream()->Add(that->is_greedy() ? "g " : that->is_possessive() ? "p " : "n "); 670 that->body()->Accept(this, data); 671 stream()->Add(")"); 672 return NULL; 673} 674 675 676void* RegExpUnparser::VisitCapture(RegExpCapture* that, void* data) { 677 stream()->Add("(^ "); 678 that->body()->Accept(this, data); 679 stream()->Add(")"); 680 return NULL; 681} 682 683 684void* RegExpUnparser::VisitLookahead(RegExpLookahead* that, void* data) { 685 stream()->Add("(-> "); 686 stream()->Add(that->is_positive() ? "+ " : "- "); 687 that->body()->Accept(this, data); 688 stream()->Add(")"); 689 return NULL; 690} 691 692 693void* RegExpUnparser::VisitBackReference(RegExpBackReference* that, 694 void* data) { 695 stream()->Add("(<- %i)", that->index()); 696 return NULL; 697} 698 699 700void* RegExpUnparser::VisitEmpty(RegExpEmpty* that, void* data) { 701 stream()->Put('%'); 702 return NULL; 703} 704 705 706SmartPointer<const char> RegExpTree::ToString() { 707 RegExpUnparser unparser; 708 Accept(&unparser, NULL); 709 return unparser.ToString(); 710} 711 712 713RegExpDisjunction::RegExpDisjunction(ZoneList<RegExpTree*>* alternatives) 714 : alternatives_(alternatives) { 715 ASSERT(alternatives->length() > 1); 716 RegExpTree* first_alternative = alternatives->at(0); 717 min_match_ = first_alternative->min_match(); 718 max_match_ = first_alternative->max_match(); 719 for (int i = 1; i < alternatives->length(); i++) { 720 RegExpTree* alternative = alternatives->at(i); 721 min_match_ = Min(min_match_, alternative->min_match()); 722 max_match_ = Max(max_match_, alternative->max_match()); 723 } 724} 725 726 727RegExpAlternative::RegExpAlternative(ZoneList<RegExpTree*>* nodes) 728 : nodes_(nodes) { 729 ASSERT(nodes->length() > 1); 730 min_match_ = 0; 731 max_match_ = 0; 732 for (int i = 0; i < nodes->length(); i++) { 733 RegExpTree* node = nodes->at(i); 734 min_match_ += node->min_match(); 735 int node_max_match = node->max_match(); 736 if (kInfinity - max_match_ < node_max_match) { 737 max_match_ = kInfinity; 738 } else { 739 max_match_ += node->max_match(); 740 } 741 } 742} 743 744 745WhileStatement::WhileStatement(ZoneStringList* labels) 746 : IterationStatement(labels), 747 cond_(NULL), 748 may_have_function_literal_(true) { 749} 750 751 752CaseClause::CaseClause(Expression* label, ZoneList<Statement*>* statements) 753 : label_(label), statements_(statements) { 754} 755 756} } // namespace v8::internal 757