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 |