| OLD | NEW |
| (Empty) |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "src/ast.h" | |
| 6 | |
| 7 #include <cmath> // For isfinite. | |
| 8 #include "src/builtins.h" | |
| 9 #include "src/code-stubs.h" | |
| 10 #include "src/contexts.h" | |
| 11 #include "src/conversions.h" | |
| 12 #include "src/hashmap.h" | |
| 13 #include "src/parser.h" | |
| 14 #include "src/property.h" | |
| 15 #include "src/property-details.h" | |
| 16 #include "src/scopes.h" | |
| 17 #include "src/string-stream.h" | |
| 18 #include "src/type-info.h" | |
| 19 | |
| 20 namespace v8 { | |
| 21 namespace internal { | |
| 22 | |
| 23 // ---------------------------------------------------------------------------- | |
| 24 // All the Accept member functions for each syntax tree node type. | |
| 25 | |
| 26 #define DECL_ACCEPT(type) \ | |
| 27 void type::Accept(AstVisitor* v) { v->Visit##type(this); } | |
| 28 AST_NODE_LIST(DECL_ACCEPT) | |
| 29 #undef DECL_ACCEPT | |
| 30 | |
| 31 | |
| 32 // ---------------------------------------------------------------------------- | |
| 33 // Implementation of other node functionality. | |
| 34 | |
| 35 | |
| 36 bool Expression::IsSmiLiteral() const { | |
| 37 return IsLiteral() && AsLiteral()->value()->IsSmi(); | |
| 38 } | |
| 39 | |
| 40 | |
| 41 bool Expression::IsStringLiteral() const { | |
| 42 return IsLiteral() && AsLiteral()->value()->IsString(); | |
| 43 } | |
| 44 | |
| 45 | |
| 46 bool Expression::IsNullLiteral() const { | |
| 47 return IsLiteral() && AsLiteral()->value()->IsNull(); | |
| 48 } | |
| 49 | |
| 50 | |
| 51 bool Expression::IsUndefinedLiteral(Isolate* isolate) const { | |
| 52 const VariableProxy* var_proxy = AsVariableProxy(); | |
| 53 if (var_proxy == NULL) return false; | |
| 54 Variable* var = var_proxy->var(); | |
| 55 // The global identifier "undefined" is immutable. Everything | |
| 56 // else could be reassigned. | |
| 57 return var != NULL && var->IsUnallocatedOrGlobalSlot() && | |
| 58 var_proxy->raw_name()->IsOneByteEqualTo("undefined"); | |
| 59 } | |
| 60 | |
| 61 | |
| 62 bool Expression::IsValidReferenceExpressionOrThis() const { | |
| 63 return IsValidReferenceExpression() || | |
| 64 (IsVariableProxy() && AsVariableProxy()->is_this()); | |
| 65 } | |
| 66 | |
| 67 | |
| 68 VariableProxy::VariableProxy(Zone* zone, Variable* var, int start_position, | |
| 69 int end_position) | |
| 70 : Expression(zone, start_position), | |
| 71 bit_field_(IsThisField::encode(var->is_this()) | | |
| 72 IsAssignedField::encode(false) | | |
| 73 IsResolvedField::encode(false)), | |
| 74 raw_name_(var->raw_name()), | |
| 75 end_position_(end_position) { | |
| 76 BindTo(var); | |
| 77 } | |
| 78 | |
| 79 | |
| 80 VariableProxy::VariableProxy(Zone* zone, const AstRawString* name, | |
| 81 Variable::Kind variable_kind, int start_position, | |
| 82 int end_position) | |
| 83 : Expression(zone, start_position), | |
| 84 bit_field_(IsThisField::encode(variable_kind == Variable::THIS) | | |
| 85 IsAssignedField::encode(false) | | |
| 86 IsResolvedField::encode(false)), | |
| 87 raw_name_(name), | |
| 88 end_position_(end_position) {} | |
| 89 | |
| 90 | |
| 91 void VariableProxy::BindTo(Variable* var) { | |
| 92 DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name()); | |
| 93 set_var(var); | |
| 94 set_is_resolved(); | |
| 95 var->set_is_used(); | |
| 96 } | |
| 97 | |
| 98 | |
| 99 void VariableProxy::AssignFeedbackVectorSlots(Isolate* isolate, | |
| 100 FeedbackVectorSpec* spec, | |
| 101 FeedbackVectorSlotCache* cache) { | |
| 102 if (UsesVariableFeedbackSlot()) { | |
| 103 // VariableProxies that point to the same Variable within a function can | |
| 104 // make their loads from the same IC slot. | |
| 105 if (var()->IsUnallocated()) { | |
| 106 ZoneHashMap::Entry* entry = cache->Get(var()); | |
| 107 if (entry != NULL) { | |
| 108 variable_feedback_slot_ = FeedbackVectorSlot( | |
| 109 static_cast<int>(reinterpret_cast<intptr_t>(entry->value))); | |
| 110 return; | |
| 111 } | |
| 112 } | |
| 113 variable_feedback_slot_ = spec->AddLoadICSlot(); | |
| 114 if (var()->IsUnallocated()) { | |
| 115 cache->Put(var(), variable_feedback_slot_); | |
| 116 } | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 | |
| 121 static void AssignVectorSlots(Expression* expr, FeedbackVectorSpec* spec, | |
| 122 FeedbackVectorSlot* out_slot) { | |
| 123 Property* property = expr->AsProperty(); | |
| 124 LhsKind assign_type = Property::GetAssignType(property); | |
| 125 if ((assign_type == VARIABLE && | |
| 126 expr->AsVariableProxy()->var()->IsUnallocated()) || | |
| 127 assign_type == NAMED_PROPERTY || assign_type == KEYED_PROPERTY) { | |
| 128 // TODO(ishell): consider using ICSlotCache for variables here. | |
| 129 FeedbackVectorSlotKind kind = assign_type == KEYED_PROPERTY | |
| 130 ? FeedbackVectorSlotKind::KEYED_STORE_IC | |
| 131 : FeedbackVectorSlotKind::STORE_IC; | |
| 132 *out_slot = spec->AddSlot(kind); | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 | |
| 137 void ForEachStatement::AssignFeedbackVectorSlots( | |
| 138 Isolate* isolate, FeedbackVectorSpec* spec, | |
| 139 FeedbackVectorSlotCache* cache) { | |
| 140 AssignVectorSlots(each(), spec, &each_slot_); | |
| 141 } | |
| 142 | |
| 143 | |
| 144 Assignment::Assignment(Zone* zone, Token::Value op, Expression* target, | |
| 145 Expression* value, int pos) | |
| 146 : Expression(zone, pos), | |
| 147 bit_field_( | |
| 148 IsUninitializedField::encode(false) | KeyTypeField::encode(ELEMENT) | | |
| 149 StoreModeField::encode(STANDARD_STORE) | TokenField::encode(op)), | |
| 150 target_(target), | |
| 151 value_(value), | |
| 152 binary_operation_(NULL) {} | |
| 153 | |
| 154 | |
| 155 void Assignment::AssignFeedbackVectorSlots(Isolate* isolate, | |
| 156 FeedbackVectorSpec* spec, | |
| 157 FeedbackVectorSlotCache* cache) { | |
| 158 AssignVectorSlots(target(), spec, &slot_); | |
| 159 } | |
| 160 | |
| 161 | |
| 162 void CountOperation::AssignFeedbackVectorSlots(Isolate* isolate, | |
| 163 FeedbackVectorSpec* spec, | |
| 164 FeedbackVectorSlotCache* cache) { | |
| 165 AssignVectorSlots(expression(), spec, &slot_); | |
| 166 } | |
| 167 | |
| 168 | |
| 169 Token::Value Assignment::binary_op() const { | |
| 170 switch (op()) { | |
| 171 case Token::ASSIGN_BIT_OR: return Token::BIT_OR; | |
| 172 case Token::ASSIGN_BIT_XOR: return Token::BIT_XOR; | |
| 173 case Token::ASSIGN_BIT_AND: return Token::BIT_AND; | |
| 174 case Token::ASSIGN_SHL: return Token::SHL; | |
| 175 case Token::ASSIGN_SAR: return Token::SAR; | |
| 176 case Token::ASSIGN_SHR: return Token::SHR; | |
| 177 case Token::ASSIGN_ADD: return Token::ADD; | |
| 178 case Token::ASSIGN_SUB: return Token::SUB; | |
| 179 case Token::ASSIGN_MUL: return Token::MUL; | |
| 180 case Token::ASSIGN_DIV: return Token::DIV; | |
| 181 case Token::ASSIGN_MOD: return Token::MOD; | |
| 182 default: UNREACHABLE(); | |
| 183 } | |
| 184 return Token::ILLEGAL; | |
| 185 } | |
| 186 | |
| 187 | |
| 188 bool FunctionLiteral::AllowsLazyCompilation() { | |
| 189 return scope()->AllowsLazyCompilation(); | |
| 190 } | |
| 191 | |
| 192 | |
| 193 bool FunctionLiteral::AllowsLazyCompilationWithoutContext() { | |
| 194 return scope()->AllowsLazyCompilationWithoutContext(); | |
| 195 } | |
| 196 | |
| 197 | |
| 198 int FunctionLiteral::start_position() const { | |
| 199 return scope()->start_position(); | |
| 200 } | |
| 201 | |
| 202 | |
| 203 int FunctionLiteral::end_position() const { | |
| 204 return scope()->end_position(); | |
| 205 } | |
| 206 | |
| 207 | |
| 208 LanguageMode FunctionLiteral::language_mode() const { | |
| 209 return scope()->language_mode(); | |
| 210 } | |
| 211 | |
| 212 | |
| 213 bool FunctionLiteral::NeedsHomeObject(Expression* expr) { | |
| 214 if (expr == nullptr || !expr->IsFunctionLiteral()) return false; | |
| 215 DCHECK_NOT_NULL(expr->AsFunctionLiteral()->scope()); | |
| 216 return expr->AsFunctionLiteral()->scope()->NeedsHomeObject(); | |
| 217 } | |
| 218 | |
| 219 | |
| 220 ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value, | |
| 221 Kind kind, bool is_static, | |
| 222 bool is_computed_name) | |
| 223 : key_(key), | |
| 224 value_(value), | |
| 225 kind_(kind), | |
| 226 emit_store_(true), | |
| 227 is_static_(is_static), | |
| 228 is_computed_name_(is_computed_name) {} | |
| 229 | |
| 230 | |
| 231 ObjectLiteralProperty::ObjectLiteralProperty(AstValueFactory* ast_value_factory, | |
| 232 Expression* key, Expression* value, | |
| 233 bool is_static, | |
| 234 bool is_computed_name) | |
| 235 : key_(key), | |
| 236 value_(value), | |
| 237 emit_store_(true), | |
| 238 is_static_(is_static), | |
| 239 is_computed_name_(is_computed_name) { | |
| 240 if (!is_computed_name && | |
| 241 key->AsLiteral()->raw_value()->EqualsString( | |
| 242 ast_value_factory->proto_string())) { | |
| 243 kind_ = PROTOTYPE; | |
| 244 } else if (value_->AsMaterializedLiteral() != NULL) { | |
| 245 kind_ = MATERIALIZED_LITERAL; | |
| 246 } else if (value_->IsLiteral()) { | |
| 247 kind_ = CONSTANT; | |
| 248 } else { | |
| 249 kind_ = COMPUTED; | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 | |
| 254 void ClassLiteral::AssignFeedbackVectorSlots(Isolate* isolate, | |
| 255 FeedbackVectorSpec* spec, | |
| 256 FeedbackVectorSlotCache* cache) { | |
| 257 // This logic that computes the number of slots needed for vector store | |
| 258 // ICs must mirror FullCodeGenerator::VisitClassLiteral. | |
| 259 if (NeedsProxySlot()) { | |
| 260 slot_ = spec->AddStoreICSlot(); | |
| 261 } | |
| 262 | |
| 263 for (int i = 0; i < properties()->length(); i++) { | |
| 264 ObjectLiteral::Property* property = properties()->at(i); | |
| 265 Expression* value = property->value(); | |
| 266 if (FunctionLiteral::NeedsHomeObject(value)) { | |
| 267 property->SetSlot(spec->AddStoreICSlot()); | |
| 268 } | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 | |
| 273 bool ObjectLiteral::Property::IsCompileTimeValue() { | |
| 274 return kind_ == CONSTANT || | |
| 275 (kind_ == MATERIALIZED_LITERAL && | |
| 276 CompileTimeValue::IsCompileTimeValue(value_)); | |
| 277 } | |
| 278 | |
| 279 | |
| 280 void ObjectLiteral::Property::set_emit_store(bool emit_store) { | |
| 281 emit_store_ = emit_store; | |
| 282 } | |
| 283 | |
| 284 | |
| 285 bool ObjectLiteral::Property::emit_store() { | |
| 286 return emit_store_; | |
| 287 } | |
| 288 | |
| 289 | |
| 290 void ObjectLiteral::AssignFeedbackVectorSlots(Isolate* isolate, | |
| 291 FeedbackVectorSpec* spec, | |
| 292 FeedbackVectorSlotCache* cache) { | |
| 293 // This logic that computes the number of slots needed for vector store | |
| 294 // ics must mirror FullCodeGenerator::VisitObjectLiteral. | |
| 295 int property_index = 0; | |
| 296 for (; property_index < properties()->length(); property_index++) { | |
| 297 ObjectLiteral::Property* property = properties()->at(property_index); | |
| 298 if (property->is_computed_name()) break; | |
| 299 if (property->IsCompileTimeValue()) continue; | |
| 300 | |
| 301 Literal* key = property->key()->AsLiteral(); | |
| 302 Expression* value = property->value(); | |
| 303 switch (property->kind()) { | |
| 304 case ObjectLiteral::Property::CONSTANT: | |
| 305 UNREACHABLE(); | |
| 306 case ObjectLiteral::Property::MATERIALIZED_LITERAL: | |
| 307 // Fall through. | |
| 308 case ObjectLiteral::Property::COMPUTED: | |
| 309 // It is safe to use [[Put]] here because the boilerplate already | |
| 310 // contains computed properties with an uninitialized value. | |
| 311 if (key->value()->IsInternalizedString()) { | |
| 312 if (property->emit_store()) { | |
| 313 property->SetSlot(spec->AddStoreICSlot()); | |
| 314 if (FunctionLiteral::NeedsHomeObject(value)) { | |
| 315 property->SetSlot(spec->AddStoreICSlot(), 1); | |
| 316 } | |
| 317 } | |
| 318 break; | |
| 319 } | |
| 320 if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) { | |
| 321 property->SetSlot(spec->AddStoreICSlot()); | |
| 322 } | |
| 323 break; | |
| 324 case ObjectLiteral::Property::PROTOTYPE: | |
| 325 break; | |
| 326 case ObjectLiteral::Property::GETTER: | |
| 327 if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) { | |
| 328 property->SetSlot(spec->AddStoreICSlot()); | |
| 329 } | |
| 330 break; | |
| 331 case ObjectLiteral::Property::SETTER: | |
| 332 if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) { | |
| 333 property->SetSlot(spec->AddStoreICSlot()); | |
| 334 } | |
| 335 break; | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 for (; property_index < properties()->length(); property_index++) { | |
| 340 ObjectLiteral::Property* property = properties()->at(property_index); | |
| 341 | |
| 342 Expression* value = property->value(); | |
| 343 if (property->kind() != ObjectLiteral::Property::PROTOTYPE) { | |
| 344 if (FunctionLiteral::NeedsHomeObject(value)) { | |
| 345 property->SetSlot(spec->AddStoreICSlot()); | |
| 346 } | |
| 347 } | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 | |
| 352 void ObjectLiteral::CalculateEmitStore(Zone* zone) { | |
| 353 const auto GETTER = ObjectLiteral::Property::GETTER; | |
| 354 const auto SETTER = ObjectLiteral::Property::SETTER; | |
| 355 | |
| 356 ZoneAllocationPolicy allocator(zone); | |
| 357 | |
| 358 ZoneHashMap table(Literal::Match, ZoneHashMap::kDefaultHashMapCapacity, | |
| 359 allocator); | |
| 360 for (int i = properties()->length() - 1; i >= 0; i--) { | |
| 361 ObjectLiteral::Property* property = properties()->at(i); | |
| 362 if (property->is_computed_name()) continue; | |
| 363 if (property->kind() == ObjectLiteral::Property::PROTOTYPE) continue; | |
| 364 Literal* literal = property->key()->AsLiteral(); | |
| 365 DCHECK(!literal->value()->IsNull()); | |
| 366 | |
| 367 // If there is an existing entry do not emit a store unless the previous | |
| 368 // entry was also an accessor. | |
| 369 uint32_t hash = literal->Hash(); | |
| 370 ZoneHashMap::Entry* entry = table.LookupOrInsert(literal, hash, allocator); | |
| 371 if (entry->value != NULL) { | |
| 372 auto previous_kind = | |
| 373 static_cast<ObjectLiteral::Property*>(entry->value)->kind(); | |
| 374 if (!((property->kind() == GETTER && previous_kind == SETTER) || | |
| 375 (property->kind() == SETTER && previous_kind == GETTER))) { | |
| 376 property->set_emit_store(false); | |
| 377 } | |
| 378 } | |
| 379 entry->value = property; | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 | |
| 384 bool ObjectLiteral::IsBoilerplateProperty(ObjectLiteral::Property* property) { | |
| 385 return property != NULL && | |
| 386 property->kind() != ObjectLiteral::Property::PROTOTYPE; | |
| 387 } | |
| 388 | |
| 389 | |
| 390 void ObjectLiteral::BuildConstantProperties(Isolate* isolate) { | |
| 391 if (!constant_properties_.is_null()) return; | |
| 392 | |
| 393 // Allocate a fixed array to hold all the constant properties. | |
| 394 Handle<FixedArray> constant_properties = isolate->factory()->NewFixedArray( | |
| 395 boilerplate_properties_ * 2, TENURED); | |
| 396 | |
| 397 int position = 0; | |
| 398 // Accumulate the value in local variables and store it at the end. | |
| 399 bool is_simple = true; | |
| 400 int depth_acc = 1; | |
| 401 uint32_t max_element_index = 0; | |
| 402 uint32_t elements = 0; | |
| 403 for (int i = 0; i < properties()->length(); i++) { | |
| 404 ObjectLiteral::Property* property = properties()->at(i); | |
| 405 if (!IsBoilerplateProperty(property)) { | |
| 406 is_simple = false; | |
| 407 continue; | |
| 408 } | |
| 409 | |
| 410 if (position == boilerplate_properties_ * 2) { | |
| 411 DCHECK(property->is_computed_name()); | |
| 412 is_simple = false; | |
| 413 break; | |
| 414 } | |
| 415 DCHECK(!property->is_computed_name()); | |
| 416 | |
| 417 MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral(); | |
| 418 if (m_literal != NULL) { | |
| 419 m_literal->BuildConstants(isolate); | |
| 420 if (m_literal->depth() >= depth_acc) depth_acc = m_literal->depth() + 1; | |
| 421 } | |
| 422 | |
| 423 // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined | |
| 424 // value for COMPUTED properties, the real value is filled in at | |
| 425 // runtime. The enumeration order is maintained. | |
| 426 Handle<Object> key = property->key()->AsLiteral()->value(); | |
| 427 Handle<Object> value = GetBoilerplateValue(property->value(), isolate); | |
| 428 | |
| 429 // Ensure objects that may, at any point in time, contain fields with double | |
| 430 // representation are always treated as nested objects. This is true for | |
| 431 // computed fields (value is undefined), and smi and double literals | |
| 432 // (value->IsNumber()). | |
| 433 // TODO(verwaest): Remove once we can store them inline. | |
| 434 if (FLAG_track_double_fields && | |
| 435 (value->IsNumber() || value->IsUninitialized())) { | |
| 436 may_store_doubles_ = true; | |
| 437 } | |
| 438 | |
| 439 is_simple = is_simple && !value->IsUninitialized(); | |
| 440 | |
| 441 // Keep track of the number of elements in the object literal and | |
| 442 // the largest element index. If the largest element index is | |
| 443 // much larger than the number of elements, creating an object | |
| 444 // literal with fast elements will be a waste of space. | |
| 445 uint32_t element_index = 0; | |
| 446 if (key->IsString() | |
| 447 && Handle<String>::cast(key)->AsArrayIndex(&element_index) | |
| 448 && element_index > max_element_index) { | |
| 449 max_element_index = element_index; | |
| 450 elements++; | |
| 451 } else if (key->IsSmi()) { | |
| 452 int key_value = Smi::cast(*key)->value(); | |
| 453 if (key_value > 0 | |
| 454 && static_cast<uint32_t>(key_value) > max_element_index) { | |
| 455 max_element_index = key_value; | |
| 456 } | |
| 457 elements++; | |
| 458 } | |
| 459 | |
| 460 // Add name, value pair to the fixed array. | |
| 461 constant_properties->set(position++, *key); | |
| 462 constant_properties->set(position++, *value); | |
| 463 } | |
| 464 | |
| 465 constant_properties_ = constant_properties; | |
| 466 fast_elements_ = | |
| 467 (max_element_index <= 32) || ((2 * elements) >= max_element_index); | |
| 468 has_elements_ = elements > 0; | |
| 469 set_is_simple(is_simple); | |
| 470 set_depth(depth_acc); | |
| 471 } | |
| 472 | |
| 473 | |
| 474 void ArrayLiteral::BuildConstantElements(Isolate* isolate) { | |
| 475 if (!constant_elements_.is_null()) return; | |
| 476 | |
| 477 int constants_length = | |
| 478 first_spread_index_ >= 0 ? first_spread_index_ : values()->length(); | |
| 479 | |
| 480 // Allocate a fixed array to hold all the object literals. | |
| 481 Handle<JSArray> array = isolate->factory()->NewJSArray( | |
| 482 FAST_HOLEY_SMI_ELEMENTS, constants_length, constants_length, | |
| 483 Strength::WEAK, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); | |
| 484 | |
| 485 // Fill in the literals. | |
| 486 bool is_simple = (first_spread_index_ < 0); | |
| 487 int depth_acc = 1; | |
| 488 bool is_holey = false; | |
| 489 int array_index = 0; | |
| 490 for (; array_index < constants_length; array_index++) { | |
| 491 Expression* element = values()->at(array_index); | |
| 492 DCHECK(!element->IsSpread()); | |
| 493 MaterializedLiteral* m_literal = element->AsMaterializedLiteral(); | |
| 494 if (m_literal != NULL) { | |
| 495 m_literal->BuildConstants(isolate); | |
| 496 if (m_literal->depth() + 1 > depth_acc) { | |
| 497 depth_acc = m_literal->depth() + 1; | |
| 498 } | |
| 499 } | |
| 500 | |
| 501 // New handle scope here, needs to be after BuildContants(). | |
| 502 HandleScope scope(isolate); | |
| 503 Handle<Object> boilerplate_value = GetBoilerplateValue(element, isolate); | |
| 504 if (boilerplate_value->IsTheHole()) { | |
| 505 is_holey = true; | |
| 506 continue; | |
| 507 } | |
| 508 | |
| 509 if (boilerplate_value->IsUninitialized()) { | |
| 510 boilerplate_value = handle(Smi::FromInt(0), isolate); | |
| 511 is_simple = false; | |
| 512 } | |
| 513 | |
| 514 JSObject::AddDataElement(array, array_index, boilerplate_value, NONE) | |
| 515 .Assert(); | |
| 516 } | |
| 517 | |
| 518 JSObject::ValidateElements(array); | |
| 519 Handle<FixedArrayBase> element_values(array->elements()); | |
| 520 | |
| 521 // Simple and shallow arrays can be lazily copied, we transform the | |
| 522 // elements array to a copy-on-write array. | |
| 523 if (is_simple && depth_acc == 1 && array_index > 0 && | |
| 524 array->HasFastSmiOrObjectElements()) { | |
| 525 element_values->set_map(isolate->heap()->fixed_cow_array_map()); | |
| 526 } | |
| 527 | |
| 528 // Remember both the literal's constant values as well as the ElementsKind | |
| 529 // in a 2-element FixedArray. | |
| 530 Handle<FixedArray> literals = isolate->factory()->NewFixedArray(2, TENURED); | |
| 531 | |
| 532 ElementsKind kind = array->GetElementsKind(); | |
| 533 kind = is_holey ? GetHoleyElementsKind(kind) : GetPackedElementsKind(kind); | |
| 534 | |
| 535 literals->set(0, Smi::FromInt(kind)); | |
| 536 literals->set(1, *element_values); | |
| 537 | |
| 538 constant_elements_ = literals; | |
| 539 set_is_simple(is_simple); | |
| 540 set_depth(depth_acc); | |
| 541 } | |
| 542 | |
| 543 | |
| 544 void ArrayLiteral::AssignFeedbackVectorSlots(Isolate* isolate, | |
| 545 FeedbackVectorSpec* spec, | |
| 546 FeedbackVectorSlotCache* cache) { | |
| 547 // This logic that computes the number of slots needed for vector store | |
| 548 // ics must mirror FullCodeGenerator::VisitArrayLiteral. | |
| 549 int array_index = 0; | |
| 550 for (; array_index < values()->length(); array_index++) { | |
| 551 Expression* subexpr = values()->at(array_index); | |
| 552 if (subexpr->IsSpread()) break; | |
| 553 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; | |
| 554 | |
| 555 // We'll reuse the same literal slot for all of the non-constant | |
| 556 // subexpressions that use a keyed store IC. | |
| 557 literal_slot_ = spec->AddKeyedStoreICSlot(); | |
| 558 return; | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 | |
| 563 Handle<Object> MaterializedLiteral::GetBoilerplateValue(Expression* expression, | |
| 564 Isolate* isolate) { | |
| 565 if (expression->IsLiteral()) { | |
| 566 return expression->AsLiteral()->value(); | |
| 567 } | |
| 568 if (CompileTimeValue::IsCompileTimeValue(expression)) { | |
| 569 return CompileTimeValue::GetValue(isolate, expression); | |
| 570 } | |
| 571 return isolate->factory()->uninitialized_value(); | |
| 572 } | |
| 573 | |
| 574 | |
| 575 void MaterializedLiteral::BuildConstants(Isolate* isolate) { | |
| 576 if (IsArrayLiteral()) { | |
| 577 return AsArrayLiteral()->BuildConstantElements(isolate); | |
| 578 } | |
| 579 if (IsObjectLiteral()) { | |
| 580 return AsObjectLiteral()->BuildConstantProperties(isolate); | |
| 581 } | |
| 582 DCHECK(IsRegExpLiteral()); | |
| 583 DCHECK(depth() >= 1); // Depth should be initialized. | |
| 584 } | |
| 585 | |
| 586 | |
| 587 void UnaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { | |
| 588 // TODO(olivf) If this Operation is used in a test context, then the | |
| 589 // expression has a ToBoolean stub and we want to collect the type | |
| 590 // information. However the GraphBuilder expects it to be on the instruction | |
| 591 // corresponding to the TestContext, therefore we have to store it here and | |
| 592 // not on the operand. | |
| 593 set_to_boolean_types(oracle->ToBooleanTypes(expression()->test_id())); | |
| 594 } | |
| 595 | |
| 596 | |
| 597 void BinaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { | |
| 598 // TODO(olivf) If this Operation is used in a test context, then the right | |
| 599 // hand side has a ToBoolean stub and we want to collect the type information. | |
| 600 // However the GraphBuilder expects it to be on the instruction corresponding | |
| 601 // to the TestContext, therefore we have to store it here and not on the | |
| 602 // right hand operand. | |
| 603 set_to_boolean_types(oracle->ToBooleanTypes(right()->test_id())); | |
| 604 } | |
| 605 | |
| 606 | |
| 607 static bool IsTypeof(Expression* expr) { | |
| 608 UnaryOperation* maybe_unary = expr->AsUnaryOperation(); | |
| 609 return maybe_unary != NULL && maybe_unary->op() == Token::TYPEOF; | |
| 610 } | |
| 611 | |
| 612 | |
| 613 // Check for the pattern: typeof <expression> equals <string literal>. | |
| 614 static bool MatchLiteralCompareTypeof(Expression* left, | |
| 615 Token::Value op, | |
| 616 Expression* right, | |
| 617 Expression** expr, | |
| 618 Handle<String>* check) { | |
| 619 if (IsTypeof(left) && right->IsStringLiteral() && Token::IsEqualityOp(op)) { | |
| 620 *expr = left->AsUnaryOperation()->expression(); | |
| 621 *check = Handle<String>::cast(right->AsLiteral()->value()); | |
| 622 return true; | |
| 623 } | |
| 624 return false; | |
| 625 } | |
| 626 | |
| 627 | |
| 628 bool CompareOperation::IsLiteralCompareTypeof(Expression** expr, | |
| 629 Handle<String>* check) { | |
| 630 return MatchLiteralCompareTypeof(left_, op_, right_, expr, check) || | |
| 631 MatchLiteralCompareTypeof(right_, op_, left_, expr, check); | |
| 632 } | |
| 633 | |
| 634 | |
| 635 static bool IsVoidOfLiteral(Expression* expr) { | |
| 636 UnaryOperation* maybe_unary = expr->AsUnaryOperation(); | |
| 637 return maybe_unary != NULL && | |
| 638 maybe_unary->op() == Token::VOID && | |
| 639 maybe_unary->expression()->IsLiteral(); | |
| 640 } | |
| 641 | |
| 642 | |
| 643 // Check for the pattern: void <literal> equals <expression> or | |
| 644 // undefined equals <expression> | |
| 645 static bool MatchLiteralCompareUndefined(Expression* left, | |
| 646 Token::Value op, | |
| 647 Expression* right, | |
| 648 Expression** expr, | |
| 649 Isolate* isolate) { | |
| 650 if (IsVoidOfLiteral(left) && Token::IsEqualityOp(op)) { | |
| 651 *expr = right; | |
| 652 return true; | |
| 653 } | |
| 654 if (left->IsUndefinedLiteral(isolate) && Token::IsEqualityOp(op)) { | |
| 655 *expr = right; | |
| 656 return true; | |
| 657 } | |
| 658 return false; | |
| 659 } | |
| 660 | |
| 661 | |
| 662 bool CompareOperation::IsLiteralCompareUndefined( | |
| 663 Expression** expr, Isolate* isolate) { | |
| 664 return MatchLiteralCompareUndefined(left_, op_, right_, expr, isolate) || | |
| 665 MatchLiteralCompareUndefined(right_, op_, left_, expr, isolate); | |
| 666 } | |
| 667 | |
| 668 | |
| 669 // Check for the pattern: null equals <expression> | |
| 670 static bool MatchLiteralCompareNull(Expression* left, | |
| 671 Token::Value op, | |
| 672 Expression* right, | |
| 673 Expression** expr) { | |
| 674 if (left->IsNullLiteral() && Token::IsEqualityOp(op)) { | |
| 675 *expr = right; | |
| 676 return true; | |
| 677 } | |
| 678 return false; | |
| 679 } | |
| 680 | |
| 681 | |
| 682 bool CompareOperation::IsLiteralCompareNull(Expression** expr) { | |
| 683 return MatchLiteralCompareNull(left_, op_, right_, expr) || | |
| 684 MatchLiteralCompareNull(right_, op_, left_, expr); | |
| 685 } | |
| 686 | |
| 687 | |
| 688 // ---------------------------------------------------------------------------- | |
| 689 // Inlining support | |
| 690 | |
| 691 bool Declaration::IsInlineable() const { | |
| 692 return proxy()->var()->IsStackAllocated(); | |
| 693 } | |
| 694 | |
| 695 bool FunctionDeclaration::IsInlineable() const { | |
| 696 return false; | |
| 697 } | |
| 698 | |
| 699 | |
| 700 // ---------------------------------------------------------------------------- | |
| 701 // Recording of type feedback | |
| 702 | |
| 703 // TODO(rossberg): all RecordTypeFeedback functions should disappear | |
| 704 // once we use the common type field in the AST consistently. | |
| 705 | |
| 706 void Expression::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { | |
| 707 set_to_boolean_types(oracle->ToBooleanTypes(test_id())); | |
| 708 } | |
| 709 | |
| 710 | |
| 711 bool Call::IsUsingCallFeedbackICSlot(Isolate* isolate) const { | |
| 712 CallType call_type = GetCallType(isolate); | |
| 713 if (call_type == POSSIBLY_EVAL_CALL) { | |
| 714 return false; | |
| 715 } | |
| 716 return true; | |
| 717 } | |
| 718 | |
| 719 | |
| 720 bool Call::IsUsingCallFeedbackSlot(Isolate* isolate) const { | |
| 721 // SuperConstructorCall uses a CallConstructStub, which wants | |
| 722 // a Slot, in addition to any IC slots requested elsewhere. | |
| 723 return GetCallType(isolate) == SUPER_CALL; | |
| 724 } | |
| 725 | |
| 726 | |
| 727 void Call::AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, | |
| 728 FeedbackVectorSlotCache* cache) { | |
| 729 if (IsUsingCallFeedbackICSlot(isolate)) { | |
| 730 ic_slot_ = spec->AddCallICSlot(); | |
| 731 } | |
| 732 if (IsUsingCallFeedbackSlot(isolate)) { | |
| 733 stub_slot_ = spec->AddGeneralSlot(); | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 | |
| 738 Call::CallType Call::GetCallType(Isolate* isolate) const { | |
| 739 VariableProxy* proxy = expression()->AsVariableProxy(); | |
| 740 if (proxy != NULL) { | |
| 741 if (proxy->var()->is_possibly_eval(isolate)) { | |
| 742 return POSSIBLY_EVAL_CALL; | |
| 743 } else if (proxy->var()->IsUnallocatedOrGlobalSlot()) { | |
| 744 return GLOBAL_CALL; | |
| 745 } else if (proxy->var()->IsLookupSlot()) { | |
| 746 return LOOKUP_SLOT_CALL; | |
| 747 } | |
| 748 } | |
| 749 | |
| 750 if (expression()->IsSuperCallReference()) return SUPER_CALL; | |
| 751 | |
| 752 Property* property = expression()->AsProperty(); | |
| 753 if (property != nullptr) { | |
| 754 bool is_super = property->IsSuperAccess(); | |
| 755 if (property->key()->IsPropertyName()) { | |
| 756 return is_super ? NAMED_SUPER_PROPERTY_CALL : NAMED_PROPERTY_CALL; | |
| 757 } else { | |
| 758 return is_super ? KEYED_SUPER_PROPERTY_CALL : KEYED_PROPERTY_CALL; | |
| 759 } | |
| 760 } | |
| 761 | |
| 762 return OTHER_CALL; | |
| 763 } | |
| 764 | |
| 765 | |
| 766 // ---------------------------------------------------------------------------- | |
| 767 // Implementation of AstVisitor | |
| 768 | |
| 769 void AstVisitor::VisitDeclarations(ZoneList<Declaration*>* declarations) { | |
| 770 for (int i = 0; i < declarations->length(); i++) { | |
| 771 Visit(declarations->at(i)); | |
| 772 } | |
| 773 } | |
| 774 | |
| 775 | |
| 776 void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) { | |
| 777 for (int i = 0; i < statements->length(); i++) { | |
| 778 Statement* stmt = statements->at(i); | |
| 779 Visit(stmt); | |
| 780 if (stmt->IsJump()) break; | |
| 781 } | |
| 782 } | |
| 783 | |
| 784 | |
| 785 void AstVisitor::VisitExpressions(ZoneList<Expression*>* expressions) { | |
| 786 for (int i = 0; i < expressions->length(); i++) { | |
| 787 // The variable statement visiting code may pass NULL expressions | |
| 788 // to this code. Maybe this should be handled by introducing an | |
| 789 // undefined expression or literal? Revisit this code if this | |
| 790 // changes | |
| 791 Expression* expression = expressions->at(i); | |
| 792 if (expression != NULL) Visit(expression); | |
| 793 } | |
| 794 } | |
| 795 | |
| 796 | |
| 797 // ---------------------------------------------------------------------------- | |
| 798 // Regular expressions | |
| 799 | |
| 800 #define MAKE_ACCEPT(Name) \ | |
| 801 void* RegExp##Name::Accept(RegExpVisitor* visitor, void* data) { \ | |
| 802 return visitor->Visit##Name(this, data); \ | |
| 803 } | |
| 804 FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ACCEPT) | |
| 805 #undef MAKE_ACCEPT | |
| 806 | |
| 807 #define MAKE_TYPE_CASE(Name) \ | |
| 808 RegExp##Name* RegExpTree::As##Name() { \ | |
| 809 return NULL; \ | |
| 810 } \ | |
| 811 bool RegExpTree::Is##Name() { return false; } | |
| 812 FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE) | |
| 813 #undef MAKE_TYPE_CASE | |
| 814 | |
| 815 #define MAKE_TYPE_CASE(Name) \ | |
| 816 RegExp##Name* RegExp##Name::As##Name() { \ | |
| 817 return this; \ | |
| 818 } \ | |
| 819 bool RegExp##Name::Is##Name() { return true; } | |
| 820 FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE) | |
| 821 #undef MAKE_TYPE_CASE | |
| 822 | |
| 823 | |
| 824 static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) { | |
| 825 Interval result = Interval::Empty(); | |
| 826 for (int i = 0; i < children->length(); i++) | |
| 827 result = result.Union(children->at(i)->CaptureRegisters()); | |
| 828 return result; | |
| 829 } | |
| 830 | |
| 831 | |
| 832 Interval RegExpAlternative::CaptureRegisters() { | |
| 833 return ListCaptureRegisters(nodes()); | |
| 834 } | |
| 835 | |
| 836 | |
| 837 Interval RegExpDisjunction::CaptureRegisters() { | |
| 838 return ListCaptureRegisters(alternatives()); | |
| 839 } | |
| 840 | |
| 841 | |
| 842 Interval RegExpLookaround::CaptureRegisters() { | |
| 843 return body()->CaptureRegisters(); | |
| 844 } | |
| 845 | |
| 846 | |
| 847 Interval RegExpCapture::CaptureRegisters() { | |
| 848 Interval self(StartRegister(index()), EndRegister(index())); | |
| 849 return self.Union(body()->CaptureRegisters()); | |
| 850 } | |
| 851 | |
| 852 | |
| 853 Interval RegExpQuantifier::CaptureRegisters() { | |
| 854 return body()->CaptureRegisters(); | |
| 855 } | |
| 856 | |
| 857 | |
| 858 bool RegExpAssertion::IsAnchoredAtStart() { | |
| 859 return assertion_type() == RegExpAssertion::START_OF_INPUT; | |
| 860 } | |
| 861 | |
| 862 | |
| 863 bool RegExpAssertion::IsAnchoredAtEnd() { | |
| 864 return assertion_type() == RegExpAssertion::END_OF_INPUT; | |
| 865 } | |
| 866 | |
| 867 | |
| 868 bool RegExpAlternative::IsAnchoredAtStart() { | |
| 869 ZoneList<RegExpTree*>* nodes = this->nodes(); | |
| 870 for (int i = 0; i < nodes->length(); i++) { | |
| 871 RegExpTree* node = nodes->at(i); | |
| 872 if (node->IsAnchoredAtStart()) { return true; } | |
| 873 if (node->max_match() > 0) { return false; } | |
| 874 } | |
| 875 return false; | |
| 876 } | |
| 877 | |
| 878 | |
| 879 bool RegExpAlternative::IsAnchoredAtEnd() { | |
| 880 ZoneList<RegExpTree*>* nodes = this->nodes(); | |
| 881 for (int i = nodes->length() - 1; i >= 0; i--) { | |
| 882 RegExpTree* node = nodes->at(i); | |
| 883 if (node->IsAnchoredAtEnd()) { return true; } | |
| 884 if (node->max_match() > 0) { return false; } | |
| 885 } | |
| 886 return false; | |
| 887 } | |
| 888 | |
| 889 | |
| 890 bool RegExpDisjunction::IsAnchoredAtStart() { | |
| 891 ZoneList<RegExpTree*>* alternatives = this->alternatives(); | |
| 892 for (int i = 0; i < alternatives->length(); i++) { | |
| 893 if (!alternatives->at(i)->IsAnchoredAtStart()) | |
| 894 return false; | |
| 895 } | |
| 896 return true; | |
| 897 } | |
| 898 | |
| 899 | |
| 900 bool RegExpDisjunction::IsAnchoredAtEnd() { | |
| 901 ZoneList<RegExpTree*>* alternatives = this->alternatives(); | |
| 902 for (int i = 0; i < alternatives->length(); i++) { | |
| 903 if (!alternatives->at(i)->IsAnchoredAtEnd()) | |
| 904 return false; | |
| 905 } | |
| 906 return true; | |
| 907 } | |
| 908 | |
| 909 | |
| 910 bool RegExpLookaround::IsAnchoredAtStart() { | |
| 911 return is_positive() && type() == LOOKAHEAD && body()->IsAnchoredAtStart(); | |
| 912 } | |
| 913 | |
| 914 | |
| 915 bool RegExpCapture::IsAnchoredAtStart() { | |
| 916 return body()->IsAnchoredAtStart(); | |
| 917 } | |
| 918 | |
| 919 | |
| 920 bool RegExpCapture::IsAnchoredAtEnd() { | |
| 921 return body()->IsAnchoredAtEnd(); | |
| 922 } | |
| 923 | |
| 924 | |
| 925 // Convert regular expression trees to a simple sexp representation. | |
| 926 // This representation should be different from the input grammar | |
| 927 // in as many cases as possible, to make it more difficult for incorrect | |
| 928 // parses to look as correct ones which is likely if the input and | |
| 929 // output formats are alike. | |
| 930 class RegExpUnparser final : public RegExpVisitor { | |
| 931 public: | |
| 932 RegExpUnparser(std::ostream& os, Zone* zone) : os_(os), zone_(zone) {} | |
| 933 void VisitCharacterRange(CharacterRange that); | |
| 934 #define MAKE_CASE(Name) void* Visit##Name(RegExp##Name*, void* data) override; | |
| 935 FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE) | |
| 936 #undef MAKE_CASE | |
| 937 private: | |
| 938 std::ostream& os_; | |
| 939 Zone* zone_; | |
| 940 }; | |
| 941 | |
| 942 | |
| 943 void* RegExpUnparser::VisitDisjunction(RegExpDisjunction* that, void* data) { | |
| 944 os_ << "(|"; | |
| 945 for (int i = 0; i < that->alternatives()->length(); i++) { | |
| 946 os_ << " "; | |
| 947 that->alternatives()->at(i)->Accept(this, data); | |
| 948 } | |
| 949 os_ << ")"; | |
| 950 return NULL; | |
| 951 } | |
| 952 | |
| 953 | |
| 954 void* RegExpUnparser::VisitAlternative(RegExpAlternative* that, void* data) { | |
| 955 os_ << "(:"; | |
| 956 for (int i = 0; i < that->nodes()->length(); i++) { | |
| 957 os_ << " "; | |
| 958 that->nodes()->at(i)->Accept(this, data); | |
| 959 } | |
| 960 os_ << ")"; | |
| 961 return NULL; | |
| 962 } | |
| 963 | |
| 964 | |
| 965 void RegExpUnparser::VisitCharacterRange(CharacterRange that) { | |
| 966 os_ << AsUC16(that.from()); | |
| 967 if (!that.IsSingleton()) { | |
| 968 os_ << "-" << AsUC16(that.to()); | |
| 969 } | |
| 970 } | |
| 971 | |
| 972 | |
| 973 | |
| 974 void* RegExpUnparser::VisitCharacterClass(RegExpCharacterClass* that, | |
| 975 void* data) { | |
| 976 if (that->is_negated()) os_ << "^"; | |
| 977 os_ << "["; | |
| 978 for (int i = 0; i < that->ranges(zone_)->length(); i++) { | |
| 979 if (i > 0) os_ << " "; | |
| 980 VisitCharacterRange(that->ranges(zone_)->at(i)); | |
| 981 } | |
| 982 os_ << "]"; | |
| 983 return NULL; | |
| 984 } | |
| 985 | |
| 986 | |
| 987 void* RegExpUnparser::VisitAssertion(RegExpAssertion* that, void* data) { | |
| 988 switch (that->assertion_type()) { | |
| 989 case RegExpAssertion::START_OF_INPUT: | |
| 990 os_ << "@^i"; | |
| 991 break; | |
| 992 case RegExpAssertion::END_OF_INPUT: | |
| 993 os_ << "@$i"; | |
| 994 break; | |
| 995 case RegExpAssertion::START_OF_LINE: | |
| 996 os_ << "@^l"; | |
| 997 break; | |
| 998 case RegExpAssertion::END_OF_LINE: | |
| 999 os_ << "@$l"; | |
| 1000 break; | |
| 1001 case RegExpAssertion::BOUNDARY: | |
| 1002 os_ << "@b"; | |
| 1003 break; | |
| 1004 case RegExpAssertion::NON_BOUNDARY: | |
| 1005 os_ << "@B"; | |
| 1006 break; | |
| 1007 } | |
| 1008 return NULL; | |
| 1009 } | |
| 1010 | |
| 1011 | |
| 1012 void* RegExpUnparser::VisitAtom(RegExpAtom* that, void* data) { | |
| 1013 os_ << "'"; | |
| 1014 Vector<const uc16> chardata = that->data(); | |
| 1015 for (int i = 0; i < chardata.length(); i++) { | |
| 1016 os_ << AsUC16(chardata[i]); | |
| 1017 } | |
| 1018 os_ << "'"; | |
| 1019 return NULL; | |
| 1020 } | |
| 1021 | |
| 1022 | |
| 1023 void* RegExpUnparser::VisitText(RegExpText* that, void* data) { | |
| 1024 if (that->elements()->length() == 1) { | |
| 1025 that->elements()->at(0).tree()->Accept(this, data); | |
| 1026 } else { | |
| 1027 os_ << "(!"; | |
| 1028 for (int i = 0; i < that->elements()->length(); i++) { | |
| 1029 os_ << " "; | |
| 1030 that->elements()->at(i).tree()->Accept(this, data); | |
| 1031 } | |
| 1032 os_ << ")"; | |
| 1033 } | |
| 1034 return NULL; | |
| 1035 } | |
| 1036 | |
| 1037 | |
| 1038 void* RegExpUnparser::VisitQuantifier(RegExpQuantifier* that, void* data) { | |
| 1039 os_ << "(# " << that->min() << " "; | |
| 1040 if (that->max() == RegExpTree::kInfinity) { | |
| 1041 os_ << "- "; | |
| 1042 } else { | |
| 1043 os_ << that->max() << " "; | |
| 1044 } | |
| 1045 os_ << (that->is_greedy() ? "g " : that->is_possessive() ? "p " : "n "); | |
| 1046 that->body()->Accept(this, data); | |
| 1047 os_ << ")"; | |
| 1048 return NULL; | |
| 1049 } | |
| 1050 | |
| 1051 | |
| 1052 void* RegExpUnparser::VisitCapture(RegExpCapture* that, void* data) { | |
| 1053 os_ << "(^ "; | |
| 1054 that->body()->Accept(this, data); | |
| 1055 os_ << ")"; | |
| 1056 return NULL; | |
| 1057 } | |
| 1058 | |
| 1059 | |
| 1060 void* RegExpUnparser::VisitLookaround(RegExpLookaround* that, void* data) { | |
| 1061 os_ << "("; | |
| 1062 os_ << (that->type() == RegExpLookaround::LOOKAHEAD ? "->" : "<-"); | |
| 1063 os_ << (that->is_positive() ? " + " : " - "); | |
| 1064 that->body()->Accept(this, data); | |
| 1065 os_ << ")"; | |
| 1066 return NULL; | |
| 1067 } | |
| 1068 | |
| 1069 | |
| 1070 void* RegExpUnparser::VisitBackReference(RegExpBackReference* that, | |
| 1071 void* data) { | |
| 1072 os_ << "(<- " << that->index() << ")"; | |
| 1073 return NULL; | |
| 1074 } | |
| 1075 | |
| 1076 | |
| 1077 void* RegExpUnparser::VisitEmpty(RegExpEmpty* that, void* data) { | |
| 1078 os_ << '%'; | |
| 1079 return NULL; | |
| 1080 } | |
| 1081 | |
| 1082 | |
| 1083 std::ostream& RegExpTree::Print(std::ostream& os, Zone* zone) { // NOLINT | |
| 1084 RegExpUnparser unparser(os, zone); | |
| 1085 Accept(&unparser, NULL); | |
| 1086 return os; | |
| 1087 } | |
| 1088 | |
| 1089 | |
| 1090 RegExpDisjunction::RegExpDisjunction(ZoneList<RegExpTree*>* alternatives) | |
| 1091 : alternatives_(alternatives) { | |
| 1092 DCHECK(alternatives->length() > 1); | |
| 1093 RegExpTree* first_alternative = alternatives->at(0); | |
| 1094 min_match_ = first_alternative->min_match(); | |
| 1095 max_match_ = first_alternative->max_match(); | |
| 1096 for (int i = 1; i < alternatives->length(); i++) { | |
| 1097 RegExpTree* alternative = alternatives->at(i); | |
| 1098 min_match_ = Min(min_match_, alternative->min_match()); | |
| 1099 max_match_ = Max(max_match_, alternative->max_match()); | |
| 1100 } | |
| 1101 } | |
| 1102 | |
| 1103 | |
| 1104 static int IncreaseBy(int previous, int increase) { | |
| 1105 if (RegExpTree::kInfinity - previous < increase) { | |
| 1106 return RegExpTree::kInfinity; | |
| 1107 } else { | |
| 1108 return previous + increase; | |
| 1109 } | |
| 1110 } | |
| 1111 | |
| 1112 RegExpAlternative::RegExpAlternative(ZoneList<RegExpTree*>* nodes) | |
| 1113 : nodes_(nodes) { | |
| 1114 DCHECK(nodes->length() > 1); | |
| 1115 min_match_ = 0; | |
| 1116 max_match_ = 0; | |
| 1117 for (int i = 0; i < nodes->length(); i++) { | |
| 1118 RegExpTree* node = nodes->at(i); | |
| 1119 int node_min_match = node->min_match(); | |
| 1120 min_match_ = IncreaseBy(min_match_, node_min_match); | |
| 1121 int node_max_match = node->max_match(); | |
| 1122 max_match_ = IncreaseBy(max_match_, node_max_match); | |
| 1123 } | |
| 1124 } | |
| 1125 | |
| 1126 | |
| 1127 CaseClause::CaseClause(Zone* zone, Expression* label, | |
| 1128 ZoneList<Statement*>* statements, int pos) | |
| 1129 : Expression(zone, pos), | |
| 1130 label_(label), | |
| 1131 statements_(statements), | |
| 1132 compare_type_(Type::None(zone)) {} | |
| 1133 | |
| 1134 | |
| 1135 uint32_t Literal::Hash() { | |
| 1136 return raw_value()->IsString() | |
| 1137 ? raw_value()->AsString()->hash() | |
| 1138 : ComputeLongHash(double_to_uint64(raw_value()->AsNumber())); | |
| 1139 } | |
| 1140 | |
| 1141 | |
| 1142 // static | |
| 1143 bool Literal::Match(void* literal1, void* literal2) { | |
| 1144 const AstValue* x = static_cast<Literal*>(literal1)->raw_value(); | |
| 1145 const AstValue* y = static_cast<Literal*>(literal2)->raw_value(); | |
| 1146 return (x->IsString() && y->IsString() && x->AsString() == y->AsString()) || | |
| 1147 (x->IsNumber() && y->IsNumber() && x->AsNumber() == y->AsNumber()); | |
| 1148 } | |
| 1149 | |
| 1150 | |
| 1151 } // namespace internal | |
| 1152 } // namespace v8 | |
| OLD | NEW |