mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[vm, reload] Remove _getUnusedChangesInLastReload.
This function has been unused for a long time. Also, computing fingerprints for bytecode function causes bytecode to be loaded, which can lead to an old enum class being initialized with enum values for the new enum class, causing a forwarding loop when migrating enum values. Change-Id: Iad9b2bb11aaf161d66f7752cb99cc4b4fc9fb310 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116442 Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
parent
abb606e613
commit
7bbfd532de
|
@ -35,7 +35,6 @@ regress_34841_test: RuntimeError # http://dartbug.com/34841
|
|||
rewind_optimized_out_test: SkipByDesign # No incremental compiler available.
|
||||
rewind_test: SkipByDesign # No incremental compiler available.
|
||||
simple_reload_test: RuntimeError, Timeout # Issue 35506
|
||||
unused_changes_in_last_reload_test: RuntimeError
|
||||
|
||||
[ $compiler == dartkp ]
|
||||
*: SkipByDesign # Non-kernel also skips precompiled mode.
|
||||
|
@ -157,7 +156,6 @@ rewind_optimized_out_test: RuntimeError # Issue #34736
|
|||
rewind_test: Pass, RuntimeError
|
||||
set_name_rpc_test: RuntimeError # Please triage.
|
||||
simple_reload_test: RuntimeError, Timeout
|
||||
unused_changes_in_last_reload_test: Skip # Times out on sim architectures.
|
||||
valid_source_locations_test: Pass, Slow, Timeout # Issue 34736
|
||||
|
||||
# Kernel works slightly different. There are kernel specific versions.
|
||||
|
@ -171,4 +169,3 @@ evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
|
|||
evaluate_activation_test/scope: RuntimeError # http://dartbug.com/20047
|
||||
get_source_report_test: RuntimeError # Should pass again when constant evaluation is relanded, see http://dartbug.com/36600
|
||||
pause_on_unhandled_async_exceptions2_test: Pass, Slow
|
||||
unused_changes_in_last_reload_test: RuntimeError
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:isolate';
|
||||
|
||||
unchangedFunction() => "unchanged";
|
||||
var unchangedField = "unchanged".toString();
|
||||
|
||||
removedFunction() => "removed";
|
||||
var removedField = "removed".toString();
|
||||
|
||||
function() => "original value";
|
||||
var uninitializedField = "original initializer".toString();
|
||||
var fieldLiteralInitializer = "original initializer";
|
||||
var initializedField = "original initializer".toString();
|
||||
var neverReferencedField = "original initializer".toString();
|
||||
|
||||
// Not initially finalized.
|
||||
class C {
|
||||
function() => "original value";
|
||||
}
|
||||
|
||||
class S {}
|
||||
class M {}
|
||||
class MA1 extends S with M {}
|
||||
class MA2 = S with M;
|
||||
|
||||
main() {
|
||||
new RawReceivePort(); // Keep alive.
|
||||
print(function());
|
||||
print(initializedField);
|
||||
print(new MA1());
|
||||
print(new MA2());
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
unchangedFunction() => "unchanged";
|
||||
var unchangedField = "unchanged".toString();
|
||||
|
||||
function() => "new value";
|
||||
var uninitializedField = "new initializer".toString();
|
||||
var fieldLiteralInitializer = "new initializer";
|
||||
var initializedField = "new initializer".toString();
|
||||
var neverReferencedField = "new initializer".toString();
|
||||
|
||||
// Not initially finalized.
|
||||
class C {
|
||||
function() => "new value";
|
||||
}
|
||||
|
||||
class S {}
|
||||
class M {
|
||||
newFunction() => "new value";
|
||||
}
|
||||
class MA1 extends S with M {
|
||||
newFunction2() => "new value";
|
||||
}
|
||||
class MA2 = S with M;
|
||||
|
||||
class NewClass {
|
||||
function() => "new value";
|
||||
}
|
||||
|
||||
typedef bool NewTypedef(Object obj);
|
||||
|
||||
main2() {
|
||||
print(function());
|
||||
print(uninitializedField);
|
||||
print(initializedField);
|
||||
print(new C().function());
|
||||
print(new NewClass().function());
|
||||
print(new MA1().newFunction());
|
||||
print(new MA1().newFunction2());
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'test_helper.dart';
|
||||
import 'dart:developer';
|
||||
import 'dart:isolate' as I;
|
||||
import 'dart:io';
|
||||
import 'service_test_common.dart';
|
||||
import 'package:observatory/service.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
// Chop off the file name.
|
||||
String baseDirectory = path.dirname(Platform.script.path) + '/';
|
||||
|
||||
Uri baseUri = Platform.script.replace(path: baseDirectory);
|
||||
Uri v1Uri = baseUri.resolveUri(Uri.parse('unused_changes_in_last_reload/v1/main.dart'));
|
||||
Uri v2Uri = baseUri.resolveUri(Uri.parse('unused_changes_in_last_reload/v2/main.dart'));
|
||||
|
||||
testMain() async {
|
||||
print(baseUri);
|
||||
debugger(); // Stop here.
|
||||
// Spawn the child isolate.
|
||||
I.Isolate isolate = await I.Isolate.spawnUri(v1Uri, [], null);
|
||||
print(isolate);
|
||||
debugger();
|
||||
}
|
||||
|
||||
var tests = <IsolateTest>[
|
||||
// Stopped at 'debugger' statement.
|
||||
hasStoppedAtBreakpoint,
|
||||
// Resume the isolate into the while loop.
|
||||
resumeIsolate,
|
||||
// Stop at 'debugger' statement.
|
||||
hasStoppedAtBreakpoint,
|
||||
(Isolate mainIsolate) async {
|
||||
// Grab the VM.
|
||||
VM vm = mainIsolate.vm;
|
||||
await vm.reloadIsolates();
|
||||
expect(vm.isolates.length, 2);
|
||||
|
||||
// Find the child isolate.
|
||||
Isolate childIsolate =
|
||||
vm.isolates.firstWhere((Isolate i) => i != mainIsolate);
|
||||
expect(childIsolate, isNotNull);
|
||||
|
||||
// Fetch unused.
|
||||
await childIsolate.invokeRpc("_getUnusedChangesInLastReload", {}).then((v) {
|
||||
print(v);
|
||||
throw "MissingError";
|
||||
}, onError: (e) {
|
||||
print(e);
|
||||
});
|
||||
|
||||
// Reload to v1 (null change).
|
||||
var response = await childIsolate.reloadSources(
|
||||
rootLibUri: v1Uri.toString(),
|
||||
);
|
||||
print(response);
|
||||
expect(response['success'], isTrue);
|
||||
|
||||
// Fetch unused.
|
||||
response = await childIsolate.invokeRpc("_getUnusedChangesInLastReload", {});
|
||||
print(response);
|
||||
var unused = response['unused'].map((ea) => ea.toString());
|
||||
expect(unused, unorderedEquals([]));
|
||||
|
||||
// Reload to v2.
|
||||
response = await childIsolate.reloadSources(
|
||||
rootLibUri: v2Uri.toString(),
|
||||
);
|
||||
print(response);
|
||||
expect(response['success'], isTrue);
|
||||
|
||||
// Fetch unused.
|
||||
response = await childIsolate.invokeRpc("_getUnusedChangesInLastReload", {});
|
||||
print(response);
|
||||
unused = response['unused'].map((ea) => ea.toString());
|
||||
expect(unused, unorderedEquals([
|
||||
'Class(C)',
|
||||
'Class(NewClass)',
|
||||
'Field(main.dart.uninitializedField)',
|
||||
'Field(main.dart.fieldLiteralInitializer)',
|
||||
'Field(main.dart.initializedField)',
|
||||
'Field(main.dart.neverReferencedField)',
|
||||
'ServiceFunction(M.newFunction)',
|
||||
'ServiceFunction(MA1.newFunction2)',
|
||||
'ServiceFunction(function)',
|
||||
'ServiceFunction(main2)',
|
||||
]));
|
||||
|
||||
// Invoke next main.
|
||||
Library lib = childIsolate.rootLibrary;
|
||||
await lib.load();
|
||||
Instance result = await lib.evaluate('main2()');
|
||||
expect(result.valueAsString, equals('null'));
|
||||
|
||||
// Fetch unused.
|
||||
response = await childIsolate.invokeRpc("_getUnusedChangesInLastReload", {});
|
||||
print(response);
|
||||
unused = response['unused'].map((ea) => ea.toString());
|
||||
expect(unused, unorderedEquals([
|
||||
'Field(main.dart.fieldLiteralInitializer)',
|
||||
'Field(main.dart.initializedField)',
|
||||
'Field(main.dart.neverReferencedField)',
|
||||
// TODO(31265): M.newFunction should be considered used.
|
||||
'ServiceFunction(M.newFunction)',
|
||||
]));
|
||||
|
||||
// Reload to v2 again.
|
||||
response = await childIsolate.reloadSources(
|
||||
rootLibUri: v2Uri.toString(),
|
||||
);
|
||||
print(response);
|
||||
expect(response['success'], isTrue);
|
||||
|
||||
// Fetch unused.
|
||||
response = await childIsolate.invokeRpc("_getUnusedChangesInLastReload", {});
|
||||
print(response);
|
||||
unused = response['unused'].map((ea) => ea.toString());
|
||||
expect(unused, unorderedEquals([]));
|
||||
}
|
||||
];
|
||||
|
||||
main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
|
|
@ -676,9 +676,6 @@ void IsolateReloadContext::Reload(bool force_reload,
|
|||
if (skip_reload) {
|
||||
ASSERT(modified_libs_->IsEmpty());
|
||||
reload_skipped_ = true;
|
||||
// Inform GetUnusedChangesInLastReload that a reload has happened.
|
||||
I->object_store()->set_changed_in_last_reload(
|
||||
GrowableObjectArray::Handle(GrowableObjectArray::New()));
|
||||
ReportOnJSON(js_);
|
||||
|
||||
// If we use the CFE and performed a compilation, we need to notify that
|
||||
|
@ -1218,81 +1215,6 @@ void IsolateReloadContext::VerifyMaps() {
|
|||
}
|
||||
#endif
|
||||
|
||||
static void RecordChanges(const GrowableObjectArray& changed_in_last_reload,
|
||||
const Class& old_cls,
|
||||
const Class& new_cls) {
|
||||
// All members of enum classes are synthetic, so nothing to report here.
|
||||
if (new_cls.is_enum_class()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't report `typedef bool Predicate(Object o)` as unused. There is nothing
|
||||
// to execute.
|
||||
if (new_cls.IsTypedefClass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_cls.raw() == old_cls.raw()) {
|
||||
// A new class maps to itself. All its functions, field initizers, and so
|
||||
// on are new.
|
||||
changed_in_last_reload.Add(new_cls);
|
||||
return;
|
||||
}
|
||||
|
||||
if (old_cls.IsTopLevel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!old_cls.is_finalized()) {
|
||||
if (new_cls.SourceFingerprint() == old_cls.SourceFingerprint()) {
|
||||
return;
|
||||
}
|
||||
// We don't know the members. Register interest in the whole class. Creates
|
||||
// false positives.
|
||||
changed_in_last_reload.Add(new_cls);
|
||||
return;
|
||||
}
|
||||
ASSERT(new_cls.is_finalized());
|
||||
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const Array& functions = Array::Handle(zone, new_cls.functions());
|
||||
const Array& fields = Array::Handle(zone, new_cls.fields());
|
||||
Function& new_function = Function::Handle(zone);
|
||||
Function& old_function = Function::Handle(zone);
|
||||
Field& new_field = Field::Handle(zone);
|
||||
Field& old_field = Field::Handle(zone);
|
||||
String& selector = String::Handle(zone);
|
||||
for (intptr_t i = 0; i < functions.Length(); i++) {
|
||||
new_function ^= functions.At(i);
|
||||
selector = new_function.name();
|
||||
old_function = old_cls.LookupFunction(selector);
|
||||
// If we made live changes with proper structed edits, this would just be
|
||||
// old != new.
|
||||
if (old_function.IsNull() || (new_function.SourceFingerprint() !=
|
||||
old_function.SourceFingerprint())) {
|
||||
ASSERT(!new_function.HasCode());
|
||||
ASSERT(new_function.usage_counter() == 0);
|
||||
changed_in_last_reload.Add(new_function);
|
||||
}
|
||||
}
|
||||
for (intptr_t i = 0; i < fields.Length(); i++) {
|
||||
new_field ^= fields.At(i);
|
||||
if (!new_field.is_static()) continue;
|
||||
selector = new_field.name();
|
||||
old_field = old_cls.LookupField(selector);
|
||||
if (old_field.IsNull() || !old_field.is_static()) {
|
||||
// New field.
|
||||
changed_in_last_reload.Add(new_field);
|
||||
} else if (new_field.SourceFingerprint() != old_field.SourceFingerprint()) {
|
||||
// Changed field.
|
||||
changed_in_last_reload.Add(new_field);
|
||||
if (!old_field.IsUninitialized()) {
|
||||
new_field.set_initializer_changed_after_initialization(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IsolateReloadContext::Commit() {
|
||||
TIMELINE_SCOPE(Commit);
|
||||
TIR_Print("---- COMMITTING RELOAD\n");
|
||||
|
@ -1331,9 +1253,6 @@ void IsolateReloadContext::Commit() {
|
|||
lib_map.Release();
|
||||
}
|
||||
|
||||
const GrowableObjectArray& changed_in_last_reload =
|
||||
GrowableObjectArray::Handle(GrowableObjectArray::New());
|
||||
|
||||
{
|
||||
TIMELINE_SCOPE(CopyStaticFieldsAndPatchFieldsAndFunctions);
|
||||
// Copy static field values from the old classes to the new classes.
|
||||
|
@ -1359,7 +1278,6 @@ void IsolateReloadContext::Commit() {
|
|||
old_cls.PatchFieldsAndFunctions();
|
||||
old_cls.MigrateImplicitStaticClosures(this, new_cls);
|
||||
}
|
||||
RecordChanges(changed_in_last_reload, old_cls, new_cls);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1378,15 +1296,6 @@ void IsolateReloadContext::Commit() {
|
|||
}
|
||||
}
|
||||
|
||||
if (FLAG_identity_reload) {
|
||||
Object& changed = Object::Handle();
|
||||
for (intptr_t i = 0; i < changed_in_last_reload.Length(); i++) {
|
||||
changed = changed_in_last_reload.At(i);
|
||||
ASSERT(changed.IsClass()); // Only fuzzy from lazy finalization.
|
||||
}
|
||||
}
|
||||
I->object_store()->set_changed_in_last_reload(changed_in_last_reload);
|
||||
|
||||
{
|
||||
TIMELINE_SCOPE(UpdateLibrariesArray);
|
||||
// Update the libraries array.
|
||||
|
|
|
@ -139,7 +139,6 @@ class ObjectPointerVisitor;
|
|||
R_(Function, megamorphic_miss_function) \
|
||||
RW(Array, code_order_table) \
|
||||
RW(Array, obfuscation_map) \
|
||||
RW(GrowableObjectArray, changed_in_last_reload) \
|
||||
RW(Class, ffi_pointer_class) \
|
||||
RW(Class, ffi_native_type_class) \
|
||||
RW(Class, ffi_struct_class) \
|
||||
|
|
|
@ -1415,70 +1415,6 @@ static bool GetScripts(Thread* thread, JSONStream* js) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static const MethodParameter* get_unused_changes_in_last_reload_params[] = {
|
||||
ISOLATE_PARAMETER, NULL,
|
||||
};
|
||||
|
||||
static bool GetUnusedChangesInLastReload(Thread* thread, JSONStream* js) {
|
||||
if (CheckCompilerDisabled(thread, js)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const GrowableObjectArray& changed_in_last_reload =
|
||||
GrowableObjectArray::Handle(
|
||||
thread->isolate()->object_store()->changed_in_last_reload());
|
||||
if (changed_in_last_reload.IsNull()) {
|
||||
js->PrintError(kIsolateMustHaveReloaded, "No change to compare with.");
|
||||
return true;
|
||||
}
|
||||
JSONObject jsobj(js);
|
||||
jsobj.AddProperty("type", "UnusedChangesInLastReload");
|
||||
JSONArray jsarr(&jsobj, "unused");
|
||||
Object& changed = Object::Handle();
|
||||
Function& function = Function::Handle();
|
||||
Field& field = Field::Handle();
|
||||
Class& cls = Class::Handle();
|
||||
Array& functions = Array::Handle();
|
||||
Array& fields = Array::Handle();
|
||||
for (intptr_t i = 0; i < changed_in_last_reload.Length(); i++) {
|
||||
changed = changed_in_last_reload.At(i);
|
||||
if (changed.IsFunction()) {
|
||||
function ^= changed.raw();
|
||||
if (!function.WasExecuted()) {
|
||||
jsarr.AddValue(function);
|
||||
}
|
||||
} else if (changed.IsField()) {
|
||||
field ^= changed.raw();
|
||||
if (field.IsUninitialized() ||
|
||||
field.initializer_changed_after_initialization()) {
|
||||
jsarr.AddValue(field);
|
||||
}
|
||||
} else if (changed.IsClass()) {
|
||||
cls ^= changed.raw();
|
||||
if (!cls.is_finalized()) {
|
||||
// Not used at all.
|
||||
jsarr.AddValue(cls);
|
||||
} else {
|
||||
functions = cls.functions();
|
||||
for (intptr_t j = 0; j < functions.Length(); j++) {
|
||||
function ^= functions.At(j);
|
||||
if (!function.WasExecuted()) {
|
||||
jsarr.AddValue(function);
|
||||
}
|
||||
}
|
||||
fields = cls.fields();
|
||||
for (intptr_t j = 0; j < fields.Length(); j++) {
|
||||
field ^= fields.At(j);
|
||||
if (field.IsUninitialized()) {
|
||||
jsarr.AddValue(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const MethodParameter* get_stack_params[] = {
|
||||
RUNNABLE_ISOLATE_PARAMETER,
|
||||
NULL,
|
||||
|
@ -4763,8 +4699,6 @@ static const ServiceMethodDescriptor service_methods_[] = {
|
|||
get_source_report_params },
|
||||
{ "getStack", GetStack,
|
||||
get_stack_params },
|
||||
{ "_getUnusedChangesInLastReload", GetUnusedChangesInLastReload,
|
||||
get_unused_changes_in_last_reload_params },
|
||||
{ "_getTagProfile", GetTagProfile,
|
||||
get_tag_profile_params },
|
||||
{ "_getTypeArgumentsList", GetTypeArgumentsList,
|
||||
|
|
Loading…
Reference in a new issue