1aa6f00107
The CL is a step towards have a more restricted and wellstructured handled of references and canonical names. The CL moves Reference to canonical_name.dart and makes CanonicalName.reference private, and replaces CanonicalName.getReference with a 'reference' getter. It also removes NamedNode.canonicalName, Field.getterCanonicalName and Field.setterCanonicalName so that these can only be accessed through the corresponding reference. This is to reduce the reliance on the canonical names which, ideally, should only be part of serialization and deserialization. TEST=existing Change-Id: I955fb7d52d4e112d8741f7c12dcf38b74ae0c91a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/190442 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
204 lines
6.8 KiB
Dart
204 lines
6.8 KiB
Dart
// 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.
|
|
|
|
// @dart = 2.9
|
|
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/binary/ast_from_binary.dart';
|
|
import 'package:kernel/binary/ast_to_binary.dart';
|
|
import 'package:kernel/text/ast_to_text.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:front_end/src/compute_platform_binaries_location.dart'
|
|
show computePlatformBinariesLocation;
|
|
|
|
/// Test metadata: to each node we attach a metadata that contains
|
|
/// * node formatted as string
|
|
/// * reference to its enclosing member
|
|
/// * type representing the first type parameter of its enclosing function
|
|
class Metadata {
|
|
final String string;
|
|
final Reference _memberRef;
|
|
final DartType type;
|
|
|
|
Member get member => _memberRef?.asMember;
|
|
|
|
Metadata.forNode(TreeNode n)
|
|
: this(
|
|
n.leakingDebugToString(),
|
|
// Refers to the member, not about the function => use getter.
|
|
getMemberReferenceGetter(getMemberForMetadata(n)),
|
|
getTypeForMetadata(n));
|
|
|
|
Metadata(this.string, this._memberRef, this.type);
|
|
}
|
|
|
|
Member getMemberForMetadata(TreeNode node) {
|
|
final parent = node.parent;
|
|
if (parent == null) return null;
|
|
if (parent is Member) return parent;
|
|
return getMemberForMetadata(parent);
|
|
}
|
|
|
|
DartType getTypeForMetadata(TreeNode node) {
|
|
final parent = node.parent;
|
|
if (parent == null) return const VoidType();
|
|
if (parent is FunctionNode) {
|
|
if (parent.typeParameters.isEmpty) {
|
|
return const VoidType();
|
|
}
|
|
return new TypeParameterType(parent.typeParameters[0], Nullability.legacy);
|
|
}
|
|
return getTypeForMetadata(parent);
|
|
}
|
|
|
|
class TestMetadataRepository extends MetadataRepository<Metadata> {
|
|
static const kTag = 'kernel.metadata.test';
|
|
|
|
final String tag = kTag;
|
|
|
|
final Map<TreeNode, Metadata> mapping = <TreeNode, Metadata>{};
|
|
|
|
void writeToBinary(Metadata metadata, Node node, BinarySink sink) {
|
|
expect(metadata, equals(mapping[node]));
|
|
sink.writeByteList(utf8.encode(metadata.string));
|
|
sink.writeStringReference(metadata.string);
|
|
sink.writeNullAllowedCanonicalNameReference(
|
|
metadata.member?.reference?.canonicalName);
|
|
sink.writeDartType(metadata.type);
|
|
}
|
|
|
|
Metadata readFromBinary(Node node, BinarySource source) {
|
|
final string1 = utf8.decode(source.readByteList());
|
|
final string2 = source.readStringReference();
|
|
final memberRef = source.readNullableCanonicalNameReference()?.reference;
|
|
final type = source.readDartType();
|
|
expect(string1, equals(string2));
|
|
return new Metadata(string2, memberRef, type);
|
|
}
|
|
}
|
|
|
|
class BytesBuilderSink implements Sink<List<int>> {
|
|
final builder = new BytesBuilder(copy: false);
|
|
|
|
@override
|
|
void add(List<int> bytes) {
|
|
builder.add(bytes);
|
|
}
|
|
|
|
@override
|
|
void close() {}
|
|
}
|
|
|
|
typedef NodePredicate = bool Function(TreeNode node);
|
|
|
|
/// Visitor calling [handle] function on every node which can have metadata
|
|
/// associated with it and also satisfies the given [predicate].
|
|
class Visitor extends RecursiveVisitor {
|
|
final NodePredicate predicate;
|
|
final void Function(TreeNode) handle;
|
|
|
|
Visitor(this.predicate, this.handle);
|
|
|
|
defaultTreeNode(TreeNode node) {
|
|
super.defaultTreeNode(node);
|
|
if (MetadataRepository.isSupported(node) && predicate(node)) {
|
|
handle(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Visit the given component assigning [Metadata] object created with
|
|
/// [Metadata.forNode] to each supported node in the component which matches
|
|
/// [shouldAnnotate] predicate.
|
|
void annotate(Component p, NodePredicate shouldAnnotate) {
|
|
globalDebuggingNames = new NameSystem();
|
|
final repository = p.metadata[TestMetadataRepository.kTag];
|
|
p.accept(new Visitor(shouldAnnotate, (node) {
|
|
repository.mapping[node] = new Metadata.forNode(node);
|
|
}));
|
|
}
|
|
|
|
/// Visit the given component and checks that each supported node in the
|
|
/// component matching [shouldAnnotate] predicate has correct metadata.
|
|
void validate(Component p, NodePredicate shouldAnnotate) {
|
|
globalDebuggingNames = new NameSystem();
|
|
final repository = p.metadata[TestMetadataRepository.kTag];
|
|
p.accept(new Visitor(shouldAnnotate, (node) {
|
|
final m = repository.mapping[node];
|
|
final expected = new Metadata.forNode(node);
|
|
|
|
expect(m, isNotNull);
|
|
expect(m.string, equals(expected.string));
|
|
expect(m.member, equals(expected.member));
|
|
expect(m.type, equals(expected.type));
|
|
}));
|
|
}
|
|
|
|
Component fromBinary(List<int> bytes) {
|
|
var component = new Component();
|
|
component.addMetadataRepository(new TestMetadataRepository());
|
|
new BinaryBuilderWithMetadata(bytes).readComponent(component);
|
|
return component;
|
|
}
|
|
|
|
List<int> toBinary(Component p) {
|
|
final sink = new BytesBuilderSink();
|
|
new BinaryPrinter(sink).writeComponentFile(p);
|
|
return sink.builder.takeBytes();
|
|
}
|
|
|
|
main() async {
|
|
bool anyNode(TreeNode node) => true;
|
|
bool onlyMethods(TreeNode node) =>
|
|
node is Procedure &&
|
|
node.kind == ProcedureKind.Method &&
|
|
node.enclosingClass != null;
|
|
|
|
final Uri platform = computePlatformBinariesLocation(forceBuildDir: true)
|
|
.resolve("vm_platform_strong.dill");
|
|
final List<int> platformBinary =
|
|
await new File(platform.toFilePath()).readAsBytes();
|
|
|
|
Future<void> testRoundTrip(List<int> Function(List<int>) binaryTransformer,
|
|
NodePredicate shouldAnnotate) async {
|
|
final component = fromBinary(platformBinary);
|
|
annotate(component, shouldAnnotate);
|
|
validate(component, shouldAnnotate);
|
|
expect(component.metadata[TestMetadataRepository.kTag].mapping.length,
|
|
greaterThan(0));
|
|
|
|
final annotatedComponentBinary = binaryTransformer(toBinary(component));
|
|
final annotatedComponentFromBinary = fromBinary(annotatedComponentBinary);
|
|
validate(annotatedComponentFromBinary, shouldAnnotate);
|
|
expect(
|
|
annotatedComponentFromBinary
|
|
.metadata[TestMetadataRepository.kTag].mapping.length,
|
|
greaterThan(0));
|
|
}
|
|
|
|
test('annotate-serialize-deserialize-validate', () async {
|
|
await testRoundTrip((binary) => binary, anyNode);
|
|
});
|
|
|
|
test('annotate-serialize-deserialize-validate-only-methods', () async {
|
|
await testRoundTrip((binary) => binary, onlyMethods);
|
|
});
|
|
|
|
test('annotate-serialize-deserialize-twice-then-validate', () async {
|
|
// This test validates that serializing a component that was just
|
|
// deserialized (without visiting anything) works.
|
|
await testRoundTrip((binary) => toBinary(fromBinary(binary)), anyNode);
|
|
});
|
|
|
|
test('annotate-serialize-deserialize-twice-then-validate-only-methods',
|
|
() async {
|
|
// This test validates that serializing a component that was just
|
|
// deserialized (without visiting anything) works.
|
|
await testRoundTrip((binary) => toBinary(fromBinary(binary)), onlyMethods);
|
|
});
|
|
}
|