// 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/code-stubs.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;
|
}
|
}
|
|
BreakableStatement* AstNode::AsBreakableStatement() {
|
switch (node_type()) {
|
BREAKABLE_NODE_LIST(RETURN_NODE);
|
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 {
|
// We don't want expressions wrapped inside RewritableExpression to be
|
// considered as valid reference expressions, as they will be rewritten
|
// to something (most probably involving a do expression).
|
if (IsRewritableExpression()) return false;
|
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) {
|
bit_field_ |= IsThisField::encode(var->is_this()) |
|
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((is_this() && var->is_this()) || 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)
|
: LiteralProperty(key, value, is_computed_name),
|
kind_(kind),
|
is_static_(is_static),
|
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;
|
}
|
|
bool ArrayLiteral::is_empty() const {
|
DCHECK(is_initialized());
|
return values()->is_empty() && (boilerplate_description().is_null() ||
|
boilerplate_description()->is_empty());
|
}
|
|
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(Expression* label, ZonePtrList<Statement>* statements)
|
: label_(label), statements_(statements) {}
|
|
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
|