Commit 1b436ae1 authored by oth's avatar oth Committed by Commit bot

[interpreter] Support for ES6 class literals.

Port of class literal support from the
ast-graph-builder implementation.

R=rmcilroy@chromium.org,mstarzinger@chromium.org
BUG=v8:4280,v8:4682
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#33746}
parent d3ac2450
......@@ -964,6 +964,13 @@ void BytecodeGraphBuilder::VisitKeyedStoreICStrictWide() {
BuildKeyedStore();
}
void BytecodeGraphBuilder::VisitLdaInitialMap() {
Node* js_function = environment()->LookupAccumulator();
Node* load = BuildLoadObjectField(js_function,
JSFunction::kPrototypeOrInitialMapOffset);
environment()->BindAccumulator(load);
}
void BytecodeGraphBuilder::VisitPushContext() {
Node* new_context = environment()->LookupAccumulator();
environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0),
......
......@@ -326,6 +326,10 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadBooleanConstant(bool value) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadPrototypeOrInitialMap() {
Output(Bytecode::kLdaInitialMap);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
Register reg) {
......
......@@ -95,6 +95,9 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
BytecodeArrayBuilder& LoadFalse();
BytecodeArrayBuilder& LoadBooleanConstant(bool value);
// Load object prototype (or initial map).
BytecodeArrayBuilder& LoadPrototypeOrInitialMap();
// Global loads to the accumulator and stores from the accumulator.
BytecodeArrayBuilder& LoadGlobal(const Handle<String> name, int feedback_slot,
LanguageMode language_mode,
......
......@@ -1228,9 +1228,148 @@ void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
UNIMPLEMENTED();
if (expr->scope()->ContextLocalCount() > 0) {
VisitNewLocalBlockContext(expr->scope());
ContextScope scope(this, expr->scope());
VisitDeclarations(expr->scope()->declarations());
VisitClassLiteralContents(expr);
} else {
VisitDeclarations(expr->scope()->declarations());
VisitClassLiteralContents(expr);
}
}
void BytecodeGenerator::VisitClassLiteralContents(ClassLiteral* expr) {
VisitClassLiteralForRuntimeDefinition(expr);
// The prototype is ensured to exist by Runtime_DefineClass in
// VisitClassForRuntimeDefinition. No access check is needed here
// since the constructor is created by the class literal.
register_allocator()->PrepareForConsecutiveAllocations(2);
Register literal = register_allocator()->NextConsecutiveRegister();
Register prototype = register_allocator()->NextConsecutiveRegister();
builder()
->StoreAccumulatorInRegister(literal)
.LoadPrototypeOrInitialMap()
.StoreAccumulatorInRegister(prototype);
VisitClassLiteralProperties(expr, literal, prototype);
builder()->CallRuntime(Runtime::kFinalizeClassDefinition, literal, 2);
// Assign to class variable.
if (expr->class_variable_proxy() != nullptr) {
Variable* var = expr->class_variable_proxy()->var();
FeedbackVectorSlot slot = expr->NeedsProxySlot()
? expr->ProxySlot()
: FeedbackVectorSlot::Invalid();
VisitVariableAssignment(var, slot);
}
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitClassLiteralForRuntimeDefinition(
ClassLiteral* expr) {
AccumulatorResultScope result_scope(this);
register_allocator()->PrepareForConsecutiveAllocations(4);
Register extends = register_allocator()->NextConsecutiveRegister();
Register constructor = register_allocator()->NextConsecutiveRegister();
Register start_position = register_allocator()->NextConsecutiveRegister();
Register end_position = register_allocator()->NextConsecutiveRegister();
VisitForAccumulatorValueOrTheHole(expr->extends());
builder()->StoreAccumulatorInRegister(extends);
VisitForAccumulatorValue(expr->constructor());
builder()
->StoreAccumulatorInRegister(constructor)
.LoadLiteral(Smi::FromInt(expr->start_position()))
.StoreAccumulatorInRegister(start_position)
.LoadLiteral(Smi::FromInt(expr->end_position()))
.StoreAccumulatorInRegister(end_position)
.CallRuntime(Runtime::kDefineClass, extends, 4);
result_scope.SetResultInAccumulator();
}
void BytecodeGenerator::VisitClassLiteralProperties(ClassLiteral* expr,
Register literal,
Register prototype) {
RegisterAllocationScope register_scope(this);
register_allocator()->PrepareForConsecutiveAllocations(4);
Register receiver = register_allocator()->NextConsecutiveRegister();
Register key = register_allocator()->NextConsecutiveRegister();
Register value = register_allocator()->NextConsecutiveRegister();
Register attr = register_allocator()->NextConsecutiveRegister();
bool attr_assigned = false;
Register old_receiver = Register::invalid_value();
// Create nodes to store method values into the literal.
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
// Set-up receiver.
Register new_receiver = property->is_static() ? literal : prototype;
if (new_receiver != old_receiver) {
builder()->MoveRegister(new_receiver, receiver);
old_receiver = new_receiver;
}
VisitForAccumulatorValue(property->key());
builder()->CastAccumulatorToName().StoreAccumulatorInRegister(key);
// The static prototype property is read only. We handle the non computed
// property name case in the parser. Since this is the only case where we
// need to check for an own read only property we special case this so we do
// not need to do this for every property.
if (property->is_static() && property->is_computed_name()) {
VisitClassLiteralStaticPrototypeWithComputedName(key);
}
VisitForAccumulatorValue(property->value());
builder()->StoreAccumulatorInRegister(value);
VisitSetHomeObject(value, receiver, property);
if ((property->kind() == ObjectLiteral::Property::GETTER ||
property->kind() == ObjectLiteral::Property::SETTER) &&
!attr_assigned) {
builder()
->LoadLiteral(Smi::FromInt(DONT_ENUM))
.StoreAccumulatorInRegister(attr);
attr_assigned = true;
}
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
case ObjectLiteral::Property::PROTOTYPE:
// Invalid properties for ES6 classes.
UNREACHABLE();
break;
case ObjectLiteral::Property::COMPUTED: {
builder()->CallRuntime(Runtime::kDefineClassMethod, receiver, 3);
break;
}
case ObjectLiteral::Property::GETTER: {
builder()->CallRuntime(Runtime::kDefineGetterPropertyUnchecked,
receiver, 4);
break;
}
case ObjectLiteral::Property::SETTER: {
builder()->CallRuntime(Runtime::kDefineSetterPropertyUnchecked,
receiver, 4);
break;
}
}
}
}
void BytecodeGenerator::VisitClassLiteralStaticPrototypeWithComputedName(
Register key) {
BytecodeLabel done;
builder()
->LoadLiteral(isolate()->factory()->prototype_string())
.CompareOperation(Token::Value::EQ_STRICT, key, Strength::WEAK)
.JumpIfFalse(&done)
.CallRuntime(Runtime::kThrowStaticPrototypeError, Register(0), 0)
.Bind(&done);
}
void BytecodeGenerator::VisitNativeFunctionLiteral(
NativeFunctionLiteral* expr) {
......@@ -2520,6 +2659,13 @@ void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) {
Visit(expr);
}
void BytecodeGenerator::VisitForAccumulatorValueOrTheHole(Expression* expr) {
if (expr == nullptr) {
builder()->LoadTheHole();
} else {
VisitForAccumulatorValue(expr);
}
}
// Visits the expression |expr| and discards the result.
void BytecodeGenerator::VisitForEffect(Expression* expr) {
......
......@@ -80,6 +80,11 @@ class BytecodeGenerator final : public AstVisitor {
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest, int index);
void VisitClassLiteralContents(ClassLiteral* expr);
void VisitClassLiteralForRuntimeDefinition(ClassLiteral* expr);
void VisitClassLiteralProperties(ClassLiteral* expr, Register literal,
Register prototype);
void VisitClassLiteralStaticPrototypeWithComputedName(Register name);
void VisitThisFunctionVariable(Variable* variable);
void VisitNewTargetVariable(Variable* variable);
void VisitNewLocalFunctionContext();
......@@ -104,9 +109,10 @@ class BytecodeGenerator final : public AstVisitor {
// Visitors for obtaining expression result in the accumulator, in a
// register, or just getting the effect.
void VisitForAccumulatorValue(Expression* expression);
MUST_USE_RESULT Register VisitForRegisterValue(Expression* expression);
void VisitForEffect(Expression* node);
void VisitForAccumulatorValue(Expression* expr);
void VisitForAccumulatorValueOrTheHole(Expression* expr);
MUST_USE_RESULT Register VisitForRegisterValue(Expression* expr);
void VisitForEffect(Expression* expr);
// Methods for tracking and remapping register.
void RecordStoreToRegister(Register reg);
......
......@@ -142,6 +142,9 @@ namespace interpreter {
V(KeyedStoreICStrictWide, OperandType::kReg8, OperandType::kReg8, \
OperandType::kIdx16) \
\
/* Class information */ \
V(LdaInitialMap, OperandType::kNone) \
\
/* Binary Operators */ \
V(Add, OperandType::kReg8) \
V(Sub, OperandType::kReg8) \
......@@ -310,10 +313,7 @@ class Register {
public:
explicit Register(int index = kInvalidIndex) : index_(index) {}
int index() const {
DCHECK(index_ != kInvalidIndex);
return index_;
}
int index() const { return index_; }
bool is_parameter() const { return index() < 0; }
bool is_valid() const { return index_ != kInvalidIndex; }
bool is_byte_operand() const;
......
......@@ -841,6 +841,17 @@ void Interpreter::DoKeyedStoreICStrictWide(
DoKeyedStoreIC(ic, assembler);
}
// LdaInitialMap
//
// Loads the prototype or initial map of the JSFunction referenced by
// the accumulator. The result is placed in the accumulator.
void Interpreter::DoLdaInitialMap(compiler::InterpreterAssembler* assembler) {
Node* js_function = __ GetAccumulator();
Node* initial_map =
__ LoadObjectField(js_function, JSFunction::kPrototypeOrInitialMapOffset);
__ SetAccumulator(initial_map);
__ Dispatch();
}
// PushContext <context>
//
......
......@@ -556,8 +556,10 @@
'test-inobject-slack-tracking/SubclassPromiseBuiltin': [FAIL],
'test-api/Regress470113': [FAIL],
'test-api/SubclassGetConstructorName': [FAIL],
'test-api/ClassPrototypeCreationContext': [FAIL],
'test-run-jsops/ClassLiteral': [FAIL],
# BUG(4333). Function name inferrer does not work for ES6 clases.
'test-func-name-inference/UpperCaseClass': [TIMEOUT],
'test-func-name-inference/LowerCaseClass': [TIMEOUT],
# TODO(rmcilroy,4681): Requires support for generators.
'test-inobject-slack-tracking/JSGeneratorObjectBasic': [FAIL],
......
......@@ -3792,6 +3792,67 @@ TEST(InterpreterWithStatement) {
}
}
TEST(InterpreterClassLiterals) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> examples[] = {
{"class C {\n"
" constructor(x) { this.x_ = x; }\n"
" method() { return this.x_; }\n"
"}\n"
"return new C(99).method();",
handle(Smi::FromInt(99), isolate)},
{"class C {\n"
" constructor(x) { this.x_ = x; }\n"
" static static_method(x) { return x; }\n"
"}\n"
"return C.static_method(101);",
handle(Smi::FromInt(101), isolate)},
{"class C {\n"
" get x() { return 102; }\n"
"}\n"
"return new C().x",
handle(Smi::FromInt(102), isolate)},
{"class C {\n"
" static get x() { return 103; }\n"
"}\n"
"return C.x",
handle(Smi::FromInt(103), isolate)},
{"class C {\n"
" constructor() { this.x_ = 0; }"
" set x(value) { this.x_ = value; }\n"
" get x() { return this.x_; }\n"
"}\n"
"var c = new C();"
"c.x = 104;"
"return c.x;",
handle(Smi::FromInt(104), isolate)},
{"var x = 0;"
"class C {\n"
" static set x(value) { x = value; }\n"
" static get x() { return x; }\n"
"}\n"
"C.x = 105;"
"return C.x;",
handle(Smi::FromInt(105), isolate)},
{"var method = 'f';"
"class C {\n"
" [method]() { return 106; }\n"
"}\n"
"return new C().f();",
handle(Smi::FromInt(106), isolate)},
};
for (size_t i = 0; i < arraysize(examples); ++i) {
std::string source(InterpreterTester::SourceForBody(examples[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(*examples[i].second));
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -55,6 +55,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.MoveRegister(reg, other);
builder.MoveRegister(reg, wide);
// Prototype info for classes.
builder.LoadPrototypeOrInitialMap();
// Emit global load / store operations.
Factory* factory = isolate()->factory();
Handle<String> name = factory->NewStringFromStaticChars("var_name");
......
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