Commit 20fba512 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Adds Object literal support.

Adds Object literal support to the interpreter. Adds the following bytecodes:
 - ToName
 - CreateObjectLiteral.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1386313005

Cr-Commit-Position: refs/heads/master@{#31253}
parent 52225f39
......@@ -315,7 +315,7 @@ void BytecodeGraphBuilder::VisitKeyedStoreICStrict(
}
void BytecodeGraphBuilder::VisitCreateClosure(
void BytecodeGraphBuilder::VisitKeyedStoreICGeneric(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
......@@ -333,13 +333,19 @@ void BytecodeGraphBuilder::VisitPopContext(
}
void BytecodeGraphBuilder::VisitCreateClosure(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitCreateArrayLiteral(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitKeyedStoreICGeneric(
void BytecodeGraphBuilder::VisitCreateObjectLiteral(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
......@@ -513,7 +519,13 @@ void BytecodeGraphBuilder::VisitTestInstanceOf(
void BytecodeGraphBuilder::VisitToBoolean(
const interpreter::BytecodeArrayIterator& ToBoolean) {
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitToName(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
......
......@@ -371,6 +371,19 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
int literal_index, int flags) {
DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bytes.
if (FitsInIdx8Operand(literal_index)) {
Output(Bytecode::kCreateObjectLiteral, static_cast<uint8_t>(literal_index),
static_cast<uint8_t>(flags));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
Output(Bytecode::kPushContext, context.ToOperand());
return *this;
......@@ -414,6 +427,12 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() {
Output(Bytecode::kToName);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
......
......@@ -86,6 +86,7 @@ class BytecodeArrayBuilder {
// Literals creation. Constant elements should be in the accumulator.
BytecodeArrayBuilder& CreateArrayLiteral(int literal_index, int flags);
BytecodeArrayBuilder& CreateObjectLiteral(int literal_index, int flags);
// Push the context in accumulator as the new context, and store in register
// |context|.
......@@ -121,6 +122,7 @@ class BytecodeArrayBuilder {
// Casts
BytecodeArrayBuilder& CastAccumulatorToBoolean();
BytecodeArrayBuilder& CastAccumulatorToName();
// Flow Control.
BytecodeArrayBuilder& Bind(BytecodeLabel* label);
......
......@@ -7,6 +7,7 @@
#include <stack>
#include "src/compiler.h"
#include "src/full-codegen/full-codegen.h"
#include "src/interpreter/control-flow-builders.h"
#include "src/objects.h"
#include "src/parser.h"
......@@ -497,7 +498,185 @@ void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
UNIMPLEMENTED();
// Deep-copy the literal boilerplate.
builder()
->LoadLiteral(expr->constant_properties())
.CreateObjectLiteral(expr->literal_index(), expr->ComputeFlags(true));
TemporaryRegisterScope temporary_register_scope(builder());
Register literal;
// Store computed values into the literal.
bool literal_in_accumulator = true;
int property_index = 0;
AccessorTable accessor_table(zone());
for (; property_index < expr->properties()->length(); property_index++) {
TemporaryRegisterScope inner_temporary_register_scope(builder());
ObjectLiteral::Property* property = expr->properties()->at(property_index);
if (property->is_computed_name()) break;
if (property->IsCompileTimeValue()) continue;
if (literal_in_accumulator) {
literal = temporary_register_scope.NewRegister();
builder()->StoreAccumulatorInRegister(literal);
literal_in_accumulator = false;
}
Literal* literal_key = property->key()->AsLiteral();
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
UNREACHABLE();
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
DCHECK(!CompileTimeValue::IsCompileTimeValue(property->value()));
// Fall through.
case ObjectLiteral::Property::COMPUTED: {
// It is safe to use [[Put]] here because the boilerplate already
// contains computed properties with an uninitialized value.
if (literal_key->value()->IsInternalizedString()) {
if (property->emit_store()) {
Register name = inner_temporary_register_scope.NewRegister();
builder()
->LoadLiteral(literal_key->AsPropertyName())
.StoreAccumulatorInRegister(name);
Visit(property->value());
builder()->StoreNamedProperty(literal, name,
feedback_index(property->GetSlot(0)),
language_mode());
} else {
Visit(property->value());
}
} else {
Register key = inner_temporary_register_scope.NewRegister();
Register value = inner_temporary_register_scope.NewRegister();
Register language = inner_temporary_register_scope.NewRegister();
DCHECK(Register::AreContiguous(literal, key, value, language));
Visit(property->key());
builder()->StoreAccumulatorInRegister(key);
Visit(property->value());
builder()->StoreAccumulatorInRegister(value);
if (property->emit_store()) {
builder()
->LoadLiteral(Smi::FromInt(SLOPPY))
.StoreAccumulatorInRegister(language)
.CallRuntime(Runtime::kSetProperty, literal, 4);
VisitSetHomeObject(value, literal, property);
}
}
break;
}
case ObjectLiteral::Property::PROTOTYPE: {
DCHECK(property->emit_store());
Register value = inner_temporary_register_scope.NewRegister();
DCHECK(Register::AreContiguous(literal, value));
Visit(property->value());
builder()->StoreAccumulatorInRegister(value).CallRuntime(
Runtime::kInternalSetPrototype, literal, 2);
break;
}
case ObjectLiteral::Property::GETTER:
if (property->emit_store()) {
accessor_table.lookup(literal_key)->second->getter = property;
}
break;
case ObjectLiteral::Property::SETTER:
if (property->emit_store()) {
accessor_table.lookup(literal_key)->second->setter = property;
}
break;
}
}
// Create nodes to define accessors, using only a single call to the runtime
// for each pair of corresponding getters and setters.
for (AccessorTable::Iterator it = accessor_table.begin();
it != accessor_table.end(); ++it) {
TemporaryRegisterScope inner_temporary_register_scope(builder());
Register name = inner_temporary_register_scope.NewRegister();
Register getter = inner_temporary_register_scope.NewRegister();
Register setter = inner_temporary_register_scope.NewRegister();
Register attr = inner_temporary_register_scope.NewRegister();
DCHECK(Register::AreContiguous(literal, name, getter, setter, attr));
Visit(it->first);
builder()->StoreAccumulatorInRegister(name);
VisitObjectLiteralAccessor(literal, it->second->getter, getter);
VisitObjectLiteralAccessor(literal, it->second->setter, setter);
builder()
->LoadLiteral(Smi::FromInt(NONE))
.StoreAccumulatorInRegister(attr)
.CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, literal, 5);
}
// Object literals have two parts. The "static" part on the left contains no
// computed property names, and so we can compute its map ahead of time; see
// Runtime_CreateObjectLiteralBoilerplate. The second "dynamic" part starts
// with the first computed property name and continues with all properties to
// its right. All the code from above initializes the static component of the
// object literal, and arranges for the map of the result to reflect the
// static order in which the keys appear. For the dynamic properties, we
// compile them into a series of "SetOwnProperty" runtime calls. This will
// preserve insertion order.
for (; property_index < expr->properties()->length(); property_index++) {
ObjectLiteral::Property* property = expr->properties()->at(property_index);
if (literal_in_accumulator) {
literal = temporary_register_scope.NewRegister();
builder()->StoreAccumulatorInRegister(literal);
literal_in_accumulator = false;
}
if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
DCHECK(property->emit_store());
TemporaryRegisterScope inner_temporary_register_scope(builder());
Register value = inner_temporary_register_scope.NewRegister();
DCHECK(Register::AreContiguous(literal, value));
Visit(property->value());
builder()->StoreAccumulatorInRegister(value).CallRuntime(
Runtime::kInternalSetPrototype, literal, 2);
continue;
}
TemporaryRegisterScope inner_temporary_register_scope(builder());
Register key = inner_temporary_register_scope.NewRegister();
Register value = inner_temporary_register_scope.NewRegister();
Register attr = inner_temporary_register_scope.NewRegister();
DCHECK(Register::AreContiguous(literal, key, value, attr));
Visit(property->key());
builder()->CastAccumulatorToName().StoreAccumulatorInRegister(key);
Visit(property->value());
builder()->StoreAccumulatorInRegister(value);
VisitSetHomeObject(value, literal, property);
builder()->LoadLiteral(Smi::FromInt(NONE)).StoreAccumulatorInRegister(attr);
Runtime::FunctionId function_id = static_cast<Runtime::FunctionId>(-1);
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::COMPUTED:
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
function_id = Runtime::kDefineDataPropertyUnchecked;
break;
case ObjectLiteral::Property::PROTOTYPE:
UNREACHABLE(); // Handled specially above.
break;
case ObjectLiteral::Property::GETTER:
function_id = Runtime::kDefineGetterPropertyUnchecked;
break;
case ObjectLiteral::Property::SETTER:
function_id = Runtime::kDefineSetterPropertyUnchecked;
break;
}
builder()->CallRuntime(function_id, literal, 4);
}
// Transform literals that contain functions to fast properties.
if (expr->has_function()) {
DCHECK(!literal_in_accumulator);
builder()->CallRuntime(Runtime::kToFastProperties, literal, 1);
}
if (!literal_in_accumulator) {
// Restore literal array into accumulator.
builder()->LoadAccumulatorWithRegister(literal);
}
}
......@@ -508,11 +687,11 @@ void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
.CreateArrayLiteral(expr->literal_index(), expr->ComputeFlags(true));
TemporaryRegisterScope temporary_register_scope(builder());
Register index, literal_array;
Register index, literal;
// Create nodes to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
bool literal_array_in_accumulator = true;
bool literal_in_accumulator = true;
for (int array_index = 0; array_index < expr->values()->length();
array_index++) {
Expression* subexpr = expr->values()->at(array_index);
......@@ -522,23 +701,23 @@ void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
UNIMPLEMENTED();
}
if (literal_array_in_accumulator) {
if (literal_in_accumulator) {
index = temporary_register_scope.NewRegister();
literal_array = temporary_register_scope.NewRegister();
builder()->StoreAccumulatorInRegister(literal_array);
literal_array_in_accumulator = false;
literal = temporary_register_scope.NewRegister();
builder()->StoreAccumulatorInRegister(literal);
literal_in_accumulator = false;
}
builder()
->LoadLiteral(Smi::FromInt(array_index))
.StoreAccumulatorInRegister(index);
Visit(subexpr);
builder()->GenericStoreKeyedProperty(literal_array, index);
builder()->GenericStoreKeyedProperty(literal, index);
}
if (!literal_array_in_accumulator) {
if (!literal_in_accumulator) {
// Restore literal array into accumulator.
builder()->LoadAccumulatorWithRegister(literal_array);
builder()->LoadAccumulatorWithRegister(literal);
}
}
......@@ -936,7 +1115,7 @@ void BytecodeGenerator::VisitNewLocalFunctionContext() {
TemporaryRegisterScope temporary_register_scope(builder());
Register closure = temporary_register_scope.NewRegister();
Register scope_info = temporary_register_scope.NewRegister();
DCHECK_EQ(closure.index() + 1, scope_info.index());
DCHECK(Register::AreContiguous(closure, scope_info));
builder()
->LoadAccumulatorWithRegister(Register::function_closure())
.StoreAccumulatorInRegister(closure)
......@@ -978,6 +1157,41 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* binop) {
}
void BytecodeGenerator::VisitObjectLiteralAccessor(
Register home_object, ObjectLiteralProperty* property, Register value_out) {
// TODO(rmcilroy): Replace value_out with VisitForRegister();
if (property == nullptr) {
builder()->LoadNull().StoreAccumulatorInRegister(value_out);
} else {
Visit(property->value());
builder()->StoreAccumulatorInRegister(value_out);
VisitSetHomeObject(value_out, home_object, property);
}
}
void BytecodeGenerator::VisitSetHomeObject(Register value, Register home_object,
ObjectLiteralProperty* property,
int slot_number) {
Expression* expr = property->value();
if (!FunctionLiteral::NeedsHomeObject(expr)) return;
// TODO(rmcilroy): Remove UNIMPLEMENTED once we have tests for setting the
// home object.
UNIMPLEMENTED();
TemporaryRegisterScope temporary_register_scope(builder());
Register name = temporary_register_scope.NewRegister();
isolate()->factory()->home_object_symbol();
builder()
->LoadLiteral(isolate()->factory()->home_object_symbol())
.StoreAccumulatorInRegister(name)
.StoreNamedProperty(home_object, name,
feedback_index(property->GetSlot(slot_number)),
language_mode());
}
LanguageMode BytecodeGenerator::language_mode() const {
return info()->language_mode();
}
......
......@@ -41,6 +41,11 @@ class BytecodeGenerator : public AstVisitor {
void VisitVariableLoad(Variable* variable, FeedbackVectorSlot slot);
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot);
void VisitNewLocalFunctionContext();
void VisitSetHomeObject(Register value, Register home_object,
ObjectLiteralProperty* property, int slot_number = 0);
void VisitObjectLiteralAccessor(Register home_object,
ObjectLiteralProperty* property,
Register value_out);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
......
......@@ -329,6 +329,24 @@ Register Register::FromOperand(uint8_t operand) {
return Register(-static_cast<int8_t>(operand));
}
bool Register::AreContiguous(Register reg1, Register reg2, Register reg3,
Register reg4, Register reg5) {
if (reg1.index() + 1 != reg2.index()) {
return false;
}
if (reg3.is_valid() && reg2.index() + 1 != reg3.index()) {
return false;
}
if (reg4.is_valid() && reg3.index() + 1 != reg4.index()) {
return false;
}
if (reg5.is_valid() && reg4.index() + 1 != reg5.index()) {
return false;
}
return true;
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -110,9 +110,11 @@ namespace interpreter {
\
/* Cast operators */ \
V(ToBoolean, OperandType::kNone) \
V(ToName, OperandType::kNone) \
\
/* Literals */ \
V(CreateArrayLiteral, OperandType::kIdx8, OperandType::kImm8) \
V(CreateObjectLiteral, OperandType::kIdx8, OperandType::kImm8) \
\
/* Closure allocation */ \
V(CreateClosure, OperandType::kImm8) \
......@@ -180,6 +182,7 @@ class Register {
return index_;
}
bool is_parameter() const { return index() < 0; }
bool is_valid() const { return index_ != kIllegalIndex; }
static Register FromParameterIndex(int index, int parameter_count);
int ToParameterIndex(int parameter_count) const;
......@@ -196,6 +199,11 @@ class Register {
static Register FromOperand(uint8_t operand);
uint8_t ToOperand() const;
static bool AreContiguous(Register reg1, Register reg2,
Register reg3 = Register(),
Register reg4 = Register(),
Register reg5 = Register());
private:
static const int kIllegalIndex = kMaxInt;
......
......@@ -349,8 +349,8 @@ void Interpreter::DoKeyedStoreICStrict(
// KeyedStoreICGeneric <object> <key>
//
// Calls the generic KeyStoreIC for <object> and the key <key> with the value in
// the accumulator.
// Calls the generic KeyedStoreIC for <object> and the key <key> with the value
// in the accumulator.
void Interpreter::DoKeyedStoreICGeneric(
compiler::InterpreterAssembler* assembler) {
Callable ic =
......@@ -656,6 +656,17 @@ void Interpreter::DoToBoolean(compiler::InterpreterAssembler* assembler) {
}
// ToName
//
// Cast the object referenced by the accumulator to a name.
void Interpreter::DoToName(compiler::InterpreterAssembler* assembler) {
Node* accumulator = __ GetAccumulator();
Node* result = __ CallRuntime(Runtime::kToName, accumulator);
__ SetAccumulator(result);
__ Dispatch();
}
// Jump <imm8>
//
// Jump by number of bytes represented by the immediate operand |imm8|.
......@@ -730,11 +741,7 @@ void Interpreter::DoJumpIfFalseConstant(
}
// CreateArrayLiteral <idx> <flags>
//
// Creates an array literal for literal index <idx> with flags <flags> and
// constant elements in the accumulator.
void Interpreter::DoCreateArrayLiteral(
void Interpreter::DoCreateLiteral(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler) {
Node* constant_elements = __ GetAccumulator();
Node* literal_index_raw = __ BytecodeOperandIdx8(0);
......@@ -744,13 +751,33 @@ void Interpreter::DoCreateArrayLiteral(
Node* closure = __ LoadRegister(Register::function_closure());
Node* literals_array =
__ LoadObjectField(closure, JSFunction::kLiteralsOffset);
Node* result = __ CallRuntime(Runtime::kCreateArrayLiteral, literals_array,
literal_index, constant_elements, flags);
Node* result = __ CallRuntime(function_id, literals_array, literal_index,
constant_elements, flags);
__ SetAccumulator(result);
__ Dispatch();
}
// CreateArrayLiteral <idx> <flags>
//
// Creates an array literal for literal index <idx> with flags <flags> and
// constant elements in the accumulator.
void Interpreter::DoCreateArrayLiteral(
compiler::InterpreterAssembler* assembler) {
DoCreateLiteral(Runtime::kCreateArrayLiteral, assembler);
}
// CreateObjectLiteral <idx> <flags>
//
// Creates an object literal for literal index <idx> with flags <flags> and
// constant elements in the accumulator.
void Interpreter::DoCreateObjectLiteral(
compiler::InterpreterAssembler* assembler) {
DoCreateLiteral(Runtime::kCreateObjectLiteral, assembler);
}
// CreateClosure <tenured>
//
// Creates a new closure for SharedFunctionInfo in the accumulator with the
......
......@@ -66,6 +66,10 @@ class Interpreter {
void DoPropertyStoreIC(Callable ic,
compiler::InterpreterAssembler* assembler);
// Generates code ro create a literal via |function_id|.
void DoCreateLiteral(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler);
bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table);
Isolate* isolate_;
......
......@@ -95,7 +95,7 @@ struct ExpectedSnippet {
int bytecode_length;
const uint8_t bytecode[512];
int constant_count;
T constants[4];
T constants[6];
};
......@@ -1238,7 +1238,7 @@ TEST(IfConditions) {
B(LdaUndefined), //
B(Return)}, //
0,
{unused, unused, unused, unused}},
{unused, unused, unused, unused, unused, unused}},
{"function f() { if ('lucky') { return 1; } else { return -1; } } f();",
0,
1,
......@@ -1255,7 +1255,7 @@ TEST(IfConditions) {
B(Return)}, //
1,
{helper.factory()->NewStringFromStaticChars("lucky"), unused, unused,
unused}},
unused, unused, unused}},
{"function f() { if (false) { return 1; } else { return -1; } } f();",
0,
1,
......@@ -1270,7 +1270,7 @@ TEST(IfConditions) {
B(LdaUndefined), //
B(Return)}, //
0,
{unused, unused, unused, unused}},
{unused, unused, unused, unused, unused, unused}},
{"function f(a) { if (a <= 0) { return 200; } else { return -200; } }"
"f(99);",
kPointerSize,
......@@ -1290,7 +1290,8 @@ TEST(IfConditions) {
B(Return)}, //
2,
{helper.factory()->NewNumberFromInt(200),
helper.factory()->NewNumberFromInt(-200), unused, unused}},
helper.factory()->NewNumberFromInt(-200), unused, unused, unused,
unused}},
{"function f(a, b) { if (a in b) { return 200; } }"
"f('prop', { prop: 'yes'});",
kPointerSize,
......@@ -1306,7 +1307,8 @@ TEST(IfConditions) {
B(LdaUndefined), //
B(Return)}, //
1,
{helper.factory()->NewNumberFromInt(200), unused, unused, unused}},
{helper.factory()->NewNumberFromInt(200), unused, unused, unused, unused,
unused}},
{"function f(z) { var a = 0; var b = 0; if (a === 0.01) { "
#define X "b = a; a = b; "
X X X X X X X X X X X X X X X X X X X X X X X X
......@@ -1338,7 +1340,8 @@ TEST(IfConditions) {
{helper.factory()->NewHeapNumber(0.01),
helper.factory()->NewNumberFromInt(200),
helper.factory()->NewNumberFromInt(199),
helper.factory()->NewNumberFromInt(-200)}},
helper.factory()->NewNumberFromInt(-200),
unused, unused}},
{"function f(a, b) {\n"
" if (a == b) { return 1; }\n"
" if (a === b) { return 1; }\n"
......@@ -1376,7 +1379,7 @@ TEST(IfConditions) {
B(LdaZero), //
B(Return)}, //
0,
{unused, unused, unused, unused}},
{unused, unused, unused, unused, unused, unused}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
......@@ -2051,6 +2054,455 @@ TEST(ArrayLiterals) {
}
}
TEST(ObjectLiterals) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
int simple_flags = ObjectLiteral::kFastElements |
ObjectLiteral::kShallowProperties |
ObjectLiteral::kDisableMementos;
int deep_elements_flags =
ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos;
ExpectedSnippet<InstanceType> snippets[] = {
{"return { };",
0,
1,
6,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(simple_flags), //
B(Return) //
},
1,
{InstanceType::FIXED_ARRAY_TYPE}},
{"return { name: 'string', val: 9.2 };",
0,
1,
6,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Return) //
},
1,
{InstanceType::FIXED_ARRAY_TYPE}},
{"var a = 1; return { name: 'string', val: a };",
3 * kPointerSize,
1,
24,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(Ldar), R(0), //
B(StoreICSloppy), R(1), R(2), U8(3), //
B(Ldar), R(1), //
B(Return), //
},
2,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"var a = 1; return { val: a, val: a + 1 };",
4 * kPointerSize,
1,
32,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(1), //
B(Ldar), R(0), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(Ldar), R(0), //
B(Star), R(3), //
B(LdaSmi8), U8(1), //
B(Add), R(3), //
B(StoreICSloppy), R(1), R(2), U8(3), //
B(Ldar), R(1), //
B(Return), //
},
2,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"return { func: function() { } };",
2 * kPointerSize,
1,
22,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(Star), R(1), //
B(LdaConstant), U8(2), //
B(CreateClosure), U8(0), //
B(StoreICSloppy), R(0), R(1), U8(3), //
B(Ldar), R(0), //
B(Return), //
},
3,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"return { func(a) { return a; } };",
2 * kPointerSize,
1,
22,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(Star), R(1), //
B(LdaConstant), U8(2), //
B(CreateClosure), U8(0), //
B(StoreICSloppy), R(0), R(1), U8(3), //
B(Ldar), R(0), //
B(Return), //
},
3,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"return { get a() { return 2; } };",
5 * kPointerSize,
1,
31,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(Star), R(1), //
B(LdaConstant), U8(2), //
B(CreateClosure), U8(0), //
B(Star), R(2), //
B(LdaNull), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), //
R(0), U8(5), //
B(Ldar), R(0), //
B(Return), //
},
3,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"return { get a() { return this.x; }, set a(val) { this.x = val } };",
5 * kPointerSize,
1,
34,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(Star), R(1), //
B(LdaConstant), U8(2), //
B(CreateClosure), U8(0), //
B(Star), R(2), //
B(LdaConstant), U8(3), //
B(CreateClosure), U8(0), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), //
R(0), U8(5), //
B(Ldar), R(0), //
B(Return), //
},
4,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"return { get a() { return this.x; }, set b(val) { this.y = val } };",
5 * kPointerSize,
1,
52,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(Star), R(1), //
B(LdaConstant), U8(2), //
B(CreateClosure), U8(0), //
B(Star), R(2), //
B(LdaNull), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), //
R(0), U8(5), //
B(LdaConstant), U8(3), //
B(Star), R(1), //
B(LdaNull), //
B(Star), R(2), //
B(LdaConstant), U8(4), //
B(CreateClosure), U8(0), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), //
R(0), U8(5), //
B(Ldar), R(0), //
B(Return), //
},
5,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"var a = 1; return { 1: a };",
5 * kPointerSize,
1,
30,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(Ldar), R(0), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kSetProperty), R(1), U8(4), //
B(Ldar), R(1), //
B(Return), //
},
1,
{InstanceType::FIXED_ARRAY_TYPE}},
{"return { __proto__: null }",
2 * kPointerSize,
1,
18,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(simple_flags), //
B(Star), R(0), //
B(LdaNull), B(Star), R(1), //
B(CallRuntime), U16(Runtime::kInternalSetPrototype), R(0), U8(2), //
B(Ldar), R(0), //
B(Return), //
},
1,
{InstanceType::FIXED_ARRAY_TYPE}},
{"var a = 'test'; return { [a]: 1 }",
5 * kPointerSize,
1,
31,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(CreateObjectLiteral), U8(0), U8(simple_flags), //
B(Star), R(1), //
B(Ldar), R(0), //
B(ToName), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), //
U8(4), //
B(Ldar), R(1), //
B(Return), //
},
2,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::FIXED_ARRAY_TYPE}},
{"var a = 'test'; return { val: a, [a]: 1 }",
5 * kPointerSize,
1,
41,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
B(Star), R(1), //
B(LdaConstant), U8(2), //
B(Star), R(2), //
B(Ldar), R(0), //
B(StoreICSloppy), R(1), R(2), U8(3), //
B(Ldar), R(0), //
B(ToName), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), //
U8(4), //
B(Ldar), R(1), //
B(Return), //
},
3,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"var a = 'test'; return { [a]: 1, __proto__: {} }",
5 * kPointerSize,
1,
43,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(CreateObjectLiteral), U8(1), U8(simple_flags), //
B(Star), R(1), //
B(Ldar), R(0), //
B(ToName), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), //
U8(4), //
B(LdaConstant), U8(1), //
B(CreateObjectLiteral), U8(0), U8(13), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kInternalSetPrototype), R(1), U8(2), //
B(Ldar), R(1), //
B(Return), //
},
2,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::FIXED_ARRAY_TYPE}},
{"var n = 'name'; return { [n]: 'val', get a() { }, set a(b) {} };",
5 * kPointerSize,
1,
69,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(CreateObjectLiteral), U8(0), U8(simple_flags), //
B(Star), R(1), //
B(Ldar), R(0), //
B(ToName), //
B(Star), R(2), //
B(LdaConstant), U8(2), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), //
U8(4), //
B(LdaConstant), U8(3), //
B(ToName), //
B(Star), R(2), //
B(LdaConstant), U8(4), //
B(CreateClosure), U8(0), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineGetterPropertyUnchecked), //
R(1), U8(4), //
B(LdaConstant), U8(3), //
B(ToName), //
B(Star), R(2), //
B(LdaConstant), U8(5), //
B(CreateClosure), U8(0), //
B(Star), R(3), //
B(LdaZero), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kDefineSetterPropertyUnchecked), //
R(1), U8(4), //
B(Ldar), R(1), //
B(Return), //
},
6,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(TopLevelObjectLiterals) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
int has_function_flags = ObjectLiteral::kFastElements |
ObjectLiteral::kHasFunction |
ObjectLiteral::kDisableMementos;
ExpectedSnippet<InstanceType> snippets[] = {
{"var a = { func: function() { } };",
7 * kPointerSize,
1,
69,
{
B(Ldar), R(Register::function_closure().index()), //
B(Star), R(2), //
B(LdaConstant), U8(0), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kNewScriptContext), R(2), U8(2), //
B(PushContext), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(LdaZero), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kDeclareGlobals), R(2), U8(2), //
B(LdaConstant), U8(2), //
B(Star), R(2), //
B(LdaZero), //
B(Star), R(3), //
B(LdaConstant), U8(3), //
B(CreateObjectLiteral), U8(0), U8(has_function_flags), //
B(Star), R(5), //
B(LdaConstant), U8(4), //
B(Star), R(6), //
B(LdaConstant), U8(5), //
B(CreateClosure), U8(1), //
B(StoreICSloppy), R(5), R(6), U8(3), //
B(CallRuntime), U16(Runtime::kToFastProperties), R(5), U8(1), //
B(Ldar), R(5), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kInitializeVarGlobal), R(2), U8(3), //
B(LdaUndefined), //
B(Return), //
},
6,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeTopLevelBytecode(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -1672,3 +1672,56 @@ TEST(InterpreterArrayLiterals) {
CHECK(return_value->SameValue(*literals[i].second));
}
}
TEST(InterpreterObjectLiterals) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[14] = {
std::make_pair("return { }.name;",
factory->undefined_value()),
std::make_pair("return { name: 'string', val: 9.2 }.name;",
factory->NewStringFromStaticChars("string")),
std::make_pair("var a = 15; return { name: 'string', val: a }.val;",
Handle<Object>(Smi::FromInt(15), isolate)),
std::make_pair("var a = 5; return { val: a, val: a + 1 }.val;",
Handle<Object>(Smi::FromInt(6), isolate)),
std::make_pair("return { func: function() { return 'test' } }.func();",
factory->NewStringFromStaticChars("test")),
std::make_pair("return { func(a) { return a + 'st'; } }.func('te');",
factory->NewStringFromStaticChars("test")),
std::make_pair("return { get a() { return 22; } }.a;",
Handle<Object>(Smi::FromInt(22), isolate)),
std::make_pair("var a = { get b() { return this.x + 't'; },\n"
" set b(val) { this.x = val + 's' } };\n"
"a.b = 'te';\n"
"return a.b;",
factory->NewStringFromStaticChars("test")),
std::make_pair("var a = 123; return { 1: a }[1];",
Handle<Object>(Smi::FromInt(123), isolate)),
std::make_pair("return Object.getPrototypeOf({ __proto__: null });",
factory->null_value()),
std::make_pair("var a = 'test'; return { [a]: 1 }.test;",
Handle<Object>(Smi::FromInt(1), isolate)),
std::make_pair("var a = 'test'; return { b: a, [a]: a + 'ing' }['test']",
factory->NewStringFromStaticChars("testing")),
std::make_pair("var a = 'proto_str';\n"
"var b = { [a]: 1, __proto__: { var : a } };\n"
"return Object.getPrototypeOf(b).var",
factory->NewStringFromStaticChars("proto_str")),
std::make_pair("var n = 'name';\n"
"return { [n]: 'val', get a() { return 987 } }['a'];",
Handle<Object>(Smi::FromInt(987), isolate)),
};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*literals[i].second));
}
}
......@@ -64,7 +64,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.CreateClosure(NOT_TENURED);
// Emit literal creation operations
builder.CreateArrayLiteral(0, 0);
builder.CreateArrayLiteral(0, 0)
.CreateObjectLiteral(0, 0);
// Call operations.
builder.Call(reg, reg, 0);
......@@ -103,7 +104,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CompareOperation(Token::Value::IN, reg, Strength::WEAK);
// Emit cast operator invocations.
builder.LoadNull().CastAccumulatorToBoolean();
builder.LoadNull()
.CastAccumulatorToBoolean()
.CastAccumulatorToName();
// Emit control flow. Return must be the last instruction.
BytecodeLabel start;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment