[VM interpreter] Support implicit getters and setters without compiling them.
Change-Id: I1e61cd79f66efe55c963d970e27f81a9d4084600
Reviewed-on: https://dart-review.googlesource.com/61860
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 5bdfcb8..7577fb7 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1555,7 +1555,7 @@
AbstractType& type = AbstractType::Handle(zone);
String& name = String::Handle(zone);
String& getter_name = String::Handle(zone);
- String& setter_name = String::Handle(zone);
+ String& other_name = String::Handle(zone);
Class& super_class = Class::Handle(zone);
const intptr_t num_fields = array.Length();
for (intptr_t i = 0; i < num_fields; i++) {
@@ -1579,8 +1579,8 @@
// An implicit setter is not generated for a static field, therefore, we
// cannot rely on the code below handling the static setter case to report
// a conflict with an instance setter. So we check explicitly here.
- setter_name = Field::SetterSymbol(name);
- super_class = FindSuperOwnerOfFunction(cls, setter_name);
+ other_name = Field::SetterSymbol(name);
+ super_class = FindSuperOwnerOfFunction(cls, other_name);
if (!super_class.IsNull()) {
const String& class_name = String::Handle(zone, cls.Name());
const String& super_cls_name = String::Handle(zone, super_class.Name());
@@ -1708,6 +1708,19 @@
}
}
}
+ if (function.IsImplicitGetterFunction() ||
+ function.IsImplicitSetterFunction() ||
+ function.IsImplicitStaticFieldInitializer()) {
+ // Cache the field object in the function data_ field.
+ if (function.IsImplicitSetterFunction()) {
+ other_name = Field::NameFromSetter(name);
+ } else {
+ other_name = Field::NameFromGetter(name);
+ }
+ field = cls.LookupFieldAllowPrivate(other_name);
+ ASSERT(!field.IsNull());
+ function.set_accessor_field(field);
+ }
if (function.IsSetterFunction() || function.IsImplicitSetterFunction()) {
if (function.is_static()) {
super_class = FindSuperOwnerOfFunction(cls, name);
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index 1b9b8e5..93bcf21bf 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -253,8 +253,7 @@
bool AotCallSpecializer::TryInlineFieldAccess(StaticCallInstr* call) {
if (call->function().IsImplicitGetterFunction()) {
- Field& field =
- Field::ZoneHandle(call->function().LookupImplicitGetterSetterField());
+ Field& field = Field::ZoneHandle(call->function().accessor_field());
if (should_clone_fields_) {
field = field.CloneFromOriginal();
}
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index 0430ef9..875983c 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -1495,9 +1495,15 @@
DartCompilationPipeline pipeline;
CompileParsedFunctionHelper helper(parsed_function, false, kNoOSRDeoptId);
const Code& code = Code::Handle(helper.Compile(&pipeline));
+ const Function& initializer = parsed_function->function();
if (!code.IsNull()) {
- const Function& initializer = parsed_function->function();
code.set_var_descriptors(Object::empty_var_descriptors());
+#if defined(DART_USE_INTERPRETER)
+ }
+ // In case the initializer has bytecode, the compilation step above only
+ // loaded the bytecode without generating code.
+ if (!code.IsNull() || initializer.HasBytecode()) {
+#endif
// Invoke the function to evaluate the expression.
return DartEntry::InvokeFunction(initializer, Object::empty_array());
}
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index 2d4305b..1e31f0f 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -887,7 +887,6 @@
DART_NOINLINE bool Interpreter::InvokeCompiled(Thread* thread,
RawFunction* function,
- RawArray* argdesc,
RawObject** call_base,
RawObject** call_top,
uint32_t** pc,
@@ -897,85 +896,139 @@
// TODO(regis): Revisit.
UNIMPLEMENTED();
#endif
- if (!Function::HasCode(function)) {
- ASSERT(!Function::HasBytecode(function));
- call_top[1] = 0; // Code result.
- call_top[2] = function;
- Exit(thread, *FP, call_top + 3, *pc);
- NativeArguments native_args(thread, 1, call_top + 2, call_top + 1);
- if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
- return false;
- }
- }
- if (Function::HasCode(function)) {
- RawCode* code = function->ptr()->code_;
- ASSERT(code != StubCode::LazyCompile_entry()->code());
- // TODO(regis): Once we share the same stack, try to invoke directly.
-#if defined(DEBUG)
- if (IsTracingExecution()) {
- THR_Print("%" Pu64 " ", icount_);
- THR_Print("invoking compiled %s\n",
- Function::Handle(function).ToCString());
- }
-#endif
- // On success, returns a RawInstance. On failure, a RawError.
- typedef RawObject* (*invokestub)(RawCode * code, RawArray * argdesc,
- RawObject * *arg0, Thread * thread);
- invokestub entrypoint = reinterpret_cast<invokestub>(
- StubCode::InvokeDartCodeFromBytecode_entry()->EntryPoint());
- RawObject* result;
- Exit(thread, *FP, call_top + 1, *pc);
- {
- InterpreterSetjmpBuffer buffer(this);
- if (!setjmp(buffer.buffer_)) {
- thread->set_vm_tag(reinterpret_cast<uword>(entrypoint));
- result = entrypoint(code, argdesc, call_base, thread);
- thread->set_vm_tag(VMTag::kDartTagId);
- thread->set_top_exit_frame_info(0);
- ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
- } else {
- return false;
- }
- }
- // Pop args and push result.
- *SP = call_base;
- **SP = result;
-
- // It is legit to call the constructor of an error object, however a
- // result of class UnhandledException must be propagated.
- if (result->IsHeapObject() &&
- result->GetClassId() == kUnhandledExceptionCid) {
- (*SP)[0] = UnhandledException::RawCast(result)->ptr()->exception_;
- (*SP)[1] = UnhandledException::RawCast(result)->ptr()->stacktrace_;
- (*SP)[2] = 0; // Space for result.
- Exit(thread, *FP, *SP + 3, *pc);
- NativeArguments args(thread, 2, *SP, *SP + 2);
- if (!InvokeRuntime(thread, this, DRT_ReThrow, args)) {
- return false;
- }
- }
- return true;
- }
- ASSERT(Function::HasBytecode(function));
- // Bytecode was loaded in the above compilation step.
- // Stay in interpreter.
+ ASSERT(Function::HasCode(function));
+ RawCode* code = function->ptr()->code_;
+ ASSERT(code != StubCode::LazyCompile_entry()->code());
+ // TODO(regis): Once we share the same stack, try to invoke directly.
#if defined(DEBUG)
if (IsTracingExecution()) {
THR_Print("%" Pu64 " ", icount_);
- THR_Print("invoking %s\n", Function::Handle(function).ToCString());
+ THR_Print("invoking compiled %s\n", Function::Handle(function).ToCString());
}
#endif
- RawCode* bytecode = function->ptr()->bytecode_;
- RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
- callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
- callee_fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(*pc);
- callee_fp[kKBCSavedCallerFpSlotFromFp] = reinterpret_cast<RawObject*>(*FP);
- pp_ = bytecode->ptr()->object_pool_;
- *pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->entry_point_);
- pc_ = reinterpret_cast<uword>(*pc); // For the profiler.
- *FP = callee_fp;
- *SP = *FP - 1;
- // Dispatch will interpret function.
+ // On success, returns a RawInstance. On failure, a RawError.
+ typedef RawObject* (*invokestub)(RawCode * code, RawArray * argdesc,
+ RawObject * *arg0, Thread * thread);
+ invokestub entrypoint = reinterpret_cast<invokestub>(
+ StubCode::InvokeDartCodeFromBytecode_entry()->EntryPoint());
+ RawObject* result;
+ Exit(thread, *FP, call_top + 1, *pc);
+ {
+ InterpreterSetjmpBuffer buffer(this);
+ if (!setjmp(buffer.buffer_)) {
+ thread->set_vm_tag(reinterpret_cast<uword>(entrypoint));
+ result = entrypoint(code, argdesc_, call_base, thread);
+ thread->set_vm_tag(VMTag::kDartTagId);
+ thread->set_top_exit_frame_info(0);
+ ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
+ } else {
+ return false;
+ }
+ }
+ // Pop args and push result.
+ *SP = call_base;
+ **SP = result;
+ pp_ = InterpreterHelpers::FrameCode(*FP)->ptr()->object_pool_;
+
+ // It is legit to call the constructor of an error object, however a
+ // result of class UnhandledException must be propagated.
+ if (result->IsHeapObject() &&
+ result->GetClassId() == kUnhandledExceptionCid) {
+ (*SP)[0] = UnhandledException::RawCast(result)->ptr()->exception_;
+ (*SP)[1] = UnhandledException::RawCast(result)->ptr()->stacktrace_;
+ (*SP)[2] = 0; // Space for result.
+ Exit(thread, *FP, *SP + 3, *pc);
+ NativeArguments args(thread, 2, *SP, *SP + 2);
+ if (!InvokeRuntime(thread, this, DRT_ReThrow, args)) {
+ return false;
+ }
+ UNREACHABLE();
+ }
+ return true;
+}
+
+DART_NOINLINE bool Interpreter::ProcessInvocation(bool* invoked,
+ Thread* thread,
+ RawFunction* function,
+ RawObject** call_base,
+ RawObject** call_top,
+ uint32_t** pc,
+ RawObject*** FP,
+ RawObject*** SP) {
+ ASSERT(!Function::HasCode(function) && !Function::HasBytecode(function));
+ // If the function is an implicit getter or setter, process its invocation
+ // here without code or bytecode.
+ RawFunction::Kind kind = Function::kind(function);
+ switch (kind) {
+ case RawFunction::kImplicitGetter: {
+ // Field offset in words is cached as a Smi in function's data_.
+ RawInstance* instance = reinterpret_cast<RawInstance*>((*SP)[0]);
+ RawField* field = reinterpret_cast<RawField*>(function->ptr()->data_);
+ intptr_t offset_in_words = Smi::Value(field->ptr()->value_.offset_);
+ (*SP)[0] =
+ reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
+ *invoked = true;
+ return true;
+ }
+ case RawFunction::kImplicitSetter: {
+ // Field offset in words is cached as a Smi in function's data_.
+ // TODO(regis): We currently ignore field.guarded_cid() and the type
+ // test of the setter value. Either execute these tests here or fall
+ // back to compiling the setter when required.
+ RawInstance* instance = reinterpret_cast<RawInstance*>((*SP)[-1]);
+ RawField* field = reinterpret_cast<RawField*>(function->ptr()->data_);
+ intptr_t offset_in_words = Smi::Value(field->ptr()->value_.offset_);
+ reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words] =
+ (*SP)[0];
+ *--(*SP) = Object::null();
+ *invoked = true;
+ return true;
+ }
+ case RawFunction::kImplicitStaticFinalGetter: {
+ // Field object is cached in function's data_.
+ RawField* field = reinterpret_cast<RawField*>(function->ptr()->data_);
+ RawInstance* value = field->ptr()->value_.static_value_;
+ if (value == Object::sentinel().raw() ||
+ value == Object::transition_sentinel().raw()) {
+ (*SP)[1] = 0; // Result of invoking the initializer.
+ (*SP)[2] = field;
+ Exit(thread, *FP, *SP + 3, *pc);
+ NativeArguments native_args(thread, 1, *SP + 2, *SP + 1);
+ if (!InvokeRuntime(thread, this, DRT_InitStaticField, native_args)) {
+ return false;
+ }
+ pp_ = InterpreterHelpers::FrameCode(*FP)->ptr()->object_pool_;
+ // The field is initialized by the runtime call, but not returned.
+ value = field->ptr()->value_.static_value_;
+ }
+ // Field was initialized. Return its value.
+ *++(*SP) = value;
+ *invoked = true;
+ return true;
+ }
+ case RawFunction::kNoSuchMethodDispatcher:
+ case RawFunction::kInvokeFieldDispatcher:
+ // TODO(regis): Implement. For now, use jitted version.
+ break;
+ default:
+ break;
+ }
+ // Compile the function to either generate code or load bytecode.
+ call_top[1] = 0; // Code result.
+ call_top[2] = function;
+ Exit(thread, *FP, call_top + 3, *pc);
+ NativeArguments native_args(thread, 1, call_top + 2, call_top + 1);
+ if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
+ return false;
+ }
+ if (Function::HasCode(function)) {
+ *invoked = true;
+ return InvokeCompiled(thread, function, call_base, call_top, pc, FP, SP);
+ }
+ ASSERT(Function::HasBytecode(function));
+ // Bytecode was loaded in the above compilation step.
+ // The caller will dispatch to the function's bytecode.
+ *invoked = false;
ASSERT(thread->vm_tag() == VMTag::kDartTagId);
ASSERT(thread->top_exit_frame_info() == 0);
return true;
@@ -990,10 +1043,17 @@
RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
RawFunction* function = FrameFunction(callee_fp);
- if (Function::HasCode(function) || !Function::HasBytecode(function)) {
- // TODO(regis): If the function is a dispatcher, execute the dispatch here.
- return InvokeCompiled(thread, function, argdesc_, call_base, call_top, pc,
- FP, SP);
+ if (Function::HasCode(function)) {
+ return InvokeCompiled(thread, function, call_base, call_top, pc, FP, SP);
+ }
+ if (!Function::HasBytecode(function)) {
+ bool invoked = false;
+ bool result = ProcessInvocation(&invoked, thread, function, call_base,
+ call_top, pc, FP, SP);
+ if (invoked || !result) {
+ return result;
+ }
+ ASSERT(Function::HasBytecode(function));
}
#if defined(DEBUG)
if (IsTracingExecution()) {
@@ -3234,6 +3294,7 @@
{
BYTECODE(InitStaticTOS, 0);
+ UNREACHABLE(); // Not used. TODO(regis): Remove this bytecode.
RawField* field = static_cast<RawField*>(*SP--);
RawObject* value = field->ptr()->value_.static_value_;
if ((value == Object::sentinel().raw()) ||
diff --git a/runtime/vm/interpreter.h b/runtime/vm/interpreter.h
index ef71e71..04b3caa 100644
--- a/runtime/vm/interpreter.h
+++ b/runtime/vm/interpreter.h
@@ -135,9 +135,17 @@
RawObject*** FP,
RawObject*** SP);
+ bool ProcessInvocation(bool* invoked,
+ Thread* thread,
+ RawFunction* function,
+ RawObject** call_base,
+ RawObject** call_top,
+ uint32_t** pc,
+ RawObject*** FP,
+ RawObject*** SP);
+
bool InvokeCompiled(Thread* thread,
RawFunction* function,
- RawArray* argdesc,
RawObject** call_base,
RawObject** call_top,
uint32_t** pc,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index c1e7654..e31beec 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5912,25 +5912,20 @@
set_data(value);
}
-RawField* Function::LookupImplicitGetterSetterField() const {
- // TODO(27590) Store Field object inside RawFunction::data_ if possible.
- Zone* Z = Thread::Current()->zone();
- String& field_name = String::Handle(Z, name());
- switch (kind()) {
- case RawFunction::kImplicitGetter:
- case RawFunction::kImplicitStaticFinalGetter:
- field_name = Field::NameFromGetter(field_name);
- break;
- case RawFunction::kImplicitSetter:
- field_name = Field::NameFromSetter(field_name);
- break;
- default:
- UNREACHABLE();
- }
- ASSERT(field_name.IsSymbol());
- const Class& owner = Class::Handle(Z, Owner());
- ASSERT(!owner.IsNull());
- return owner.LookupField(field_name);
+RawField* Function::accessor_field() const {
+ ASSERT(kind() == RawFunction::kImplicitGetter ||
+ kind() == RawFunction::kImplicitSetter ||
+ kind() == RawFunction::kImplicitStaticFinalGetter);
+ return Field::RawCast(raw_ptr()->data_);
+}
+
+void Function::set_accessor_field(const Field& value) const {
+ ASSERT(kind() == RawFunction::kImplicitGetter ||
+ kind() == RawFunction::kImplicitSetter ||
+ kind() == RawFunction::kImplicitStaticFinalGetter);
+ // Top level classes may be finalized multiple times.
+ ASSERT(raw_ptr()->data_ == Object::null() || raw_ptr()->data_ == value.raw());
+ set_data(value);
}
RawFunction* Function::parent_function() const {
@@ -5974,7 +5969,8 @@
}
RawFunction* Function::implicit_closure_function() const {
- if (IsClosureFunction() || IsSignatureFunction() || IsFactory()) {
+ if (IsClosureFunction() || IsSignatureFunction() || IsFactory() ||
+ IsDispatcherOrImplicitAccessor() || IsImplicitStaticFieldInitializer()) {
return Function::null();
}
const Object& obj = Object::Handle(raw_ptr()->data_);
@@ -6193,6 +6189,9 @@
// Array[2] = Kernel offset of enclosing library
// signature function: SignatureData
// method extractor: Function extracted closure function
+// implicit getter: Field
+// implicit setter: Field
+// impl. static final gttr: Field
// noSuchMethod dispatcher: Array arguments descriptor
// invoke-field dispatcher: Array arguments descriptor
// redirecting constructor: RedirectionData
@@ -7217,6 +7216,7 @@
clone.set_owner(clone_owner);
clone.ClearICDataArray();
clone.ClearCode();
+ clone.set_data(Object::null_object());
clone.set_usage_counter(0);
clone.set_deoptimization_counter(0);
clone.set_optimized_instruction_count(0);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 8c69cb2..ba4f605 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2283,8 +2283,6 @@
RawContextScope* context_scope() const;
void set_context_scope(const ContextScope& value) const;
- RawField* LookupImplicitGetterSetterField() const;
-
// Enclosing function of this local function.
RawFunction* parent_function() const;
@@ -2294,6 +2292,9 @@
void set_saved_args_desc(const Array& array) const;
RawArray* saved_args_desc() const;
+ void set_accessor_field(const Field& value) const;
+ RawField* accessor_field() const;
+
bool IsMethodExtractor() const {
return kind() == RawFunction::kMethodExtractor;
}
@@ -2348,6 +2349,9 @@
RawFunction::Kind kind() const {
return KindBits::decode(raw_ptr()->kind_tag_);
}
+ static RawFunction::Kind kind(RawFunction* function) {
+ return KindBits::decode(function->ptr()->kind_tag_);
+ }
RawFunction::AsyncModifier modifier() const {
return ModifierBits::decode(raw_ptr()->kind_tag_);
@@ -2646,6 +2650,12 @@
return kind() == RawFunction::kImplicitSetter;
}
+ // Returns true if this function represents an implicit static field
+ // initializer function.
+ bool IsImplicitStaticFieldInitializer() const {
+ return kind() == RawFunction::kImplicitStaticFinalGetter;
+ }
+
// Returns true if this function represents a (possibly implicit) closure
// function.
bool IsClosureFunction() const {
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 89661ef..6dd1d5b 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -330,7 +330,7 @@
if ((kind() == RawFunction::kImplicitGetter) ||
(kind() == RawFunction::kImplicitSetter) ||
(kind() == RawFunction::kImplicitStaticFinalGetter)) {
- const Field& field = Field::Handle(LookupImplicitGetterSetterField());
+ const Field& field = Field::Handle(accessor_field());
if (!field.IsNull()) {
jsobj.AddProperty("_field", field);
}
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index a7a67b3..eb810c7 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -1361,7 +1361,7 @@
SequenceNode* Parser::ParseStaticInitializer() {
ExpectIdentifier("field name expected");
- CheckToken(Token::kASSIGN, "field initialier expected");
+ CheckToken(Token::kASSIGN, "field initializer expected");
ConsumeToken();
OpenFunctionBlock(parsed_function()->function());
TokenPosition expr_pos = TokenPos();
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 38d5fa1..bcbb625 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1782,6 +1782,7 @@
}
Exceptions::PropagateError(Error::Cast(result));
}
+ arguments.SetReturn(result);
#else
UNREACHABLE();
#endif // defined(DART_USE_INTERPRETER)