Files
sdk/pkg/kernel/test/metadata_test.dart
T
Johnni Winther 1aa6f00107 [kernel] Refactor CanonicalName/Reference integration
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>
2021-03-10 15:39:28 +00:00

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