336969538c
* Type arguments are moved to global object table (and de-duplicated). * Finalize non-recursive generic types at bytecode generation time. * Constants are moved to global object table (and de-duplicated). * ICData creation is avoided for direct calls. * Interface calls reference target member instead of selector name. Size of a dilp files: 17868K => 15896K (-11%). Change-Id: I0c9bf338137a0fae6ed90ab6b125ed2b24a1a8ad Reviewed-on: https://dart-review.googlesource.com/c/91108 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Zach Anderson <zra@google.com> Reviewed-by: Régis Crelier <regis@google.com>
229 lines
6.2 KiB
Dart
229 lines
6.2 KiB
Dart
// 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.
|
|
|
|
library vm.bytecode.generics;
|
|
|
|
import 'dart:math' show min;
|
|
|
|
import 'package:kernel/ast.dart' hide MapEntry;
|
|
import 'package:kernel/type_algebra.dart' show Substitution;
|
|
|
|
bool hasInstantiatorTypeArguments(Class c) {
|
|
for (; c != null; c = c.superclass) {
|
|
if (c.typeParameters.isNotEmpty) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _canReuseSuperclassTypeArguments(List<DartType> superTypeArgs,
|
|
List<TypeParameter> typeParameters, int overlap) {
|
|
for (int i = 0; i < overlap; ++i) {
|
|
final superTypeArg = superTypeArgs[superTypeArgs.length - overlap + i];
|
|
if (!(superTypeArg is TypeParameterType &&
|
|
superTypeArg.parameter == typeParameters[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
List<DartType> flattenInstantiatorTypeArguments(
|
|
Class instantiatedClass, List<DartType> typeArgs) {
|
|
final typeParameters = instantiatedClass.typeParameters;
|
|
assert(typeArgs.length == typeParameters.length);
|
|
|
|
final supertype = instantiatedClass.supertype;
|
|
if (supertype == null) {
|
|
return typeArgs;
|
|
}
|
|
|
|
final superTypeArgs = flattenInstantiatorTypeArguments(
|
|
supertype.classNode, supertype.typeArguments);
|
|
|
|
// Shrink type arguments by reusing portion of superclass type arguments
|
|
// if there is an overlapping. This optimization should be consistent with
|
|
// VM in order to correctly reuse instantiator type arguments.
|
|
int overlap = min(superTypeArgs.length, typeArgs.length);
|
|
for (; overlap > 0; --overlap) {
|
|
if (_canReuseSuperclassTypeArguments(
|
|
superTypeArgs, typeParameters, overlap)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
final substitution = Substitution.fromPairs(typeParameters, typeArgs);
|
|
|
|
List<DartType> flatTypeArgs = <DartType>[];
|
|
flatTypeArgs.addAll(superTypeArgs.map((t) => substitution.substituteType(t)));
|
|
flatTypeArgs.addAll(typeArgs.getRange(overlap, typeArgs.length));
|
|
|
|
return flatTypeArgs;
|
|
}
|
|
|
|
List<DartType> getInstantiatorTypeArguments(
|
|
Class instantiatedClass, List<DartType> typeArgs) {
|
|
final flatTypeArgs =
|
|
flattenInstantiatorTypeArguments(instantiatedClass, typeArgs);
|
|
if (_isAllDynamic(flatTypeArgs)) {
|
|
return null;
|
|
}
|
|
return flatTypeArgs;
|
|
}
|
|
|
|
bool _isAllDynamic(List<DartType> typeArgs) {
|
|
for (var t in typeArgs) {
|
|
if (t != const DynamicType()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool hasFreeTypeParameters(List<DartType> typeArgs) {
|
|
final findTypeParams = new FindFreeTypeParametersVisitor();
|
|
return typeArgs.any((t) => t.accept(findTypeParams));
|
|
}
|
|
|
|
class FindFreeTypeParametersVisitor extends DartTypeVisitor<bool> {
|
|
Set<TypeParameter> _declaredTypeParameters;
|
|
|
|
bool visit(DartType type) => type.accept(this);
|
|
|
|
@override
|
|
bool defaultDartType(DartType node) =>
|
|
throw 'Unexpected type ${node.runtimeType} $node';
|
|
|
|
@override
|
|
bool visitInvalidType(InvalidType node) => false;
|
|
|
|
@override
|
|
bool visitDynamicType(DynamicType node) => false;
|
|
|
|
@override
|
|
bool visitVoidType(VoidType node) => false;
|
|
|
|
@override
|
|
bool visitBottomType(BottomType node) => false;
|
|
|
|
@override
|
|
bool visitTypeParameterType(TypeParameterType node) =>
|
|
_declaredTypeParameters == null ||
|
|
!_declaredTypeParameters.contains(node.parameter);
|
|
|
|
@override
|
|
bool visitInterfaceType(InterfaceType node) =>
|
|
node.typeArguments.any((t) => t.accept(this));
|
|
|
|
@override
|
|
bool visitTypedefType(TypedefType node) =>
|
|
node.typeArguments.any((t) => t.accept(this));
|
|
|
|
@override
|
|
bool visitFunctionType(FunctionType node) {
|
|
if (node.typeParameters.isNotEmpty) {
|
|
_declaredTypeParameters ??= new Set<TypeParameter>();
|
|
_declaredTypeParameters.addAll(node.typeParameters);
|
|
}
|
|
|
|
final bool result = node.positionalParameters.any((t) => t.accept(this)) ||
|
|
node.namedParameters.any((p) => p.type.accept(this)) ||
|
|
node.returnType.accept(this);
|
|
|
|
if (node.typeParameters.isNotEmpty) {
|
|
_declaredTypeParameters.removeAll(node.typeParameters);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// Returns true if the given type is recursive after its type arguments
|
|
/// are flattened.
|
|
bool isRecursiveAfterFlattening(InterfaceType type) {
|
|
final visitor = new IsRecursiveAfterFlatteningVisitor();
|
|
visitor.visit(type);
|
|
return visitor.isRecursive(type);
|
|
}
|
|
|
|
class IsRecursiveAfterFlatteningVisitor extends DartTypeVisitor<void> {
|
|
Set<DartType> _visited = new Set<DartType>();
|
|
List<DartType> _stack = <DartType>[];
|
|
Set<DartType> _recursive;
|
|
|
|
bool isRecursive(DartType type) =>
|
|
_recursive != null && _recursive.contains(type);
|
|
|
|
void visit(DartType type) {
|
|
if (!_visited.add(type)) {
|
|
_recordRecursiveType(type);
|
|
return;
|
|
}
|
|
_stack.add(type);
|
|
|
|
type.accept(this);
|
|
|
|
_stack.removeLast();
|
|
_visited.remove(type);
|
|
}
|
|
|
|
void _recordRecursiveType(DartType type) {
|
|
final int start = _stack.lastIndexOf(type);
|
|
final recursive = (_recursive ??= new Set<DartType>());
|
|
for (int i = start; i < _stack.length; ++i) {
|
|
recursive.add(_stack[i]);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void defaultDartType(DartType node) =>
|
|
throw 'Unexpected type ${node.runtimeType} $node';
|
|
|
|
@override
|
|
void visitInvalidType(InvalidType node) {}
|
|
|
|
@override
|
|
void visitDynamicType(DynamicType node) {}
|
|
|
|
@override
|
|
void visitVoidType(VoidType node) {}
|
|
|
|
@override
|
|
void visitBottomType(BottomType node) {}
|
|
|
|
@override
|
|
void visitTypeParameterType(TypeParameterType node) {}
|
|
|
|
@override
|
|
void visitInterfaceType(InterfaceType node) {
|
|
for (var typeArg in node.typeArguments) {
|
|
visit(typeArg);
|
|
}
|
|
if (isRecursive(node)) {
|
|
return;
|
|
}
|
|
final flatTypeArgs =
|
|
flattenInstantiatorTypeArguments(node.classNode, node.typeArguments);
|
|
for (var typeArg in flatTypeArgs.getRange(
|
|
0, flatTypeArgs.length - node.typeArguments.length)) {
|
|
visit(typeArg);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitTypedefType(TypedefType node) => visit(node.unalias);
|
|
|
|
@override
|
|
void visitFunctionType(FunctionType node) {
|
|
for (var p in node.positionalParameters) {
|
|
visit(p);
|
|
}
|
|
for (var p in node.namedParameters) {
|
|
visit(p.type);
|
|
}
|
|
visit(node.returnType);
|
|
}
|
|
}
|