current commit
261004.7 · deps: update V8 to 15.2.124.38
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/ast/ast.h"

#include <cmath>  // For isfinite.
#include <vector>

#include "src/ast/prettyprinter.h"
#include "src/ast/scopes.h"
#include "src/base/hashmap.h"
#include "src/base/logging.h"
#include "src/base/numbers/double.h"
#include "src/base/vector.h"
#include "src/builtins/builtins-constructor.h"
#include "src/builtins/builtins.h"
#include "src/common/assert-scope.h"
#include "src/heap/local-factory-inl.h"
#include "src/numbers/conversions-inl.h"
#include "src/objects/contexts.h"
#include "src/objects/elements-kind.h"
#include "src/objects/elements.h"
#include "src/objects/fixed-array.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/literal-objects.h"
#include "src/objects/map.h"
#include "src/objects/objects-inl.h"
#include "src/objects/property-details.h"
#include "src/objects/property.h"
#include "src/strings/string-stream.h"
#include "src/zone/zone-list-inl.h"

namespace v8 {
namespace internal {

// ----------------------------------------------------------------------------
// Implementation of other node functionality.

#ifdef DEBUG

void AstNode::Print(Isolate* isolate) { AstPrinter::PrintOut(isolate, this); }

#endif  // DEBUG

#define RETURN_NODE(Node) \
  case k##Node:           \
    return static_cast<Node*>(this);

IterationStatement* AstNode::AsIterationStatement() {
  switch (node_type()) {
    ITERATION_NODE_LIST(RETURN_NODE);
    default:
      return nullptr;
  }
}

MaterializedLiteral* AstNode::AsMaterializedLiteral() {
  switch (node_type()) {
    LITERAL_NODE_LIST(RETURN_NODE);
    default:
      return nullptr;
  }
}

#undef RETURN_NODE

bool Expression::IsSmiLiteral() const {
  return IsLiteral() && AsLiteral()->type() == Literal::kSmi;
}

bool Expression::IsNumberLiteral() const {
  return IsLiteral() && AsLiteral()->IsNumber();
}

bool Expression::IsStringLiteral() const {
  return IsLiteral() && AsLiteral()->type() == Literal::kString;
}

bool Expression::IsConsStringLiteral() const {
  return IsLiteral() && AsLiteral()->type() == Literal::kConsString;
}

bool Expression::IsPropertyName() const {
  return IsLiteral() && AsLiteral()->IsPropertyName();
}

bool Expression::IsNullLiteral() const {
  return IsLiteral() && AsLiteral()->type() == Literal::kNull;
}

bool Expression::IsBooleanLiteral() const {
  return IsLiteral() && AsLiteral()->type() == Literal::kBoolean;
}

bool Expression::IsTheHoleLiteral() const {
  return IsLiteral() && AsLiteral()->type() == Literal::kTheHole;
}

bool Expression::IsCompileTimeValue() {
  if (IsLiteral()) return true;
  MaterializedLiteral* literal = AsMaterializedLiteral();
  if (literal == nullptr) return false;
  return literal->IsSimple();
}

bool Expression::IsUndefinedLiteral() const {
  if (IsLiteral() && AsLiteral()->type() == Literal::kUndefined) return true;

  const VariableProxy* var_proxy = AsVariableProxy();
  if (var_proxy == nullptr) return false;
  Variable* var = var_proxy->var();
  // The global identifier "undefined" is immutable. Everything
  // else could be reassigned.
  return var != nullptr && var->IsUnallocated() &&
         var_proxy->raw_name()->IsOneByteEqualTo("undefined");
}

bool Expression::IsLiteralButNotNullOrUndefined() const {
  return IsLiteral() && !IsNullOrUndefinedLiteral();
}

bool Expression::ToBooleanIsTrue() const {
  return IsLiteral() && AsLiteral()->ToBooleanIsTrue();
}

bool Expression::ToBooleanIsFalse() const {
  return IsLiteral() && AsLiteral()->ToBooleanIsFalse();
}

bool Expression::IsPrivateName() const {
  return IsVariableProxy() && AsVariableProxy()->IsPrivateName();
}

bool Expression::IsValidReferenceExpression() const {
  return IsProperty() ||
         (IsVariableProxy() && AsVariableProxy()->IsValidReferenceExpression());
}

bool Expression::IsAnonymousFunctionDefinition() const {
  return (IsFunctionLiteral() &&
          AsFunctionLiteral()->IsAnonymousFunctionDefinition()) ||
         (IsClassLiteral() &&
          AsClassLiteral()->IsAnonymousFunctionDefinition());
}

bool Expression::IsConciseMethodDefinition() const {
  return IsFunctionLiteral() && IsConciseMethod(AsFunctionLiteral()->kind());
}

bool Expression::IsAccessorFunctionDefinition() const {
  return IsFunctionLiteral() && IsAccessorFunction(AsFunctionLiteral()->kind());
}

VariableProxy::VariableProxy(Variable* var, int start_position)
    : Expression(start_position, kVariableProxy),
      raw_name_(var->raw_name()),
      next_unresolved_(nullptr) {
  DCHECK(!var->is_this());
  bit_field_ |= IsAssignedField::encode(false) |
                IsResolvedField::encode(false) |
                HoleCheckModeField::encode(HoleCheckMode::kElided);
  BindTo(var);
}

VariableProxy::VariableProxy(const VariableProxy* copy_from)
    : Expression(copy_from->position(), kVariableProxy),
      next_unresolved_(nullptr) {
  bit_field_ = copy_from->bit_field_;
  DCHECK(!copy_from->is_resolved());
  raw_name_ = copy_from->raw_name_;
}

void VariableProxy::BindTo(Variable* var, BindingMode mode) {
  DCHECK_EQ(raw_name(), var->raw_name());
  set_var(var);
  set_is_resolved();
  if (mode == BindingMode::kMarkUse) {
    var->set_is_used();
  }
  if (is_assigned()) var->SetMaybeAssigned();
}

Assignment::Assignment(NodeType node_type, Token::Value op, Expression* target,
                       Expression* value, int pos)
    : Expression(pos, node_type), target_(target), value_(value) {
  bit_field_ |= TokenField::encode(op);
}

void FunctionLiteral::set_raw_inferred_name(AstConsString* raw_inferred_name) {
  DCHECK_NOT_NULL(raw_inferred_name);
  DCHECK(shared_function_info_.is_null());
  raw_inferred_name_ = raw_inferred_name;
  scope()->set_has_inferred_function_name(true);
}

Handle<String> FunctionLiteral::GetInferredName(Isolate* isolate) {
  if (raw_inferred_name_ != nullptr) {
    return raw_inferred_name_->GetString(isolate);
  }
  DCHECK(!shared_function_info_.is_null());
  return handle(shared_function_info_->inferred_name(), isolate);
}

void FunctionLiteral::set_shared_function_info(
    Handle<SharedFunctionInfo> shared_function_info) {
  DCHECK(shared_function_info_.is_null());
  CHECK_EQ(shared_function_info->function_literal_id(kRelaxedLoad),
           function_literal_id_);
  shared_function_info_ = shared_function_info;
}

bool FunctionLiteral::ShouldEagerCompile() const {
  return scope()->ShouldEagerCompile();
}

void FunctionLiteral::SetShouldEagerCompile() {
  scope()->set_should_eager_compile();
}

bool FunctionLiteral::AllowsLazyCompilation() {
  return scope()->AllowsLazyCompilation();
}

int FunctionLiteral::start_position() const {
  return scope()->start_position();
}

int FunctionLiteral::end_position() const { return scope()->end_position(); }

bool FunctionLiteral::is_hoisted_in_context() const {
  return scope_->is_hoisted_in_context();
}

LanguageMode FunctionLiteral::language_mode() const {
  return scope()->language_mode();
}

FunctionKind FunctionLiteral::kind() const { return scope()->function_kind(); }

std::unique_ptr<char[]> FunctionLiteral::GetDebugName() const {
  const AstConsString* cons_string;
  if (raw_name_ != nullptr && !raw_name_->IsEmpty()) {
    cons_string = raw_name_;
  } else if (raw_inferred_name_ != nullptr && !raw_inferred_name_->IsEmpty()) {
    cons_string = raw_inferred_name_;
  } else if (!shared_function_info_.is_null()) {
    return shared_function_info_->inferred_name()->ToCString();
  } else {
    char* empty_str = new char[1];
    empty_str[0] = 0;
    return std::unique_ptr<char[]>(empty_str);
  }

  // TODO(rmcilroy): Deal with two-character strings.
  std::vector<char> result_vec;
  std::forward_list<const AstRawString*> strings = cons_string->ToRawStrings();
  for (const AstRawString* string : strings) {
    if (!string->is_one_byte()) break;
    for (int i = 0; i < string->length(); i++) {
      result_vec.push_back(string->raw_data()[i]);
    }
  }
  std::unique_ptr<char[]> result(new char[result_vec.size() + 1]);
  if (result_vec.size()) {
    memcpy(result.get(), result_vec.data(), result_vec.size());
  }
  result[result_vec.size()] = '\0';
  return result;
}

bool FunctionLiteral::private_name_lookup_skips_outer_class() const {
  return scope()->private_name_lookup_skips_outer_class();
}

bool FunctionLiteral::class_scope_has_private_brand() const {
  return scope()->class_scope_has_private_brand();
}

void FunctionLiteral::set_class_scope_has_private_brand(bool value) {
  return scope()->set_class_scope_has_private_brand(value);
}

ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value,
                                             Kind kind, bool is_computed_name)
    : LiteralProperty(key, value, is_computed_name), kind_(kind) {}

ObjectLiteralProperty::ObjectLiteralProperty(AstValueFactory* ast_value_factory,
                                             Expression* key, Expression* value,
                                             bool is_computed_name)
    : LiteralProperty(key, value, is_computed_name) {
  if (!is_computed_name && key->AsLiteral()->IsRawString() &&
      key->AsLiteral()->AsRawString() == ast_value_factory->proto_string()) {
    kind_ = PROTOTYPE;
  } else if (value_->AsMaterializedLiteral() != nullptr) {
    kind_ = MATERIALIZED_LITERAL;
  } else if (value_->IsLiteral()) {
    kind_ = CONSTANT;
  } else {
    kind_ = COMPUTED;
  }
}

bool LiteralProperty::NeedsSetFunctionName() const {
  return is_computed_name() && (value_->IsAnonymousFunctionDefinition() ||
                                value_->IsConciseMethodDefinition() ||
                                value_->IsAccessorFunctionDefinition());
}

ClassLiteralProperty::ClassLiteralProperty(Expression* key, Expression* value,
                                           Kind kind, bool is_static,
                                           bool is_computed_name,
                                           bool is_private)
    : LiteralProperty(key, value, is_computed_name),
      kind_(kind),
      is_static_(is_static),
      is_private_(is_private),
      private_or_computed_name_proxy_(nullptr) {}

ClassLiteralProperty::ClassLiteralProperty(Expression* key, Expression* value,
                                           AutoAccessorInfo* info,
                                           bool is_static,
                                           bool is_computed_name,
                                           bool is_private)
    : LiteralProperty(key, value, is_computed_name),
      kind_(Kind::AUTO_ACCESSOR),
      is_static_(is_static),
      is_private_(is_private),
      auto_accessor_info_(info) {
  DCHECK_NOT_NULL(info);
}

bool ObjectLiteral::Property::IsCompileTimeValue() const {
  return kind_ == CONSTANT ||
         (kind_ == MATERIALIZED_LITERAL && value_->IsCompileTimeValue());
}

void ObjectLiteral::Property::set_emit_store(bool emit_store) {
  emit_store_ = emit_store;
}

bool ObjectLiteral::Property::emit_store() const { return emit_store_; }

void ObjectLiteral::CalculateEmitStore(Zone* zone) {
  const auto GETTER = ObjectLiteral::Property::GETTER;
  const auto SETTER = ObjectLiteral::Property::SETTER;

  using LiteralMatcher =
      base::HashEqualityThenKeyMatcher<Literal*, bool (*)(void*, void*)>;
  base::TemplateHashMapImpl<Literal*, int, LiteralMatcher, ZoneAllocationPolicy>
      table(ZoneHashMap::kDefaultHashMapCapacity,
            LiteralMatcher(Literal::Match), ZoneAllocationPolicy(zone));

  // We iterate backwards, so the first property we see is the last one in
  // source order.
  for (int i = properties()->length() - 1; i >= 0; i--) {
    ObjectLiteral::Property* property = properties()->at(i);
    if (property->is_computed_name()) continue;
    if (property->IsPrototype()) continue;
    Literal* literal = property->key()->AsLiteral();
    DCHECK(!literal->IsNullLiteral());

    uint32_t hash = literal->Hash();
    auto* entry = table.LookupOrInsert(literal, hash, []() { return -1; });
    if (entry->value == -1) {
      // First time we see this key (it's the last property in the literal).
      entry->value = i;
      DCHECK(property->is_first_instance_of_key());
      property->set_last_instance_index(i);
    } else {
      int previous_index = entry->value;
      ObjectLiteral::Property* previous_prop = properties()->at(previous_index);

      // Properties are deduplicated preserving source order. For a given key,
      // we only keep the last-occurring instance in the boilerplate. Earlier
      // instances must have their stores eliminated, unless they are a
      // complementary accessor to the last-occurring instance.
      //
      // Examples:
      // { a: 1, a: 2 }             -> [a: 1] disabled, [a: 2] kept.
      // { get a(){}, a: 1 }        -> [get a] disabled, [a: 1] kept.
      // { get a(){}, set a(){} }   -> [get a] kept, [set a] kept.
      // { get a(){}_1, get a(){}_2, set a(){} }
      //                            -> [get a]_1 disabled (redundant),
      //                               [get a]_2 kept, [set a] kept.
      // { get a(){}, a: 1, set a(){} }
      //                            -> [get a] disabled (shielded by a: 1),
      //                               [a: 1] disabled, [set a] kept.

      int last_index = previous_prop->last_instance_index();
      bool is_candidate;
      if (last_index == previous_index) {
        // This is the first duplicate we've found for this key. previous_prop
        // is the absolute last instance in source order.
        is_candidate = (previous_prop->kind() == GETTER ||
                        previous_prop->kind() == SETTER);
      } else {
        is_candidate = previous_prop->is_complementary_accessor_candidate();
      }

      if (is_candidate) {
        ObjectLiteral::Property* last_prop = properties()->at(last_index);
        auto last_kind = last_prop->kind();
        bool complementary_accessors =
            (property->kind() == GETTER && last_kind == SETTER) ||
            (property->kind() == SETTER && last_kind == GETTER);

        if (complementary_accessors) {
          // The current property is a complementary accessor to the last one.
          // It stays emit_store = true. We don't propagate the candidate bit
          // so that any further accessors of the same kind are disabled.
        } else {
          property->set_emit_store(false);
          // If this duplicate is the same kind of accessor as the last one,
          // it doesn't shield earlier properties, so we propagate the bit.
          //
          // Example: { get x(){}_1, set x(){}, get x(){}_2 }
          // Here [get x]_2 is the last instance. When we see [set x], we
          // don't propagate the bit, so that [get x]_1 is later disabled
          // because it's a redundant getter.
          //
          // However, in { get x(){}_1, set x(){}_1, set x(){}_2 }, when we see
          // [set x]_1, it matches the last instance's kind (SETTER), so we
          // propagate the bit to [set x]_1 so that it can later pair with
          // [get x]_1.
          if (property->kind() == last_kind) {
            property->set_is_complementary_accessor_candidate(true);
          }
        }
      } else {
        // No accessor candidate, so this duplicate definitely doesn't need to
        // emit a store.
        property->set_emit_store(false);
      }

      // Transition the previous instance. It's no longer the first instance.
      previous_prop->set_is_first_instance_of_key(false);

      // The current property (at index i) is now the earliest instance of this
      // key we've seen so far. We mark it as the "first instance" to preserve
      // insertion order in the boilerplate, and link it to the absolute last
      // instance (which provides the final value for this key).
      DCHECK(property->is_first_instance_of_key());
      property->set_last_instance_index(last_index);
      entry->value = i;
    }
  }
}

int ObjectLiteralBoilerplateBuilder::ComputeFlags(bool disable_mementos) const {
  int flags = LiteralBoilerplateBuilder::ComputeFlags(disable_mementos);
  if (fast_elements()) flags |= ObjectLiteral::kFastElements;
  if (has_null_prototype()) flags |= ObjectLiteral::kHasNullPrototype;
  return flags;
}

void ObjectLiteralBoilerplateBuilder::InitFlagsForPendingNullPrototype(int i) {
  // We still check for __proto__:null after computed property names.
  for (; i < properties()->length(); i++) {
    if (properties()->at(i)->IsNullPrototype()) {
      set_has_null_protoype(true);
      break;
    }
  }
}

int ObjectLiteralBoilerplateBuilder::EncodeLiteralType() {
  int flags = AggregateLiteral::kNoFlags;
  if (fast_elements()) flags |= ObjectLiteral::kFastElements;
  if (has_null_prototype()) flags |= ObjectLiteral::kHasNullPrototype;
  return flags;
}

void ObjectLiteralBoilerplateBuilder::InitDepthAndFlags() {
  if (is_initialized()) return;
  bool is_simple = true;
  bool has_seen_prototype = false;
  bool needs_initial_allocation_site = false;
  DepthKind depth_acc = kShallow;
  uint32_t nof_properties = 0;
  uint32_t elements = 0;
  uint32_t max_element_index = 0;
  for (int i = 0; i < properties()->length(); i++) {
    ObjectLiteral::Property* property = properties()->at(i);
    if (property->IsPrototype()) {
      has_seen_prototype = true;
      // __proto__:null has no side-effects and is set directly on the
      // boilerplate.
      if (property->IsNullPrototype()) {
        set_has_null_protoype(true);
        continue;
      }
      DCHECK(!has_null_prototype());
      is_simple = false;
      continue;
    }
    if (nof_properties == boilerplate_properties_) {
      DCHECK(property->is_computed_name());
      is_simple = false;
      if (!has_seen_prototype) InitFlagsForPendingNullPrototype(i);
      break;
    }
    DCHECK(!property->is_computed_name());

    MaterializedLiteral* literal = property->value()->AsMaterializedLiteral();
    if (literal != nullptr) {
      LiteralBoilerplateBuilder::InitDepthAndFlags(literal);
      depth_acc = kNotShallow;
      needs_initial_allocation_site |= literal->NeedsInitialAllocationSite();
    }

    Literal* key = property->key()->AsLiteral();
    Expression* value = property->value();

    bool is_compile_time_value = value->IsCompileTimeValue();
    is_simple = is_simple && is_compile_time_value;

    // Keep track of the number of elements in the object literal and
    // the largest element index.  If the largest element index is
    // much larger than the number of elements, creating an object
    // literal with fast elements will be a waste of space.
    uint32_t element_index = 0;
    if (key->AsArrayIndex(&element_index)) {
      max_element_index = std::max(element_index, max_element_index);
      elements++;
    } else {
      DCHECK(key->IsPropertyName());
    }

    nof_properties++;
  }

  set_depth(depth_acc);
  set_is_simple(is_simple);
  set_needs_initial_allocation_site(needs_initial_allocation_site);
  set_has_elements(elements > 0);
  set_fast_elements((max_element_index <= 32) ||
                    ((2 * elements) >= max_element_index));
}

namespace {}  // namespace

template <typename IsolateT>
void ObjectLiteralBoilerplateBuilder::BuildBoilerplateDescription(
    IsolateT* isolate) {
  if (!boilerplate_description_.is_null()) return;

  int backing_store_size = 0;
  bool saw_computed_name = false;
  int boilerplate_property_count = 0;

  for (int i = 0; i < properties()->length(); i++) {
    ObjectLiteral::Property* property = properties()->at(i);
    if (property->IsPrototype()) continue;

    if (property->is_computed_name()) {
      saw_computed_name = true;
      backing_store_size++;
      continue;
    }

    Literal* key = property->key()->AsLiteral();
    if (saw_computed_name) {
      if (key->IsPropertyName()) backing_store_size++;
      continue;
    }

    if (property->is_first_instance_of_key()) {
      boilerplate_property_count++;
      if (key->IsPropertyName()) backing_store_size++;
    }
  }

  Handle<ObjectBoilerplateDescription> boilerplate_description =
      isolate->factory()->NewObjectBoilerplateDescription(
          boilerplate_property_count, backing_store_size);

  int position = 0;
  for (int i = 0; i < properties()->length(); i++) {
    ObjectLiteral::Property* property = properties()->at(i);
    if (property->IsPrototype()) continue;
    if (property->is_computed_name()) break;
    if (property->emit_store()) {
      if (!property->is_first_instance_of_key()) continue;
    } else {
      if (!property->is_first_instance_of_key()) continue;
      property = properties()->at(property->last_instance_index());
    }

    DCHECK(!property->is_computed_name());

    MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
    if (m_literal != nullptr) {
      BuildConstants(isolate, m_literal);
    }

    // Add CONSTANT and COMPUTED properties to boilerplate. Use the
    // 'uninitialized' Oddball for COMPUTED properties, the real value is filled
    // in at runtime. The enumeration order is maintained.
    Literal* key_literal = property->key()->AsLiteral();
    uint32_t element_index = 0;
    DirectHandle<ObjectBoilerplateDescription::KeyT> key;
    if (key_literal->AsArrayIndex(&element_index)) {
      key =
          isolate->factory()->template NewNumberFromUint<AllocationType::kOld>(
              element_index);
    } else {
      key = key_literal->AsRawPropertyName()->string();
    }
    DirectHandle<Object> value =
        GetBoilerplateValue(property->value(), isolate);
    boilerplate_description->set_key_value(position++, *key, *value);
  }

  DCHECK_EQ(position, boilerplate_property_count);

  boilerplate_description->set_flags(EncodeLiteralType());

  boilerplate_description_ = boilerplate_description;
}
template EXPORT_TEMPLATE_DEFINE(
    V8_BASE_EXPORT) void ObjectLiteralBoilerplateBuilder::
    BuildBoilerplateDescription(Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(
    V8_BASE_EXPORT) void ObjectLiteralBoilerplateBuilder::
    BuildBoilerplateDescription(LocalIsolate* isolate);

bool ObjectLiteralBoilerplateBuilder::IsFastCloningSupported() const {
  // The CreateShallowObjectLiteratal builtin doesn't copy elements, and object
  // literals don't support copy-on-write (COW) elements for now.
  // TODO(mvstanton): make object literals support COW elements.
  return fast_elements() && is_shallow() &&
         properties_count() <=
             ConstructorBuiltins::kMaximumClonedShallowObjectProperties;
}

// static
template <typename IsolateT>
DirectHandle<Object> LiteralBoilerplateBuilder::GetBoilerplateValue(
    Expression* expression, IsolateT* isolate) {
  if (expression->IsLiteral()) {
    return expression->AsLiteral()->BuildValue(isolate);
  }
  if (expression->IsCompileTimeValue()) {
    if (expression->IsObjectLiteral()) {
      ObjectLiteral* object_literal = expression->AsObjectLiteral();
      DCHECK(object_literal->builder()->is_simple());
      return object_literal->builder()->boilerplate_description();
    } else {
      DCHECK(expression->IsArrayLiteral());
      ArrayLiteral* array_literal = expression->AsArrayLiteral();
      DCHECK(array_literal->builder()->is_simple());
      return array_literal->builder()->boilerplate_description();
    }
  }
  return isolate->factory()->uninitialized_value();
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    DirectHandle<Object> LiteralBoilerplateBuilder::GetBoilerplateValue(
        Expression* expression, Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    DirectHandle<Object> LiteralBoilerplateBuilder::GetBoilerplateValue(
        Expression* expression, LocalIsolate* isolate);

void ArrayLiteralBoilerplateBuilder::InitDepthAndFlags() {
  if (is_initialized()) return;

  int constants_length =
      first_spread_index_ >= 0 ? first_spread_index_ : values_->length();

  // Fill in the literals.
  bool is_simple = first_spread_index_ < 0;
  bool is_holey = false;
  ElementsKind kind = FIRST_FAST_ELEMENTS_KIND;
  DepthKind depth_acc = kShallow;
  int array_index = 0;
  for (; array_index < constants_length; array_index++) {
    Expression* element = values_->at(array_index);
    MaterializedLiteral* materialized_literal =
        element->AsMaterializedLiteral();
    if (materialized_literal != nullptr) {
      LiteralBoilerplateBuilder::InitDepthAndFlags(materialized_literal);
      depth_acc = kNotShallow;
    }

    if (!element->IsCompileTimeValue()) {
      is_simple = false;

      // Don't change kind here: non-compile time values resolve to an unknown
      // elements kind, so we allow them to be considered as any one of them.

      // TODO(leszeks): It would be nice to DCHECK here that GetBoilerplateValue
      // will return IsUninitialized, but that would require being on the main
      // thread which we may not be.
    } else {
      Literal* literal = element->AsLiteral();

      if (!literal) {
        // Only arrays and objects are compile-time values but not (primitive)
        // literals.
        DCHECK(element->IsObjectLiteral() || element->IsArrayLiteral());
        kind = PACKED_ELEMENTS;
      } else {
        switch (literal->type()) {
          case Literal::kTheHole:
            is_holey = true;
            // The hole is allowed in holey double arrays (and holey Smi
            // arrays), so ignore it as far as is_all_number is concerned.
            break;
          case Literal::kHeapNumber:
            if (kind == PACKED_SMI_ELEMENTS) kind = PACKED_DOUBLE_ELEMENTS;
            DCHECK_EQ(kind,
                      GetMoreGeneralElementsKind(kind, PACKED_DOUBLE_ELEMENTS));
            break;
          case Literal::kSmi:
            DCHECK_EQ(kind,
                      GetMoreGeneralElementsKind(kind, PACKED_SMI_ELEMENTS));
            break;
          case Literal::kBigInt:
          case Literal::kString:
          case Literal::kConsString:
          case Literal::kBoolean:
          case Literal::kUndefined:
          case Literal::kNull:
            kind = PACKED_ELEMENTS;
            break;
        }
      }
    }
  }

  if (is_holey) {
    kind = GetHoleyElementsKind(kind);
  }

  set_depth(depth_acc);
  set_is_simple(is_simple);
  set_boilerplate_descriptor_kind(kind);

  // Array literals always need an initial allocation site to properly track
  // elements transitions.
  set_needs_initial_allocation_site(true);
}

template <typename IsolateT>
void ArrayLiteralBoilerplateBuilder::BuildBoilerplateDescription(
    IsolateT* isolate) {
  if (!boilerplate_description_.is_null()) return;

  int constants_length =
      first_spread_index_ >= 0 ? first_spread_index_ : values_->length();
  ElementsKind kind = boilerplate_descriptor_kind();
  bool use_doubles = IsDoubleElementsKind(kind);

  DirectHandle<FixedArrayBase> elements;
  if (use_doubles) {
    elements = isolate->factory()->NewFixedDoubleArray(constants_length,
                                                       AllocationType::kOld);
  } else {
    elements = isolate->factory()->NewFixedArrayWithHoles(constants_length,
                                                          AllocationType::kOld);
  }

  // Fill in the literals.
  int array_index = 0;
  for (; array_index < constants_length; array_index++) {
    Expression* element = values_->at(array_index);
    DCHECK(!element->IsSpread());
    if (use_doubles) {
      Literal* literal = element->AsLiteral();

      if (literal && literal->type() == Literal::kTheHole) {
        DCHECK(IsHoleyElementsKind(kind));
        DCHECK(IsTheHole(*GetBoilerplateValue(element, isolate)));
        Cast<FixedDoubleArray>(*elements)->set_the_hole(array_index);
        continue;
      } else if (literal && literal->IsNumber()) {
        Cast<FixedDoubleArray>(*elements)->set(array_index,
                                               literal->AsNumber());
      } else {
        DCHECK(IsUninitializedHole(*GetBoilerplateValue(element, isolate)));
        Cast<FixedDoubleArray>(*elements)->set(array_index, 0);
      }

    } else {
      MaterializedLiteral* m_literal = element->AsMaterializedLiteral();
      if (m_literal != nullptr) {
        BuildConstants(isolate, m_literal);
      }

      // New handle scope here, needs to be after BuildConstants().
      typename IsolateT::HandleScopeType scope(isolate);

      Tagged<Object> boilerplate_value = *GetBoilerplateValue(element, isolate);
      // We shouldn't allocate after creating the boilerplate value.
      DisallowGarbageCollection no_gc;

      if (IsTheHole(boilerplate_value)) {
        DCHECK(IsHoleyElementsKind(kind));
        continue;
      }

      if (IsUninitializedHole(boilerplate_value)) {
        boilerplate_value = Smi::zero();
      }

      DCHECK_EQ(kind,
                GetMoreGeneralElementsKind(
                    kind, Object::OptimalElementsKind(boilerplate_value)));

      Cast<FixedArray>(*elements)->set(array_index, boilerplate_value);
    }
  }  // namespace internal

  // Simple and shallow arrays can be lazily copied, we transform the
  // elements array to a copy-on-write array.
  if (is_simple() && depth() == kShallow && array_index > 0 &&
      IsSmiOrObjectElementsKind(kind)) {
    elements->set_map_safe_transition(
        isolate, ReadOnlyRoots(isolate).fixed_cow_array_map(), kReleaseStore);
  }

  boilerplate_description_ =
      isolate->factory()->NewArrayBoilerplateDescription(kind, elements);
}
template EXPORT_TEMPLATE_DEFINE(
    V8_BASE_EXPORT) void ArrayLiteralBoilerplateBuilder::
    BuildBoilerplateDescription(Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(
    V8_BASE_EXPORT) void ArrayLiteralBoilerplateBuilder::
    BuildBoilerplateDescription(LocalIsolate*

                                    isolate);

bool ArrayLiteralBoilerplateBuilder::IsFastCloningSupported() const {
  return depth() <= kShallow &&
         values_->length() <=
             ConstructorBuiltins::kMaximumClonedShallowArrayElements;
}

bool MaterializedLiteral::IsSimple() const {
  if (IsArrayLiteral()) return AsArrayLiteral()->builder()->is_simple();
  if (IsObjectLiteral()) return AsObjectLiteral()->builder()->is_simple();
  DCHECK(IsRegExpLiteral());
  return false;
}

// static
void LiteralBoilerplateBuilder::InitDepthAndFlags(MaterializedLiteral* expr) {
  if (expr->IsArrayLiteral()) {
    return expr->AsArrayLiteral()->builder()->InitDepthAndFlags();
  }
  if (expr->IsObjectLiteral()) {
    return expr->AsObjectLiteral()->builder()->InitDepthAndFlags();
  }
  DCHECK(expr->IsRegExpLiteral());
}

bool MaterializedLiteral::NeedsInitialAllocationSite(

) {
  if (IsArrayLiteral()) {
    return AsArrayLiteral()->builder()->needs_initial_allocation_site();
  }
  if (IsObjectLiteral()) {
    return AsObjectLiteral()->builder()->needs_initial_allocation_site();
  }
  DCHECK(IsRegExpLiteral());
  return false;
}

template <typename IsolateT>
void LiteralBoilerplateBuilder::BuildConstants(IsolateT* isolate,
                                               MaterializedLiteral* expr) {
  if (expr->IsArrayLiteral()) {
    expr->AsArrayLiteral()->builder()->BuildBoilerplateDescription(isolate);
    return;
  }
  if (expr->IsObjectLiteral()) {
    expr->AsObjectLiteral()->builder()->BuildBoilerplateDescription(isolate);
    return;
  }
  DCHECK(expr->IsRegExpLiteral());
}
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT) void LiteralBoilerplateBuilder::
    BuildConstants(Isolate* isolate, MaterializedLiteral* expr);
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT) void LiteralBoilerplateBuilder::
    BuildConstants(LocalIsolate* isolate, MaterializedLiteral* expr);

template <typename IsolateT>
Handle<TemplateObjectDescription> GetTemplateObject::GetOrBuildDescription(
    IsolateT* isolate) {
  DirectHandle<FixedArray> raw_strings_handle =
      isolate->factory()->NewFixedArray(this->raw_strings()->length(),
                                        AllocationType::kOld);
  bool raw_and_cooked_match = true;
  {
    DisallowGarbageCollection no_gc;
    Tagged<FixedArray> raw_strings = *raw_strings_handle;
    uint32_t raw_strings_len = raw_strings->ulength().value();
    for (uint32_t i = 0; i < raw_strings_len; ++i) {
      if (this->raw_strings()->at(i) != this->cooked_strings()->at(i)) {
        // If the AstRawStrings don't match, then neither should the allocated
        // Strings, since the AstValueFactory should have deduplicated them
        // already.
        DCHECK_IMPLIES(this->cooked_strings()->at(i) != nullptr,
                       *this->cooked_strings()->at(i)->string() !=
                           *this->raw_strings()->at(i)->string());

        raw_and_cooked_match = false;
      }
      raw_strings->set(i, *this->raw_strings()->at(i)->string());
    }
  }
  DirectHandle<FixedArray> cooked_strings_handle = raw_strings_handle;
  if (!raw_and_cooked_match) {
    cooked_strings_handle = isolate->factory()->NewFixedArray(
        this->cooked_strings()->length(), AllocationType::kOld);
    DisallowGarbageCollection no_gc;
    Tagged<FixedArray> cooked_strings = *cooked_strings_handle;
    uint32_t cooked_strings_len = cooked_strings->ulength().value();
    ReadOnlyRoots roots(isolate);
    for (uint32_t i = 0; i < cooked_strings_len; ++i) {
      if (this->cooked_strings()->at(i) != nullptr) {
        cooked_strings->set(i, *this->cooked_strings()->at(i)->string());
      } else {
        cooked_strings->set(i, roots.undefined_value(), SKIP_WRITE_BARRIER);
      }
    }
  }
  return isolate->factory()->NewTemplateObjectDescription(
      raw_strings_handle, cooked_strings_handle);
}
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
    Handle<TemplateObjectDescription> GetTemplateObject::GetOrBuildDescription(
        Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
    Handle<TemplateObjectDescription> GetTemplateObject::GetOrBuildDescription(
        LocalIsolate* isolate);

static bool IsCommutativeOperationWithSmiLiteral(Token::Value op) {
  // Add is not commutative due to potential for string addition.
  return op == Token::kMul || op == Token::kBitAnd || op == Token::kBitOr ||
         op == Token::kBitXor;
}

// Check for the pattern: x + 1.
static bool MatchSmiLiteralOperation(Expression* left, Expression* right,
                                     Expression** expr, Tagged<Smi>* literal) {
  if (right->IsSmiLiteral()) {
    *expr = left;
    *literal = right->AsLiteral()->AsSmiLiteral();
    return true;
  }
  return false;
}

bool BinaryOperation::IsSmiLiteralOperation(Expression** subexpr,
                                            Tagged<Smi>* literal) {
  return MatchSmiLiteralOperation(left_, right_, subexpr, literal) ||
         (IsCommutativeOperationWithSmiLiteral(op()) &&
          MatchSmiLiteralOperation(right_, left_, subexpr, literal));
}

static bool IsVoidOfLiteral(Expression* expr) {
  UnaryOperation* maybe_unary = expr->AsUnaryOperation();
  return maybe_unary != nullptr && maybe_unary->op() == Token::kVoid &&
         maybe_unary->expression()->IsLiteral();
}

static bool MatchLiteralStrictCompareBoolean(Expression* left, Token::Value op,
                                             Expression* right,
                                             Expression** expr,
                                             Literal** literal) {
  if (left->IsBooleanLiteral() && op == Token::kEqStrict) {
    *expr = right;
    *literal = left->AsLiteral();
    return true;
  }
  return false;
}

bool CompareOperation::IsLiteralStrictCompareBoolean(Expression** expr,
                                                     Literal** literal) {
  return MatchLiteralStrictCompareBoolean(left_, op(), right_, expr, literal) ||
         MatchLiteralStrictCompareBoolean(right_, op(), left_, expr, literal);
}

// Check for the pattern: void <literal> equals <expression> or
// undefined equals <expression>
static bool MatchLiteralCompareUndefined(Expression* left, Token::Value op,
                                         Expression* right, Expression** expr) {
  if (IsVoidOfLiteral(left) && Token::IsEqualityOp(op)) {
    *expr = right;
    return true;
  }
  if (left->IsUndefinedLiteral() && Token::IsEqualityOp(op)) {
    *expr = right;
    return true;
  }
  return false;
}

bool CompareOperation::IsLiteralCompareUndefined(Expression** expr) {
  return MatchLiteralCompareUndefined(left_, op(), right_, expr) ||
         MatchLiteralCompareUndefined(right_, op(), left_, expr);
}

// Check for the pattern: null equals <expression>
static bool MatchLiteralCompareNull(Expression* left, Token::Value op,
                                    Expression* right, Expression** expr) {
  if (left->IsNullLiteral() && Token::IsEqualityOp(op)) {
    *expr = right;
    return true;
  }
  return false;
}

bool CompareOperation::IsLiteralCompareNull(Expression** expr) {
  return MatchLiteralCompareNull(left_, op(), right_, expr) ||
         MatchLiteralCompareNull(right_, op(), left_, expr);
}

static bool MatchLiteralCompareEqualVariable(Expression* left, Token::Value op,
                                             Expression* right,
                                             Expression** expr,
                                             Literal** literal) {
  if (Token::IsEqualityOp(op) && left->AsVariableProxy() &&
      right->IsStringLiteral()) {
    *expr = left->AsVariableProxy();
    *literal = right->AsLiteral();
    return true;
  }
  return false;
}

bool CompareOperation::IsLiteralCompareEqualVariable(Expression** expr,
                                                     Literal** literal) {
  return (
      MatchLiteralCompareEqualVariable(left_, op(), right_, expr, literal) ||
      MatchLiteralCompareEqualVariable(right_, op(), left_, expr, literal));
}

void CallBase::ComputeSpreadPosition() {
  int arguments_length = arguments_.length();
  int first_spread_index = 0;
  for (; first_spread_index < arguments_length; first_spread_index++) {
    if (arguments_.at(first_spread_index)->IsSpread()) break;
  }
  SpreadPosition position;
  if (first_spread_index == arguments_length - 1) {
    position = kHasFinalSpread;
  } else {
    DCHECK_LT(first_spread_index, arguments_length - 1);
    position = kHasNonFinalSpread;
  }
  bit_field_ |= SpreadPositionField::encode(position);
}

Call::CallType Call::GetCallType() const {
  VariableProxy* proxy = expression()->AsVariableProxy();
  if (proxy != nullptr) {
    if (proxy->var()->IsUnallocated()) {
      return GLOBAL_CALL;
    } else if (proxy->var()->IsLookupSlot()) {
      // Calls going through 'with' always use VariableMode::kDynamic rather
      // than VariableMode::kDynamicLocal or VariableMode::kDynamicGlobal.
      return proxy->var()->mode() == VariableMode::kDynamic ? WITH_CALL
                                                            : OTHER_CALL;
    }
  }

  if (expression()->IsSuperCallReference()) return SUPER_CALL;

  Property* property = expression()->AsProperty();
  bool is_optional_chain = false;
  if (V8_UNLIKELY(property == nullptr && expression()->IsOptionalChain())) {
    is_optional_chain = true;
    property = expression()->AsOptionalChain()->expression()->AsProperty();
  }
  if (property != nullptr) {
    if (property->IsPrivateReference()) {
      if (is_optional_chain) return PRIVATE_OPTIONAL_CHAIN_CALL;
      return PRIVATE_CALL;
    }
    bool is_super = property->IsSuperAccess();
    // `super?.` is not syntactically valid, so a property load cannot be both
    // super and an optional chain.
    DCHECK(!is_super || !is_optional_chain);
    if (property->key()->IsPropertyName()) {
      if (is_super) return NAMED_SUPER_PROPERTY_CALL;
      if (is_optional_chain) return NAMED_OPTIONAL_CHAIN_PROPERTY_CALL;
      return NAMED_PROPERTY_CALL;
    } else {
      if (is_super) return KEYED_SUPER_PROPERTY_CALL;
      if (is_optional_chain) return KEYED_OPTIONAL_CHAIN_PROPERTY_CALL;
      return KEYED_PROPERTY_CALL;
    }
  }

  return OTHER_CALL;
}

CaseClause::CaseClause(Zone* zone, Expression* label,
                       const ScopedPtrList<Statement>& statements)
    : label_(label), statements_(statements.ToConstVector(), zone) {}

bool Literal::IsPropertyName() const {
  if (type() != kString) return false;
  uint32_t index;
  return !string_->AsArrayIndex(&index);
}

bool Literal::ToUint32(uint32_t* value) const {
  switch (type()) {
    case kString:
      return string_->AsArrayIndex(value);
    case kSmi:
      if (smi_ < 0) return false;
      *value = static_cast<uint32_t>(smi_);
      return true;
    case kHeapNumber:
      return DoubleToUint32IfEqualToSelf(AsNumber(), value);
    default:
      return false;
  }
}

bool Literal::AsArrayIndex(uint32_t* value) const {
  return ToUint32(value) && *value != kMaxUInt32;
}

template <typename IsolateT>
DirectHandle<Object> Literal::BuildValue(IsolateT* isolate) const {
  switch (type()) {
    case kSmi:
      return direct_handle(Smi::FromInt(smi_), isolate);
    case kHeapNumber:
      return isolate->factory()->template NewNumber<AllocationType::kOld>(
          number_);
    case kString:
      return string_->string();
    case kConsString:
      return cons_string_->AllocateFlat(isolate);
    case kBoolean:
      return isolate->factory()->ToBoolean(boolean_);
    case kNull:
      return isolate->factory()->null_value();
    case kUndefined:
      return isolate->factory()->undefined_value();
    case kTheHole:
      return isolate->factory()->the_hole_value();
    case kBigInt:
      // This should never fail: the parser will never create a BigInt
      // literal that cannot be allocated.
      return BigIntLiteral(isolate, bigint_.c_str()).ToHandleChecked();
  }
  UNREACHABLE();
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    DirectHandle<Object> Literal::BuildValue(Isolate* isolate) const;
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
    DirectHandle<Object> Literal::BuildValue(LocalIsolate* isolate) const;

bool Literal::ToBooleanIsTrue() const {
  switch (type()) {
    case kSmi:
      return smi_ != 0;
    case kHeapNumber:
      return DoubleToBoolean(number_);
    case kString:
      return !string_->IsEmpty();
    case kConsString:
      return !cons_string_->IsEmpty();
    case kNull:
    case kUndefined:
      return false;
    case kBoolean:
      return boolean_;
    case kBigInt: {
      const char* bigint_str = bigint_.c_str();
      size_t length = strlen(bigint_str);
      DCHECK_GT(length, 0);
      if (length == 1 && bigint_str[0] == '0') return false;
      // Skip over any radix prefix; BigInts with length > 1 only
      // begin with zero if they include a radix.
      for (size_t i = (bigint_str[0] == '0') ? 2 : 0; i < length; ++i) {
        if (bigint_str[i] != '0') return true;
      }
      return false;
    }
    case kTheHole:
      UNREACHABLE();
  }
  UNREACHABLE();
}

uint32_t Literal::Hash() {
  DCHECK(IsRawString() || IsNumber());
  uint32_t index;
  if (AsArrayIndex(&index)) {
    // Treat array indices as numbers, so that array indices are de-duped
    // correctly even if one of them is a string and the other is a number.
    return static_cast<uint32_t>(base::hash64(index));
  }
  return IsRawString() ? AsRawString()->Hash()
                       : static_cast<uint32_t>(
                             base::hash64(base::double_to_uint64(AsNumber())));
}

// static
bool Literal::Match(void* a, void* b) {
  Literal* x = static_cast<Literal*>(a);
  Literal* y = static_cast<Literal*>(b);
  uint32_t index_x;
  uint32_t index_y;
  if (x->AsArrayIndex(&index_x)) {
    return y->AsArrayIndex(&index_y) && index_x == index_y;
  }
  return (x->IsRawString() && y->IsRawString() &&
          x->AsRawString() == y->AsRawString()) ||
         (x->IsNumber() && y->IsNumber() && x->AsNumber() == y->AsNumber());
}

Literal* AstNodeFactory::NewNumberLiteral(double number, int pos) {
  int int_value;
  if (DoubleToSmiInteger(number, &int_value)) {
    return NewSmiLiteral(int_value, pos);
  }
  return zone_->New<Literal>(number, pos);
}

}  // namespace internal
}  // namespace v8