blob: 41daa5683b6d2479c64632ab07d7362012803d1d [file] [log] [blame]
// 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.
// @dart = 2.9
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();
}
class TestCase<T extends Node> {
final String name;
final T node;
final SerializationState Function() makeSerializationState;
final DeserializationState Function() makeDeserializationState;
final String expectation;
final TextSerializer<T> serializer;
TestCase(
{this.name,
this.node,
this.expectation,
this.serializer,
SerializationState Function() makeSerializationState,
DeserializationState Function() makeDeserializationState})
: assert(node != null),
assert(expectation != null),
assert(serializer != null),
this.makeSerializationState = makeSerializationState ??
(() => new SerializationState(new SerializationEnvironment(null))),
this.makeDeserializationState = makeDeserializationState ??
(() => new DeserializationState(
new DeserializationEnvironment(null),
new CanonicalName.root()));
T readNode(String input, DeserializationState state) {
TextIterator stream = new TextIterator(input, 0);
stream.moveNext();
T result = serializer.readFrom(stream, state);
if (stream.moveNext()) {
throw new StateError("Found extra tokens at the end.");
}
return result;
}
String writeNode(T node, SerializationState state) {
StringBuffer buffer = new StringBuffer();
serializer.writeTo(buffer, node, state);
return buffer.toString();
}
}
void test() {
List<String> failures = [];
List<TestCase> tests = <TestCase>[
new TestCase<Statement>(
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 "x^0" () (dynamic) (int 42) ()'
' (get-var "x^0" _)))',
serializer: statementSerializer),
new TestCase<Statement>(
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 "x^0" () (dynamic) (int 42) ()'
' (let "x^1" () (bottom) (null) ()'
' (get-var "x^0" _))))',
serializer: statementSerializer),
new TestCase<Statement>(
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 "x^0" () (dynamic) (int 42) ()'
' (let "x^1" () (bottom) (null) ()'
' (get-var "x^1" _))))',
serializer: statementSerializer),
() {
VariableDeclaration x =
new VariableDeclaration('x', type: const DynamicType());
return new TestCase<Statement>(
name: '/* suppose: dynamic x; */ x = 42;',
node: new ExpressionStatement(new VariableSet(x, new IntLiteral(42))),
expectation: '(expr (set-var "x^0" (int 42)))',
makeSerializationState: () => new SerializationState(
new SerializationEnvironment(null)
..addBinder(x, nameClue: x.name)
..extend(),
),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null)
..addBinder(x, "x^0")
..extend(),
new CanonicalName.root()),
serializer: statementSerializer);
}(),
() {
Field field =
new Field.immutable(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<Statement>(
name: '/* suppose top-level: dynamic field; */ field;',
node: new ExpressionStatement(new StaticGet(field)),
expectation: ''
'(expr (get-static "package:foo/bar.dart::@fields::field"))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
serializer: statementSerializer);
}(),
() {
Field field =
new Field.mutable(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<Statement>(
name: '/* suppose top-level: dynamic field; */ field;',
node: new ExpressionStatement(new StaticGet(field)),
expectation: ''
'(expr (get-static "package:foo/bar.dart::@fields::field"))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
serializer: statementSerializer);
}(),
() {
Field field =
new Field.mutable(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<Statement>(
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)))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
serializer: statementSerializer);
}(),
() {
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<Statement>(
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)) ()))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
serializer: statementSerializer);
}(),
() {
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<Statement>(
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"'
' () () ()))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
serializer: statementSerializer);
}(),
() {
Field field =
new Field.immutable(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<Statement>(
name: '/* suppose A {dynamic field;} A x; */ x.{A::field};',
node: new ExpressionStatement(new PropertyGet.byReference(
new VariableGet(x), field.name, field.getterReference)),
expectation: ''
'(expr (get-prop (get-var "x^0" _) (public "field")))',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)
..addBinder(x, nameClue: 'x')
..extend()),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null)
..addBinder(x, "x^0")
..extend(),
component.root),
serializer: statementSerializer);
}(),
() {
Field field =
new Field.mutable(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<Statement>(
name: '/* suppose A {dynamic field;} A x; */ x.{A::field};',
node: new ExpressionStatement(new PropertyGet.byReference(
new VariableGet(x), field.name, field.getterReference)),
expectation: ''
'(expr (get-prop (get-var "x^0" _) (public "field")))',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)
..addBinder(x, nameClue: 'x')
..extend()),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null)
..addBinder(x, "x^0")
..extend(),
component.root),
serializer: statementSerializer);
}(),
() {
Field field =
new Field.mutable(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<Statement>(
name: '/* suppose A {dynamic field;} A x; */ x.{A::field} = 42;',
node: new ExpressionStatement(PropertySet.byReference(
new VariableGet(x),
field.name,
new IntLiteral(42),
field.setterReference)),
expectation: ''
'(expr (set-prop (get-var "x^0" _) (public "field") (int 42)))',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)
..addBinder(x, nameClue: 'x')
..extend()),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null)
..addBinder(x, "x^0")
..extend(),
component.root),
serializer: statementSerializer);
}(),
() {
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<Statement>(
name: '/* suppose A {foo() {...}} A x; */ x.{A::foo}();',
node: new ExpressionStatement(new MethodInvocation.byReference(
new VariableGet(x),
method.name,
new Arguments([]),
method.reference)),
expectation: ''
'(expr (invoke-method (get-var "x^0" _) (public "foo")'
' () () ()))',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)
..addBinder(x, nameClue: 'x')
..extend()),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null)
..addBinder(x, "x^0")
..extend(),
component.root),
serializer: statementSerializer);
}(),
() {
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<Statement>(
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"'
' () () ()))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
serializer: statementSerializer);
}(),
() {
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<Statement>(
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"'
' () () ()))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
serializer: statementSerializer);
}(),
() {
TypeParameter outterParam =
new TypeParameter('T', const DynamicType(), const DynamicType());
TypeParameter innerParam =
new TypeParameter('T', const DynamicType(), const DynamicType());
return new TestCase<Statement>(
name: '/* T Function<T>(T Function<T>()); */',
node: new ExpressionStatement(new TypeLiteral(new FunctionType(
[
new FunctionType(
[],
new TypeParameterType(innerParam, Nullability.legacy),
Nullability.legacy,
typeParameters: [innerParam])
],
new TypeParameterType(outterParam, Nullability.legacy),
Nullability.legacy,
typeParameters: [outterParam]))),
expectation: ''
'(expr (type (-> ("T^0") ((dynamic)) ((dynamic)) '
'((-> ("T^1") ((dynamic)) ((dynamic)) () () () '
'(par "T^1" _))) () () (par "T^0" _))))',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null), null),
serializer: statementSerializer);
}(),
() {
TypeParameter t =
new TypeParameter('T', const DynamicType(), const DynamicType());
VariableDeclaration t1 = new VariableDeclaration('t1',
type: new TypeParameterType(t, Nullability.legacy));
VariableDeclaration t2 = new VariableDeclaration('t2',
type: new TypeParameterType(t, Nullability.legacy));
return new TestCase<Statement>(
name: '/* <T>(T t1, [T t2]) => t1; */',
node: new ExpressionStatement(new FunctionExpression(new FunctionNode(
new ReturnStatement(new VariableGet(t1)),
typeParameters: [t],
positionalParameters: [t1, t2],
requiredParameterCount: 1,
namedParameters: [],
returnType: new TypeParameterType(t, Nullability.legacy),
asyncMarker: AsyncMarker.Sync))),
expectation: ''
'(expr (fun (sync) ("T^0") ((dynamic)) ((dynamic)) ("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(
new DeserializationEnvironment(null), null),
serializer: statementSerializer);
}(),
() {
VariableDeclaration x = VariableDeclaration('x', type: DynamicType());
Procedure foo = Procedure(
Name('foo'),
ProcedureKind.Method,
FunctionNode(ReturnStatement(VariableGet(x)),
positionalParameters: [x]),
isStatic: true);
Library library = Library(Uri(scheme: 'package', path: 'foo/bar.dart'),
procedures: [foo]);
Component component = Component(libraries: [library]);
component.computeCanonicalNames();
return new TestCase<Member>(
name: 'foo(x) => x;',
node: foo,
expectation: ''
'(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: memberSerializer);
}(),
() {
VariableDeclaration x1 = VariableDeclaration('x', type: DynamicType());
VariableDeclaration x2 = VariableDeclaration('x', type: DynamicType());
Procedure foo = Procedure(
Name('foo'),
ProcedureKind.Method,
FunctionNode(ReturnStatement(VariableGet(x1)),
positionalParameters: [x1]),
isStatic: true);
Procedure bar = Procedure(
Name('bar'),
ProcedureKind.Method,
FunctionNode(
ReturnStatement(
StaticInvocation(foo, Arguments([VariableGet(x2)]))),
positionalParameters: [x2]),
isStatic: true);
Library library = Library(Uri(scheme: 'package', path: 'foo/bar.dart'),
procedures: [foo, bar]);
Component component = Component(libraries: [library]);
component.computeCanonicalNames();
return new TestCase<Library>(
name: 'foo(x) => x; bar(x) => foo(x);',
node: library,
expectation: ''
'"package:foo/bar.dart" () ()'
''
' ((method (public "foo") ((static))'
' (sync) () () () ("x^0" () (dynamic) _ ()) () () (dynamic)'
' _ (ret (get-var "x^0" _)))'
''
' (method (public "bar") ((static))'
' (sync) () () () ("x^0" () (dynamic) _ ()) () () (dynamic)'
' _ (ret'
' (invoke-static "package:foo/bar.dart::@methods::foo"'
' () ((get-var "x^0" _)) ()))))'
''
' ()'
''
' ()'
''
' ()',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null), new CanonicalName.root()),
serializer: librarySerializer);
}(),
() {
Class a = Class(name: "A");
Procedure foo = Procedure(
Name("foo"),
ProcedureKind.Method,
FunctionNode(ReturnStatement(NullLiteral()),
returnType: InterfaceType(a, Nullability.legacy)),
isStatic: true);
Library library = Library(Uri(scheme: "package", path: "foo/bar.dart"),
classes: [a], procedures: [foo]);
Component component = Component(libraries: [library]);
component.computeCanonicalNames();
return new TestCase<Library>(
name: 'class A{} A foo() => null;',
node: library,
expectation: ''
'"package:foo/bar.dart" () ()'
''
' ((method (public "foo") ((static))'
' (sync) () () () () () () (interface "package:foo/bar.dart::A" ())'
' _ (ret (null))))'
''
' ("A" () () () () _ _ () ())'
''
' ()'
''
' ()',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null), component.root),
serializer: librarySerializer);
}(),
() {
return new TestCase<Statement>(
name: 'dynamic x;',
node: VariableDeclaration('x', type: const DynamicType()),
expectation: '(local "x^0" () (dynamic) _ ())',
makeSerializationState: () =>
new SerializationState(new SerializationEnvironment(null)),
makeDeserializationState: () => new DeserializationState(
new DeserializationEnvironment(null), new CanonicalName.root()),
serializer: statementSerializer);
}(),
];
for (TestCase testCase in tests) {
String roundTripInput =
testCase.writeNode(testCase.node, testCase.makeSerializationState());
if (roundTripInput != testCase.expectation) {
failures.add(''
"* initial serialization for test '${testCase.name}'"
" gave output '${roundTripInput}'"
" but expected '${testCase.expectation}'");
}
TreeNode deserialized =
testCase.readNode(roundTripInput, testCase.makeDeserializationState());
String roundTripOutput =
testCase.writeNode(deserialized, testCase.makeSerializationState());
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');
}
}