[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:
Ryan Macnak 2019-09-10 17:36:24 +00:00 committed by commit-bot@chromium.org
parent abb606e613
commit 7bbfd532de
7 changed files with 0 additions and 364 deletions

View file

@ -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

View file

@ -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());
}

View file

@ -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());
}

View file

@ -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);

View file

@ -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.

View file

@ -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) \

View file

@ -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,