blob: f0786277051725195a7afab1666ae82c66011f7b [file] [log] [blame]
// Copyright (c) 2022, 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:front_end/src/api_prototype/incremental_kernel_generator.dart'
show isLegalIdentifier;
import 'package:front_end/src/api_prototype/lowering_predicates.dart'
show isExtensionThisName;
import 'package:kernel/ast.dart'
show
Class,
DartType,
DynamicType,
InterfaceType,
Library,
Nullability,
TypeParameter;
import 'package:kernel/library_index.dart' show LibraryIndex;
Map<String, DartType>? createDefinitionsWithTypes(
Iterable<Library>? knownLibraries,
List<String> definitionTypes,
List<String> definitions) {
if (knownLibraries == null) {
return null;
}
List<ParsedType> definitionTypesParsed =
parseDefinitionTypes(definitionTypes);
if (definitionTypesParsed.length != definitions.length) {
return null;
}
Set<String> libraryUris = collectParsedTypeUris(definitionTypesParsed);
LibraryIndex libraryIndex =
new LibraryIndex.fromLibraries(knownLibraries, libraryUris);
Map<String, DartType> completeDefinitions = {};
for (int i = 0; i < definitions.length; i++) {
String name = definitions[i];
if (isLegalIdentifier(name) || (i == 0 && isExtensionThisName(name))) {
ParsedType type = definitionTypesParsed[i];
DartType dartType = type.createDartType(libraryIndex);
completeDefinitions[name] = dartType;
}
}
return completeDefinitions;
}
List<TypeParameter>? createTypeParametersWithBounds(
Iterable<Library>? knownLibraries,
List<String> typeBounds,
List<String> typeDefaults,
List<String> typeDefinitions) {
if (knownLibraries == null) {
return null;
}
List<ParsedType> typeBoundsParsed = parseDefinitionTypes(typeBounds);
if (typeBoundsParsed.length != typeDefinitions.length) {
return null;
}
List<ParsedType> typeDefaultsParsed = parseDefinitionTypes(typeDefaults);
if (typeDefaultsParsed.length != typeDefinitions.length) {
return null;
}
Set<String> libraryUris = collectParsedTypeUris(typeBoundsParsed)
..addAll(collectParsedTypeUris(typeDefaultsParsed));
LibraryIndex libraryIndex =
new LibraryIndex.fromLibraries(knownLibraries, libraryUris);
List<TypeParameter> typeParameters = [];
for (int i = 0; i < typeDefinitions.length; i++) {
String name = typeDefinitions[i];
if (!isLegalIdentifier(name)) continue;
ParsedType bound = typeBoundsParsed[i];
DartType dartTypeBound = bound.createDartType(libraryIndex);
ParsedType defaultType = typeDefaultsParsed[i];
DartType dartTypeDefaultType = defaultType.createDartType(libraryIndex);
typeParameters
.add(new TypeParameter(name, dartTypeBound, dartTypeDefaultType));
}
return typeParameters;
}
List<ParsedType> parseDefinitionTypes(List<String> definitionTypes) {
List<ParsedType> result = [];
int i = 0;
List<ParsedType> argumentReceivers = [];
while (i < definitionTypes.length) {
String uriOrNullString = definitionTypes[i];
if (uriOrNullString == "null") {
if (argumentReceivers.isEmpty) {
result.add(new ParsedType.nullType());
} else {
argumentReceivers
.removeLast()
.arguments!
.add(new ParsedType.nullType());
}
i++;
continue;
} else {
// We expect at least 4 elements: Uri, class name, nullability,
// number of type arguments.
if (i + 4 > definitionTypes.length) throw "invalid input";
String className = definitionTypes[i + 1];
int nullability = int.parse(definitionTypes[i + 2]);
int typeArgumentsCount = int.parse(definitionTypes[i + 3]);
ParsedType type = new ParsedType(uriOrNullString, className, nullability);
if (argumentReceivers.isEmpty) {
result.add(type);
} else {
argumentReceivers.removeLast().arguments!.add(type);
}
for (int j = 0; j < typeArgumentsCount; j++) {
argumentReceivers.add(type);
}
i += 4;
}
}
if (argumentReceivers.isNotEmpty) {
throw "Nesting error on input $definitionTypes";
}
return result;
}
class ParsedType {
final String? uri;
final String? className;
final int? nullability;
final List<ParsedType>? arguments;
bool get isNullType => uri == null;
ParsedType(this.uri, this.className, this.nullability) : arguments = [];
ParsedType.nullType()
: uri = null,
className = null,
nullability = null,
arguments = null;
@override
bool operator ==(Object other) {
if (other is! ParsedType) return false;
if (uri != other.uri) return false;
if (className != other.className) return false;
if (nullability != other.nullability) return false;
if (arguments?.length != other.arguments?.length) return false;
if (arguments != null) {
for (int i = 0; i < arguments!.length; i++) {
if (arguments![i] != other.arguments![i]) return false;
}
}
return true;
}
@override
int get hashCode {
if (isNullType) return 0;
int hash = 0x3fffffff & uri.hashCode;
hash = 0x3fffffff & (hash * 31 + (hash ^ className.hashCode));
hash = 0x3fffffff & (hash * 31 + (hash ^ nullability.hashCode));
for (ParsedType argument in arguments!) {
hash = 0x3fffffff & (hash * 31 + (hash ^ argument.hashCode));
}
return hash;
}
@override
String toString() {
if (isNullType) return "null-type";
return "$uri[$className] ($nullability) <$arguments>";
}
DartType createDartType(LibraryIndex libraryIndex) {
if (isNullType) return new DynamicType();
Class? classNode = libraryIndex.tryGetClass(uri!, className!);
if (classNode == null) return new DynamicType();
return new InterfaceType(
classNode,
_getDartNullability(),
arguments
?.map((e) => e.createDartType(libraryIndex))
.toList(growable: false));
}
Nullability _getDartNullability() {
if (isNullType) throw "No nullability on the null type";
if (nullability == 0) return Nullability.nullable;
if (nullability == 1) return Nullability.nonNullable;
if (nullability == 2) return Nullability.legacy;
throw "Unknown nullability";
}
}
Set<String> collectParsedTypeUris(List<ParsedType> parsedTypes) {
Set<String> result = {};
List<ParsedType> workList = new List.from(parsedTypes);
while (workList.isNotEmpty) {
ParsedType type = workList.removeLast();
if (type.isNullType) continue;
result.add(type.uri!);
workList.addAll(type.arguments!);
}
return result;
}