blob: 90b3202dc8aebe5691db72ecdeb849e27c6ddf01 [file] [log] [blame]
// Copyright (c) 2013, 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.7
library type_test_helper;
import 'dart:async';
import 'package:expect/expect.dart';
import 'package:compiler/src/common_elements.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/compiler.dart' show Compiler;
import 'package:compiler/src/elements/types.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/kernel/kelements.dart';
import 'package:compiler/src/kernel/kernel_strategy.dart';
import 'package:compiler/src/options.dart';
import 'package:compiler/src/world.dart' show JClosedWorld, KClosedWorld;
import 'memory_compiler.dart' as memory;
extension DartTypeHelpers on DartType {
T withoutNullabilityAs<T extends DartType>() => withoutNullability as T;
}
DartType instantiate(
DartTypes dartTypes, ClassEntity element, List<DartType> arguments) {
return dartTypes.interfaceType(element, arguments);
}
class TypeEnvironment {
final Compiler compiler;
final bool testBackendWorld;
static Future<TypeEnvironment> create(String source,
{bool expectNoErrors: false,
bool expectNoWarningsOrErrors: false,
bool testBackendWorld: false,
List<String> options: const <String>[],
Map<String, String> fieldTypeMap: const <String, String>{}}) async {
memory.DiagnosticCollector collector = new memory.DiagnosticCollector();
Uri uri = Uri.parse('memory:main.dart');
memory.CompilationResult result = await memory.runCompiler(
entryPoint: uri,
memorySourceFiles: {'main.dart': source},
options: [Flags.disableTypeInference]..addAll(options),
diagnosticHandler: collector,
beforeRun: (compiler) {
compiler.stopAfterTypeInference = true;
});
Compiler compiler = result.compiler;
if (expectNoErrors || expectNoWarningsOrErrors) {
var errors = collector.errors;
Expect.isTrue(errors.isEmpty, 'Unexpected errors: ${errors}');
}
if (expectNoWarningsOrErrors) {
var warnings = collector.warnings;
Expect.isTrue(warnings.isEmpty, 'Unexpected warnings: ${warnings}');
}
return new TypeEnvironment._(compiler, testBackendWorld: testBackendWorld);
}
TypeEnvironment._(Compiler this.compiler, {this.testBackendWorld: false});
DartType legacyWrap(DartType type) {
return options.useLegacySubtyping ? types.legacyType(type) : type;
}
ElementEnvironment get elementEnvironment {
if (testBackendWorld) {
return compiler.backendClosedWorldForTesting.elementEnvironment;
} else {
return compiler.frontendStrategy.elementEnvironment;
}
}
CommonElements get commonElements {
if (testBackendWorld) {
return compiler.backendClosedWorldForTesting.commonElements;
} else {
return compiler.frontendStrategy.commonElements;
}
}
CompilerOptions get options => compiler.options;
DartTypes get types {
if (testBackendWorld) {
return compiler.backendClosedWorldForTesting.dartTypes;
} else {
KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
return frontendStrategy.elementMap.types;
}
}
Entity getElement(String name) {
LibraryEntity mainLibrary = elementEnvironment.mainLibrary;
dynamic element = elementEnvironment.lookupLibraryMember(mainLibrary, name);
element ??= elementEnvironment.lookupClass(mainLibrary, name);
element ??=
elementEnvironment.lookupClass(commonElements.coreLibrary, name);
Expect.isNotNull(element, "No element named '$name' found.");
return element;
}
ClassEntity getClass(String name) {
LibraryEntity mainLibrary = elementEnvironment.mainLibrary;
ClassEntity element = elementEnvironment.lookupClass(mainLibrary, name);
Expect.isNotNull(element, "No class named '$name' found.");
return element;
}
DartType getElementType(String name) {
dynamic element = getElement(name);
if (element is FieldEntity) {
return elementEnvironment.getFieldType(element);
} else if (element is FunctionEntity) {
return elementEnvironment.getFunctionType(element);
} else if (element is ClassEntity) {
return elementEnvironment.getThisType(element);
} else {
throw 'Unexpected element $element';
}
}
DartType operator [](String name) {
if (name == 'dynamic') {
return types.dynamicType();
}
if (name == 'void') {
return types.voidType();
}
return getElementType(name);
}
MemberEntity _getMember(String name, [ClassEntity cls]) {
if (cls != null) {
return elementEnvironment.lookupLocalClassMember(cls, name);
} else {
LibraryEntity mainLibrary = elementEnvironment.mainLibrary;
return elementEnvironment.lookupLibraryMember(mainLibrary, name);
}
}
DartType getMemberType(String name, [ClassEntity cls]) {
MemberEntity member = _getMember(name, cls);
if (member is FieldEntity) {
return elementEnvironment.getFieldType(member);
} else if (member is FunctionEntity) {
return elementEnvironment.getFunctionType(member);
}
throw 'Unexpected member: $member for ${name}, cls=$cls';
}
DartType getClosureType(String name, [ClassEntity cls]) {
if (testBackendWorld) {
throw new UnsupportedError(
"getClosureType not supported for backend testing.");
}
MemberEntity member = _getMember(name, cls);
DartType type;
for (KLocalFunction local
in compiler.frontendClosedWorldForTesting.localFunctions) {
if (local.memberContext == member) {
type ??= elementEnvironment.getLocalFunctionType(local);
}
}
return type;
}
DartType getFieldType(String name) {
LibraryEntity mainLibrary = elementEnvironment.mainLibrary;
FieldEntity field =
elementEnvironment.lookupLibraryMember(mainLibrary, name);
Expect.isNotNull(field);
return elementEnvironment.getFieldType(field);
}
bool isSubtype(DartType T, DartType S) {
return types.isSubtype(T, S);
}
bool isPotentialSubtype(DartType T, DartType S) {
return types.isPotentialSubtype(T, S);
}
JClosedWorld get jClosedWorld {
assert(testBackendWorld);
return compiler.backendClosedWorldForTesting;
}
KClosedWorld get kClosedWorld {
assert(!testBackendWorld);
return compiler.frontendClosedWorldForTesting;
}
String printType(DartType type) => type.toStructuredText(types, options);
List<String> printTypes(List<DartType> types) =>
types.map(printType).toList();
DartType instantiate(ClassEntity element, List<DartType> arguments) =>
types.interfaceType(element, arguments.map(legacyWrap).toList());
}
/// Data used to create a function type either as method declaration or a
/// typedef declaration.
class FunctionTypeData {
final String returnType;
final String name;
final String parameters;
const FunctionTypeData(this.returnType, this.name, this.parameters);
@override
String toString() => '$returnType $name$parameters';
}
/// Return source code that declares the function types in [dataList] as
/// method declarations of the form:
///
/// $returnType $name$parameters => null;
String createMethods(List<FunctionTypeData> dataList,
{String additionalData: '', String prefix: ''}) {
StringBuffer sb = new StringBuffer();
for (FunctionTypeData data in dataList) {
sb.writeln(
'${data.returnType} $prefix${data.name}${data.parameters} => null;');
}
sb.write(additionalData);
return sb.toString();
}
/// Return source code that declares the function types in [dataList] as
/// typedefs of the form:
///
/// typedef fx = $returnType Function$parameters;
/// fx $name;
///
/// where a field using the typedef is add to make the type accessible by name.
String createTypedefs(List<FunctionTypeData> dataList,
{String additionalData: '', String prefix: ''}) {
StringBuffer sb = new StringBuffer();
for (int index = 0; index < dataList.length; index++) {
FunctionTypeData data = dataList[index];
sb.writeln(
'typedef f$index = ${data.returnType} Function${data.parameters};');
}
for (int index = 0; index < dataList.length; index++) {
FunctionTypeData data = dataList[index];
sb.writeln('f$index $prefix${data.name};');
}
sb.write(additionalData);
return sb.toString();
}
/// Return source code that uses the function types in [dataList].
String createUses(List<FunctionTypeData> dataList, {String prefix: ''}) {
StringBuffer sb = new StringBuffer();
for (int index = 0; index < dataList.length; index++) {
FunctionTypeData data = dataList[index];
sb.writeln('$prefix${data.name};');
}
return sb.toString();
}