3e44898e08
TEST=existing Change-Id: I72583e500cb0b69d9352b040c3e2885f9e0450c5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195681 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
203 lines
8.2 KiB
Dart
203 lines
8.2 KiB
Dart
// Copyright (c) 2020, 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.
|
|
|
|
// @dart = 2.9
|
|
|
|
import 'package:kernel/binary/ast_from_binary.dart';
|
|
import 'package:kernel/binary/ast_to_binary.dart';
|
|
import 'binary/utils.dart';
|
|
|
|
main() {
|
|
final Uri lib1Uri = Uri.parse('org-dartlang:///lib.dart');
|
|
final Library lib1 = new Library(lib1Uri, fileUri: lib1Uri);
|
|
final Field field = new Field.mutable(new Name("f"), fileUri: lib1Uri);
|
|
lib1.addField(field);
|
|
final Block libProcedureBody = new Block([
|
|
new ExpressionStatement(new StaticSet(field, new IntLiteral(42))),
|
|
new ReturnStatement(new StaticGet(field)),
|
|
]);
|
|
final Procedure libProcedure = new Procedure(
|
|
new Name("method"),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(libProcedureBody, returnType: new DynamicType()),
|
|
fileUri: lib1Uri);
|
|
lib1.addProcedure(libProcedure);
|
|
|
|
final Uri lib2Uri = Uri.parse('org-dartlang:///lib2.dart');
|
|
final Library lib2 = new Library(lib2Uri, fileUri: lib2Uri);
|
|
final Block lib2ProcedureBody = new Block([
|
|
new ExpressionStatement(new StaticSet(field, new IntLiteral(43))),
|
|
new ReturnStatement(new StaticGet(field)),
|
|
]);
|
|
final Procedure lib2Procedure = new Procedure(
|
|
new Name("method"),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(lib2ProcedureBody, returnType: new DynamicType()),
|
|
fileUri: lib2Uri);
|
|
lib2.addProcedure(lib2Procedure);
|
|
|
|
verifyTargets(libProcedure, lib2Procedure, field, field);
|
|
List<int> writtenBytesFieldOriginal = serialize(lib1, lib2);
|
|
// Canonical names are now set: Verify that the field is marked as such,
|
|
// canonical-name-wise.
|
|
String getterCanonicalName = '${field.getterReference.canonicalName}';
|
|
if (field.getterReference.canonicalName.parent.name != "@getters") {
|
|
throw "Expected @getters parent, but had "
|
|
"${field.getterReference.canonicalName.parent.name}";
|
|
}
|
|
String setterCanonicalName = '${field.setterReference.canonicalName}';
|
|
if (field.setterReference.canonicalName.parent.name != "@setters") {
|
|
throw "Expected @setters parent, but had "
|
|
"${field.setterReference.canonicalName.parent.name}";
|
|
}
|
|
|
|
// Replace the field with a setter/getter pair.
|
|
lib1.fields.remove(field);
|
|
FunctionNode getterFunction = new FunctionNode(new Block([]));
|
|
Procedure getter = new Procedure(
|
|
new Name("f"), ProcedureKind.Getter, getterFunction,
|
|
reference: field.getterReference, fileUri: lib1Uri);
|
|
// Important: Unbind any old canonical name
|
|
// (nulling out the canonical name is not enough because it leaves the old
|
|
// canonical name (which always stays alive) with a pointer to the reference,
|
|
// meaning that if one tried to rebind it (e.g. if going back to a field from
|
|
// a setter/getter), the reference wouldn't (because of the way `bindTo` is
|
|
// implemented) actually have it's canonical name set, and serialization
|
|
// wouldn't work.)
|
|
field.getterReference?.canonicalName?.unbind();
|
|
lib1.addProcedure(getter);
|
|
|
|
FunctionNode setterFunction = new FunctionNode(new Block([]),
|
|
positionalParameters: [new VariableDeclaration("foo")]);
|
|
Procedure setter = new Procedure(
|
|
new Name("f"), ProcedureKind.Setter, setterFunction,
|
|
reference: field.setterReference, fileUri: lib1Uri);
|
|
// Important: Unbind any old canonical name
|
|
// (nulling out the canonical name is not enough, see above).
|
|
field.setterReference?.canonicalName?.unbind();
|
|
lib1.addProcedure(setter);
|
|
|
|
verifyTargets(libProcedure, lib2Procedure, getter, setter);
|
|
List<int> writtenBytesGetterSetter = serialize(lib1, lib2);
|
|
// Canonical names are now set: Verify that the getter/setter is marked as
|
|
// such, canonical-name-wise.
|
|
if (getter.reference.canonicalName.parent.name != "@getters") {
|
|
throw "Expected @getters parent, but had "
|
|
"${getter.reference.canonicalName.parent.name}";
|
|
}
|
|
if ('${getter.reference.canonicalName}' != getterCanonicalName) {
|
|
throw "Unexpected getter canonical name. "
|
|
"Expected $getterCanonicalName, "
|
|
"actual ${getter.reference.canonicalName}.";
|
|
}
|
|
if (setter.reference.canonicalName.parent.name != "@setters") {
|
|
throw "Expected @setters parent, but had "
|
|
"${setter.reference.canonicalName.parent.name}";
|
|
}
|
|
if ('${setter.reference.canonicalName}' != setterCanonicalName) {
|
|
throw "Unexpected setter canonical name. "
|
|
"Expected $setterCanonicalName, "
|
|
"actual ${setter.reference.canonicalName}.";
|
|
}
|
|
|
|
// Replace getter/setter with field.
|
|
lib1.procedures.remove(getter);
|
|
lib1.procedures.remove(setter);
|
|
final Field fieldReplacement = new Field.mutable(new Name("f"),
|
|
getterReference: getter.reference,
|
|
setterReference: setter.reference,
|
|
fileUri: lib1Uri);
|
|
// Important: Unbind any old canonical name
|
|
// (nulling out the canonical name is not enough, see above).
|
|
fieldReplacement.getterReference?.canonicalName?.unbind();
|
|
fieldReplacement.setterReference?.canonicalName?.unbind();
|
|
lib1.addField(fieldReplacement);
|
|
|
|
verifyTargets(
|
|
libProcedure, lib2Procedure, fieldReplacement, fieldReplacement);
|
|
List<int> writtenBytesFieldNew = serialize(lib1, lib2);
|
|
// Canonical names are now set: Verify that the field is marked as such,
|
|
// canonical-name-wise.
|
|
if (fieldReplacement.getterReference.canonicalName.parent.name !=
|
|
"@getters") {
|
|
throw "Expected @getters parent, but had "
|
|
"${fieldReplacement.getterReference.canonicalName.parent.name}";
|
|
}
|
|
if (fieldReplacement.setterReference.canonicalName.parent.name !=
|
|
"@setters") {
|
|
throw "Expected @setters parent, but had "
|
|
"${fieldReplacement.setterReference.canonicalName.parent.name}";
|
|
}
|
|
|
|
// Load the written stuff and ensure it is as expected.
|
|
// First one has a field.
|
|
Component componentLoaded = new Component();
|
|
new BinaryBuilder(writtenBytesFieldOriginal)
|
|
.readSingleFileComponent(componentLoaded);
|
|
verifyTargets(
|
|
componentLoaded.libraries[0].procedures.single,
|
|
componentLoaded.libraries[1].procedures.single,
|
|
componentLoaded.libraries[0].fields.single,
|
|
componentLoaded.libraries[0].fields.single);
|
|
|
|
// Second one has a getter/setter pair.
|
|
componentLoaded = new Component();
|
|
new BinaryBuilder(writtenBytesGetterSetter)
|
|
.readSingleFileComponent(componentLoaded);
|
|
assert(componentLoaded.libraries[0].procedures[2].isSetter);
|
|
verifyTargets(
|
|
componentLoaded.libraries[0].procedures[0],
|
|
componentLoaded.libraries[1].procedures[0],
|
|
componentLoaded.libraries[0].procedures[1],
|
|
componentLoaded.libraries[0].procedures[2]);
|
|
|
|
// Third one has a field again.
|
|
componentLoaded = new Component();
|
|
new BinaryBuilder(writtenBytesFieldNew)
|
|
.readSingleFileComponent(componentLoaded);
|
|
verifyTargets(
|
|
componentLoaded.libraries[0].procedures.single,
|
|
componentLoaded.libraries[1].procedures.single,
|
|
componentLoaded.libraries[0].fields.single,
|
|
componentLoaded.libraries[0].fields.single);
|
|
}
|
|
|
|
void verifyTargets(Procedure libProcedure, Procedure lib2Procedure,
|
|
Member getterTarget, Member setterTarget) {
|
|
if (getGetTarget(libProcedure) != getterTarget) {
|
|
throw "Unexpected get target for lib #1";
|
|
}
|
|
if (getSetTarget(libProcedure) != setterTarget) {
|
|
throw "Unexpected set target for lib #1";
|
|
}
|
|
if (getGetTarget(lib2Procedure) != getterTarget) {
|
|
throw "Unexpected get target for lib #2";
|
|
}
|
|
if (getSetTarget(lib2Procedure) != setterTarget) {
|
|
throw "Unexpected set target for lib #2";
|
|
}
|
|
}
|
|
|
|
List<int> serialize(Library lib1, Library lib2) {
|
|
Component component = new Component(libraries: [lib1, lib2])
|
|
..setMainMethodAndMode(null, false, NonNullableByDefaultCompiledMode.Weak);
|
|
ByteSink sink = new ByteSink();
|
|
new BinaryPrinter(sink).writeComponentFile(component);
|
|
return sink.builder.takeBytes();
|
|
}
|
|
|
|
Member getSetTarget(Procedure p) {
|
|
Block block = p.function.body;
|
|
ExpressionStatement getterStatement = block.statements[0];
|
|
StaticSet staticSet = getterStatement.expression;
|
|
return staticSet.target;
|
|
}
|
|
|
|
Member getGetTarget(Procedure p) {
|
|
Block block = p.function.body;
|
|
ReturnStatement setterStatement = block.statements[1];
|
|
StaticGet staticGet = setterStatement.expression;
|
|
return staticGet.target;
|
|
}
|