text: // 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/builtins/builtins-constructor.h"
  #include "src/builtins/builtins.h"
  #include "src/contexts.h"
  #include "src/conversions-inl.h"
  #include "src/double.h"
  #include "src/elements.h"
  #include "src/objects-inl.h"
  #include "src/objects/literal-objects-inl.h"
  #include "src/objects/literal-objects.h"
  #include "src/objects/map.h"
  #include "src/property-details.h"
  #include "src/property.h"
  #include "src/string-stream.h"
  
  namespace v8 {
  namespace internal {
  
  // ----------------------------------------------------------------------------
  // Implementation of other node functionality.
  
  #ifdef DEBUG
  
  static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) {
    switch (idx) {
  #define NATIVE_CONTEXT_FIELDS_IDX(NAME, Type, name) \
    case Context::NAME:                               \
      return #name;
  
      NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELDS_IDX)
  #undef NATIVE_CONTEXT_FIELDS_IDX
  
      default:
        break;
    }
  
    return "UnknownIntrinsicIndex";
  }
  
  void AstNode::Print() { Print(Isolate::Current()); }
  
  void AstNode::Print(Isolate* isolate) {
    AllowHandleDereference allow_deref;
    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::IsPropertyName() const {
    return IsLiteral() && AsLiteral()->IsPropertyName();
  }
  
  bool Expression::IsNullLiteral() const {
    return IsLiteral() && AsLiteral()->type() == Literal::kNull;
  }
  
  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::ToBooleanIsTrue() const {
    return IsLiteral() && AsLiteral()->ToBooleanIsTrue();
  }
  
  bool Expression::ToBooleanIsFalse() const {
    return IsLiteral() && AsLiteral()->ToBooleanIsFalse();
  }
  
  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());
  }
  
  bool Statement::IsJump() const {
    switch (node_type()) {
  #define JUMP_NODE_LIST(V) \
    V(Block)                \
    V(ExpressionStatement)  \
    V(ContinueStatement)    \
    V(BreakStatement)       \
    V(ReturnStatement)      \
    V(IfStatement)
  #define GENERATE_CASE(Node) \
    case k##Node:             \
      return static_cast<const Node*>(this)->IsJump();
      JUMP_NODE_LIST(GENERATE_CASE)
  #undef GENERATE_CASE
  #undef JUMP_NODE_LIST
      default:
        return false;
    }
  }
  
  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) {
    DCHECK_EQ(raw_name(), var->raw_name());
    set_var(var);
    set_is_resolved();
    var->set_is_used();
    if (is_assigned()) var->set_maybe_assigned();
  }
  
  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_inferred_name(Handle<String> inferred_name) {
    DCHECK(!inferred_name.is_null());
    inferred_name_ = inferred_name;
    DCHECK(raw_inferred_name_ == nullptr || raw_inferred_name_->IsEmpty());
    raw_inferred_name_ = nullptr;
    scope()->set_has_inferred_function_name(true);
  }
  
  void FunctionLiteral::set_raw_inferred_name(
      const AstConsString* raw_inferred_name) {
    DCHECK_NOT_NULL(raw_inferred_name);
    raw_inferred_name_ = raw_inferred_name;
    DCHECK(inferred_name_.is_null());
    inferred_name_ = Handle<String>();
    scope()->set_has_inferred_function_name(true);
  }
  
  bool FunctionLiteral::ShouldEagerCompile() const {
    return scope()->ShouldEagerCompile();
  }
  
  void FunctionLiteral::SetShouldEagerCompile() {
    scope()->set_should_eager_compile();
  }
  
  bool FunctionLiteral::AllowsLazyCompilation() {
    return scope()->AllowsLazyCompilation();
  }
  
  Handle<String> FunctionLiteral::name(Isolate* isolate) const {
    return raw_name_ ? raw_name_->string() : isolate->factory()->empty_string();
  }
  
  int FunctionLiteral::start_position() const {
    return scope()->start_position();
  }
  
  
  int FunctionLiteral::end_position() const {
    return scope()->end_position();
  }
  
  
  LanguageMode FunctionLiteral::language_mode() const {
    return scope()->language_mode();
  }
  
  FunctionKind FunctionLiteral::kind() const { return scope()->function_kind(); }
  
  bool FunctionLiteral::NeedsHomeObject(Expression* expr) {
    if (expr == nullptr || !expr->IsFunctionLiteral()) return false;
    DCHECK_NOT_NULL(expr->AsFunctionLiteral()->scope());
    return expr->AsFunctionLiteral()->scope()->NeedsHomeObject();
  }
  
  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 (!inferred_name_.is_null()) {
      AllowHandleDereference allow_deref;
      return 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]);
    memcpy(result.get(), result_vec.data(), result_vec.size());
    result[result_vec.size()] = '\0';
    return result;
  }
  
  ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value,
                                               Kind kind, bool is_computed_name)
      : LiteralProperty(key, value, is_computed_name),
        kind_(kind),
        emit_store_(true) {}
  
  ObjectLiteralProperty::ObjectLiteralProperty(AstValueFactory* ast_value_factory,
                                               Expression* key, Expression* value,
                                               bool is_computed_name)
      : LiteralProperty(key, value, is_computed_name), emit_store_(true) {
    if (!is_computed_name && key->AsLiteral()->IsString() &&
        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_var_(nullptr) {}
  
  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;
  
    ZoneAllocationPolicy allocator(zone);
  
    CustomMatcherZoneHashMap table(
        Literal::Match, ZoneHashMap::kDefaultHashMapCapacity, allocator);
    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();
      ZoneHashMap::Entry* entry = table.LookupOrInsert(literal, hash, allocator);
      if (entry->value == nullptr) {
        entry->value = property;
      } else {
        // We already have a later definition of this property, so we don't need
        // to emit a store for the current one.
        //
        // There are two subtleties here.
        //
        // (1) Emitting a store might actually be incorrect. For example, in {get
        // foo() {}, foo: 42}, the getter store would override the data property
        // (which, being a non-computed compile-time valued property, is already
        // part of the initial literal object.
        //
        // (2) If the later definition is an accessor (say, a getter), and the
        // current definition is a complementary accessor (here, a setter), then
        // we still must emit a store for the current definition.
  
        auto later_kind =
            static_cast<ObjectLiteral::Property*>(entry->value)->kind();
        bool complementary_accessors =
            (property->kind() == GETTER && later_kind == SETTER) ||
            (property->kind() == SETTER && later_kind == GETTER);
        if (!complementary_accessors) {
          property->set_emit_store(false);
          if (later_kind == GETTER || later_kind == SETTER) {
            entry->value = property;
          }
        }
      }
    }
  }
  
  void ObjectLiteral::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 ObjectLiteral::InitDepthAndFlags() {
    if (is_initialized()) return depth();
    bool is_simple = true;
    bool has_seen_prototype = false;
    bool needs_initial_allocation_site = false;
    int depth_acc = 1;
    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) {
        int subliteral_depth = literal->InitDepthAndFlags() + 1;
        if (subliteral_depth > depth_acc) depth_acc = subliteral_depth;
        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 = 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));
    return depth_acc;
  }
  
  void ObjectLiteral::BuildBoilerplateDescription(Isolate* isolate) {
    if (!boilerplate_description_.is_null()) return;
  
    int index_keys = 0;
    bool has_seen_proto = false;
    for (int i = 0; i < properties()->length(); i++) {
      ObjectLiteral::Property* property = properties()->at(i);
      if (property->IsPrototype()) {
        has_seen_proto = true;
        continue;
      }
      if (property->is_computed_name()) continue;
  
      Literal* key = property->key()->AsLiteral();
      if (!key->IsPropertyName()) index_keys++;
    }
  
    Handle<ObjectBoilerplateDescription> boilerplate_description =
        isolate->factory()->NewObjectBoilerplateDescription(
            boilerplate_properties_, properties()->length(), index_keys,
            has_seen_proto);
  
    int position = 0;
    for (int i = 0; i < properties()->length(); i++) {
      ObjectLiteral::Property* property = properties()->at(i);
      if (property->IsPrototype()) continue;
  
      if (static_cast<uint32_t>(position) == boilerplate_properties_) {
        DCHECK(property->is_computed_name());
        break;
      }
      DCHECK(!property->is_computed_name());
  
      MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
      if (m_literal != nullptr) {
        m_literal->BuildConstants(isolate);
      }
  
      // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined
      // value 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;
      Handle<Object> key =
          key_literal->AsArrayIndex(&element_index)
              ? isolate->factory()->NewNumberFromUint(element_index)
              : Handle<Object>::cast(key_literal->AsRawPropertyName()->string());
  
      Handle<Object> value = GetBoilerplateValue(property->value(), isolate);
  
      // Add name, value pair to the fixed array.
      boilerplate_description->set_key_value(position++, *key, *value);
    }
  
    boilerplate_description->set_flags(EncodeLiteralType());
  
    boilerplate_description_ = boilerplate_description;
  }
  
  bool ObjectLiteral::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;
  }
  
  int ArrayLiteral::InitDepthAndFlags() {
    if (is_initialized()) return depth();
  
    int constants_length =
        first_spread_index_ >= 0 ? first_spread_index_ : values()->length();
  
    // Fill in the literals.
    bool is_simple = first_spread_index_ < 0;
    int depth_acc = 1;
    int array_index = 0;
    for (; array_index < constants_length; array_index++) {
      Expression* element = values()->at(array_index);
      MaterializedLiteral* literal = element->AsMaterializedLiteral();
      if (literal != nullptr) {
        int subliteral_depth = literal->InitDepthAndFlags() + 1;
        if (subliteral_depth > depth_acc) depth_acc = subliteral_depth;
      }
  
      if (!element->IsCompileTimeValue()) {
        is_simple = false;
      }
    }
  
    set_depth(depth_acc);
    set_is_simple(is_simple);
    // Array literals always need an initial allocation site to properly track
    // elements transitions.
    set_needs_initial_allocation_site(true);
    return depth_acc;
  }
  
  void ArrayLiteral::BuildBoilerplateDescription(Isolate* isolate) {
    if (!boilerplate_description_.is_null()) return;
  
    int constants_length =
        first_spread_index_ >= 0 ? first_spread_index_ : values()->length();
    ElementsKind kind = FIRST_FAST_ELEMENTS_KIND;
    Handle<FixedArray> fixed_array =
        isolate->factory()->NewFixedArrayWithHoles(constants_length);
  
    // Fill in the literals.
    bool is_holey = false;
    int array_index = 0;
    for (; array_index < constants_length; array_index++) {
      Expression* element = values()->at(array_index);
      DCHECK(!element->IsSpread());
      MaterializedLiteral* m_literal = element->AsMaterializedLiteral();
      if (m_literal != nullptr) {
        m_literal->BuildConstants(isolate);
      }
  
      // New handle scope here, needs to be after BuildContants().
      HandleScope scope(isolate);
      Handle<Object> boilerplate_value = GetBoilerplateValue(element, isolate);
      if (boilerplate_value->IsTheHole(isolate)) {
        is_holey = true;
        continue;
      }
  
      if (boilerplate_value->IsUninitialized(isolate)) {
        boilerplate_value = handle(Smi::kZero, isolate);
      }
  
      kind = GetMoreGeneralElementsKind(kind,
                                        boilerplate_value->OptimalElementsKind());
      fixed_array->set(array_index, *boilerplate_value);
    }
  
    if (is_holey) kind = GetHoleyElementsKind(kind);
  
    // Simple and shallow arrays can be lazily copied, we transform the
    // elements array to a copy-on-write array.
    if (is_simple() && depth() == 1 && array_index > 0 &&
        IsSmiOrObjectElementsKind(kind)) {
      fixed_array->set_map(ReadOnlyRoots(isolate).fixed_cow_array_map());
    }
  
    Handle<FixedArrayBase> elements = fixed_array;
    if (IsDoubleElementsKind(kind)) {
      ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
      elements = isolate->factory()->NewFixedDoubleArray(constants_length);
      // We are copying from non-fast-double to fast-double.
      ElementsKind from_kind = TERMINAL_FAST_ELEMENTS_KIND;
      accessor->CopyElements(isolate, fixed_array, from_kind, elements,
                             constants_length);
    }
  
    boilerplate_description_ =
        isolate->factory()->NewArrayBoilerplateDescription(kind, elements);
  }
  
  bool ArrayLiteral::IsFastCloningSupported() const {
    return depth() <= 1 &&
           values_.length() <=
               ConstructorBuiltins::kMaximumClonedShallowArrayElements;
  }
  
  bool MaterializedLiteral::IsSimple() const {
    if (IsArrayLiteral()) return AsArrayLiteral()->is_simple();
    if (IsObjectLiteral()) return AsObjectLiteral()->is_simple();
    DCHECK(IsRegExpLiteral());
    return false;
  }
  
  Handle<Object> MaterializedLiteral::GetBoilerplateValue(Expression* expression,
                                                          Isolate* isolate) {
    if (expression->IsLiteral()) {
      return expression->AsLiteral()->BuildValue(isolate);
    }
    if (expression->IsCompileTimeValue()) {
      if (expression->IsObjectLiteral()) {
        ObjectLiteral* object_literal = expression->AsObjectLiteral();
        DCHECK(object_literal->is_simple());
        return object_literal->boilerplate_description();
      } else {
        DCHECK(expression->IsArrayLiteral());
        ArrayLiteral* array_literal = expression->AsArrayLiteral();
        DCHECK(array_literal->is_simple());
        return array_literal->boilerplate_description();
      }
    }
    return isolate->factory()->uninitialized_value();
  }
  
  int MaterializedLiteral::InitDepthAndFlags() {
    if (IsArrayLiteral()) return AsArrayLiteral()->InitDepthAndFlags();
    if (IsObjectLiteral()) return AsObjectLiteral()->InitDepthAndFlags();
    DCHECK(IsRegExpLiteral());
    return 1;
  }
  
  bool MaterializedLiteral::NeedsInitialAllocationSite() {
    if (IsArrayLiteral()) {
      return AsArrayLiteral()->needs_initial_allocation_site();
    }
    if (IsObjectLiteral()) {
      return AsObjectLiteral()->needs_initial_allocation_site();
    }
    DCHECK(IsRegExpLiteral());
    return false;
  }
  
  void MaterializedLiteral::BuildConstants(Isolate* isolate) {
    if (IsArrayLiteral()) {
      AsArrayLiteral()->BuildBoilerplateDescription(isolate);
      return;
    }
    if (IsObjectLiteral()) {
      AsObjectLiteral()->BuildBoilerplateDescription(isolate);
      return;
    }
    DCHECK(IsRegExpLiteral());
  }
  
  Handle<TemplateObjectDescription> GetTemplateObject::GetOrBuildDescription(
      Isolate* isolate) {
    Handle<FixedArray> raw_strings =
        isolate->factory()->NewFixedArray(this->raw_strings()->length(), TENURED);
    bool raw_and_cooked_match = true;
    for (int i = 0; i < raw_strings->length(); ++i) {
      if (this->cooked_strings()->at(i) == nullptr ||
          *this->raw_strings()->at(i)->string() !=
              *this->cooked_strings()->at(i)->string()) {
        raw_and_cooked_match = false;
      }
      raw_strings->set(i, *this->raw_strings()->at(i)->string());
    }
    Handle<FixedArray> cooked_strings = raw_strings;
    if (!raw_and_cooked_match) {
      cooked_strings = isolate->factory()->NewFixedArray(
          this->cooked_strings()->length(), TENURED);
      for (int i = 0; i < cooked_strings->length(); ++i) {
        if (this->cooked_strings()->at(i) != nullptr) {
          cooked_strings->set(i, *this->cooked_strings()->at(i)->string());
        } else {
          cooked_strings->set(i, ReadOnlyRoots(isolate).undefined_value());
        }
      }
    }
    return isolate->factory()->NewTemplateObjectDescription(raw_strings,
                                                            cooked_strings);
  }
  
  static bool IsCommutativeOperationWithSmiLiteral(Token::Value op) {
    // Add is not commutative due to potential for string addition.
    return op == Token::MUL || op == Token::BIT_AND || op == Token::BIT_OR ||
           op == Token::BIT_XOR;
  }
  
  // Check for the pattern: x + 1.
  static bool MatchSmiLiteralOperation(Expression* left, Expression* right,
                                       Expression** expr, Smi* literal) {
    if (right->IsSmiLiteral()) {
      *expr = left;
      *literal = right->AsLiteral()->AsSmiLiteral();
      return true;
    }
    return false;
  }
  
  bool BinaryOperation::IsSmiLiteralOperation(Expression** subexpr,
                                              Smi* literal) {
    return MatchSmiLiteralOperation(left_, right_, subexpr, literal) ||
           (IsCommutativeOperationWithSmiLiteral(op()) &&
            MatchSmiLiteralOperation(right_, left_, subexpr, literal));
  }
  
  static bool IsTypeof(Expression* expr) {
    UnaryOperation* maybe_unary = expr->AsUnaryOperation();
    return maybe_unary != nullptr && maybe_unary->op() == Token::TYPEOF;
  }
  
  // Check for the pattern: typeof <expression> equals <string literal>.
  static bool MatchLiteralCompareTypeof(Expression* left, Token::Value op,
                                        Expression* right, Expression** expr,
                                        Literal** literal) {
    if (IsTypeof(left) && right->IsStringLiteral() && Token::IsEqualityOp(op)) {
      *expr = left->AsUnaryOperation()->expression();
      *literal = right->AsLiteral();
      return true;
    }
    return false;
  }
  
  bool CompareOperation::IsLiteralCompareTypeof(Expression** expr,
                                                Literal** literal) {
    return MatchLiteralCompareTypeof(left_, op(), right_, expr, literal) ||
           MatchLiteralCompareTypeof(right_, op(), left_, expr, literal);
  }
  
  
  static bool IsVoidOfLiteral(Expression* expr) {
    UnaryOperation* maybe_unary = expr->AsUnaryOperation();
    return maybe_unary != nullptr && maybe_unary->op() == Token::VOID &&
           maybe_unary->expression()->IsLiteral();
  }
  
  
  // 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);
  }
  
  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();
    if (property != nullptr) {
      bool is_super = property->IsSuperAccess();
      if (property->key()->IsPropertyName()) {
        return is_super ? NAMED_SUPER_PROPERTY_CALL : NAMED_PROPERTY_CALL;
      } else {
        return is_super ? KEYED_SUPER_PROPERTY_CALL : KEYED_PROPERTY_CALL;
      }
    }
  
    if (expression()->IsResolvedProperty()) {
      return RESOLVED_PROPERTY_CALL;
    }
  
    return OTHER_CALL;
  }
  
  CaseClause::CaseClause(Zone* zone, Expression* label,
                         const ScopedPtrList<Statement>& statements)
      : label_(label), statements_(0, nullptr) {
    statements.CopyTo(&statements_, 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;
  }
  
  Handle<Object> Literal::BuildValue(Isolate* isolate) const {
    switch (type()) {
      case kSmi:
        return handle(Smi::FromInt(smi_), isolate);
      case kHeapNumber:
        return isolate->factory()->NewNumber(number_, TENURED);
      case kString:
        return string_->string();
      case kSymbol:
        return isolate->factory()->home_object_symbol();
      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();
  }
  
  bool Literal::ToBooleanIsTrue() const {
    switch (type()) {
      case kSmi:
        return smi_ != 0;
      case kHeapNumber:
        return DoubleToBoolean(number_);
      case kString:
        return !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 kSymbol:
        return true;
      case kTheHole:
        UNREACHABLE();
    }
    UNREACHABLE();
  }
  
  uint32_t Literal::Hash() {
    return IsString() ? AsRawString()->Hash()
                      : ComputeLongHash(double_to_uint64(AsNumber()));
  }
  
  
  // static
  bool Literal::Match(void* a, void* b) {
    Literal* x = static_cast<Literal*>(a);
    Literal* y = static_cast<Literal*>(b);
    return (x->IsString() && y->IsString() &&
            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 new (zone_) Literal(number, pos);
  }
  
  const char* CallRuntime::debug_name() {
  #ifdef DEBUG
    return is_jsruntime() ? NameForNativeContextIntrinsicIndex(context_index_)
                          : function_->name;
  #else
    return is_jsruntime() ? "(context function)" : function_->name;
  #endif  // DEBUG
  }
  
  #define RETURN_LABELS(NodeType) \
    case k##NodeType:             \
      return static_cast<const NodeType*>(this)->labels();
  
  ZonePtrList<const AstRawString>* BreakableStatement::labels() const {
    switch (node_type()) {
      BREAKABLE_NODE_LIST(RETURN_LABELS)
      ITERATION_NODE_LIST(RETURN_LABELS)
      default:
        UNREACHABLE();
    }
  }
  
  #undef RETURN_LABELS
  
  }  // namespace internal
  }  // namespace v8
  