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/ast.h" |
6 | |
7 | #include <cmath> // For isfinite. |
8 | #include <vector> |
9 | |
10 | #include "src/ast/prettyprinter.h" |
11 | #include "src/ast/scopes.h" |
12 | #include "src/base/hashmap.h" |
13 | #include "src/builtins/builtins-constructor.h" |
14 | #include "src/builtins/builtins.h" |
15 | #include "src/contexts.h" |
16 | #include "src/conversions-inl.h" |
17 | #include "src/double.h" |
18 | #include "src/elements.h" |
19 | #include "src/objects-inl.h" |
20 | #include "src/objects/literal-objects-inl.h" |
21 | #include "src/objects/literal-objects.h" |
22 | #include "src/objects/map.h" |
23 | #include "src/property-details.h" |
24 | #include "src/property.h" |
25 | #include "src/string-stream.h" |
26 | #include "src/zone/zone-list-inl.h" |
27 | |
28 | namespace v8 { |
29 | namespace internal { |
30 | |
31 | // ---------------------------------------------------------------------------- |
32 | // Implementation of other node functionality. |
33 | |
34 | #ifdef DEBUG |
35 | |
36 | static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) { |
37 | switch (idx) { |
38 | #define NATIVE_CONTEXT_FIELDS_IDX(NAME, Type, name) \ |
39 | case Context::NAME: \ |
40 | return #name; |
41 | |
42 | NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELDS_IDX) |
43 | #undef NATIVE_CONTEXT_FIELDS_IDX |
44 | |
45 | default: |
46 | break; |
47 | } |
48 | |
49 | return "UnknownIntrinsicIndex" ; |
50 | } |
51 | |
52 | void AstNode::Print() { Print(Isolate::Current()); } |
53 | |
54 | void AstNode::Print(Isolate* isolate) { |
55 | AllowHandleDereference allow_deref; |
56 | AstPrinter::PrintOut(isolate, this); |
57 | } |
58 | |
59 | |
60 | #endif // DEBUG |
61 | |
62 | #define RETURN_NODE(Node) \ |
63 | case k##Node: \ |
64 | return static_cast<Node*>(this); |
65 | |
66 | IterationStatement* AstNode::AsIterationStatement() { |
67 | switch (node_type()) { |
68 | ITERATION_NODE_LIST(RETURN_NODE); |
69 | default: |
70 | return nullptr; |
71 | } |
72 | } |
73 | |
74 | MaterializedLiteral* AstNode::AsMaterializedLiteral() { |
75 | switch (node_type()) { |
76 | LITERAL_NODE_LIST(RETURN_NODE); |
77 | default: |
78 | return nullptr; |
79 | } |
80 | } |
81 | |
82 | #undef RETURN_NODE |
83 | |
84 | bool Expression::IsSmiLiteral() const { |
85 | return IsLiteral() && AsLiteral()->type() == Literal::kSmi; |
86 | } |
87 | |
88 | bool Expression::IsNumberLiteral() const { |
89 | return IsLiteral() && AsLiteral()->IsNumber(); |
90 | } |
91 | |
92 | bool Expression::IsStringLiteral() const { |
93 | return IsLiteral() && AsLiteral()->type() == Literal::kString; |
94 | } |
95 | |
96 | bool Expression::IsPropertyName() const { |
97 | return IsLiteral() && AsLiteral()->IsPropertyName(); |
98 | } |
99 | |
100 | bool Expression::IsNullLiteral() const { |
101 | return IsLiteral() && AsLiteral()->type() == Literal::kNull; |
102 | } |
103 | |
104 | bool Expression::IsTheHoleLiteral() const { |
105 | return IsLiteral() && AsLiteral()->type() == Literal::kTheHole; |
106 | } |
107 | |
108 | bool Expression::IsCompileTimeValue() { |
109 | if (IsLiteral()) return true; |
110 | MaterializedLiteral* literal = AsMaterializedLiteral(); |
111 | if (literal == nullptr) return false; |
112 | return literal->IsSimple(); |
113 | } |
114 | |
115 | bool Expression::IsUndefinedLiteral() const { |
116 | if (IsLiteral() && AsLiteral()->type() == Literal::kUndefined) return true; |
117 | |
118 | const VariableProxy* var_proxy = AsVariableProxy(); |
119 | if (var_proxy == nullptr) return false; |
120 | Variable* var = var_proxy->var(); |
121 | // The global identifier "undefined" is immutable. Everything |
122 | // else could be reassigned. |
123 | return var != nullptr && var->IsUnallocated() && |
124 | var_proxy->raw_name()->IsOneByteEqualTo("undefined" ); |
125 | } |
126 | |
127 | bool Expression::ToBooleanIsTrue() const { |
128 | return IsLiteral() && AsLiteral()->ToBooleanIsTrue(); |
129 | } |
130 | |
131 | bool Expression::ToBooleanIsFalse() const { |
132 | return IsLiteral() && AsLiteral()->ToBooleanIsFalse(); |
133 | } |
134 | |
135 | bool Expression::IsValidReferenceExpression() const { |
136 | return IsProperty() || |
137 | (IsVariableProxy() && AsVariableProxy()->IsValidReferenceExpression()); |
138 | } |
139 | |
140 | bool Expression::IsAnonymousFunctionDefinition() const { |
141 | return (IsFunctionLiteral() && |
142 | AsFunctionLiteral()->IsAnonymousFunctionDefinition()) || |
143 | (IsClassLiteral() && |
144 | AsClassLiteral()->IsAnonymousFunctionDefinition()); |
145 | } |
146 | |
147 | bool Expression::IsConciseMethodDefinition() const { |
148 | return IsFunctionLiteral() && IsConciseMethod(AsFunctionLiteral()->kind()); |
149 | } |
150 | |
151 | bool Expression::IsAccessorFunctionDefinition() const { |
152 | return IsFunctionLiteral() && IsAccessorFunction(AsFunctionLiteral()->kind()); |
153 | } |
154 | |
155 | VariableProxy::VariableProxy(Variable* var, int start_position) |
156 | : Expression(start_position, kVariableProxy), |
157 | raw_name_(var->raw_name()), |
158 | next_unresolved_(nullptr) { |
159 | DCHECK(!var->is_this()); |
160 | bit_field_ |= IsAssignedField::encode(false) | |
161 | IsResolvedField::encode(false) | |
162 | HoleCheckModeField::encode(HoleCheckMode::kElided); |
163 | BindTo(var); |
164 | } |
165 | |
166 | VariableProxy::VariableProxy(const VariableProxy* copy_from) |
167 | : Expression(copy_from->position(), kVariableProxy), |
168 | next_unresolved_(nullptr) { |
169 | bit_field_ = copy_from->bit_field_; |
170 | DCHECK(!copy_from->is_resolved()); |
171 | raw_name_ = copy_from->raw_name_; |
172 | } |
173 | |
174 | void VariableProxy::BindTo(Variable* var) { |
175 | DCHECK_EQ(raw_name(), var->raw_name()); |
176 | set_var(var); |
177 | set_is_resolved(); |
178 | var->set_is_used(); |
179 | if (is_assigned()) var->set_maybe_assigned(); |
180 | } |
181 | |
182 | Assignment::Assignment(NodeType node_type, Token::Value op, Expression* target, |
183 | Expression* value, int pos) |
184 | : Expression(pos, node_type), target_(target), value_(value) { |
185 | bit_field_ |= TokenField::encode(op); |
186 | } |
187 | |
188 | void FunctionLiteral::set_inferred_name(Handle<String> inferred_name) { |
189 | DCHECK(!inferred_name.is_null()); |
190 | inferred_name_ = inferred_name; |
191 | DCHECK(raw_inferred_name_ == nullptr || raw_inferred_name_->IsEmpty()); |
192 | raw_inferred_name_ = nullptr; |
193 | scope()->set_has_inferred_function_name(true); |
194 | } |
195 | |
196 | void FunctionLiteral::set_raw_inferred_name( |
197 | const AstConsString* raw_inferred_name) { |
198 | DCHECK_NOT_NULL(raw_inferred_name); |
199 | raw_inferred_name_ = raw_inferred_name; |
200 | DCHECK(inferred_name_.is_null()); |
201 | inferred_name_ = Handle<String>(); |
202 | scope()->set_has_inferred_function_name(true); |
203 | } |
204 | |
205 | bool FunctionLiteral::ShouldEagerCompile() const { |
206 | return scope()->ShouldEagerCompile(); |
207 | } |
208 | |
209 | void FunctionLiteral::SetShouldEagerCompile() { |
210 | scope()->set_should_eager_compile(); |
211 | } |
212 | |
213 | bool FunctionLiteral::AllowsLazyCompilation() { |
214 | return scope()->AllowsLazyCompilation(); |
215 | } |
216 | |
217 | bool FunctionLiteral::SafeToSkipArgumentsAdaptor() const { |
218 | // TODO(bmeurer,verwaest): The --fast_calls_with_arguments_mismatches |
219 | // is mostly here for checking the real-world impact of the calling |
220 | // convention. There's not really a point in turning off this flag |
221 | // otherwise, so we should remove it at some point, when we're done |
222 | // with the experiments (https://crbug.com/v8/8895). |
223 | return FLAG_fast_calls_with_arguments_mismatches && |
224 | language_mode() == LanguageMode::kStrict && |
225 | scope()->arguments() == nullptr && |
226 | scope()->rest_parameter() == nullptr; |
227 | } |
228 | |
229 | Handle<String> FunctionLiteral::name(Isolate* isolate) const { |
230 | return raw_name_ ? raw_name_->string() : isolate->factory()->empty_string(); |
231 | } |
232 | |
233 | int FunctionLiteral::start_position() const { |
234 | return scope()->start_position(); |
235 | } |
236 | |
237 | |
238 | int FunctionLiteral::end_position() const { |
239 | return scope()->end_position(); |
240 | } |
241 | |
242 | |
243 | LanguageMode FunctionLiteral::language_mode() const { |
244 | return scope()->language_mode(); |
245 | } |
246 | |
247 | FunctionKind FunctionLiteral::kind() const { return scope()->function_kind(); } |
248 | |
249 | bool FunctionLiteral::NeedsHomeObject(Expression* expr) { |
250 | if (expr == nullptr || !expr->IsFunctionLiteral()) return false; |
251 | DCHECK_NOT_NULL(expr->AsFunctionLiteral()->scope()); |
252 | return expr->AsFunctionLiteral()->scope()->NeedsHomeObject(); |
253 | } |
254 | |
255 | std::unique_ptr<char[]> FunctionLiteral::GetDebugName() const { |
256 | const AstConsString* cons_string; |
257 | if (raw_name_ != nullptr && !raw_name_->IsEmpty()) { |
258 | cons_string = raw_name_; |
259 | } else if (raw_inferred_name_ != nullptr && !raw_inferred_name_->IsEmpty()) { |
260 | cons_string = raw_inferred_name_; |
261 | } else if (!inferred_name_.is_null()) { |
262 | AllowHandleDereference allow_deref; |
263 | return inferred_name_->ToCString(); |
264 | } else { |
265 | char* empty_str = new char[1]; |
266 | empty_str[0] = 0; |
267 | return std::unique_ptr<char[]>(empty_str); |
268 | } |
269 | |
270 | // TODO(rmcilroy): Deal with two-character strings. |
271 | std::vector<char> result_vec; |
272 | std::forward_list<const AstRawString*> strings = cons_string->ToRawStrings(); |
273 | for (const AstRawString* string : strings) { |
274 | if (!string->is_one_byte()) break; |
275 | for (int i = 0; i < string->length(); i++) { |
276 | result_vec.push_back(string->raw_data()[i]); |
277 | } |
278 | } |
279 | std::unique_ptr<char[]> result(new char[result_vec.size() + 1]); |
280 | memcpy(result.get(), result_vec.data(), result_vec.size()); |
281 | result[result_vec.size()] = '\0'; |
282 | return result; |
283 | } |
284 | |
285 | ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value, |
286 | Kind kind, bool is_computed_name) |
287 | : LiteralProperty(key, value, is_computed_name), |
288 | kind_(kind), |
289 | emit_store_(true) {} |
290 | |
291 | ObjectLiteralProperty::ObjectLiteralProperty(AstValueFactory* ast_value_factory, |
292 | Expression* key, Expression* value, |
293 | bool is_computed_name) |
294 | : LiteralProperty(key, value, is_computed_name), emit_store_(true) { |
295 | if (!is_computed_name && key->AsLiteral()->IsString() && |
296 | key->AsLiteral()->AsRawString() == ast_value_factory->proto_string()) { |
297 | kind_ = PROTOTYPE; |
298 | } else if (value_->AsMaterializedLiteral() != nullptr) { |
299 | kind_ = MATERIALIZED_LITERAL; |
300 | } else if (value_->IsLiteral()) { |
301 | kind_ = CONSTANT; |
302 | } else { |
303 | kind_ = COMPUTED; |
304 | } |
305 | } |
306 | |
307 | bool LiteralProperty::NeedsSetFunctionName() const { |
308 | return is_computed_name() && (value_->IsAnonymousFunctionDefinition() || |
309 | value_->IsConciseMethodDefinition() || |
310 | value_->IsAccessorFunctionDefinition()); |
311 | } |
312 | |
313 | ClassLiteralProperty::ClassLiteralProperty(Expression* key, Expression* value, |
314 | Kind kind, bool is_static, |
315 | bool is_computed_name, |
316 | bool is_private) |
317 | : LiteralProperty(key, value, is_computed_name), |
318 | kind_(kind), |
319 | is_static_(is_static), |
320 | is_private_(is_private), |
321 | private_or_computed_name_var_(nullptr) {} |
322 | |
323 | bool ObjectLiteral::Property::IsCompileTimeValue() const { |
324 | return kind_ == CONSTANT || |
325 | (kind_ == MATERIALIZED_LITERAL && value_->IsCompileTimeValue()); |
326 | } |
327 | |
328 | |
329 | void ObjectLiteral::Property::set_emit_store(bool emit_store) { |
330 | emit_store_ = emit_store; |
331 | } |
332 | |
333 | bool ObjectLiteral::Property::emit_store() const { return emit_store_; } |
334 | |
335 | void ObjectLiteral::CalculateEmitStore(Zone* zone) { |
336 | const auto GETTER = ObjectLiteral::Property::GETTER; |
337 | const auto SETTER = ObjectLiteral::Property::SETTER; |
338 | |
339 | ZoneAllocationPolicy allocator(zone); |
340 | |
341 | CustomMatcherZoneHashMap table( |
342 | Literal::Match, ZoneHashMap::kDefaultHashMapCapacity, allocator); |
343 | for (int i = properties()->length() - 1; i >= 0; i--) { |
344 | ObjectLiteral::Property* property = properties()->at(i); |
345 | if (property->is_computed_name()) continue; |
346 | if (property->IsPrototype()) continue; |
347 | Literal* literal = property->key()->AsLiteral(); |
348 | DCHECK(!literal->IsNullLiteral()); |
349 | |
350 | uint32_t hash = literal->Hash(); |
351 | ZoneHashMap::Entry* entry = table.LookupOrInsert(literal, hash, allocator); |
352 | if (entry->value == nullptr) { |
353 | entry->value = property; |
354 | } else { |
355 | // We already have a later definition of this property, so we don't need |
356 | // to emit a store for the current one. |
357 | // |
358 | // There are two subtleties here. |
359 | // |
360 | // (1) Emitting a store might actually be incorrect. For example, in {get |
361 | // foo() {}, foo: 42}, the getter store would override the data property |
362 | // (which, being a non-computed compile-time valued property, is already |
363 | // part of the initial literal object. |
364 | // |
365 | // (2) If the later definition is an accessor (say, a getter), and the |
366 | // current definition is a complementary accessor (here, a setter), then |
367 | // we still must emit a store for the current definition. |
368 | |
369 | auto later_kind = |
370 | static_cast<ObjectLiteral::Property*>(entry->value)->kind(); |
371 | bool complementary_accessors = |
372 | (property->kind() == GETTER && later_kind == SETTER) || |
373 | (property->kind() == SETTER && later_kind == GETTER); |
374 | if (!complementary_accessors) { |
375 | property->set_emit_store(false); |
376 | if (later_kind == GETTER || later_kind == SETTER) { |
377 | entry->value = property; |
378 | } |
379 | } |
380 | } |
381 | } |
382 | } |
383 | |
384 | void ObjectLiteral::InitFlagsForPendingNullPrototype(int i) { |
385 | // We still check for __proto__:null after computed property names. |
386 | for (; i < properties()->length(); i++) { |
387 | if (properties()->at(i)->IsNullPrototype()) { |
388 | set_has_null_protoype(true); |
389 | break; |
390 | } |
391 | } |
392 | } |
393 | |
394 | int ObjectLiteral::InitDepthAndFlags() { |
395 | if (is_initialized()) return depth(); |
396 | bool is_simple = true; |
397 | bool has_seen_prototype = false; |
398 | bool needs_initial_allocation_site = false; |
399 | int depth_acc = 1; |
400 | uint32_t nof_properties = 0; |
401 | uint32_t elements = 0; |
402 | uint32_t max_element_index = 0; |
403 | for (int i = 0; i < properties()->length(); i++) { |
404 | ObjectLiteral::Property* property = properties()->at(i); |
405 | if (property->IsPrototype()) { |
406 | has_seen_prototype = true; |
407 | // __proto__:null has no side-effects and is set directly on the |
408 | // boilerplate. |
409 | if (property->IsNullPrototype()) { |
410 | set_has_null_protoype(true); |
411 | continue; |
412 | } |
413 | DCHECK(!has_null_prototype()); |
414 | is_simple = false; |
415 | continue; |
416 | } |
417 | if (nof_properties == boilerplate_properties_) { |
418 | DCHECK(property->is_computed_name()); |
419 | is_simple = false; |
420 | if (!has_seen_prototype) InitFlagsForPendingNullPrototype(i); |
421 | break; |
422 | } |
423 | DCHECK(!property->is_computed_name()); |
424 | |
425 | MaterializedLiteral* literal = property->value()->AsMaterializedLiteral(); |
426 | if (literal != nullptr) { |
427 | int subliteral_depth = literal->InitDepthAndFlags() + 1; |
428 | if (subliteral_depth > depth_acc) depth_acc = subliteral_depth; |
429 | needs_initial_allocation_site |= literal->NeedsInitialAllocationSite(); |
430 | } |
431 | |
432 | Literal* key = property->key()->AsLiteral(); |
433 | Expression* value = property->value(); |
434 | |
435 | bool is_compile_time_value = value->IsCompileTimeValue(); |
436 | is_simple = is_simple && is_compile_time_value; |
437 | |
438 | // Keep track of the number of elements in the object literal and |
439 | // the largest element index. If the largest element index is |
440 | // much larger than the number of elements, creating an object |
441 | // literal with fast elements will be a waste of space. |
442 | uint32_t element_index = 0; |
443 | if (key->AsArrayIndex(&element_index)) { |
444 | max_element_index = Max(element_index, max_element_index); |
445 | elements++; |
446 | } else { |
447 | DCHECK(key->IsPropertyName()); |
448 | } |
449 | |
450 | nof_properties++; |
451 | } |
452 | |
453 | set_depth(depth_acc); |
454 | set_is_simple(is_simple); |
455 | set_needs_initial_allocation_site(needs_initial_allocation_site); |
456 | set_has_elements(elements > 0); |
457 | set_fast_elements((max_element_index <= 32) || |
458 | ((2 * elements) >= max_element_index)); |
459 | return depth_acc; |
460 | } |
461 | |
462 | void ObjectLiteral::BuildBoilerplateDescription(Isolate* isolate) { |
463 | if (!boilerplate_description_.is_null()) return; |
464 | |
465 | int index_keys = 0; |
466 | bool has_seen_proto = false; |
467 | for (int i = 0; i < properties()->length(); i++) { |
468 | ObjectLiteral::Property* property = properties()->at(i); |
469 | if (property->IsPrototype()) { |
470 | has_seen_proto = true; |
471 | continue; |
472 | } |
473 | if (property->is_computed_name()) continue; |
474 | |
475 | Literal* key = property->key()->AsLiteral(); |
476 | if (!key->IsPropertyName()) index_keys++; |
477 | } |
478 | |
479 | Handle<ObjectBoilerplateDescription> boilerplate_description = |
480 | isolate->factory()->NewObjectBoilerplateDescription( |
481 | boilerplate_properties_, properties()->length(), index_keys, |
482 | has_seen_proto); |
483 | |
484 | int position = 0; |
485 | for (int i = 0; i < properties()->length(); i++) { |
486 | ObjectLiteral::Property* property = properties()->at(i); |
487 | if (property->IsPrototype()) continue; |
488 | |
489 | if (static_cast<uint32_t>(position) == boilerplate_properties_) { |
490 | DCHECK(property->is_computed_name()); |
491 | break; |
492 | } |
493 | DCHECK(!property->is_computed_name()); |
494 | |
495 | MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral(); |
496 | if (m_literal != nullptr) { |
497 | m_literal->BuildConstants(isolate); |
498 | } |
499 | |
500 | // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined |
501 | // value for COMPUTED properties, the real value is filled in at |
502 | // runtime. The enumeration order is maintained. |
503 | Literal* key_literal = property->key()->AsLiteral(); |
504 | uint32_t element_index = 0; |
505 | Handle<Object> key = |
506 | key_literal->AsArrayIndex(&element_index) |
507 | ? isolate->factory()->NewNumberFromUint(element_index) |
508 | : Handle<Object>::cast(key_literal->AsRawPropertyName()->string()); |
509 | |
510 | Handle<Object> value = GetBoilerplateValue(property->value(), isolate); |
511 | |
512 | // Add name, value pair to the fixed array. |
513 | boilerplate_description->set_key_value(position++, *key, *value); |
514 | } |
515 | |
516 | boilerplate_description->set_flags(EncodeLiteralType()); |
517 | |
518 | boilerplate_description_ = boilerplate_description; |
519 | } |
520 | |
521 | bool ObjectLiteral::IsFastCloningSupported() const { |
522 | // The CreateShallowObjectLiteratal builtin doesn't copy elements, and object |
523 | // literals don't support copy-on-write (COW) elements for now. |
524 | // TODO(mvstanton): make object literals support COW elements. |
525 | return fast_elements() && is_shallow() && |
526 | properties_count() <= |
527 | ConstructorBuiltins::kMaximumClonedShallowObjectProperties; |
528 | } |
529 | |
530 | int ArrayLiteral::InitDepthAndFlags() { |
531 | if (is_initialized()) return depth(); |
532 | |
533 | int constants_length = |
534 | first_spread_index_ >= 0 ? first_spread_index_ : values()->length(); |
535 | |
536 | // Fill in the literals. |
537 | bool is_simple = first_spread_index_ < 0; |
538 | int depth_acc = 1; |
539 | int array_index = 0; |
540 | for (; array_index < constants_length; array_index++) { |
541 | Expression* element = values()->at(array_index); |
542 | MaterializedLiteral* literal = element->AsMaterializedLiteral(); |
543 | if (literal != nullptr) { |
544 | int subliteral_depth = literal->InitDepthAndFlags() + 1; |
545 | if (subliteral_depth > depth_acc) depth_acc = subliteral_depth; |
546 | } |
547 | |
548 | if (!element->IsCompileTimeValue()) { |
549 | is_simple = false; |
550 | } |
551 | } |
552 | |
553 | set_depth(depth_acc); |
554 | set_is_simple(is_simple); |
555 | // Array literals always need an initial allocation site to properly track |
556 | // elements transitions. |
557 | set_needs_initial_allocation_site(true); |
558 | return depth_acc; |
559 | } |
560 | |
561 | void ArrayLiteral::BuildBoilerplateDescription(Isolate* isolate) { |
562 | if (!boilerplate_description_.is_null()) return; |
563 | |
564 | int constants_length = |
565 | first_spread_index_ >= 0 ? first_spread_index_ : values()->length(); |
566 | ElementsKind kind = FIRST_FAST_ELEMENTS_KIND; |
567 | Handle<FixedArray> fixed_array = |
568 | isolate->factory()->NewFixedArrayWithHoles(constants_length); |
569 | |
570 | // Fill in the literals. |
571 | bool is_holey = false; |
572 | int array_index = 0; |
573 | for (; array_index < constants_length; array_index++) { |
574 | Expression* element = values()->at(array_index); |
575 | DCHECK(!element->IsSpread()); |
576 | MaterializedLiteral* m_literal = element->AsMaterializedLiteral(); |
577 | if (m_literal != nullptr) { |
578 | m_literal->BuildConstants(isolate); |
579 | } |
580 | |
581 | // New handle scope here, needs to be after BuildContants(). |
582 | HandleScope scope(isolate); |
583 | Handle<Object> boilerplate_value = GetBoilerplateValue(element, isolate); |
584 | if (boilerplate_value->IsTheHole(isolate)) { |
585 | is_holey = true; |
586 | continue; |
587 | } |
588 | |
589 | if (boilerplate_value->IsUninitialized(isolate)) { |
590 | boilerplate_value = handle(Smi::kZero, isolate); |
591 | } |
592 | |
593 | kind = GetMoreGeneralElementsKind(kind, |
594 | boilerplate_value->OptimalElementsKind()); |
595 | fixed_array->set(array_index, *boilerplate_value); |
596 | } |
597 | |
598 | if (is_holey) kind = GetHoleyElementsKind(kind); |
599 | |
600 | // Simple and shallow arrays can be lazily copied, we transform the |
601 | // elements array to a copy-on-write array. |
602 | if (is_simple() && depth() == 1 && array_index > 0 && |
603 | IsSmiOrObjectElementsKind(kind)) { |
604 | fixed_array->set_map(ReadOnlyRoots(isolate).fixed_cow_array_map()); |
605 | } |
606 | |
607 | Handle<FixedArrayBase> elements = fixed_array; |
608 | if (IsDoubleElementsKind(kind)) { |
609 | ElementsAccessor* accessor = ElementsAccessor::ForKind(kind); |
610 | elements = isolate->factory()->NewFixedDoubleArray(constants_length); |
611 | // We are copying from non-fast-double to fast-double. |
612 | ElementsKind from_kind = TERMINAL_FAST_ELEMENTS_KIND; |
613 | accessor->CopyElements(isolate, fixed_array, from_kind, elements, |
614 | constants_length); |
615 | } |
616 | |
617 | boilerplate_description_ = |
618 | isolate->factory()->NewArrayBoilerplateDescription(kind, elements); |
619 | } |
620 | |
621 | bool ArrayLiteral::IsFastCloningSupported() const { |
622 | return depth() <= 1 && |
623 | values_.length() <= |
624 | ConstructorBuiltins::kMaximumClonedShallowArrayElements; |
625 | } |
626 | |
627 | bool MaterializedLiteral::IsSimple() const { |
628 | if (IsArrayLiteral()) return AsArrayLiteral()->is_simple(); |
629 | if (IsObjectLiteral()) return AsObjectLiteral()->is_simple(); |
630 | DCHECK(IsRegExpLiteral()); |
631 | return false; |
632 | } |
633 | |
634 | Handle<Object> MaterializedLiteral::GetBoilerplateValue(Expression* expression, |
635 | Isolate* isolate) { |
636 | if (expression->IsLiteral()) { |
637 | return expression->AsLiteral()->BuildValue(isolate); |
638 | } |
639 | if (expression->IsCompileTimeValue()) { |
640 | if (expression->IsObjectLiteral()) { |
641 | ObjectLiteral* object_literal = expression->AsObjectLiteral(); |
642 | DCHECK(object_literal->is_simple()); |
643 | return object_literal->boilerplate_description(); |
644 | } else { |
645 | DCHECK(expression->IsArrayLiteral()); |
646 | ArrayLiteral* array_literal = expression->AsArrayLiteral(); |
647 | DCHECK(array_literal->is_simple()); |
648 | return array_literal->boilerplate_description(); |
649 | } |
650 | } |
651 | return isolate->factory()->uninitialized_value(); |
652 | } |
653 | |
654 | int MaterializedLiteral::InitDepthAndFlags() { |
655 | if (IsArrayLiteral()) return AsArrayLiteral()->InitDepthAndFlags(); |
656 | if (IsObjectLiteral()) return AsObjectLiteral()->InitDepthAndFlags(); |
657 | DCHECK(IsRegExpLiteral()); |
658 | return 1; |
659 | } |
660 | |
661 | bool MaterializedLiteral::NeedsInitialAllocationSite() { |
662 | if (IsArrayLiteral()) { |
663 | return AsArrayLiteral()->needs_initial_allocation_site(); |
664 | } |
665 | if (IsObjectLiteral()) { |
666 | return AsObjectLiteral()->needs_initial_allocation_site(); |
667 | } |
668 | DCHECK(IsRegExpLiteral()); |
669 | return false; |
670 | } |
671 | |
672 | void MaterializedLiteral::BuildConstants(Isolate* isolate) { |
673 | if (IsArrayLiteral()) { |
674 | AsArrayLiteral()->BuildBoilerplateDescription(isolate); |
675 | return; |
676 | } |
677 | if (IsObjectLiteral()) { |
678 | AsObjectLiteral()->BuildBoilerplateDescription(isolate); |
679 | return; |
680 | } |
681 | DCHECK(IsRegExpLiteral()); |
682 | } |
683 | |
684 | Handle<TemplateObjectDescription> GetTemplateObject::GetOrBuildDescription( |
685 | Isolate* isolate) { |
686 | Handle<FixedArray> raw_strings = isolate->factory()->NewFixedArray( |
687 | this->raw_strings()->length(), AllocationType::kOld); |
688 | bool raw_and_cooked_match = true; |
689 | for (int i = 0; i < raw_strings->length(); ++i) { |
690 | if (this->cooked_strings()->at(i) == nullptr || |
691 | *this->raw_strings()->at(i)->string() != |
692 | *this->cooked_strings()->at(i)->string()) { |
693 | raw_and_cooked_match = false; |
694 | } |
695 | raw_strings->set(i, *this->raw_strings()->at(i)->string()); |
696 | } |
697 | Handle<FixedArray> cooked_strings = raw_strings; |
698 | if (!raw_and_cooked_match) { |
699 | cooked_strings = isolate->factory()->NewFixedArray( |
700 | this->cooked_strings()->length(), AllocationType::kOld); |
701 | for (int i = 0; i < cooked_strings->length(); ++i) { |
702 | if (this->cooked_strings()->at(i) != nullptr) { |
703 | cooked_strings->set(i, *this->cooked_strings()->at(i)->string()); |
704 | } else { |
705 | cooked_strings->set(i, ReadOnlyRoots(isolate).undefined_value()); |
706 | } |
707 | } |
708 | } |
709 | return isolate->factory()->NewTemplateObjectDescription(raw_strings, |
710 | cooked_strings); |
711 | } |
712 | |
713 | static bool IsCommutativeOperationWithSmiLiteral(Token::Value op) { |
714 | // Add is not commutative due to potential for string addition. |
715 | return op == Token::MUL || op == Token::BIT_AND || op == Token::BIT_OR || |
716 | op == Token::BIT_XOR; |
717 | } |
718 | |
719 | // Check for the pattern: x + 1. |
720 | static bool MatchSmiLiteralOperation(Expression* left, Expression* right, |
721 | Expression** expr, Smi* literal) { |
722 | if (right->IsSmiLiteral()) { |
723 | *expr = left; |
724 | *literal = right->AsLiteral()->AsSmiLiteral(); |
725 | return true; |
726 | } |
727 | return false; |
728 | } |
729 | |
730 | bool BinaryOperation::IsSmiLiteralOperation(Expression** subexpr, |
731 | Smi* literal) { |
732 | return MatchSmiLiteralOperation(left_, right_, subexpr, literal) || |
733 | (IsCommutativeOperationWithSmiLiteral(op()) && |
734 | MatchSmiLiteralOperation(right_, left_, subexpr, literal)); |
735 | } |
736 | |
737 | static bool IsTypeof(Expression* expr) { |
738 | UnaryOperation* maybe_unary = expr->AsUnaryOperation(); |
739 | return maybe_unary != nullptr && maybe_unary->op() == Token::TYPEOF; |
740 | } |
741 | |
742 | // Check for the pattern: typeof <expression> equals <string literal>. |
743 | static bool MatchLiteralCompareTypeof(Expression* left, Token::Value op, |
744 | Expression* right, Expression** expr, |
745 | Literal** literal) { |
746 | if (IsTypeof(left) && right->IsStringLiteral() && Token::IsEqualityOp(op)) { |
747 | *expr = left->AsUnaryOperation()->expression(); |
748 | *literal = right->AsLiteral(); |
749 | return true; |
750 | } |
751 | return false; |
752 | } |
753 | |
754 | bool CompareOperation::IsLiteralCompareTypeof(Expression** expr, |
755 | Literal** literal) { |
756 | return MatchLiteralCompareTypeof(left_, op(), right_, expr, literal) || |
757 | MatchLiteralCompareTypeof(right_, op(), left_, expr, literal); |
758 | } |
759 | |
760 | |
761 | static bool IsVoidOfLiteral(Expression* expr) { |
762 | UnaryOperation* maybe_unary = expr->AsUnaryOperation(); |
763 | return maybe_unary != nullptr && maybe_unary->op() == Token::VOID && |
764 | maybe_unary->expression()->IsLiteral(); |
765 | } |
766 | |
767 | |
768 | // Check for the pattern: void <literal> equals <expression> or |
769 | // undefined equals <expression> |
770 | static bool MatchLiteralCompareUndefined(Expression* left, |
771 | Token::Value op, |
772 | Expression* right, |
773 | Expression** expr) { |
774 | if (IsVoidOfLiteral(left) && Token::IsEqualityOp(op)) { |
775 | *expr = right; |
776 | return true; |
777 | } |
778 | if (left->IsUndefinedLiteral() && Token::IsEqualityOp(op)) { |
779 | *expr = right; |
780 | return true; |
781 | } |
782 | return false; |
783 | } |
784 | |
785 | bool CompareOperation::IsLiteralCompareUndefined(Expression** expr) { |
786 | return MatchLiteralCompareUndefined(left_, op(), right_, expr) || |
787 | MatchLiteralCompareUndefined(right_, op(), left_, expr); |
788 | } |
789 | |
790 | // Check for the pattern: null equals <expression> |
791 | static bool MatchLiteralCompareNull(Expression* left, |
792 | Token::Value op, |
793 | Expression* right, |
794 | Expression** expr) { |
795 | if (left->IsNullLiteral() && Token::IsEqualityOp(op)) { |
796 | *expr = right; |
797 | return true; |
798 | } |
799 | return false; |
800 | } |
801 | |
802 | bool CompareOperation::IsLiteralCompareNull(Expression** expr) { |
803 | return MatchLiteralCompareNull(left_, op(), right_, expr) || |
804 | MatchLiteralCompareNull(right_, op(), left_, expr); |
805 | } |
806 | |
807 | Call::CallType Call::GetCallType() const { |
808 | VariableProxy* proxy = expression()->AsVariableProxy(); |
809 | if (proxy != nullptr) { |
810 | if (proxy->var()->IsUnallocated()) { |
811 | return GLOBAL_CALL; |
812 | } else if (proxy->var()->IsLookupSlot()) { |
813 | // Calls going through 'with' always use VariableMode::kDynamic rather |
814 | // than VariableMode::kDynamicLocal or VariableMode::kDynamicGlobal. |
815 | return proxy->var()->mode() == VariableMode::kDynamic ? WITH_CALL |
816 | : OTHER_CALL; |
817 | } |
818 | } |
819 | |
820 | if (expression()->IsSuperCallReference()) return SUPER_CALL; |
821 | |
822 | Property* property = expression()->AsProperty(); |
823 | if (property != nullptr) { |
824 | bool is_super = property->IsSuperAccess(); |
825 | if (property->key()->IsPropertyName()) { |
826 | return is_super ? NAMED_SUPER_PROPERTY_CALL : NAMED_PROPERTY_CALL; |
827 | } else { |
828 | return is_super ? KEYED_SUPER_PROPERTY_CALL : KEYED_PROPERTY_CALL; |
829 | } |
830 | } |
831 | |
832 | if (expression()->IsResolvedProperty()) { |
833 | return RESOLVED_PROPERTY_CALL; |
834 | } |
835 | |
836 | return OTHER_CALL; |
837 | } |
838 | |
839 | CaseClause::CaseClause(Zone* zone, Expression* label, |
840 | const ScopedPtrList<Statement>& statements) |
841 | : label_(label), statements_(0, nullptr) { |
842 | statements.CopyTo(&statements_, zone); |
843 | } |
844 | |
845 | bool Literal::IsPropertyName() const { |
846 | if (type() != kString) return false; |
847 | uint32_t index; |
848 | return !string_->AsArrayIndex(&index); |
849 | } |
850 | |
851 | bool Literal::ToUint32(uint32_t* value) const { |
852 | switch (type()) { |
853 | case kString: |
854 | return string_->AsArrayIndex(value); |
855 | case kSmi: |
856 | if (smi_ < 0) return false; |
857 | *value = static_cast<uint32_t>(smi_); |
858 | return true; |
859 | case kHeapNumber: |
860 | return DoubleToUint32IfEqualToSelf(AsNumber(), value); |
861 | default: |
862 | return false; |
863 | } |
864 | } |
865 | |
866 | bool Literal::AsArrayIndex(uint32_t* value) const { |
867 | return ToUint32(value) && *value != kMaxUInt32; |
868 | } |
869 | |
870 | Handle<Object> Literal::BuildValue(Isolate* isolate) const { |
871 | switch (type()) { |
872 | case kSmi: |
873 | return handle(Smi::FromInt(smi_), isolate); |
874 | case kHeapNumber: |
875 | return isolate->factory()->NewNumber(number_, AllocationType::kOld); |
876 | case kString: |
877 | return string_->string(); |
878 | case kSymbol: |
879 | return isolate->factory()->home_object_symbol(); |
880 | case kBoolean: |
881 | return isolate->factory()->ToBoolean(boolean_); |
882 | case kNull: |
883 | return isolate->factory()->null_value(); |
884 | case kUndefined: |
885 | return isolate->factory()->undefined_value(); |
886 | case kTheHole: |
887 | return isolate->factory()->the_hole_value(); |
888 | case kBigInt: |
889 | // This should never fail: the parser will never create a BigInt |
890 | // literal that cannot be allocated. |
891 | return BigIntLiteral(isolate, bigint_.c_str()).ToHandleChecked(); |
892 | } |
893 | UNREACHABLE(); |
894 | } |
895 | |
896 | bool Literal::ToBooleanIsTrue() const { |
897 | switch (type()) { |
898 | case kSmi: |
899 | return smi_ != 0; |
900 | case kHeapNumber: |
901 | return DoubleToBoolean(number_); |
902 | case kString: |
903 | return !string_->IsEmpty(); |
904 | case kNull: |
905 | case kUndefined: |
906 | return false; |
907 | case kBoolean: |
908 | return boolean_; |
909 | case kBigInt: { |
910 | const char* bigint_str = bigint_.c_str(); |
911 | size_t length = strlen(bigint_str); |
912 | DCHECK_GT(length, 0); |
913 | if (length == 1 && bigint_str[0] == '0') return false; |
914 | // Skip over any radix prefix; BigInts with length > 1 only |
915 | // begin with zero if they include a radix. |
916 | for (size_t i = (bigint_str[0] == '0') ? 2 : 0; i < length; ++i) { |
917 | if (bigint_str[i] != '0') return true; |
918 | } |
919 | return false; |
920 | } |
921 | case kSymbol: |
922 | return true; |
923 | case kTheHole: |
924 | UNREACHABLE(); |
925 | } |
926 | UNREACHABLE(); |
927 | } |
928 | |
929 | uint32_t Literal::Hash() { |
930 | return IsString() ? AsRawString()->Hash() |
931 | : ComputeLongHash(double_to_uint64(AsNumber())); |
932 | } |
933 | |
934 | |
935 | // static |
936 | bool Literal::Match(void* a, void* b) { |
937 | Literal* x = static_cast<Literal*>(a); |
938 | Literal* y = static_cast<Literal*>(b); |
939 | return (x->IsString() && y->IsString() && |
940 | x->AsRawString() == y->AsRawString()) || |
941 | (x->IsNumber() && y->IsNumber() && x->AsNumber() == y->AsNumber()); |
942 | } |
943 | |
944 | Literal* AstNodeFactory::NewNumberLiteral(double number, int pos) { |
945 | int int_value; |
946 | if (DoubleToSmiInteger(number, &int_value)) { |
947 | return NewSmiLiteral(int_value, pos); |
948 | } |
949 | return new (zone_) Literal(number, pos); |
950 | } |
951 | |
952 | const char* CallRuntime::debug_name() { |
953 | #ifdef DEBUG |
954 | return is_jsruntime() ? NameForNativeContextIntrinsicIndex(context_index_) |
955 | : function_->name; |
956 | #else |
957 | return is_jsruntime() ? "(context function)" : function_->name; |
958 | #endif // DEBUG |
959 | } |
960 | |
961 | #define RETURN_LABELS(NodeType) \ |
962 | case k##NodeType: \ |
963 | return static_cast<const NodeType*>(this)->labels(); |
964 | |
965 | ZonePtrList<const AstRawString>* BreakableStatement::labels() const { |
966 | switch (node_type()) { |
967 | BREAKABLE_NODE_LIST(RETURN_LABELS) |
968 | ITERATION_NODE_LIST(RETURN_LABELS) |
969 | default: |
970 | UNREACHABLE(); |
971 | } |
972 | } |
973 | |
974 | #undef RETURN_LABELS |
975 | |
976 | } // namespace internal |
977 | } // namespace v8 |
978 | |