blob: ad23ffd143031055cbd28256078d816eb27869b2 [file] [log] [blame]
// Copyright (c) 2021, 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.
import 'package:dev_compiler/src/kernel/module_symbols.dart';
import 'package:test/test.dart';
const source = '''
import 'dart:core'; // pos:1
import 'package:lib2/lib2.dart; // pos:2
MyClass g; // pos:10
class MyClass<T> // pos:20
implements MyInterface
extends Object {
static final int f; // pos:30
int foo(int x, [int m], {int n}) { // pos:40
int y = 0; // pos:50
{ // pos:60
int z = 0; // pos:70
} // pos:80
} // pos:90
} // pos:100
''';
void main() {
var intType = ClassSymbol(
name: 'int',
localId: 'int',
scopeId: 'dart:core',
location: SourceLocation(
scriptId: 'sdkIdForTest', tokenPos: 42, endTokenPos: 42));
var libraryId = 'lib1';
var main = Script(
uri: 'package:example/hello_world.dart',
localId: '1',
libraryId: libraryId);
var myClassId = 'MyClass<T>';
var fooId = 'foo';
var scopeId = '1';
var g = VariableSymbol(
name: 'g',
kind: VariableSymbolKind.global,
localId: '_g',
scopeId: libraryId,
typeId: myClassId,
location: SourceLocation(scriptId: main.id, tokenPos: 10, endTokenPos: 15),
);
var x = VariableSymbol(
name: 'x',
kind: VariableSymbolKind.formal,
localId: '_x',
scopeId: fooId,
typeId: intType.id,
location: SourceLocation(scriptId: main.id, tokenPos: 40, endTokenPos: 42),
);
var n = VariableSymbol(
name: 'n',
kind: VariableSymbolKind.formal,
localId: '_n',
scopeId: fooId,
typeId: intType.id,
location: SourceLocation(scriptId: main.id, tokenPos: 43, endTokenPos: 45),
);
var m = VariableSymbol(
name: 'm',
kind: VariableSymbolKind.formal,
localId: '_m',
scopeId: fooId,
typeId: intType.id,
location: SourceLocation(scriptId: main.id, tokenPos: 45, endTokenPos: 47),
);
var y = VariableSymbol(
name: 'y',
kind: VariableSymbolKind.local,
localId: '_y',
scopeId: fooId,
typeId: intType.id,
location: SourceLocation(scriptId: main.id, tokenPos: 50, endTokenPos: 55),
);
var z = VariableSymbol(
name: 'z',
kind: VariableSymbolKind.local,
localId: '_z',
scopeId: scopeId,
typeId: intType.id,
location: SourceLocation(scriptId: main.id, tokenPos: 70, endTokenPos: 75),
);
var f = VariableSymbol(
name: 'f',
kind: VariableSymbolKind.field,
localId: '_f',
scopeId: fooId,
typeId: intType.id,
isStatic: true,
isFinal: true,
isConst: true,
location: SourceLocation(scriptId: main.id, tokenPos: 30, endTokenPos: 35),
);
var scope = ScopeSymbol(
localId: scopeId,
scopeId: fooId,
variableIds: [z.id],
scopeIds: [],
location: SourceLocation(scriptId: main.id, tokenPos: 60, endTokenPos: 80),
);
var funType = FunctionTypeSymbol(
localId: '($myClassId, int) => int',
scopeId: libraryId,
typeParameters: {'T': 'A'},
parameterTypeIds: [intType.id],
optionalParameterTypeIds: [m.id],
namedParameterTypeIds: {n.name: n.id},
returnTypeId: intType.id,
location: SourceLocation(scriptId: main.id, tokenPos: 40, endTokenPos: 45),
);
var foo = FunctionSymbol(
name: 'foo',
localId: fooId,
scopeId: myClassId,
typeId: funType.id,
isStatic: false,
isConst: false,
variableIds: [x.id, n.id, y.id],
scopeIds: [scope.id],
location: SourceLocation(scriptId: main.id, tokenPos: 40, endTokenPos: 90),
);
var myClass = ClassSymbol(
name: 'MyClass',
localId: myClassId,
scopeId: libraryId,
isAbstract: false,
isConst: false,
superClassId: 'dart:core|Object',
interfaceIds: ['lib2|MyInterface'],
variableIds: [f.id],
scopeIds: [foo.id],
typeParameters: {'T': 'B'},
location: SourceLocation(scriptId: main.id, tokenPos: 20, endTokenPos: 100),
);
var library = LibrarySymbol(
name: 'package:example/hello_world.dart',
uri: 'package:example/hello_world.dart',
dependencies: [
LibrarySymbolDependency(isImport: true, targetId: 'dart:core'),
LibrarySymbolDependency(isImport: true, targetId: 'lib2'),
],
scriptIds: [main.id],
variableIds: [g.id], // global variables
scopeIds: [myClass.id], // global functions and classes
);
var info = ModuleSymbols(
version: ModuleSymbols.current.version,
moduleName: 'package:example/hello_world.dart',
libraries: [library],
scripts: [main],
classes: [myClass],
functionTypes: [funType],
functions: [foo],
scopes: [scope],
variables: [x, y, z, n, g, f],
);
test('Read and write symbols', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
var write = read.toJson();
expect(json, equals(write));
});
test('Write and read libraries', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
expect(read.libraries.length, 1);
expect(read.libraries[0], matchesLibrary(library));
});
test('Write and read classes', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
expect(read.classes.length, 1);
expect(read.classes[0], matchesClass(myClass));
});
test('Write and read function types', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
expect(read.functionTypes.length, 1);
expect(read.functionTypes[0], matchesFunctionType(funType));
});
test('Write and read functions', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
expect(read.functions.length, 1);
expect(read.functions[0], matchesFunction(foo));
});
test('Write and read scripts', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
expect(read.scripts.length, 1);
expect(read.scripts[0], matchesScript(main));
});
test('Write and read scopes', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
expect(read.scopes.length, 1);
expect(read.scopes[0], matchesScope(scope));
});
test('Write and read variables', () {
var json = info.toJson();
var read = ModuleSymbols.fromJson(json);
expect(read.variables.length, 6);
expect(read.variables[0], matchesVariable(x));
expect(read.variables[1], matchesVariable(y));
expect(read.variables[2], matchesVariable(z));
expect(read.variables[3], matchesVariable(n));
expect(read.variables[4], matchesVariable(g));
expect(read.variables[5], matchesVariable(f));
});
test('Read supported version', () {
var version = SemanticVersion(0, 2, 3).version;
var json = ModuleSymbols(version: version, moduleName: 'moduleNameForTest')
.toJson();
expect(ModuleSymbols.fromJson(json).version, equals(version));
});
test('Read unsupported version', () {
var version = SemanticVersion(1, 2, 3).version;
var json = ModuleSymbols(version: version, moduleName: 'moduleNameForTest')
.toJson();
expect(() => ModuleSymbols.fromJson(json), throwsException);
});
}
TypeMatcher<SourceLocation> matchesLocation(SourceLocation other) =>
isA<SourceLocation>()
.having((loc) => loc.scriptId, 'scriptId', other.scriptId)
.having((loc) => loc.tokenPos, 'tokenPos', other.tokenPos)
.having((loc) => loc.endTokenPos, 'endTokenPos', other.endTokenPos);
TypeMatcher<LibrarySymbolDependency> matchesDependency(
LibrarySymbolDependency other) =>
isA<LibrarySymbolDependency>()
.having((dep) => dep.isDeferred, 'isDeferred', other.isDeferred)
.having((dep) => dep.isImport, 'isImport', other.isImport)
.having((dep) => dep.prefix, 'prefix', other.prefix)
.having((dep) => dep.targetId, 'targetId', other.targetId);
Iterable<Matcher> matchDependencies(List<LibrarySymbolDependency> list) =>
[for (var e in list) matchesDependency(e)];
TypeMatcher<LibrarySymbol> matchesLibrary(LibrarySymbol other) =>
isA<LibrarySymbol>()
.having((lib) => lib.name, 'name', other.name)
.having((lib) => lib.uri, 'uri', other.uri)
.having((lib) => lib.id, 'id', other.id)
.having((lib) => lib.scriptIds, 'scriptIds', other.scriptIds)
.having((lib) => lib.scopeIds, 'scopeIds', other.scopeIds)
.having((lib) => lib.scopeIds, 'scopeIds', other.scopeIds)
.having((lib) => lib.variableIds, 'variableIds', other.variableIds)
.having((lib) => lib.location, 'location', isNull)
.having((lib) => lib.dependencies, 'dependencies',
matchDependencies(other.dependencies));
TypeMatcher<ClassSymbol> matchesClass(ClassSymbol other) => isA<ClassSymbol>()
.having((cls) => cls.name, 'name', other.name)
.having((cls) => cls.id, 'id', other.id)
.having((fun) => fun.localId, 'localId', other.localId)
.having((fun) => fun.scopeId, 'scopeId', other.scopeId)
.having((cls) => cls.superClassId, 'superClassId', other.superClassId)
.having((cls) => cls.typeParameters, 'typeParameters', other.typeParameters)
.having((cls) => cls.functionIds, 'functionIds', other.functionIds)
.having((cls) => cls.interfaceIds, 'interfaceIds', other.interfaceIds)
.having((cls) => cls.isAbstract, 'isAbstract', other.isAbstract)
.having((cls) => cls.isConst, 'isConst', other.isConst)
.having((cls) => cls.libraryId, 'libraryId', other.libraryId)
.having((cls) => cls.scopeIds, 'scopeIds', other.scopeIds)
.having((cls) => cls.variableIds, 'variableIds', other.variableIds)
.having(
(cls) => cls.location, 'location', matchesLocation(other.location!));
TypeMatcher<FunctionTypeSymbol> matchesFunctionType(FunctionTypeSymbol other) =>
isA<FunctionTypeSymbol>()
.having((fun) => fun.id, 'id', other.id)
.having((fun) => fun.localId, 'localId', other.localId)
.having((fun) => fun.scopeId, 'scopeId', other.scopeId)
.having(
(fun) => fun.typeParameters, 'typeParameters', other.typeParameters)
.having((fun) => fun.parameterTypeIds, 'parameterTypeIds',
other.parameterTypeIds)
.having((fun) => fun.optionalParameterTypeIds,
'optionalParameterTypeIds', other.optionalParameterTypeIds)
.having((fun) => fun.namedParameterTypeIds, 'namedParameterTypeIds',
other.namedParameterTypeIds)
.having((fun) => fun.location, 'location',
matchesLocation(other.location!));
TypeMatcher<FunctionSymbol> matchesFunction(FunctionSymbol other) =>
isA<FunctionSymbol>()
.having((fun) => fun.name, 'name', other.name)
.having((fun) => fun.localId, 'localId', other.localId)
.having((fun) => fun.scopeId, 'scopeId', other.scopeId)
.having((fun) => fun.id, 'id', other.id)
.having((fun) => fun.isConst, 'isConst', other.isConst)
.having((fun) => fun.isStatic, 'isStatic', other.isStatic)
.having((fun) => fun.typeId, 'typeId', other.typeId)
.having((fun) => fun.scopeIds, 'scopeIds', other.scopeIds)
.having((fun) => fun.variableIds, 'variableIds', other.variableIds)
.having((fun) => fun.location, 'location',
matchesLocation(other.location!));
TypeMatcher<ScopeSymbol> matchesScope(ScopeSymbol other) => isA<ScopeSymbol>()
.having((scope) => scope.localId, 'localId', other.localId)
.having((scope) => scope.scopeId, 'scopeId', other.scopeId)
.having((scope) => scope.id, 'id', other.id)
.having((scope) => scope.scopeIds, 'scopeIds', other.scopeIds)
.having((scope) => scope.variableIds, 'variableIds', other.variableIds)
.having((scope) => scope.location, 'location',
matchesLocation(other.location!));
TypeMatcher<VariableSymbol> matchesVariable(VariableSymbol other) =>
isA<VariableSymbol>()
.having((variable) => variable.name, 'name', other.name)
.having((variable) => variable.localId, 'localId', other.localId)
.having((variable) => variable.scopeId, 'scopeId', other.scopeId)
.having((variable) => variable.id, 'id', other.id)
.having((variable) => variable.isConst, 'isConst', other.isConst)
.having((variable) => variable.isStatic, 'isStatic', other.isStatic)
.having((variable) => variable.isFinal, 'isFinal', other.isFinal)
.having((variable) => variable.location, 'location',
matchesLocation(other.location!));
TypeMatcher<Script> matchesScript(Script other) => isA<Script>()
.having((script) => script.uri, 'uri', other.uri)
.having((script) => script.id, 'id', other.id);