From 145d4585e7012aeba584d26d0a03c8fcadccdc0a Mon Sep 17 00:00:00 2001 From: Dmitry Stefantsov Date: Fri, 10 Jul 2020 07:23:08 +0000 Subject: [PATCH] [cfe] Add support for Fields to round-trip text serialization The flags on the fields are serialized as lists of flag names, allowing for any combination of the flags in any order. Similar technique is used in this CL to serialize the flags on Procedures and VariableDeclarations. This is in the contrast with the previous approach for those nodes, when the flags were the part of the serialized name of the node (such as "static-method" for static Procedures or "final" for final VariableDeclarations). Change-Id: I02eb5ac92f6273542f08f269aee8e6519d3085e9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153766 Commit-Queue: Dmitry Stefantsov Reviewed-by: Johnni Winther --- .../lib/text/text_serialization_verifier.dart | 5 +- pkg/kernel/lib/text/text_serializer.dart | 246 +++++++++++------- ...ext_serializer_from_kernel_nodes_test.dart | 34 +-- pkg/kernel/test/text_serializer_test.dart | 12 +- 4 files changed, 172 insertions(+), 125 deletions(-) diff --git a/pkg/kernel/lib/text/text_serialization_verifier.dart b/pkg/kernel/lib/text/text_serialization_verifier.dart index ee673a61b995..747920efc067 100644 --- a/pkg/kernel/lib/text/text_serialization_verifier.dart +++ b/pkg/kernel/lib/text/text_serialization_verifier.dart @@ -333,7 +333,6 @@ class VerificationState { node is Component || node is Constructor || node is Extension || - node is Field || node is FieldInitializer || node is InvalidInitializer || node is Library || @@ -474,8 +473,8 @@ class TextSerializationVerifier extends RecursiveVisitor { makeRoundTrip(node, argumentsSerializer); } else if (node is FunctionNode) { makeRoundTrip(node, functionNodeSerializer); - } else if (node is Procedure) { - makeRoundTrip(node, procedureSerializer); + } else if (node is Member) { + makeRoundTrip(node, memberSerializer); } else if (node is TypeParameter) { makeRoundTrip(node, typeParameterSerializer); } else if (node is NamedType) { diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart index 00316f626da1..4514bf79068b 100644 --- a/pkg/kernel/lib/text/text_serializer.dart +++ b/pkg/kernel/lib/text/text_serializer.dart @@ -799,76 +799,53 @@ Arguments wrapArguments( return new Arguments(tuple.second, types: tuple.first, named: tuple.third); } -class VariableDeclarationTagger implements Tagger { - const VariableDeclarationTagger(); - - String tag(VariableDeclaration decl) { - String prefix = ""; - if (decl.isCovariant) { - prefix = "${prefix}covariant-"; - if (decl.isFieldFormal) { - // Field formals can only be used in constructors, - // and "covariant" keyword doesn't make sense for them. - throw StateError("Encountered covariant field formal."); - } - } - if (decl.isFieldFormal) { - prefix = "${prefix}fieldformal-"; - } - - if (decl.isConst) { - // It's not clear what invariants we assume about const/final. For now - // throw if we have both. - if (decl.isFinal) { - throw UnimplementedError( - "Encountered a variable that is both const and final."); - } - return "${prefix}const"; - } - if (decl.isFinal) { - return "${prefix}final"; - } - return "${prefix}var"; +const Map variableDeclarationFlagToName = const { + VariableDeclaration.FlagFinal: "final", + VariableDeclaration.FlagConst: "const", + VariableDeclaration.FlagFieldFormal: "field-formal", + VariableDeclaration.FlagCovariant: "covariant", + VariableDeclaration.FlagInScope: "in-scope", + VariableDeclaration.FlagGenericCovariantImpl: "generic-covariant-impl", + VariableDeclaration.FlagLate: "late", + VariableDeclaration.FlagRequired: "required", +}; + +class VariableDeclarationFlagTagger implements Tagger { + String tag(IntLiteral wrappedFlag) { + int flag = wrappedFlag.value; + return variableDeclarationFlagToName[flag] ?? + (throw StateError("Unknown VariableDeclaration flag value: ${flag}.")); } } -TextSerializer makeVariableDeclarationSerializer( - {bool isFinal = false, - bool isConst = false, - bool isCovariant = false, - bool isFieldFormal = false}) => - new Wrapped( - (w) => Tuple3(w.type, w.initializer, w.annotations), - (u) => u.third.fold( - VariableDeclaration(null, - type: u.first, - initializer: u.second, - isFinal: isFinal, - isConst: isConst, - isCovariant: isCovariant, - isFieldFormal: isFieldFormal), - (v, a) => v..addAnnotation(a)), - Tuple3Serializer(dartTypeSerializer, new Optional(expressionSerializer), - new ListSerializer(expressionSerializer))); +TextSerializer variableDeclarationFlagsSerializer = Wrapped( + (w) => List.generate(30, (i) => IntLiteral(w & (1 << i))) + .where((f) => f.value != 0) + .toList(), + (u) => u.fold(0, (fs, f) => fs |= f.value), + ListSerializer(Case( + VariableDeclarationFlagTagger(), + Map.fromIterable(variableDeclarationFlagToName.entries, + key: (e) => e.value, + value: (e) => + Wrapped((_) => null, (_) => IntLiteral(e.key), Nothing()))))); TextSerializer variableDeclarationSerializer = Wrapped( (v) => Tuple2(v.name, v), (t) => t.second..name = t.first, - Binder(Case(VariableDeclarationTagger(), { - "var": makeVariableDeclarationSerializer(), - "final": makeVariableDeclarationSerializer(isFinal: true), - "const": makeVariableDeclarationSerializer(isConst: true), - "covariant-var": makeVariableDeclarationSerializer(isCovariant: true), - "covariant-final": - makeVariableDeclarationSerializer(isCovariant: true, isFinal: true), - "covariant-const": - makeVariableDeclarationSerializer(isCovariant: true, isConst: true), - "fieldformal-var": makeVariableDeclarationSerializer(isFieldFormal: true), - "fieldformal-final": - makeVariableDeclarationSerializer(isFieldFormal: true, isFinal: true), - "fieldformal-const": - makeVariableDeclarationSerializer(isFieldFormal: true, isConst: true), - }))); + Binder( + new Wrapped( + (w) => Tuple4(w.flags, w.type, w.initializer, w.annotations), + (u) => u.fourth.fold( + VariableDeclaration(null, + flags: u.first, type: u.second, initializer: u.third), + (v, a) => v..addAnnotation(a)), + Tuple4Serializer( + variableDeclarationFlagsSerializer, + dartTypeSerializer, + new Optional(expressionSerializer), + new ListSerializer(expressionSerializer))), + )); TextSerializer typeParameterSerializer = Wrapped( (p) => Tuple2(p.name, p), @@ -1593,36 +1570,112 @@ FunctionNode wrapSyncYieldingFunctionNode( Case functionNodeSerializer = new Case.uninitialized(const FunctionNodeTagger()); -class ProcedureTagger implements Tagger { - const ProcedureTagger(); - - String tag(Procedure node) { - String prefix = node.isStatic ? "static-" : ""; - switch (node.kind) { - case ProcedureKind.Method: - return "${prefix}method"; - default: - throw new UnsupportedError("${node.kind}"); - } +const Map procedureFlagToName = const { + Procedure.FlagStatic: "static", + Procedure.FlagAbstract: "abstract", + Procedure.FlagExternal: "external", + Procedure.FlagConst: "const", + Procedure.FlagForwardingStub: "forwarding-stub", + Procedure.FlagForwardingSemiStub: "forwarding-semi-stub", + Procedure.FlagRedirectingFactoryConstructor: + "redirecting-factory-constructor", + Procedure.FlagNoSuchMethodForwarder: "no-such-method-forwarder", + Procedure.FlagExtensionMember: "extension-member", + Procedure.FlagMemberSignature: "member-signature", + Procedure.FlagNonNullableByDefault: "non-nullable-by-default", +}; + +class ProcedureFlagTagger implements Tagger { + const ProcedureFlagTagger(); + + String tag(IntLiteral wrappedFlag) { + int flag = wrappedFlag.value; + return procedureFlagToName[flag] ?? + (throw StateError("Unknown Procedure flag value: ${flag}.")); } } -TextSerializer staticMethodSerializer = new Wrapped( - unwrapStaticMethod, - wrapStaticMethod, - new Tuple2Serializer(nameSerializer, functionNodeSerializer)); - -Tuple2 unwrapStaticMethod(Procedure procedure) { - return new Tuple2(procedure.name, procedure.function); +TextSerializer procedureFlagsSerializer = Wrapped( + (w) => List.generate(30, (i) => IntLiteral(w & (1 << i))) + .where((f) => f.value != 0) + .toList(), + (u) => u.fold(0, (fs, f) => fs |= f.value), + ListSerializer(Case( + ProcedureFlagTagger(), + Map.fromIterable(procedureFlagToName.entries, + key: (e) => e.value, + value: (e) => + Wrapped((_) => null, (_) => IntLiteral(e.key), Nothing()))))); + +const Map fieldFlagToName = const { + Field.FlagFinal: "final", + Field.FlagConst: "const", + Field.FlagStatic: "static", + Field.FlagHasImplicitGetter: "has-implicit-getter", + Field.FlagHasImplicitSetter: "has-implicit-setter", + Field.FlagCovariant: "covariant", + Field.FlagGenericCovariantImpl: "generic-covariant-impl", + Field.FlagLate: "late", + Field.FlagExtensionMember: "extension-member", + Field.FlagNonNullableByDefault: "non-nullable-by-default", + Field.FlagInternalImplementation: "internal-implementation", +}; + +class FieldFlagTagger implements Tagger { + const FieldFlagTagger(); + + String tag(IntLiteral wrappedFlag) { + int flag = wrappedFlag.value; + return fieldFlagToName[flag] ?? + (throw StateError("Unknown Field flag value: ${flag}.")); + } } -Procedure wrapStaticMethod(Tuple2 tuple) { - return new Procedure(tuple.first, ProcedureKind.Method, tuple.second, - isStatic: true); +TextSerializer fieldFlagsSerializer = Wrapped( + (w) => List.generate(30, (i) => IntLiteral(w & (1 << i))) + .where((f) => f.value != 0) + .toList(), + (u) => u.fold(0, (fs, f) => fs |= f.value), + ListSerializer(Case( + FieldFlagTagger(), + Map.fromIterable(fieldFlagToName.entries, + key: (e) => e.value, + value: (e) => + Wrapped((_) => null, (_) => IntLiteral(e.key), Nothing()))))); + +class MemberTagger implements Tagger { + const MemberTagger(); + + String tag(Member node) { + if (node is Field) { + return "field"; + } else if (node is Procedure) { + switch (node.kind) { + case ProcedureKind.Method: + return "method"; + default: + throw new UnsupportedError("${node.kind}"); + } + } else { + throw UnimplementedError("MemberTagger.tag(${node.runtimeType})"); + } + } } -Case procedureSerializer = - new Case.uninitialized(const ProcedureTagger()); +TextSerializer fieldSerializer = Wrapped( + (w) => Tuple4(w.name, w.flags, w.type, w.initializer), + (u) => + Field(u.first, type: u.third, initializer: u.fourth)..flags = u.second, + Tuple4Serializer(nameSerializer, fieldFlagsSerializer, dartTypeSerializer, + Optional(expressionSerializer))); + +TextSerializer methodSerializer = Wrapped( + (w) => Tuple3(w.name, w.flags, w.function), + (u) => Procedure(u.first, ProcedureKind.Method, u.third)..flags = u.second, + Tuple3Serializer( + nameSerializer, procedureFlagsSerializer, functionNodeSerializer)); + +Case memberSerializer = new Case.uninitialized(const MemberTagger()); class LibraryTagger implements Tagger { const LibraryTagger(); @@ -1633,20 +1686,14 @@ class LibraryTagger implements Tagger { } TextSerializer libraryContentsSerializer = new Wrapped( - unwrapLibraryNode, - wrapLibraryNode, - new Tuple2Serializer( - const UriSerializer(), new ListSerializer(procedureSerializer)), + (w) => Tuple2(w.importUri, [...w.fields, ...w.procedures]), + (u) => Library(u.first, + fields: u.second.where((m) => m is Field).cast().toList(), + procedures: + u.second.where((m) => m is Procedure).cast().toList()), + new Tuple2Serializer(const UriSerializer(), ListSerializer(memberSerializer)), ); -Tuple2> unwrapLibraryNode(Library library) { - return new Tuple2(library.importUri, library.procedures); -} - -Library wrapLibraryNode(Tuple2> tuple) { - return new Library(tuple.first, procedures: tuple.second); -} - Case librarySerializer = new Case.uninitialized(const LibraryTagger()); class ShowHideTagger implements Tagger { @@ -1881,7 +1928,8 @@ void initializeSerializers() { "async-star": asyncStarFunctionNodeSerializer, "sync-yielding": syncYieldingStarFunctionNodeSerializer, }); - procedureSerializer.registerTags({"static-method": staticMethodSerializer}); + memberSerializer + .registerTags({"field": fieldSerializer, "method": methodSerializer}); librarySerializer.registerTags({ "legacy": libraryContentsSerializer, "null-safe": libraryContentsSerializer, diff --git a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart index 6c25315fb07c..aa5fe6d31668 100644 --- a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart +++ b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart @@ -66,7 +66,7 @@ void test() { return new ExpressionStatement(new Let(x, new VariableGet(x))); }(), expectation: '' - '(expr (let "x^0" (var (dynamic) (int 42) ())' + '(expr (let "x^0" () (dynamic) (int 42) ()' ' (get-var "x^0" _)))', serializer: statementSerializer), new TestCase( @@ -80,8 +80,8 @@ void test() { new Let(innerLetVar, new VariableGet(outterLetVar)))); }(), expectation: '' - '(expr (let "x^0" (var (dynamic) (int 42) ())' - ' (let "x^1" (var (bottom) (null) ())' + '(expr (let "x^0" () (dynamic) (int 42) ()' + ' (let "x^1" () (bottom) (null) ()' ' (get-var "x^0" _))))', serializer: statementSerializer), new TestCase( @@ -95,8 +95,8 @@ void test() { new Let(innerLetVar, new VariableGet(innerLetVar)))); }(), expectation: '' - '(expr (let "x^0" (var (dynamic) (int 42) ())' - ' (let "x^1" (var (bottom) (null) ())' + '(expr (let "x^0" () (dynamic) (int 42) ()' + ' (let "x^1" () (bottom) (null) ()' ' (get-var "x^1" _))))', serializer: statementSerializer), () { @@ -390,8 +390,8 @@ void test() { asyncMarker: AsyncMarker.Sync))), expectation: '' '(expr (fun (sync ("T^0") ((dynamic)) ((dynamic)) ("t1^1" ' - '(var (par "T^0" _) _ ())) ("t2^2" (var (par "T^0" _) ' - '_ ())) () (par "T^0" _) (ret (get-var "t1^1" _)))))', + '() (par "T^0" _) _ ()) ("t2^2" () (par "T^0" _) ' + '_ ()) () (par "T^0" _) (ret (get-var "t1^1" _)))))', makeSerializationState: () => new SerializationState(new SerializationEnvironment(null)), makeDeserializationState: () => new DeserializationState( @@ -410,18 +410,18 @@ void test() { procedures: [foo]); Component component = Component(libraries: [library]); component.computeCanonicalNames(); - return new TestCase( + return new TestCase( name: 'foo(x) => x;', node: foo, expectation: '' - '(static-method (public "foo")' - ' (sync () () () ("x^0" (var (dynamic) _ ())) () ()' + '(method (public "foo") ((static))' + ' (sync () () () ("x^0" () (dynamic) _ ()) () ()' ' (dynamic) (ret (get-var "x^0" _))))', makeSerializationState: () => new SerializationState(new SerializationEnvironment(null)), makeDeserializationState: () => new DeserializationState( new DeserializationEnvironment(null), null), - serializer: procedureSerializer); + serializer: memberSerializer); }(), () { VariableDeclaration x1 = VariableDeclaration('x', type: DynamicType()); @@ -450,12 +450,12 @@ void test() { expectation: '' '(legacy "package:foo/bar.dart"' '' - ' ((static-method (public "foo")' - ' (sync () () () ("x^0" (var (dynamic) _ ())) () () (dynamic)' + ' ((method (public "foo") ((static))' + ' (sync () () () ("x^0" () (dynamic) _ ()) () () (dynamic)' ' (ret (get-var "x^0" _))))' '' - ' (static-method (public "bar")' - ' (sync () () () ("x^0" (var (dynamic) _ ())) () () (dynamic)' + ' (method (public "bar") ((static))' + ' (sync () () () ("x^0" () (dynamic) _ ()) () () (dynamic)' ' (ret (invoke-static "package:foo/bar.dart::@methods::foo"' ' () ((get-var "x^0" _)) ()))))))', makeSerializationState: () => @@ -481,7 +481,7 @@ void test() { node: library, expectation: '' '(legacy "package:foo/bar.dart"' - ' ((static-method (public "foo")' + ' ((method (public "foo") ((static))' ' (sync () () () () () ()' ' (interface "package:foo/bar.dart::A" ())' ' (ret (null))))))', @@ -495,7 +495,7 @@ void test() { return new TestCase( name: 'dynamic x;', node: VariableDeclaration('x', type: const DynamicType()), - expectation: '(local "x^0" (var (dynamic) _ ()))', + expectation: '(local "x^0" () (dynamic) _ ())', makeSerializationState: () => new SerializationState(new SerializationEnvironment(null)), makeDeserializationState: () => new DeserializationState( diff --git a/pkg/kernel/test/text_serializer_test.dart b/pkg/kernel/test/text_serializer_test.dart index 2b79abb6791d..f4b363159520 100644 --- a/pkg/kernel/test/text_serializer_test.dart +++ b/pkg/kernel/test/text_serializer_test.dart @@ -47,12 +47,12 @@ class TestRunner { test('(invoke-method (int 0) (public "foo") () ((int 1) (int 2)) ())'); test('(invoke-method (int 0) (public "foo") ((dynamic) (void)) ' '((int 1) (int 2)) ("others" (list (dynamic) ((int 3) (int 4)))))'); - test('(let "x^0" (var (dynamic) (int 0) ()) (null))'); - test('(let "x^0" (var (dynamic) _ ()) (null))'); - test('(let "x^0" (const (dynamic) (int 0) ()) (null))'); - test('(let "x^0" (const (dynamic) _ ()) (null))'); - test('(let "x^0" (final (dynamic) (int 0) ()) (null))'); - test('(let "x^0" (final (dynamic) _ ()) (null))'); + test('(let "x^0" () (dynamic) (int 0) () (null))'); + test('(let "x^0" () (dynamic) _ () (null))'); + test('(let "x^0" ((const)) (dynamic) (int 0) () (null))'); + test('(let "x^0" ((const)) (dynamic) _ () (null))'); + test('(let "x^0" ((final)) (dynamic) (int 0) () (null))'); + test('(let "x^0" ((final)) (dynamic) _ () (null))'); test(r'''(string "Hello, 'string'!")'''); test(r'''(string "Hello, \"string\"!")'''); test(r'''(string "Yeah nah yeah, here is\nthis really long string haiku\n'''