Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d0f2888

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[VM] Fix expression evaluation implementation: Never register temporary/unused classes with the system.
Until now the expression evaluation implementation was using normal kernel loader functionality, which registered a new class with the isolate. Then the cid of that class was set to kIllegalCid. This caused the direct_subclasses/direct_implementors CHA information to contain a class with kIllegalCid. This CL fixes this by ensuring we never register the libraries/classes created for expression evaluation (which are not even used, they are an artifact of how the expressions are encoded in kernel). Issue flutter/flutter#20255 Issue flutter/flutter#20307 Change-Id: Ie6dd76c7ff696cd8adf4f27e9a072274afd90136 Reviewed-on: https://dart-review.googlesource.com/68681 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
1 parent e2a1807 commit d0f2888

6 files changed

Lines changed: 263 additions & 79 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
// VMOptions=--no-background-compilation --enable-inlining-annotations
5+
6+
import 'dart:async';
7+
import 'dart:developer';
8+
9+
import 'package:observatory/service_io.dart';
10+
import 'package:unittest/unittest.dart';
11+
12+
import 'service_test_common.dart';
13+
import 'test_helper.dart';
14+
15+
const String NeverInline = 'NeverInline';
16+
17+
class Base<T> {
18+
String field;
19+
20+
Base(this.field);
21+
String foo() => 'Base-$field';
22+
}
23+
24+
class Sub<T> extends Base<T> {
25+
String field;
26+
27+
Sub(this.field) : super(field);
28+
String foo() {
29+
debugger();
30+
return 'Sub-$field';
31+
}
32+
}
33+
34+
class ISub<T> implements Base<T> {
35+
String field;
36+
37+
ISub(this.field);
38+
String foo() => 'ISub-$field';
39+
}
40+
41+
class Box<T> {
42+
T value;
43+
44+
@NeverInline
45+
void setValue(T value) {
46+
this.value = value;
47+
}
48+
}
49+
50+
final objects = <Base>[
51+
new Base<int>('b'),
52+
new Sub<double>('a'),
53+
new ISub<bool>('c')
54+
];
55+
56+
String triggerTypeTestingStubGeneration() {
57+
final Box<Object> box = new Box<Base>();
58+
for (int i = 0; i < 1000000; ++i) {
59+
box.setValue(objects.last);
60+
}
61+
return 'tts-generated';
62+
}
63+
64+
void testFunction() {
65+
// Triggers the debugger, which will evaluate an expression in the context of
66+
// [Sub<double>], which will make a subclass of [Base<T>].
67+
print(objects[1].foo());
68+
69+
triggerTypeTestingStubGeneration();
70+
71+
// Triggers the debugger, which will evaluate an expression in the context of
72+
// [Sub<double>], which will make a subclass of [Base<T>].
73+
print(objects[1].foo());
74+
}
75+
76+
Future triggerEvaluation(Isolate isolate) async {
77+
ServiceMap stack = await isolate.getStack();
78+
79+
// Make sure we are in the right place.
80+
expect(stack.type, equals('Stack'));
81+
expect(stack['frames'].length, greaterThanOrEqualTo(2));
82+
expect(stack['frames'][0].function.name, equals('foo'));
83+
expect(stack['frames'][0].function.dartOwner.name, equals('Sub'));
84+
85+
// Trigger an evaluation, which will create a new subclass of Base<T>.
86+
final dynamic result =
87+
await isolate.evalFrame(0, 'this.field + " world \$T"');
88+
if (result is DartError) {
89+
throw 'Got an error "$result", expected result of expression evaluation.';
90+
}
91+
expect(result.valueAsString, equals('a world double'));
92+
93+
// Trigger an optimization of a type testing stub (and usage of it).
94+
final dynamic result2 =
95+
await isolate.evalFrame(0, 'triggerTypeTestingStubGeneration()');
96+
if (result2 is DartError) {
97+
throw 'Got an error "$result", expected result of expression evaluation.';
98+
}
99+
expect(result2.valueAsString, equals('tts-generated'));
100+
}
101+
102+
final testSteps = <IsolateTest>[
103+
hasStoppedAtBreakpoint,
104+
triggerEvaluation,
105+
resumeIsolate,
106+
hasStoppedAtBreakpoint,
107+
triggerEvaluation,
108+
resumeIsolate,
109+
];
110+
111+
main(args) => runIsolateTests(args, testSteps, testeeConcurrent: testFunction);

