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
9 #include "src/ast/compile-time-value.h"
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/code-stubs.h"
16 #include "src/contexts.h"
17 #include "src/conversions.h"
18 #include "src/double.h"
19 #include "src/elements.h"
20 #include "src/objects-inl.h"
21 #include "src/objects/literal-objects.h"
22 #include "src/property-details.h"
23 #include "src/property.h"
24 #include "src/string-stream.h"
25 #include "src/type-info.h"
26
27 namespace v8 {
28 namespace internal {
29
30 // ----------------------------------------------------------------------------
31 // Implementation of other node functionality.
32
33 #ifdef DEBUG
34
NameForNativeContextIntrinsicIndex(uint32_t idx)35 static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) {
36 switch (idx) {
37 #define NATIVE_CONTEXT_FIELDS_IDX(NAME, Type, name) \
38 case Context::NAME: \
39 return #name;
40
41 NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELDS_IDX)
42 #undef NATIVE_CONTEXT_FIELDS_IDX
43
44 default:
45 break;
46 }
47
48 return "UnknownIntrinsicIndex";
49 }
50
Print()51 void AstNode::Print() { Print(Isolate::Current()); }
52
Print(Isolate * isolate)53 void AstNode::Print(Isolate* isolate) {
54 AstPrinter::PrintOut(isolate, this);
55 }
56
57
58 #endif // DEBUG
59
60 #define RETURN_NODE(Node) \
61 case k##Node: \
62 return static_cast<Node*>(this);
63
AsIterationStatement()64 IterationStatement* AstNode::AsIterationStatement() {
65 switch (node_type()) {
66 ITERATION_NODE_LIST(RETURN_NODE);
67 default:
68 return nullptr;
69 }
70 }
71
AsBreakableStatement()72 BreakableStatement* AstNode::AsBreakableStatement() {
73 switch (node_type()) {
74 BREAKABLE_NODE_LIST(RETURN_NODE);
75 ITERATION_NODE_LIST(RETURN_NODE);
76 default:
77 return nullptr;
78 }
79 }
80
AsMaterializedLiteral()81 MaterializedLiteral* AstNode::AsMaterializedLiteral() {
82 switch (node_type()) {
83 LITERAL_NODE_LIST(RETURN_NODE);
84 default:
85 return nullptr;
86 }
87 }
88
89 #undef RETURN_NODE
90
IsSmiLiteral() const91 bool Expression::IsSmiLiteral() const {
92 return IsLiteral() && AsLiteral()->raw_value()->IsSmi();
93 }
94
IsNumberLiteral() const95 bool Expression::IsNumberLiteral() const {
96 return IsLiteral() && AsLiteral()->raw_value()->IsNumber();
97 }
98
IsStringLiteral() const99 bool Expression::IsStringLiteral() const {
100 return IsLiteral() && AsLiteral()->raw_value()->IsString();
101 }
102
IsPropertyName() const103 bool Expression::IsPropertyName() const {
104 return IsLiteral() && AsLiteral()->IsPropertyName();
105 }
106
IsNullLiteral() const107 bool Expression::IsNullLiteral() const {
108 if (!IsLiteral()) return false;
109 return AsLiteral()->raw_value()->IsNull();
110 }
111
IsUndefinedLiteral() const112 bool Expression::IsUndefinedLiteral() const {
113 if (IsLiteral() && AsLiteral()->raw_value()->IsUndefined()) return true;
114
115 const VariableProxy* var_proxy = AsVariableProxy();
116 if (var_proxy == nullptr) return false;
117 Variable* var = var_proxy->var();
118 // The global identifier "undefined" is immutable. Everything
119 // else could be reassigned.
120 return var != NULL && var->IsUnallocated() &&
121 var_proxy->raw_name()->IsOneByteEqualTo("undefined");
122 }
123
ToBooleanIsTrue() const124 bool Expression::ToBooleanIsTrue() const {
125 return IsLiteral() && AsLiteral()->ToBooleanIsTrue();
126 }
127
ToBooleanIsFalse() const128 bool Expression::ToBooleanIsFalse() const {
129 return IsLiteral() && AsLiteral()->ToBooleanIsFalse();
130 }
131
IsValidReferenceExpression() const132 bool Expression::IsValidReferenceExpression() const {
133 // We don't want expressions wrapped inside RewritableExpression to be
134 // considered as valid reference expressions, as they will be rewritten
135 // to something (most probably involving a do expression).
136 if (IsRewritableExpression()) return false;
137 return IsProperty() ||
138 (IsVariableProxy() && AsVariableProxy()->IsValidReferenceExpression());
139 }
140
IsValidReferenceExpressionOrThis() const141 bool Expression::IsValidReferenceExpressionOrThis() const {
142 return IsValidReferenceExpression() ||
143 (IsVariableProxy() && AsVariableProxy()->is_this());
144 }
145
IsAnonymousFunctionDefinition() const146 bool Expression::IsAnonymousFunctionDefinition() const {
147 return (IsFunctionLiteral() &&
148 AsFunctionLiteral()->IsAnonymousFunctionDefinition()) ||
149 (IsDoExpression() &&
150 AsDoExpression()->IsAnonymousFunctionDefinition());
151 }
152
MarkTail()153 void Expression::MarkTail() {
154 if (IsConditional()) {
155 AsConditional()->MarkTail();
156 } else if (IsCall()) {
157 AsCall()->MarkTail();
158 } else if (IsBinaryOperation()) {
159 AsBinaryOperation()->MarkTail();
160 }
161 }
162
IsAnonymousFunctionDefinition() const163 bool DoExpression::IsAnonymousFunctionDefinition() const {
164 // This is specifically to allow DoExpressions to represent ClassLiterals.
165 return represented_function_ != nullptr &&
166 represented_function_->raw_name()->length() == 0;
167 }
168
IsJump() const169 bool Statement::IsJump() const {
170 switch (node_type()) {
171 #define JUMP_NODE_LIST(V) \
172 V(Block) \
173 V(ExpressionStatement) \
174 V(ContinueStatement) \
175 V(BreakStatement) \
176 V(ReturnStatement) \
177 V(IfStatement)
178 #define GENERATE_CASE(Node) \
179 case k##Node: \
180 return static_cast<const Node*>(this)->IsJump();
181 JUMP_NODE_LIST(GENERATE_CASE)
182 #undef GENERATE_CASE
183 #undef JUMP_NODE_LIST
184 default:
185 return false;
186 }
187 }
188
VariableProxy(Variable * var,int start_position)189 VariableProxy::VariableProxy(Variable* var, int start_position)
190 : Expression(start_position, kVariableProxy),
191 raw_name_(var->raw_name()),
192 next_unresolved_(nullptr) {
193 bit_field_ |= IsThisField::encode(var->is_this()) |
194 IsAssignedField::encode(false) |
195 IsResolvedField::encode(false) |
196 HoleCheckModeField::encode(HoleCheckMode::kElided);
197 BindTo(var);
198 }
199
VariableProxy(const AstRawString * name,VariableKind variable_kind,int start_position)200 VariableProxy::VariableProxy(const AstRawString* name,
201 VariableKind variable_kind, int start_position)
202 : Expression(start_position, kVariableProxy),
203 raw_name_(name),
204 next_unresolved_(nullptr) {
205 bit_field_ |= IsThisField::encode(variable_kind == THIS_VARIABLE) |
206 IsAssignedField::encode(false) |
207 IsResolvedField::encode(false) |
208 HoleCheckModeField::encode(HoleCheckMode::kElided);
209 }
210
VariableProxy(const VariableProxy * copy_from)211 VariableProxy::VariableProxy(const VariableProxy* copy_from)
212 : Expression(copy_from->position(), kVariableProxy),
213 next_unresolved_(nullptr) {
214 bit_field_ = copy_from->bit_field_;
215 DCHECK(!copy_from->is_resolved());
216 raw_name_ = copy_from->raw_name_;
217 }
218
BindTo(Variable * var)219 void VariableProxy::BindTo(Variable* var) {
220 DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name());
221 set_var(var);
222 set_is_resolved();
223 var->set_is_used();
224 if (is_assigned()) var->set_maybe_assigned();
225 }
226
AssignFeedbackSlots(FeedbackVectorSpec * spec,TypeofMode typeof_mode,FeedbackSlotCache * cache)227 void VariableProxy::AssignFeedbackSlots(FeedbackVectorSpec* spec,
228 TypeofMode typeof_mode,
229 FeedbackSlotCache* cache) {
230 if (UsesVariableFeedbackSlot()) {
231 // VariableProxies that point to the same Variable within a function can
232 // make their loads from the same IC slot.
233 if (var()->IsUnallocated() || var()->mode() == DYNAMIC_GLOBAL) {
234 FeedbackSlot slot = cache->Get(typeof_mode, var());
235 if (!slot.IsInvalid()) {
236 variable_feedback_slot_ = slot;
237 return;
238 }
239 variable_feedback_slot_ = spec->AddLoadGlobalICSlot(typeof_mode);
240 cache->Put(typeof_mode, var(), variable_feedback_slot_);
241 } else {
242 variable_feedback_slot_ = spec->AddLoadICSlot();
243 }
244 }
245 }
246
AssignVectorSlots(Expression * expr,FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlot * out_slot)247 static void AssignVectorSlots(Expression* expr, FeedbackVectorSpec* spec,
248 LanguageMode language_mode,
249 FeedbackSlot* out_slot) {
250 Property* property = expr->AsProperty();
251 LhsKind assign_type = Property::GetAssignType(property);
252 if ((assign_type == VARIABLE &&
253 expr->AsVariableProxy()->var()->IsUnallocated()) ||
254 assign_type == NAMED_PROPERTY || assign_type == KEYED_PROPERTY) {
255 // TODO(ishell): consider using ICSlotCache for variables here.
256 if (assign_type == KEYED_PROPERTY) {
257 *out_slot = spec->AddKeyedStoreICSlot(language_mode);
258
259 } else {
260 *out_slot = spec->AddStoreICSlot(language_mode);
261 }
262 }
263 }
264
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)265 void ForInStatement::AssignFeedbackSlots(FeedbackVectorSpec* spec,
266 LanguageMode language_mode,
267 FeedbackSlotCache* cache) {
268 AssignVectorSlots(each(), spec, language_mode, &each_slot_);
269 for_in_feedback_slot_ = spec->AddGeneralSlot();
270 }
271
Assignment(Token::Value op,Expression * target,Expression * value,int pos)272 Assignment::Assignment(Token::Value op, Expression* target, Expression* value,
273 int pos)
274 : Expression(pos, kAssignment),
275 target_(target),
276 value_(value),
277 binary_operation_(NULL) {
278 bit_field_ |= IsUninitializedField::encode(false) |
279 KeyTypeField::encode(ELEMENT) |
280 StoreModeField::encode(STANDARD_STORE) | TokenField::encode(op);
281 }
282
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)283 void Assignment::AssignFeedbackSlots(FeedbackVectorSpec* spec,
284 LanguageMode language_mode,
285 FeedbackSlotCache* cache) {
286 AssignVectorSlots(target(), spec, language_mode, &slot_);
287 }
288
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)289 void CountOperation::AssignFeedbackSlots(FeedbackVectorSpec* spec,
290 LanguageMode language_mode,
291 FeedbackSlotCache* cache) {
292 AssignVectorSlots(expression(), spec, language_mode, &slot_);
293 // Assign a slot to collect feedback about binary operations. Used only in
294 // ignition. Fullcodegen uses AstId to record type feedback.
295 binary_operation_slot_ = spec->AddInterpreterBinaryOpICSlot();
296 }
297
298
binary_op() const299 Token::Value Assignment::binary_op() const {
300 switch (op()) {
301 case Token::ASSIGN_BIT_OR: return Token::BIT_OR;
302 case Token::ASSIGN_BIT_XOR: return Token::BIT_XOR;
303 case Token::ASSIGN_BIT_AND: return Token::BIT_AND;
304 case Token::ASSIGN_SHL: return Token::SHL;
305 case Token::ASSIGN_SAR: return Token::SAR;
306 case Token::ASSIGN_SHR: return Token::SHR;
307 case Token::ASSIGN_ADD: return Token::ADD;
308 case Token::ASSIGN_SUB: return Token::SUB;
309 case Token::ASSIGN_MUL: return Token::MUL;
310 case Token::ASSIGN_DIV: return Token::DIV;
311 case Token::ASSIGN_MOD: return Token::MOD;
312 default: UNREACHABLE();
313 }
314 return Token::ILLEGAL;
315 }
316
ShouldEagerCompile() const317 bool FunctionLiteral::ShouldEagerCompile() const {
318 return scope()->ShouldEagerCompile();
319 }
320
SetShouldEagerCompile()321 void FunctionLiteral::SetShouldEagerCompile() {
322 scope()->set_should_eager_compile();
323 }
324
AllowsLazyCompilation()325 bool FunctionLiteral::AllowsLazyCompilation() {
326 return scope()->AllowsLazyCompilation();
327 }
328
329
start_position() const330 int FunctionLiteral::start_position() const {
331 return scope()->start_position();
332 }
333
334
end_position() const335 int FunctionLiteral::end_position() const {
336 return scope()->end_position();
337 }
338
339
language_mode() const340 LanguageMode FunctionLiteral::language_mode() const {
341 return scope()->language_mode();
342 }
343
kind() const344 FunctionKind FunctionLiteral::kind() const { return scope()->function_kind(); }
345
NeedsHomeObject(Expression * expr)346 bool FunctionLiteral::NeedsHomeObject(Expression* expr) {
347 if (expr == nullptr || !expr->IsFunctionLiteral()) return false;
348 DCHECK_NOT_NULL(expr->AsFunctionLiteral()->scope());
349 return expr->AsFunctionLiteral()->scope()->NeedsHomeObject();
350 }
351
ObjectLiteralProperty(Expression * key,Expression * value,Kind kind,bool is_computed_name)352 ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value,
353 Kind kind, bool is_computed_name)
354 : LiteralProperty(key, value, is_computed_name),
355 kind_(kind),
356 emit_store_(true) {}
357
ObjectLiteralProperty(AstValueFactory * ast_value_factory,Expression * key,Expression * value,bool is_computed_name)358 ObjectLiteralProperty::ObjectLiteralProperty(AstValueFactory* ast_value_factory,
359 Expression* key, Expression* value,
360 bool is_computed_name)
361 : LiteralProperty(key, value, is_computed_name), emit_store_(true) {
362 if (!is_computed_name &&
363 key->AsLiteral()->raw_value()->EqualsString(
364 ast_value_factory->proto_string())) {
365 kind_ = PROTOTYPE;
366 } else if (value_->AsMaterializedLiteral() != NULL) {
367 kind_ = MATERIALIZED_LITERAL;
368 } else if (value_->IsLiteral()) {
369 kind_ = CONSTANT;
370 } else {
371 kind_ = COMPUTED;
372 }
373 }
374
GetStoreDataPropertySlot() const375 FeedbackSlot LiteralProperty::GetStoreDataPropertySlot() const {
376 int offset = FunctionLiteral::NeedsHomeObject(value_) ? 1 : 0;
377 return GetSlot(offset);
378 }
379
SetStoreDataPropertySlot(FeedbackSlot slot)380 void LiteralProperty::SetStoreDataPropertySlot(FeedbackSlot slot) {
381 int offset = FunctionLiteral::NeedsHomeObject(value_) ? 1 : 0;
382 return SetSlot(slot, offset);
383 }
384
NeedsSetFunctionName() const385 bool LiteralProperty::NeedsSetFunctionName() const {
386 return is_computed_name_ &&
387 (value_->IsAnonymousFunctionDefinition() ||
388 (value_->IsFunctionLiteral() &&
389 IsConciseMethod(value_->AsFunctionLiteral()->kind())));
390 }
391
ClassLiteralProperty(Expression * key,Expression * value,Kind kind,bool is_static,bool is_computed_name)392 ClassLiteralProperty::ClassLiteralProperty(Expression* key, Expression* value,
393 Kind kind, bool is_static,
394 bool is_computed_name)
395 : LiteralProperty(key, value, is_computed_name),
396 kind_(kind),
397 is_static_(is_static) {}
398
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)399 void ClassLiteral::AssignFeedbackSlots(FeedbackVectorSpec* spec,
400 LanguageMode language_mode,
401 FeedbackSlotCache* cache) {
402 // This logic that computes the number of slots needed for vector store
403 // ICs must mirror BytecodeGenerator::VisitClassLiteral.
404 if (FunctionLiteral::NeedsHomeObject(constructor())) {
405 home_object_slot_ = spec->AddStoreICSlot(language_mode);
406 }
407
408 if (NeedsProxySlot()) {
409 proxy_slot_ = spec->AddStoreICSlot(language_mode);
410 }
411
412 for (int i = 0; i < properties()->length(); i++) {
413 ClassLiteral::Property* property = properties()->at(i);
414 Expression* value = property->value();
415 if (FunctionLiteral::NeedsHomeObject(value)) {
416 property->SetSlot(spec->AddStoreICSlot(language_mode));
417 }
418 property->SetStoreDataPropertySlot(
419 spec->AddStoreDataPropertyInLiteralICSlot());
420 }
421 }
422
IsCompileTimeValue() const423 bool ObjectLiteral::Property::IsCompileTimeValue() const {
424 return kind_ == CONSTANT ||
425 (kind_ == MATERIALIZED_LITERAL &&
426 CompileTimeValue::IsCompileTimeValue(value_));
427 }
428
429
set_emit_store(bool emit_store)430 void ObjectLiteral::Property::set_emit_store(bool emit_store) {
431 emit_store_ = emit_store;
432 }
433
emit_store() const434 bool ObjectLiteral::Property::emit_store() const { return emit_store_; }
435
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)436 void ObjectLiteral::AssignFeedbackSlots(FeedbackVectorSpec* spec,
437 LanguageMode language_mode,
438 FeedbackSlotCache* cache) {
439 MaterializedLiteral::AssignFeedbackSlots(spec, language_mode, cache);
440
441 // This logic that computes the number of slots needed for vector store
442 // ics must mirror FullCodeGenerator::VisitObjectLiteral.
443 int property_index = 0;
444 for (; property_index < properties()->length(); property_index++) {
445 ObjectLiteral::Property* property = properties()->at(property_index);
446 if (property->is_computed_name()) break;
447 if (property->IsCompileTimeValue()) continue;
448
449 Literal* key = property->key()->AsLiteral();
450 Expression* value = property->value();
451 switch (property->kind()) {
452 case ObjectLiteral::Property::SPREAD:
453 case ObjectLiteral::Property::CONSTANT:
454 UNREACHABLE();
455 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
456 // Fall through.
457 case ObjectLiteral::Property::COMPUTED:
458 // It is safe to use [[Put]] here because the boilerplate already
459 // contains computed properties with an uninitialized value.
460 if (key->IsStringLiteral()) {
461 if (property->emit_store()) {
462 property->SetSlot(spec->AddStoreOwnICSlot());
463 if (FunctionLiteral::NeedsHomeObject(value)) {
464 property->SetSlot(spec->AddStoreICSlot(language_mode), 1);
465 }
466 }
467 break;
468 }
469 if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) {
470 property->SetSlot(spec->AddStoreICSlot(language_mode));
471 }
472 break;
473 case ObjectLiteral::Property::PROTOTYPE:
474 break;
475 case ObjectLiteral::Property::GETTER:
476 if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) {
477 property->SetSlot(spec->AddStoreICSlot(language_mode));
478 }
479 break;
480 case ObjectLiteral::Property::SETTER:
481 if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) {
482 property->SetSlot(spec->AddStoreICSlot(language_mode));
483 }
484 break;
485 }
486 }
487
488 for (; property_index < properties()->length(); property_index++) {
489 ObjectLiteral::Property* property = properties()->at(property_index);
490
491 Expression* value = property->value();
492 if (property->kind() != ObjectLiteral::Property::PROTOTYPE) {
493 if (FunctionLiteral::NeedsHomeObject(value)) {
494 property->SetSlot(spec->AddStoreICSlot(language_mode));
495 }
496 }
497 property->SetStoreDataPropertySlot(
498 spec->AddStoreDataPropertyInLiteralICSlot());
499 }
500 }
501
502
CalculateEmitStore(Zone * zone)503 void ObjectLiteral::CalculateEmitStore(Zone* zone) {
504 const auto GETTER = ObjectLiteral::Property::GETTER;
505 const auto SETTER = ObjectLiteral::Property::SETTER;
506
507 ZoneAllocationPolicy allocator(zone);
508
509 CustomMatcherZoneHashMap table(
510 Literal::Match, ZoneHashMap::kDefaultHashMapCapacity, allocator);
511 for (int i = properties()->length() - 1; i >= 0; i--) {
512 ObjectLiteral::Property* property = properties()->at(i);
513 if (property->is_computed_name()) continue;
514 if (property->kind() == ObjectLiteral::Property::PROTOTYPE) continue;
515 Literal* literal = property->key()->AsLiteral();
516 DCHECK(!literal->IsNullLiteral());
517
518 // If there is an existing entry do not emit a store unless the previous
519 // entry was also an accessor.
520 uint32_t hash = literal->Hash();
521 ZoneHashMap::Entry* entry = table.LookupOrInsert(literal, hash, allocator);
522 if (entry->value != NULL) {
523 auto previous_kind =
524 static_cast<ObjectLiteral::Property*>(entry->value)->kind();
525 if (!((property->kind() == GETTER && previous_kind == SETTER) ||
526 (property->kind() == SETTER && previous_kind == GETTER))) {
527 property->set_emit_store(false);
528 }
529 }
530 entry->value = property;
531 }
532 }
533
534
IsBoilerplateProperty(ObjectLiteral::Property * property)535 bool ObjectLiteral::IsBoilerplateProperty(ObjectLiteral::Property* property) {
536 return property != NULL &&
537 property->kind() != ObjectLiteral::Property::PROTOTYPE;
538 }
539
InitDepthAndFlags()540 void ObjectLiteral::InitDepthAndFlags() {
541 if (depth_ > 0) return;
542
543 int position = 0;
544 // Accumulate the value in local variables and store it at the end.
545 bool is_simple = true;
546 int depth_acc = 1;
547 uint32_t max_element_index = 0;
548 uint32_t elements = 0;
549 for (int i = 0; i < properties()->length(); i++) {
550 ObjectLiteral::Property* property = properties()->at(i);
551 if (!IsBoilerplateProperty(property)) {
552 is_simple = false;
553 continue;
554 }
555
556 if (static_cast<uint32_t>(position) == boilerplate_properties_ * 2) {
557 DCHECK(property->is_computed_name());
558 is_simple = false;
559 break;
560 }
561 DCHECK(!property->is_computed_name());
562
563 MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
564 if (m_literal != NULL) {
565 m_literal->InitDepthAndFlags();
566 if (m_literal->depth() >= depth_acc) depth_acc = m_literal->depth() + 1;
567 }
568
569 const AstValue* key = property->key()->AsLiteral()->raw_value();
570 Expression* value = property->value();
571
572 bool is_compile_time_value = CompileTimeValue::IsCompileTimeValue(value);
573
574 // Ensure objects that may, at any point in time, contain fields with double
575 // representation are always treated as nested objects. This is true for
576 // computed fields, and smi and double literals.
577 // TODO(verwaest): Remove once we can store them inline.
578 if (FLAG_track_double_fields &&
579 (value->IsNumberLiteral() || !is_compile_time_value)) {
580 bit_field_ = MayStoreDoublesField::update(bit_field_, true);
581 }
582
583 is_simple = is_simple && is_compile_time_value;
584
585 // Keep track of the number of elements in the object literal and
586 // the largest element index. If the largest element index is
587 // much larger than the number of elements, creating an object
588 // literal with fast elements will be a waste of space.
589 uint32_t element_index = 0;
590 if (key->IsString() && key->AsString()->AsArrayIndex(&element_index)) {
591 max_element_index = Max(element_index, max_element_index);
592 elements++;
593 } else if (key->ToUint32(&element_index) && element_index != kMaxUInt32) {
594 max_element_index = Max(element_index, max_element_index);
595 elements++;
596 }
597
598 // Increment the position for the key and the value.
599 position += 2;
600 }
601
602 bit_field_ = FastElementsField::update(
603 bit_field_,
604 (max_element_index <= 32) || ((2 * elements) >= max_element_index));
605 bit_field_ = HasElementsField::update(bit_field_, elements > 0);
606
607 set_is_simple(is_simple);
608 set_depth(depth_acc);
609 }
610
BuildConstantProperties(Isolate * isolate)611 void ObjectLiteral::BuildConstantProperties(Isolate* isolate) {
612 if (!constant_properties_.is_null()) return;
613
614 int index_keys = 0;
615 bool has_seen_proto = false;
616 for (int i = 0; i < properties()->length(); i++) {
617 ObjectLiteral::Property* property = properties()->at(i);
618 if (!IsBoilerplateProperty(property)) {
619 has_seen_proto = true;
620 continue;
621 }
622 if (property->is_computed_name()) {
623 continue;
624 }
625
626 Handle<Object> key = property->key()->AsLiteral()->value();
627
628 uint32_t element_index = 0;
629 if (key->ToArrayIndex(&element_index) ||
630 (key->IsString() && String::cast(*key)->AsArrayIndex(&element_index))) {
631 index_keys++;
632 }
633 }
634
635 Handle<BoilerplateDescription> constant_properties =
636 isolate->factory()->NewBoilerplateDescription(boilerplate_properties_,
637 properties()->length(),
638 index_keys, has_seen_proto);
639
640 int position = 0;
641 for (int i = 0; i < properties()->length(); i++) {
642 ObjectLiteral::Property* property = properties()->at(i);
643 if (!IsBoilerplateProperty(property)) {
644 continue;
645 }
646
647 if (static_cast<uint32_t>(position) == boilerplate_properties_ * 2) {
648 DCHECK(property->is_computed_name());
649 break;
650 }
651 DCHECK(!property->is_computed_name());
652
653 MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
654 if (m_literal != NULL) {
655 m_literal->BuildConstants(isolate);
656 }
657
658 // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined
659 // value for COMPUTED properties, the real value is filled in at
660 // runtime. The enumeration order is maintained.
661 Handle<Object> key = property->key()->AsLiteral()->value();
662 Handle<Object> value = GetBoilerplateValue(property->value(), isolate);
663
664 uint32_t element_index = 0;
665 if (key->IsString() && String::cast(*key)->AsArrayIndex(&element_index)) {
666 key = isolate->factory()->NewNumberFromUint(element_index);
667 } else if (key->IsNumber() && !key->ToArrayIndex(&element_index)) {
668 key = isolate->factory()->NumberToString(key);
669 }
670
671 // Add name, value pair to the fixed array.
672 constant_properties->set(position++, *key);
673 constant_properties->set(position++, *value);
674 }
675
676 constant_properties_ = constant_properties;
677 }
678
IsFastCloningSupported() const679 bool ObjectLiteral::IsFastCloningSupported() const {
680 // The FastCloneShallowObject builtin doesn't copy elements, and object
681 // literals don't support copy-on-write (COW) elements for now.
682 // TODO(mvstanton): make object literals support COW elements.
683 return fast_elements() && has_shallow_properties() &&
684 properties_count() <= ConstructorBuiltinsAssembler::
685 kMaximumClonedShallowObjectProperties;
686 }
687
constant_elements_kind() const688 ElementsKind ArrayLiteral::constant_elements_kind() const {
689 return static_cast<ElementsKind>(constant_elements()->elements_kind());
690 }
691
InitDepthAndFlags()692 void ArrayLiteral::InitDepthAndFlags() {
693 DCHECK_LT(first_spread_index_, 0);
694
695 if (depth_ > 0) return;
696
697 int constants_length = values()->length();
698
699 // Fill in the literals.
700 bool is_simple = true;
701 int depth_acc = 1;
702 int array_index = 0;
703 for (; array_index < constants_length; array_index++) {
704 Expression* element = values()->at(array_index);
705 DCHECK(!element->IsSpread());
706 MaterializedLiteral* m_literal = element->AsMaterializedLiteral();
707 if (m_literal != NULL) {
708 m_literal->InitDepthAndFlags();
709 if (m_literal->depth() + 1 > depth_acc) {
710 depth_acc = m_literal->depth() + 1;
711 }
712 }
713
714 if (!CompileTimeValue::IsCompileTimeValue(element)) {
715 is_simple = false;
716 }
717 }
718
719 set_is_simple(is_simple);
720 set_depth(depth_acc);
721 }
722
BuildConstantElements(Isolate * isolate)723 void ArrayLiteral::BuildConstantElements(Isolate* isolate) {
724 DCHECK_LT(first_spread_index_, 0);
725
726 if (!constant_elements_.is_null()) return;
727
728 int constants_length = values()->length();
729 ElementsKind kind = FIRST_FAST_ELEMENTS_KIND;
730 Handle<FixedArray> fixed_array =
731 isolate->factory()->NewFixedArrayWithHoles(constants_length);
732
733 // Fill in the literals.
734 bool is_holey = false;
735 int array_index = 0;
736 for (; array_index < constants_length; array_index++) {
737 Expression* element = values()->at(array_index);
738 DCHECK(!element->IsSpread());
739 MaterializedLiteral* m_literal = element->AsMaterializedLiteral();
740 if (m_literal != NULL) {
741 m_literal->BuildConstants(isolate);
742 }
743
744 // New handle scope here, needs to be after BuildContants().
745 HandleScope scope(isolate);
746 Handle<Object> boilerplate_value = GetBoilerplateValue(element, isolate);
747 if (boilerplate_value->IsTheHole(isolate)) {
748 is_holey = true;
749 continue;
750 }
751
752 if (boilerplate_value->IsUninitialized(isolate)) {
753 boilerplate_value = handle(Smi::kZero, isolate);
754 }
755
756 kind = GetMoreGeneralElementsKind(kind,
757 boilerplate_value->OptimalElementsKind());
758 fixed_array->set(array_index, *boilerplate_value);
759 }
760
761 if (is_holey) kind = GetHoleyElementsKind(kind);
762
763 // Simple and shallow arrays can be lazily copied, we transform the
764 // elements array to a copy-on-write array.
765 if (is_simple() && depth() == 1 && array_index > 0 &&
766 IsFastSmiOrObjectElementsKind(kind)) {
767 fixed_array->set_map(isolate->heap()->fixed_cow_array_map());
768 }
769
770 Handle<FixedArrayBase> elements = fixed_array;
771 if (IsFastDoubleElementsKind(kind)) {
772 ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
773 elements = isolate->factory()->NewFixedDoubleArray(constants_length);
774 // We are copying from non-fast-double to fast-double.
775 ElementsKind from_kind = TERMINAL_FAST_ELEMENTS_KIND;
776 accessor->CopyElements(fixed_array, from_kind, elements, constants_length);
777 }
778
779 // Remember both the literal's constant values as well as the ElementsKind.
780 Handle<ConstantElementsPair> literals =
781 isolate->factory()->NewConstantElementsPair(kind, elements);
782
783 constant_elements_ = literals;
784 }
785
IsFastCloningSupported() const786 bool ArrayLiteral::IsFastCloningSupported() const {
787 return depth() <= 1 &&
788 values()->length() <=
789 ConstructorBuiltinsAssembler::kMaximumClonedShallowArrayElements;
790 }
791
RewindSpreads()792 void ArrayLiteral::RewindSpreads() {
793 values_->Rewind(first_spread_index_);
794 first_spread_index_ = -1;
795 }
796
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)797 void ArrayLiteral::AssignFeedbackSlots(FeedbackVectorSpec* spec,
798 LanguageMode language_mode,
799 FeedbackSlotCache* cache) {
800 MaterializedLiteral::AssignFeedbackSlots(spec, language_mode, cache);
801
802 // This logic that computes the number of slots needed for vector store
803 // ics must mirror FullCodeGenerator::VisitArrayLiteral.
804 for (int array_index = 0; array_index < values()->length(); array_index++) {
805 Expression* subexpr = values()->at(array_index);
806 DCHECK(!subexpr->IsSpread());
807 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
808
809 // We'll reuse the same literal slot for all of the non-constant
810 // subexpressions that use a keyed store IC.
811 literal_slot_ = spec->AddKeyedStoreICSlot(language_mode);
812 return;
813 }
814 }
815
816
GetBoilerplateValue(Expression * expression,Isolate * isolate)817 Handle<Object> MaterializedLiteral::GetBoilerplateValue(Expression* expression,
818 Isolate* isolate) {
819 if (expression->IsLiteral()) {
820 return expression->AsLiteral()->value();
821 }
822 if (CompileTimeValue::IsCompileTimeValue(expression)) {
823 return CompileTimeValue::GetValue(isolate, expression);
824 }
825 return isolate->factory()->uninitialized_value();
826 }
827
InitDepthAndFlags()828 void MaterializedLiteral::InitDepthAndFlags() {
829 if (IsArrayLiteral()) {
830 return AsArrayLiteral()->InitDepthAndFlags();
831 }
832 if (IsObjectLiteral()) {
833 return AsObjectLiteral()->InitDepthAndFlags();
834 }
835 DCHECK(IsRegExpLiteral());
836 DCHECK_LE(1, depth()); // Depth should be initialized.
837 }
838
BuildConstants(Isolate * isolate)839 void MaterializedLiteral::BuildConstants(Isolate* isolate) {
840 if (IsArrayLiteral()) {
841 return AsArrayLiteral()->BuildConstantElements(isolate);
842 }
843 if (IsObjectLiteral()) {
844 return AsObjectLiteral()->BuildConstantProperties(isolate);
845 }
846 DCHECK(IsRegExpLiteral());
847 }
848
849
RecordToBooleanTypeFeedback(TypeFeedbackOracle * oracle)850 void UnaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
851 // TODO(olivf) If this Operation is used in a test context, then the
852 // expression has a ToBoolean stub and we want to collect the type
853 // information. However the GraphBuilder expects it to be on the instruction
854 // corresponding to the TestContext, therefore we have to store it here and
855 // not on the operand.
856 set_to_boolean_types(oracle->ToBooleanTypes(expression()->test_id()));
857 }
858
859
RecordToBooleanTypeFeedback(TypeFeedbackOracle * oracle)860 void BinaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
861 // TODO(olivf) If this Operation is used in a test context, then the right
862 // hand side has a ToBoolean stub and we want to collect the type information.
863 // However the GraphBuilder expects it to be on the instruction corresponding
864 // to the TestContext, therefore we have to store it here and not on the
865 // right hand operand.
866 set_to_boolean_types(oracle->ToBooleanTypes(right()->test_id()));
867 }
868
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)869 void BinaryOperation::AssignFeedbackSlots(FeedbackVectorSpec* spec,
870 LanguageMode language_mode,
871 FeedbackSlotCache* cache) {
872 // Feedback vector slot is only used by interpreter for binary operations.
873 // Full-codegen uses AstId to record type feedback.
874 switch (op()) {
875 // Comma, logical_or and logical_and do not collect type feedback.
876 case Token::COMMA:
877 case Token::AND:
878 case Token::OR:
879 return;
880 default:
881 feedback_slot_ = spec->AddInterpreterBinaryOpICSlot();
882 return;
883 }
884 }
885
IsTypeof(Expression * expr)886 static bool IsTypeof(Expression* expr) {
887 UnaryOperation* maybe_unary = expr->AsUnaryOperation();
888 return maybe_unary != NULL && maybe_unary->op() == Token::TYPEOF;
889 }
890
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache_)891 void CompareOperation::AssignFeedbackSlots(FeedbackVectorSpec* spec,
892 LanguageMode language_mode,
893 FeedbackSlotCache* cache_) {
894 // Feedback vector slot is only used by interpreter for binary operations.
895 // Full-codegen uses AstId to record type feedback.
896 switch (op()) {
897 // instanceof and in do not collect type feedback.
898 case Token::INSTANCEOF:
899 case Token::IN:
900 return;
901 default:
902 feedback_slot_ = spec->AddInterpreterCompareICSlot();
903 }
904 }
905
906 // Check for the pattern: typeof <expression> equals <string literal>.
MatchLiteralCompareTypeof(Expression * left,Token::Value op,Expression * right,Expression ** expr,Handle<String> * check)907 static bool MatchLiteralCompareTypeof(Expression* left,
908 Token::Value op,
909 Expression* right,
910 Expression** expr,
911 Handle<String>* check) {
912 if (IsTypeof(left) && right->IsStringLiteral() && Token::IsEqualityOp(op)) {
913 *expr = left->AsUnaryOperation()->expression();
914 *check = Handle<String>::cast(right->AsLiteral()->value());
915 return true;
916 }
917 return false;
918 }
919
920
IsLiteralCompareTypeof(Expression ** expr,Handle<String> * check)921 bool CompareOperation::IsLiteralCompareTypeof(Expression** expr,
922 Handle<String>* check) {
923 return MatchLiteralCompareTypeof(left_, op(), right_, expr, check) ||
924 MatchLiteralCompareTypeof(right_, op(), left_, expr, check);
925 }
926
927
IsVoidOfLiteral(Expression * expr)928 static bool IsVoidOfLiteral(Expression* expr) {
929 UnaryOperation* maybe_unary = expr->AsUnaryOperation();
930 return maybe_unary != NULL &&
931 maybe_unary->op() == Token::VOID &&
932 maybe_unary->expression()->IsLiteral();
933 }
934
935
936 // Check for the pattern: void <literal> equals <expression> or
937 // undefined equals <expression>
MatchLiteralCompareUndefined(Expression * left,Token::Value op,Expression * right,Expression ** expr)938 static bool MatchLiteralCompareUndefined(Expression* left,
939 Token::Value op,
940 Expression* right,
941 Expression** expr) {
942 if (IsVoidOfLiteral(left) && Token::IsEqualityOp(op)) {
943 *expr = right;
944 return true;
945 }
946 if (left->IsUndefinedLiteral() && Token::IsEqualityOp(op)) {
947 *expr = right;
948 return true;
949 }
950 return false;
951 }
952
IsLiteralCompareUndefined(Expression ** expr)953 bool CompareOperation::IsLiteralCompareUndefined(Expression** expr) {
954 return MatchLiteralCompareUndefined(left_, op(), right_, expr) ||
955 MatchLiteralCompareUndefined(right_, op(), left_, expr);
956 }
957
958
959 // Check for the pattern: null equals <expression>
MatchLiteralCompareNull(Expression * left,Token::Value op,Expression * right,Expression ** expr)960 static bool MatchLiteralCompareNull(Expression* left,
961 Token::Value op,
962 Expression* right,
963 Expression** expr) {
964 if (left->IsNullLiteral() && Token::IsEqualityOp(op)) {
965 *expr = right;
966 return true;
967 }
968 return false;
969 }
970
971
IsLiteralCompareNull(Expression ** expr)972 bool CompareOperation::IsLiteralCompareNull(Expression** expr) {
973 return MatchLiteralCompareNull(left_, op(), right_, expr) ||
974 MatchLiteralCompareNull(right_, op(), left_, expr);
975 }
976
977
978 // ----------------------------------------------------------------------------
979 // Recording of type feedback
980
981 // TODO(rossberg): all RecordTypeFeedback functions should disappear
982 // once we use the common type field in the AST consistently.
983
RecordToBooleanTypeFeedback(TypeFeedbackOracle * oracle)984 void Expression::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
985 if (IsUnaryOperation()) {
986 AsUnaryOperation()->RecordToBooleanTypeFeedback(oracle);
987 } else if (IsBinaryOperation()) {
988 AsBinaryOperation()->RecordToBooleanTypeFeedback(oracle);
989 } else {
990 set_to_boolean_types(oracle->ToBooleanTypes(test_id()));
991 }
992 }
993
GetReceiverTypes()994 SmallMapList* Expression::GetReceiverTypes() {
995 switch (node_type()) {
996 #define NODE_LIST(V) \
997 PROPERTY_NODE_LIST(V) \
998 V(Call)
999 #define GENERATE_CASE(Node) \
1000 case k##Node: \
1001 return static_cast<Node*>(this)->GetReceiverTypes();
1002 NODE_LIST(GENERATE_CASE)
1003 #undef NODE_LIST
1004 #undef GENERATE_CASE
1005 default:
1006 UNREACHABLE();
1007 return nullptr;
1008 }
1009 }
1010
GetStoreMode() const1011 KeyedAccessStoreMode Expression::GetStoreMode() const {
1012 switch (node_type()) {
1013 #define GENERATE_CASE(Node) \
1014 case k##Node: \
1015 return static_cast<const Node*>(this)->GetStoreMode();
1016 PROPERTY_NODE_LIST(GENERATE_CASE)
1017 #undef GENERATE_CASE
1018 default:
1019 UNREACHABLE();
1020 return STANDARD_STORE;
1021 }
1022 }
1023
GetKeyType() const1024 IcCheckType Expression::GetKeyType() const {
1025 switch (node_type()) {
1026 #define GENERATE_CASE(Node) \
1027 case k##Node: \
1028 return static_cast<const Node*>(this)->GetKeyType();
1029 PROPERTY_NODE_LIST(GENERATE_CASE)
1030 #undef GENERATE_CASE
1031 default:
1032 UNREACHABLE();
1033 return PROPERTY;
1034 }
1035 }
1036
IsMonomorphic() const1037 bool Expression::IsMonomorphic() const {
1038 switch (node_type()) {
1039 #define GENERATE_CASE(Node) \
1040 case k##Node: \
1041 return static_cast<const Node*>(this)->IsMonomorphic();
1042 PROPERTY_NODE_LIST(GENERATE_CASE)
1043 CALL_NODE_LIST(GENERATE_CASE)
1044 #undef GENERATE_CASE
1045 default:
1046 UNREACHABLE();
1047 return false;
1048 }
1049 }
1050
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)1051 void Call::AssignFeedbackSlots(FeedbackVectorSpec* spec,
1052 LanguageMode language_mode,
1053 FeedbackSlotCache* cache) {
1054 ic_slot_ = spec->AddCallICSlot();
1055 }
1056
GetCallType() const1057 Call::CallType Call::GetCallType() const {
1058 VariableProxy* proxy = expression()->AsVariableProxy();
1059 if (proxy != NULL) {
1060 if (proxy->var()->IsUnallocated()) {
1061 return GLOBAL_CALL;
1062 } else if (proxy->var()->IsLookupSlot()) {
1063 // Calls going through 'with' always use DYNAMIC rather than DYNAMIC_LOCAL
1064 // or DYNAMIC_GLOBAL.
1065 return proxy->var()->mode() == DYNAMIC ? WITH_CALL : OTHER_CALL;
1066 }
1067 }
1068
1069 if (expression()->IsSuperCallReference()) return SUPER_CALL;
1070
1071 Property* property = expression()->AsProperty();
1072 if (property != nullptr) {
1073 bool is_super = property->IsSuperAccess();
1074 if (property->key()->IsPropertyName()) {
1075 return is_super ? NAMED_SUPER_PROPERTY_CALL : NAMED_PROPERTY_CALL;
1076 } else {
1077 return is_super ? KEYED_SUPER_PROPERTY_CALL : KEYED_PROPERTY_CALL;
1078 }
1079 }
1080
1081 return OTHER_CALL;
1082 }
1083
CaseClause(Expression * label,ZoneList<Statement * > * statements,int pos)1084 CaseClause::CaseClause(Expression* label, ZoneList<Statement*>* statements,
1085 int pos)
1086 : Expression(pos, kCaseClause),
1087 label_(label),
1088 statements_(statements),
1089 compare_type_(AstType::None()) {}
1090
AssignFeedbackSlots(FeedbackVectorSpec * spec,LanguageMode language_mode,FeedbackSlotCache * cache)1091 void CaseClause::AssignFeedbackSlots(FeedbackVectorSpec* spec,
1092 LanguageMode language_mode,
1093 FeedbackSlotCache* cache) {
1094 feedback_slot_ = spec->AddInterpreterCompareICSlot();
1095 }
1096
Hash()1097 uint32_t Literal::Hash() {
1098 return raw_value()->IsString()
1099 ? raw_value()->AsString()->hash()
1100 : ComputeLongHash(double_to_uint64(raw_value()->AsNumber()));
1101 }
1102
1103
1104 // static
Match(void * literal1,void * literal2)1105 bool Literal::Match(void* literal1, void* literal2) {
1106 const AstValue* x = static_cast<Literal*>(literal1)->raw_value();
1107 const AstValue* y = static_cast<Literal*>(literal2)->raw_value();
1108 return (x->IsString() && y->IsString() && x->AsString() == y->AsString()) ||
1109 (x->IsNumber() && y->IsNumber() && x->AsNumber() == y->AsNumber());
1110 }
1111
debug_name()1112 const char* CallRuntime::debug_name() {
1113 #ifdef DEBUG
1114 return is_jsruntime() ? NameForNativeContextIntrinsicIndex(context_index_)
1115 : function_->name;
1116 #else
1117 return is_jsruntime() ? "(context function)" : function_->name;
1118 #endif // DEBUG
1119 }
1120
1121 } // namespace internal
1122 } // namespace v8
1123