blob: a9afcec520e40359d7965c35aac0bad719c9af27 [file] [log] [blame]
library kernel.transformations.vip_instantiate_generics;
import '../class_hierarchy.dart';
import '../core_types.dart';
import '../type_algebra.dart';
import '../type_environment.dart';
import '../visitor.dart';
import '../ast.dart';
import '../clone.dart';
Program transformProgram(
CoreTypes coreTypes, ClassHierarchy hierarchy, Program program) {
new GenericInstantiator(coreTypes, program).processProgram();
return program;
}
enum TypeKind { Reference, Integer, Double }
class TypeKindAnalyzer {
final Program program;
final CoreTypes coreTypes;
Map<TypeParameter, TypeKind> substitution;
TypeKindAnalyzer(this.program, this.coreTypes);
TypeKind toTypeKind(DartType type, {TreeNode where}) {
if (type is InterfaceType) {
final classNode = type.classNode;
if (classNode == coreTypes.numClass) {
throw "num type is forbiden";
}
if (classNode == coreTypes.intClass) {
return TypeKind.Integer;
} else if (classNode == coreTypes.doubleClass) {
return TypeKind.Double;
} else {
return TypeKind.Reference;
}
} else if (type is FunctionType) {
return TypeKind.Reference;
} else if (type is BottomType) {
return TypeKind.Reference;
} else if (type is TypeParameterType &&
substitution?.containsKey(type.parameter) == true) {
return substitution[type.parameter];
} else {
// report(program, where, "Can't establish whether ${type} is integer, double or reference");
throw "Can't establish whether ${type} is integer, double or reference";
}
}
}
class GenericInstantiator extends RecursiveVisitor {
final Program program;
final TypeKindAnalyzer typeKindAnalyzer;
CoreTypes get coreTypes => typeKindAnalyzer.coreTypes;
GenericInstantiator(CoreTypes coreTypes, this.program)
: typeKindAnalyzer = new TypeKindAnalyzer(program, coreTypes) {
processProgram();
}
void processClass(Class class_) {
if (class_.typeParameters.isNotEmpty) {
return;
}
for (var constructor in class_.constructors) {
if (constructor.function.typeParameters.isNotEmpty) {
continue;
}
constructor.accept(this);
}
for (var procedure in class_.procedures) {
if (procedure.function.typeParameters.isNotEmpty) {
continue;
}
procedure.accept(this);
}
class_.fields.forEach((f) => f.accept(this));
}
void processProgram() {
for (var library in program.libraries) {
library.classes.toList(growable: false).forEach(processClass);
for (var procedure in library.procedures) {
procedure.accept(this);
}
for (var field in library.fields) {
field.accept(this);
}
}
drainWorklist();
}
@override
visitConstructorInvocation(ConstructorInvocation node) {
final List<DartType> types = node.arguments.types;
if (types.isNotEmpty) {
final Class klass = node.target.enclosingClass;
final kinds = types
.map((t) => typeKindAnalyzer.toTypeKind(t, where: node))
.toList(growable: false);
final Class clone = instantiate(klass, kinds);
if (!identical(clone, klass)) {
final name = node.target.name;
final ctor = clone.constructors
.firstWhere((ctor) => ctor.name == name);
node.target = ctor;
var j = 0;
for (var i = 0; i < types.length; i++) {
if (kinds[i] == TypeKind.Reference) {
types[j++] = types[i];
}
}
types.length = j;
}
}
}
final Set<Instantiation> insns = new Set<Instantiation>();
List<Instantiation> insnsWorklist = new List<Instantiation>();
Class instantiate(Class klass, List<TypeKind> types) {
final insn = new Instantiation(klass, types);
if (insns.add(insn)) {
print('Instantiating ${insn.klass} with ${insn.types}');
if (!insn.types.every((t) => t == TypeKind.Reference)) {
final orig = insn.klass;
final suffix = insn.types.map((t) {
switch (t) {
case TypeKind.Reference:
return "R";
case TypeKind.Double:
return "D";
case TypeKind.Integer:
return "I";
}
}).join('');
Map<TypeParameter, DartType> typeSubstitution =
<TypeParameter, DartType>{};
for (var i = 0; i < orig.typeParameters.length; i++) {
final param = orig.typeParameters[i];
final kind = insn.types[i];
switch (kind) {
case TypeKind.Reference:
break;
case TypeKind.Integer:
typeSubstitution[param] = coreTypes.intClass.rawType;
break;
case TypeKind.Double:
typeSubstitution[param] = coreTypes.doubleClass.rawType;
break;
}
}
final cloner = new CloneVisitor(
typeSubstitution: typeSubstitution,
classRenamer: (Class node) {
if (identical(node, orig)) {
return "${orig.name}%${suffix}";
}
return node.name;
});
final clone = cloner.visitClass(orig);
orig.enclosingLibrary.addClass(clone);
insn.node = clone;
} else {
insn.node = insn.klass;
}
insnsWorklist.add(insn);
return insn.node;
} else {
return insns.lookup(insn).node;
}
}
void drainWorklist() {
while (insnsWorklist.isNotEmpty) {
final items = insnsWorklist;
insnsWorklist = new List<Instantiation>();
for (var insn in items) {
processClass(insn.node);
//typeKindAnalyzer.substitution =
// new Map<TypeParameter, TypeKind>.fromIterables(
// insn.klass.typeParameters, insn.types);
//insn.klass.accept(this);
//typeKindAnalyzer.substitution = null;
}
}
}
}
class Instantiation {
final Class klass;
final List<TypeKind> types;
Class node;
Instantiation(this.klass, this.types);
bool operator ==(other) {
return other is Instantiation &&
this.klass == other.klass &&
_listEquals(this.types, other.types);
}
int get hashCode => klass.hashCode ^ _listHash(types);
static bool _listEquals(List<TypeKind> a, List<TypeKind> b) {
if (a.length != b.length) return false;
for (var i = 0; i < a.length; i++) {
if (a[i] != b[i]) return false;
}
return true;
}
static int _listHash(List<TypeKind> a) {
int result = a.length;
for (var i = 0; i < a.length; i++) {
result |= a[i].hashCode;
}
return result;
}
}