runtime/observatory/tests/service/service_kernel.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ complex_reload_test: Skip # Times out on sim architectures, also RuntimeError.
227227
debugger_inspect_test: RuntimeError, Timeout # Issue #33087
228228
developer_service_get_isolate_id_test: RuntimeError # Issue #33087
229229
eval_internal_class_test: RuntimeError # Issue #33087
230+
eval_regression_flutter20255_test: RuntimeError # "No incremental compiler available for this isolate"
230231
eval_test: RuntimeError # Issue #33087
231232
evaluate_activation_test/none: RuntimeError # Issue #33087
232233
evaluate_async_closure_test: RuntimeError # Issue #33087

runtime/vm/kernel_loader.cc

Lines changed: 109 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,9 @@ KernelLoader::KernelLoader(Program* program)
183183
external_name_field_(Field::Handle(Z)),
184184
potential_natives_(GrowableObjectArray::Handle(Z)),
185185
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
186-
pragma_class_(Class::Handle(Z)) {
186+
pragma_class_(Class::Handle(Z)),
187+
expression_evaluation_library_(Library::Handle(Z)),
188+
expression_evaluation_function_(Function::Handle(Z)) {
187189
if (!program->is_single_program()) {
188190
FATAL(
189191
"Trying to load a concatenated dill file at a time where that is "
@@ -344,7 +346,9 @@ KernelLoader::KernelLoader(const Script& script,
344346
external_name_field_(Field::Handle(Z)),
345347
potential_natives_(GrowableObjectArray::Handle(Z)),
346348
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
347-
pragma_class_(Class::Handle(Z)) {
349+
pragma_class_(Class::Handle(Z)),
350+
expression_evaluation_library_(Library::Handle(Z)),
351+
expression_evaluation_function_(Function::Handle(Z)) {
348352
ASSERT(T.active_class_ == &active_class_);
349353
T.finalize_ = false;
350354

@@ -617,6 +621,49 @@ RawObject* KernelLoader::LoadProgram(bool process_pending_classes) {
617621
return error;
618622
}
619623

624+
RawObject* KernelLoader::LoadExpressionEvaluationFunction(
625+
const String& library_url,
626+
const String& klass) {
627+
// Find the original context, i.e. library/class, in which the evaluation will
628+
// happen.
629+
const Library& real_library = Library::Handle(
630+
Z, Library::LookupLibrary(Thread::Current(), library_url));
631+
ASSERT(!real_library.IsNull());
632+
const Class& real_class = Class::Handle(
633+
Z, klass.IsNull() ? real_library.toplevel_class()
634+
: real_library.LookupClassAllowPrivate(klass));
635+
ASSERT(!real_class.IsNull());
636+
637+
const intptr_t num_cids = I->class_table()->NumCids();
638+
const intptr_t num_libs =
639+
GrowableObjectArray::Handle(I->object_store()->libraries()).Length();
640+
641+
// Load the "evaluate:source" expression evaluation library.
642+
ASSERT(expression_evaluation_library_.IsNull());
643+
ASSERT(expression_evaluation_function_.IsNull());
644+
const Object& result = Object::Handle(Z, LoadProgram(true));
645+
if (result.IsError()) {
646+
return result.raw();
647+
}
648+
ASSERT(!expression_evaluation_library_.IsNull());
649+
ASSERT(!expression_evaluation_function_.IsNull());
650+
ASSERT(GrowableObjectArray::Handle(I->object_store()->libraries()).Length() ==
651+
num_libs);
652+
ASSERT(I->class_table()->NumCids() == num_cids);
653+
654+
// Make the expression evaluation function have the right kernel data and
655+
// parent.
656+
auto& eval_data = ExternalTypedData::Handle(
657+
Z, expression_evaluation_library_.kernel_data());
658+
auto& eval_script =
659+
Script::Handle(Z, expression_evaluation_function_.script());
660+
expression_evaluation_function_.SetKernelDataAndScript(
661+
eval_script, eval_data, expression_evaluation_library_.kernel_offset());
662+
expression_evaluation_function_.set_owner(real_class);
663+
664+
return expression_evaluation_function_.raw();
665+
}
666+
620667
void KernelLoader::FindModifiedLibraries(Program* program,
621668
Isolate* isolate,
622669
BitVector* modified_libs,
@@ -780,9 +827,16 @@ RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
780827
library_helper.SetJustRead(LibraryHelper::kAnnotations);
781828

782829
// Setup toplevel class (which contains library fields/procedures).
830+
831+
// We do not register expression evaluation classes with the VM:
832+
// The expression evaluation functions should be GC-able as soon as
833+
// they are not reachable anymore and we never look them up by name.
834+
const bool register_class =
835+
library.raw() != expression_evaluation_library_.raw();
836+
783837
Class& toplevel_class =
784838
Class::Handle(Z, Class::New(library, Symbols::TopLevel(), script,
785-
TokenPosition::kNoSource));
839+
TokenPosition::kNoSource, register_class));
786840
toplevel_class.set_is_cycle_free();
787841
library.set_toplevel_class(toplevel_class);
788842

@@ -801,8 +855,10 @@ RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
801855
for (intptr_t i = 0; i < class_count; ++i) {
802856
helper_.SetOffset(next_class_offset);
803857
next_class_offset = library_index.ClassOffset(i + 1);
804-
classes.Add(LoadClass(library, toplevel_class, next_class_offset),
805-
Heap::kOld);
858+
const Class& klass = LoadClass(library, toplevel_class, next_class_offset);
859+
if (register_class) {
860+
classes.Add(klass, Heap::kOld);
861+
}
806862
}
807863
helper_.SetOffset(next_class_offset);
808864

@@ -881,7 +937,9 @@ RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
881937
}
882938

883939
toplevel_class.SetFunctions(Array::Handle(MakeFunctionsArray()));
884-
classes.Add(toplevel_class, Heap::kOld);
940+
if (register_class) {
941+
classes.Add(toplevel_class, Heap::kOld);
942+
}
885943
if (!library.Loaded()) library.SetLoaded();
886944

887945
return library.raw();
@@ -1161,7 +1219,13 @@ Class& KernelLoader::LoadClass(const Library& library,
11611219
class_offset - correction_offset_);
11621220
}
11631221

1164-
if (loading_native_wrappers_library_) {
1222+
// We do not register expression evaluation classes with the VM:
1223+
// The expression evaluation functions should be GC-able as soon as
1224+
// they are not reachable anymore and we never look them up by name.
1225+
const bool register_class =
1226+
library.raw() != expression_evaluation_library_.raw();
1227+
1228+
if (loading_native_wrappers_library_ || !register_class) {
11651229
FinishClassLoading(klass, library, toplevel_class, class_offset,
11661230
class_index, &class_helper);
11671231
}
@@ -1514,6 +1578,12 @@ void KernelLoader::LoadProcedure(const Library& library,
15141578
const Object& script_class =
15151579
ClassForScriptAt(owner, procedure_helper.source_uri_index_);
15161580
RawFunction::Kind kind = GetFunctionType(procedure_helper.kind_);
1581+
1582+
// We do not register expression evaluation libraries with the VM:
1583+
// The expression evaluation functions should be GC-able as soon as
1584+
// they are not reachable anymore and we never look them up by name.
1585+
const bool register_function = !name.Equals(Symbols::DebugProcedureName());
1586+
15171587
Function& function = Function::ZoneHandle(
15181588
Z, Function::New(name, kind,
15191589
!is_method, // is_static
@@ -1523,7 +1593,11 @@ void KernelLoader::LoadProcedure(const Library& library,
15231593
script_class, procedure_helper.start_position_));
15241594
function.set_has_pragma(has_pragma_annotation);
15251595
function.set_end_token_pos(procedure_helper.end_position_);
1526-
functions_.Add(&function);
1596+
if (register_function) {
1597+
functions_.Add(&function);
1598+
} else {
1599+
expression_evaluation_function_ = function.raw();
1600+
}
15271601
function.set_kernel_offset(procedure_offset);
15281602

15291603
ActiveMemberScope active_member(&active_class_, &function);
@@ -1787,11 +1861,23 @@ Library& KernelLoader::LookupLibraryOrNull(NameIndex library) {
17871861
Library& KernelLoader::LookupLibrary(NameIndex library) {
17881862
Library* handle = NULL;
17891863
if (!libraries_.Lookup(library, &handle)) {
1790-
const String& url = H.DartString(H.CanonicalNameString(library));
1791-
handle = &Library::Handle(Z, Library::LookupLibrary(thread_, url));
1792-
if (handle->IsNull()) {
1793-
*handle = Library::New(url);
1794-
handle->Register(thread_);
1864+
handle = &Library::Handle(Z);
1865+
const String& url = H.DartSymbolPlain(H.CanonicalNameString(library));
1866+
1867+
// We do not register expression evaluation libraries with the VM:
1868+
// The expression evaluation functions should be GC-able as soon as
1869+
// they are not reachable anymore and we never look them up by name.
1870+
if (url.Equals(Symbols::EvalSourceUri())) {
1871+
if (handle->IsNull()) {
1872+
*handle = Library::New(url);
1873+
expression_evaluation_library_ = handle->raw();
1874+
}
1875+
} else {
1876+
*handle = Library::LookupLibrary(thread_, url);
1877+
if (handle->IsNull()) {
1878+
*handle = Library::New(url);
1879+
handle->Register(thread_);
1880+
}
17951881
}
17961882
ASSERT(!handle->IsNull());
17971883
libraries_.Insert(library, handle);
@@ -1806,9 +1892,17 @@ Class& KernelLoader::LookupClass(NameIndex klass) {
18061892
const String& name = H.DartClassName(klass);
18071893
handle = &Class::Handle(Z, library.LookupLocalClass(name));
18081894
if (handle->IsNull()) {
1895+
// We do not register expression evaluation classes with the VM:
1896+
// The expression evaluation functions should be GC-able as soon as
1897+
// they are not reachable anymore and we never look them up by name.
1898+
const bool register_class =
1899+
library.raw() != expression_evaluation_library_.raw();
1900+
18091901
*handle = Class::New(library, name, Script::Handle(Z),
1810-
TokenPosition::kNoSource);
1811-
library.AddClass(*handle);
1902+
TokenPosition::kNoSource, register_class);
1903+
if (register_class) {
1904+
library.AddClass(*handle);
1905+
}
18121906
}
18131907
// Insert the class in the cache before calling ReadPreliminaryClass so
18141908
// we do not risk allocating the class again by calling LookupClass

runtime/vm/kernel_loader.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ class KernelLoader : public ValueObject {
133133
// was no main procedure, or a failure object if there was an error.
134134
RawObject* LoadProgram(bool process_pending_classes = true);
135135

136+
// Returns the function which will evaluate the expression, or a failure
137+
// object if there was an error.
138+
RawObject* LoadExpressionEvaluationFunction(const String& library_url,
139+
const String& klass);
140+
136141
// Finds all libraries that have been modified in this incremental
137142
// version of the kernel program file.
138143
static void FindModifiedLibraries(Program* program,
@@ -331,6 +336,33 @@ class KernelLoader : public ValueObject {
331336
Mapping<Library> libraries_;
332337
Mapping<Class> classes_;
333338

339+
// We "re-use" the normal .dill file format for encoding compiled evaluation
340+
// expressions from the debugger. This allows us to also reuse the normal
341+
// a) kernel loader b) flow graph building code. The encoding is either one
342+
// of the following two options:
343+
//
344+
// * Option a) The expression is evaluated inside an instance method call
345+
// context:
346+
//
347+
// Program:
348+
// |> library "evaluate:source"
349+
// |> class "#DebugClass"
350+
// |> procedure ":Eval"
351+
//
352+
// * Option b) The expression is evaluated outside an instance method call
353+
// context:
354+
//
355+
// Program:
356+
// |> library "evaluate:source"
357+
// |> procedure ":Eval"
358+
//
359+
// See
360+
// * pkg/front_end/lib/src/fasta/incremental_compiler.dart:compileExpression
361+
// * pkg/front_end/lib/src/fasta/kernel/utils.dart:serializeProcedure
362+
//
363+
Library& expression_evaluation_library_;
364+
Function& expression_evaluation_function_;
365+
334366
GrowableArray<const Function*> functions_;
335367
GrowableArray<const Field*> fields_;
336368

0 commit comments

Comments
 (0)