| // Copyright (c) 2019, 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. |
| library kernel.text_serializer_from_kernel_nodes_test; |
| |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/text/serializer_combinators.dart'; |
| import 'package:kernel/text/text_reader.dart'; |
| import 'package:kernel/text/text_serializer.dart'; |
| |
| void main() { |
| initializeSerializers(); |
| test(); |
| } |
| |
| // Wrappers for testing. |
| Statement readStatement(String input, DeserializationState state) { |
| TextIterator stream = new TextIterator(input, 0); |
| stream.moveNext(); |
| Statement result = statementSerializer.readFrom(stream, state); |
| if (stream.moveNext()) { |
| throw StateError("extra cruft in basic literal"); |
| } |
| return result; |
| } |
| |
| String writeStatement(Statement statement, SerializationState state) { |
| StringBuffer buffer = new StringBuffer(); |
| statementSerializer.writeTo(buffer, statement, state); |
| return buffer.toString(); |
| } |
| |
| class TestCase { |
| final String name; |
| final Node node; |
| final SerializationState serializationState; |
| final DeserializationState deserializationState; |
| final String expectation; |
| |
| TestCase( |
| {this.name, |
| this.node, |
| this.expectation, |
| SerializationState serializationState, |
| DeserializationState deserializationState}) |
| : this.serializationState = |
| serializationState ?? new SerializationState(null), |
| this.deserializationState = deserializationState ?? |
| new DeserializationState(null, new CanonicalName.root()); |
| } |
| |
| void test() { |
| List<String> failures = []; |
| List<TestCase> tests = <TestCase>[ |
| new TestCase( |
| name: "let dynamic x = 42 in x;", |
| node: () { |
| VariableDeclaration x = new VariableDeclaration("x", |
| type: const DynamicType(), initializer: new IntLiteral(42)); |
| return new ExpressionStatement(new Let(x, new VariableGet(x))); |
| }(), |
| expectation: "" |
| "(expr (let (var \"x^0\" (dynamic) (int 42) ())" |
| " (get-var \"x^0\" _)))"), |
| new TestCase( |
| name: "let dynamic x = 42 in let Bottom x^0 = null in x;", |
| node: () { |
| VariableDeclaration outterLetVar = new VariableDeclaration("x", |
| type: const DynamicType(), initializer: new IntLiteral(42)); |
| VariableDeclaration innerLetVar = new VariableDeclaration("x", |
| type: const BottomType(), initializer: new NullLiteral()); |
| return new ExpressionStatement(new Let(outterLetVar, |
| new Let(innerLetVar, new VariableGet(outterLetVar)))); |
| }(), |
| expectation: "" |
| "(expr (let (var \"x^0\" (dynamic) (int 42) ())" |
| " (let (var \"x^1\" (bottom) (null) ())" |
| " (get-var \"x^0\" _))))"), |
| new TestCase( |
| name: "let dynamic x = 42 in let Bottom x^0 = null in x^0;", |
| node: () { |
| VariableDeclaration outterLetVar = new VariableDeclaration("x", |
| type: const DynamicType(), initializer: new IntLiteral(42)); |
| VariableDeclaration innerLetVar = new VariableDeclaration("x", |
| type: const BottomType(), initializer: new NullLiteral()); |
| return new ExpressionStatement(new Let(outterLetVar, |
| new Let(innerLetVar, new VariableGet(innerLetVar)))); |
| }(), |
| expectation: "" |
| "(expr (let (var \"x^0\" (dynamic) (int 42) ())" |
| " (let (var \"x^1\" (bottom) (null) ())" |
| " (get-var \"x^1\" _))))"), |
| () { |
| VariableDeclaration x = |
| new VariableDeclaration("x", type: const DynamicType()); |
| return new TestCase( |
| name: "/* suppose: dynamic x; */ x = 42;", |
| node: new ExpressionStatement(new VariableSet(x, new IntLiteral(42))), |
| expectation: "(expr (set-var \"x^0\" (int 42)))", |
| serializationState: new SerializationState( |
| new SerializationEnvironment(null) |
| ..addBinder(x, "x^0") |
| ..close(), |
| ), |
| deserializationState: new DeserializationState( |
| new DeserializationEnvironment(null) |
| ..addBinder("x^0", x) |
| ..close(), |
| new CanonicalName.root())); |
| }(), |
| () { |
| Field field = new Field(new Name("field"), type: const DynamicType()); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| fields: <Field>[field]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| return new TestCase( |
| name: "/* suppose top-level: dynamic field; */ field;", |
| node: new ExpressionStatement(new StaticGet(field)), |
| expectation: "" |
| "(expr (get-static \"package:foo/bar.dart::@fields::field\"))", |
| serializationState: new SerializationState(null), |
| deserializationState: new DeserializationState(null, component.root)); |
| }(), |
| () { |
| Field field = new Field(new Name("field"), type: const DynamicType()); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| fields: <Field>[field]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| return new TestCase( |
| name: "/* suppose top-level: dynamic field; */ field = 1;", |
| node: |
| new ExpressionStatement(new StaticSet(field, new IntLiteral(1))), |
| expectation: "" |
| "(expr" |
| " (set-static \"package:foo/bar.dart::@fields::field\" (int 1)))", |
| serializationState: new SerializationState(null), |
| deserializationState: new DeserializationState(null, component.root)); |
| }(), |
| () { |
| Procedure topLevelProcedure = new Procedure( |
| new Name("foo"), |
| ProcedureKind.Method, |
| new FunctionNode(null, positionalParameters: <VariableDeclaration>[ |
| new VariableDeclaration("x", type: const DynamicType()) |
| ]), |
| isStatic: true); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| procedures: <Procedure>[topLevelProcedure]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| return new TestCase( |
| name: "/* suppose top-level: foo(dynamic x) {...}; */ foo(42);", |
| node: new ExpressionStatement(new StaticInvocation.byReference( |
| topLevelProcedure.reference, |
| new Arguments(<Expression>[new IntLiteral(42)]), |
| isConst: false)), |
| expectation: "" |
| "(expr (invoke-static \"package:foo/bar.dart::@methods::foo\"" |
| " () ((int 42)) ()))", |
| serializationState: new SerializationState(null), |
| deserializationState: new DeserializationState(null, component.root)); |
| }(), |
| () { |
| Procedure factoryConstructor = new Procedure( |
| new Name("foo"), ProcedureKind.Factory, new FunctionNode(null), |
| isStatic: true, isConst: true); |
| Class klass = |
| new Class(name: "A", procedures: <Procedure>[factoryConstructor]); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| classes: <Class>[klass]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| return new TestCase( |
| name: "" |
| "/* suppose A { const A(); const factory A.foo() = A; } */" |
| " const A.foo();", |
| node: new ExpressionStatement(new StaticInvocation.byReference( |
| factoryConstructor.reference, new Arguments([]), |
| isConst: true)), |
| expectation: "" |
| "(expr (invoke-const-static" |
| " \"package:foo/bar.dart::A::@factories::foo\"" |
| " () () ()))", |
| serializationState: new SerializationState(null), |
| deserializationState: new DeserializationState(null, component.root)); |
| }(), |
| () { |
| Field field = new Field(new Name("field"), type: const DynamicType()); |
| Class klass = new Class(name: "A", fields: <Field>[field]); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| classes: <Class>[klass]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| |
| VariableDeclaration x = |
| new VariableDeclaration("x", type: const DynamicType()); |
| return new TestCase( |
| name: "/* suppose A {dynamic field;} A x; */ x.{A::field};", |
| node: new ExpressionStatement(new DirectPropertyGet.byReference( |
| new VariableGet(x), field.reference)), |
| expectation: "" |
| "(expr (get-direct-prop (get-var \"x^0\" _)" |
| " \"package:foo/bar.dart::A::@fields::field\"))", |
| serializationState: |
| new SerializationState(new SerializationEnvironment(null) |
| ..addBinder(x, "x^0") |
| ..close()), |
| deserializationState: new DeserializationState( |
| new DeserializationEnvironment(null) |
| ..addBinder("x^0", x) |
| ..close(), |
| component.root)); |
| }(), |
| () { |
| Field field = new Field(new Name("field"), type: const DynamicType()); |
| Class klass = new Class(name: "A", fields: <Field>[field]); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| classes: <Class>[klass]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| |
| VariableDeclaration x = |
| new VariableDeclaration("x", type: const DynamicType()); |
| return new TestCase( |
| name: "/* suppose A {dynamic field;} A x; */ x.{A::field} = 42;", |
| node: new ExpressionStatement(new DirectPropertySet.byReference( |
| new VariableGet(x), field.reference, new IntLiteral(42))), |
| expectation: "" |
| "(expr (set-direct-prop (get-var \"x^0\" _)" |
| " \"package:foo/bar.dart::A::@fields::field\" (int 42)))", |
| serializationState: |
| new SerializationState(new SerializationEnvironment(null) |
| ..addBinder(x, "x^0") |
| ..close()), |
| deserializationState: new DeserializationState( |
| new DeserializationEnvironment(null) |
| ..addBinder("x^0", x) |
| ..close(), |
| component.root)); |
| }(), |
| () { |
| Procedure method = new Procedure( |
| new Name("foo"), ProcedureKind.Method, new FunctionNode(null), |
| isStatic: true, isConst: true); |
| Class klass = new Class(name: "A", procedures: <Procedure>[method]); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| classes: <Class>[klass]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| |
| VariableDeclaration x = |
| new VariableDeclaration("x", type: const DynamicType()); |
| return new TestCase( |
| name: "/* suppose A {foo() {...}} A x; */ x.{A::foo}();", |
| node: new ExpressionStatement(new DirectMethodInvocation.byReference( |
| new VariableGet(x), method.reference, new Arguments([]))), |
| expectation: "" |
| "(expr (invoke-direct-method (get-var \"x^0\" _)" |
| " \"package:foo/bar.dart::A::@methods::foo\"" |
| " () () ()))", |
| serializationState: |
| new SerializationState(new SerializationEnvironment(null) |
| ..addBinder(x, "x^0") |
| ..close()), |
| deserializationState: new DeserializationState( |
| new DeserializationEnvironment(null) |
| ..addBinder("x^0", x) |
| ..close(), |
| component.root)); |
| }(), |
| () { |
| Constructor constructor = |
| new Constructor(new FunctionNode(null), name: new Name("foo")); |
| Class klass = |
| new Class(name: "A", constructors: <Constructor>[constructor]); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| classes: <Class>[klass]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| return new TestCase( |
| name: "/* suppose A {A.foo();} */ new A();", |
| node: new ExpressionStatement(new ConstructorInvocation.byReference( |
| constructor.reference, new Arguments([]))), |
| expectation: "" |
| "(expr (invoke-constructor" |
| " \"package:foo/bar.dart::A::@constructors::foo\"" |
| " () () ()))", |
| serializationState: new SerializationState(null), |
| deserializationState: new DeserializationState(null, component.root)); |
| }(), |
| () { |
| Constructor constructor = new Constructor(new FunctionNode(null), |
| name: new Name("foo"), isConst: true); |
| Class klass = |
| new Class(name: "A", constructors: <Constructor>[constructor]); |
| Library library = new Library( |
| new Uri(scheme: "package", path: "foo/bar.dart"), |
| classes: <Class>[klass]); |
| Component component = new Component(libraries: <Library>[library]); |
| component.computeCanonicalNames(); |
| return new TestCase( |
| name: "/* suppose A {const A.foo();} */ const A();", |
| node: new ExpressionStatement(new ConstructorInvocation.byReference( |
| constructor.reference, new Arguments([]), |
| isConst: true)), |
| expectation: "" |
| "(expr (invoke-const-constructor" |
| " \"package:foo/bar.dart::A::@constructors::foo\"" |
| " () () ()))", |
| serializationState: new SerializationState(null), |
| deserializationState: new DeserializationState(null, component.root)); |
| }(), |
| () { |
| TypeParameter outterParam = |
| new TypeParameter("T", const DynamicType(), const DynamicType()); |
| TypeParameter innerParam = |
| new TypeParameter("T", const DynamicType(), const DynamicType()); |
| return new TestCase( |
| name: "/* T Function<T>(T Function<T>()); */", |
| node: new ExpressionStatement(new TypeLiteral(new FunctionType( |
| [ |
| new FunctionType([], new TypeParameterType(innerParam), |
| typeParameters: [innerParam]) |
| ], |
| new TypeParameterType(outterParam), |
| typeParameters: [outterParam]))), |
| expectation: "" |
| "(expr (type (-> (\"T^0\") ((dynamic)) ((dynamic)) " |
| "((-> (\"T^1\") ((dynamic)) ((dynamic)) () () () " |
| "(par \"T^1\" _))) () () (par \"T^0\" _))))", |
| serializationState: |
| new SerializationState(new SerializationEnvironment(null)), |
| deserializationState: new DeserializationState( |
| new DeserializationEnvironment(null), null)); |
| }(), |
| ]; |
| for (TestCase testCase in tests) { |
| String roundTripInput = |
| writeStatement(testCase.node, testCase.serializationState); |
| if (roundTripInput != testCase.expectation) { |
| failures.add('' |
| '* initial serialization for test "${testCase.name}"' |
| ' gave output "${roundTripInput}"'); |
| } |
| |
| TreeNode deserialized = |
| readStatement(roundTripInput, testCase.deserializationState); |
| String roundTripOutput = |
| writeStatement(deserialized, testCase.serializationState); |
| if (roundTripOutput != roundTripInput) { |
| failures.add('' |
| '* input "${testCase.name}" gave output "${roundTripOutput}"'); |
| } |
| } |
| if (failures.isNotEmpty) { |
| print('Round trip failures:'); |
| failures.forEach(print); |
| throw StateError('Round trip failures'); |
| } |
| } |