Files
sdk/pkg/vm/lib/bytecode/generics.dart
T
Alexander Markov da8cb470cc [vm/bytecode] Represent classes, libraries, scripts and recursive types in bytecode
Size of a large application:
Before: 26628600
After: 21480120 (-19.3%)

Size of snapshots:

isolate_snapshot_framework.bin
Before: 9322496
After: 6782976 (-27.2%)

isolate_snapshot_product_framework.bin
Before: 9166848
After: 6602752 (-27.9%)

Regressions in tests:

1) Test language_2/type_alias_equality_test/04 fails similarly to default mode, as VM
does not implement comparison of function types according to the specification.
Previously this test was passing as function types were canonicalized in bytecode,
which was not always correct. This CL fixes the problem with canonicalization of
function types in bytecode and the test starts failing again.

2) Tests standalone_2/entrypoints_verification_test, standalone_2/io/test_extension_test,
standalone_2/io/test_extension_fail_test fail as native extensions are not supported
in bytecode yet. These tests start passing after df5e7aac17,
which switched bytecode tests to kernel service (on x64), because kernel service doesn't
drop ASTs. This CL switches from reading AST library declarations to bytecode even if
AST is not removed, so tests fail again.

Change-Id: I8b7ba44bfa49d0b1599b2509553ff7c831a4e244
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104700
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
2019-06-20 18:27:21 +00:00

232 lines
6.7 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/core_types.dart' show CoreTypes;
import 'package:kernel/type_algebra.dart' show Substitution;
import 'package:kernel/type_environment.dart' show TypeEnvironment;
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;
}
List<DartType> getDefaultFunctionTypeArguments(FunctionNode function) {
List<DartType> defaultTypes = function.typeParameters
.map((p) => p.defaultType ?? const DynamicType())
.toList();
if (_isAllDynamic(defaultTypes)) {
return null;
}
return defaultTypes;
}
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 static type of [expr].
DartType getStaticType(Expression expr, TypeEnvironment typeEnvironment) {
// TODO(dartbug.com/34496): Remove this try/catch once
// getStaticType() is reliable.
try {
return expr.getStaticType(typeEnvironment);
} catch (e) {
return const DynamicType();
}
}
/// Returns `true` if [type] cannot be extended in user code.
bool isSealedType(DartType type, CoreTypes coreTypes) {
if (type is InterfaceType) {
final cls = type.classNode;
return cls == coreTypes.intClass ||
cls == coreTypes.doubleClass ||
cls == coreTypes.boolClass ||
cls == coreTypes.stringClass ||
cls == coreTypes.nullClass;
}
return false;
}
// Returns true if an instance call to [interfaceTarget] with given
// [receiver] can omit argument type checks needed due to generic-covariant
// parameters.
bool isUncheckedCall(Member interfaceTarget, Expression receiver,
TypeEnvironment typeEnvironment) {
if (interfaceTarget == null) {
// Dynamic call cannot be unchecked.
return false;
}
if (!_hasGenericCovariantParameters(interfaceTarget)) {
// Unchecked call makes sense only if there are generic-covariant parameters.
return false;
}
// Calls via [this] do not require checks.
if (receiver is ThisExpression) {
return true;
}
DartType receiverStaticType = getStaticType(receiver, typeEnvironment);
if (receiverStaticType is InterfaceType) {
if (receiverStaticType.typeArguments.isEmpty) {
return true;
}
if (receiverStaticType.typeArguments
.every((t) => isSealedType(t, typeEnvironment.coreTypes))) {
return true;
}
}
return false;
}
bool _hasGenericCovariantParameters(Member target) {
if (target is Field) {
return target.isGenericCovariantImpl;
} else if (target is Procedure) {
for (var param in target.function.positionalParameters) {
if (param.isGenericCovariantImpl) {
return true;
}
}
for (var param in target.function.namedParameters) {
if (param.isGenericCovariantImpl) {
return true;
}
}
return false;
} else {
throw 'Unexpected instance call target ${target.runtimeType} $target';
}
}