|  | // 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. | 
|  |  | 
|  | import 'dart:io'; | 
|  |  | 
|  | import 'package:kernel/ast.dart'; | 
|  | import 'package:kernel/binary/ast_from_binary.dart'; | 
|  | import 'package:kernel/binary/ast_to_binary.dart'; | 
|  |  | 
|  | main() { | 
|  | final Library lib1 = new Library(Uri.parse('org-dartlang:///lib.dart')); | 
|  | final Field field = new Field(new Name("f")); | 
|  | 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())); | 
|  | lib1.addProcedure(libProcedure); | 
|  |  | 
|  | final Library lib2 = new Library(Uri.parse('org-dartlang:///lib2.dart')); | 
|  | 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())); | 
|  | 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. | 
|  | if (field.getterCanonicalName.parent.name != "@fields") { | 
|  | throw "Expected @fields parent, but had " | 
|  | "${field.getterCanonicalName.parent.name}"; | 
|  | } | 
|  | if (field.setterCanonicalName.parent.name != "@=fields") { | 
|  | throw "Expected @=fields parent, but had " | 
|  | "${field.setterCanonicalName.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); | 
|  | // 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); | 
|  | // 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.canonicalName.parent.name != "@getters") { | 
|  | throw "Expected @getters parent, but had " | 
|  | "${getter.canonicalName.parent.name}"; | 
|  | } | 
|  | if (setter.canonicalName.parent.name != "@setters") { | 
|  | throw "Expected @setters parent, but had " | 
|  | "${setter.canonicalName.parent.name}"; | 
|  | } | 
|  |  | 
|  | // Replace getter/setter with field. | 
|  | lib1.procedures.remove(getter); | 
|  | lib1.procedures.remove(setter); | 
|  | final Field fieldReplacement = new Field(new Name("f"), | 
|  | getterReference: getter.reference, setterReference: setter.reference); | 
|  | // 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.getterCanonicalName.parent.name != "@fields") { | 
|  | throw "Expected @fields parent, but had " | 
|  | "${fieldReplacement.getterCanonicalName.parent.name}"; | 
|  | } | 
|  | if (fieldReplacement.setterCanonicalName.parent.name != "@=fields") { | 
|  | throw "Expected @=fields parent, but had " | 
|  | "${fieldReplacement.setterCanonicalName.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; | 
|  | } | 
|  |  | 
|  | /// A [Sink] that directly writes data into a byte builder. | 
|  | class ByteSink implements Sink<List<int>> { | 
|  | final BytesBuilder builder = new BytesBuilder(); | 
|  |  | 
|  | void add(List<int> data) { | 
|  | builder.add(data); | 
|  | } | 
|  |  | 
|  | void close() {} | 
|  | } |