| // Copyright (c) 2017, 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:kernel/ast.dart' as ir; |
| |
| import '../constants/constant_system.dart' as constant_system; |
| import '../constants/values.dart'; |
| import '../elements/entities.dart'; |
| import '../elements/types.dart'; |
| import '../ir/element_map.dart'; |
| import 'util.dart' show recordShapeOfRecordType; |
| |
| /// Visitor that converts string literals and concatenations of string literals |
| /// into the string value. |
| class Stringifier extends ir.ExpressionVisitor<String?> |
| with ir.ExpressionVisitorDefaultMixin<String?> { |
| @override |
| String visitStringLiteral(ir.StringLiteral node) => node.value; |
| |
| @override |
| String? visitStringConcatenation(ir.StringConcatenation node) { |
| StringBuffer sb = StringBuffer(); |
| for (ir.Expression expression in node.expressions) { |
| String? value = expression.accept(this); |
| if (value == null) return null; |
| sb.write(value); |
| } |
| return sb.toString(); |
| } |
| |
| @override |
| String? visitConstantExpression(ir.ConstantExpression node) { |
| ir.Constant constant = node.constant; |
| if (constant is ir.StringConstant) { |
| return constant.value; |
| } |
| return null; |
| } |
| |
| @override |
| String? defaultExpression(ir.Expression node) => null; |
| } |
| |
| /// Visitor that converts kernel dart types into [DartType]. |
| class DartTypeConverter extends ir.DartTypeVisitor<DartType> { |
| final IrToElementMap elementMap; |
| final Map<ir.StructuralParameter, DartType> currentFunctionTypeParameters = |
| <ir.StructuralParameter, DartType>{}; |
| |
| DartTypeConverter(this.elementMap); |
| |
| DartTypes get _dartTypes => elementMap.commonElements.dartTypes; |
| |
| DartType _convertNullability( |
| DartType baseType, |
| ir.DartType nullabilitySource, |
| ) { |
| final nullability = nullabilitySource.declaredNullability; |
| switch (nullability) { |
| case ir.Nullability.nonNullable: |
| return baseType; |
| case ir.Nullability.nullable: |
| return _dartTypes.nullableType(baseType); |
| case ir.Nullability.undetermined: |
| // Type parameters may have undetermined nullability since it is derived |
| // from the intersection of the declared nullability with the |
| // nullability of the bound. We don't need a nullability wrapper in this |
| // case. |
| if (nullabilitySource is ir.TypeParameterType || |
| nullabilitySource is ir.StructuralParameterType || |
| nullabilitySource is ir.ExtensionType) { |
| return baseType; |
| } |
| |
| // Iff `T` has undetermined nullability, then so will `FutureOr<T>` |
| // since it's the union of `T`, which has undetermined nullability, and |
| // `Future<T>`, which does not. We can treat the `Future`/`FutureOr` as |
| // non-nullable and rely on the conversion of the type argument to |
| // produce the right nullability. |
| if (nullabilitySource is ir.FutureOrType && |
| nullabilitySource.typeArgument.nullability == |
| ir.Nullability.undetermined) { |
| return baseType; |
| } |
| |
| throw UnsupportedError( |
| 'Undetermined nullability on $nullabilitySource', |
| ); |
| } |
| } |
| |
| DartType visitType(ir.DartType type) { |
| return type.accept(this); |
| } |
| |
| InterfaceType visitSupertype(ir.Supertype node) => |
| visitInterfaceType(node.asInterfaceType).withoutNullability |
| as InterfaceType; |
| |
| List<DartType> visitTypes(List<ir.DartType> types) { |
| return List.generate( |
| types.length, |
| (int index) => types[index].accept(this), |
| ); |
| } |
| |
| @override |
| DartType visitTypeParameterType(ir.TypeParameterType node) { |
| if (node.parameter.declaration is ir.Typedef) { |
| // Typedefs are only used in type literals so we never need their type |
| // variables. |
| return _dartTypes.dynamicType(); |
| } |
| return _convertNullability( |
| _dartTypes.typeVariableType(elementMap.getTypeVariable(node.parameter)), |
| node, |
| ); |
| } |
| |
| @override |
| DartType visitStructuralParameterType(ir.StructuralParameterType node) { |
| DartType typeParameter = currentFunctionTypeParameters[node.parameter]!; |
| return _convertNullability(typeParameter, node); |
| } |
| |
| @override |
| DartType visitIntersectionType(ir.IntersectionType node) { |
| return node.left.accept(this); |
| } |
| |
| @override |
| DartType visitFunctionType(ir.FunctionType node) { |
| int index = 0; |
| List<FunctionTypeVariable>? typeVariables; |
| for (ir.StructuralParameter typeParameter in node.typeParameters) { |
| FunctionTypeVariable typeVariable = _dartTypes.functionTypeVariable( |
| index, |
| ); |
| currentFunctionTypeParameters[typeParameter] = typeVariable; |
| typeVariables ??= <FunctionTypeVariable>[]; |
| typeVariables.add(typeVariable); |
| index++; |
| } |
| if (typeVariables != null) { |
| for (int index = 0; index < typeVariables.length; index++) { |
| typeVariables[index].bound = node.typeParameters[index].bound.accept( |
| this, |
| ); |
| } |
| } |
| |
| FunctionType functionType = _dartTypes.functionType( |
| visitType(node.returnType), |
| visitTypes( |
| node.positionalParameters.take(node.requiredParameterCount).toList(), |
| ), |
| visitTypes( |
| node.positionalParameters.skip(node.requiredParameterCount).toList(), |
| ), |
| node.namedParameters.map((n) => n.name).toList(), |
| node.namedParameters |
| .where((n) => n.isRequired) |
| .map((n) => n.name) |
| .toSet(), |
| node.namedParameters.map((n) => visitType(n.type)).toList(), |
| typeVariables ?? const <FunctionTypeVariable>[], |
| ); |
| DartType type = _convertNullability(functionType, node); |
| for (ir.StructuralParameter typeParameter in node.typeParameters) { |
| currentFunctionTypeParameters.remove(typeParameter); |
| } |
| return type; |
| } |
| |
| @override |
| DartType visitInterfaceType(ir.InterfaceType node) { |
| ClassEntity cls = elementMap.getClass(node.classNode); |
| return _convertNullability( |
| _dartTypes.interfaceType(cls, visitTypes(node.typeArguments)), |
| node, |
| ); |
| } |
| |
| @override |
| DartType visitRecordType(ir.RecordType node) { |
| final shape = recordShapeOfRecordType(node); |
| List<DartType> fields = [ |
| for (final type in node.positional) visitType(type), |
| for (final namedType in node.named) visitType(namedType.type), |
| ].toList(growable: false); |
| return _convertNullability(_dartTypes.recordType(shape, fields), node); |
| } |
| |
| @override |
| DartType visitFutureOrType(ir.FutureOrType node) { |
| return _convertNullability( |
| _dartTypes.futureOrType(visitType(node.typeArgument)), |
| node, |
| ); |
| } |
| |
| @override |
| DartType visitVoidType(ir.VoidType node) { |
| return _dartTypes.voidType(); |
| } |
| |
| @override |
| DartType visitDynamicType(ir.DynamicType node) { |
| return _dartTypes.dynamicType(); |
| } |
| |
| @override |
| DartType visitInvalidType(ir.InvalidType node) { |
| // Root uses such a `o is Unresolved` and `o as Unresolved` must be special |
| // cased in the builder, nested invalid types are treated as `dynamic`. |
| return _dartTypes.dynamicType(); |
| } |
| |
| @override |
| DartType visitNeverType(ir.NeverType node) { |
| return _convertNullability(_dartTypes.neverType(), node); |
| } |
| |
| @override |
| DartType visitNullType(ir.NullType node) { |
| return elementMap.commonElements.nullType; |
| } |
| |
| @override |
| DartType visitExtensionType(ir.ExtensionType node) { |
| return node.extensionTypeErasure.accept(this); |
| } |
| |
| @override |
| DartType visitAuxiliaryType(ir.AuxiliaryType node) { |
| throw UnsupportedError( |
| 'Unsupported auxiliary type $node (${node.runtimeType}).', |
| ); |
| } |
| |
| @override |
| DartType visitTypedefType(ir.TypedefType node) { |
| throw UnsupportedError('Unsupported type $node (${node.runtimeType})'); |
| } |
| } |
| |
| class ConstantValuefier extends ir.ComputeOnceConstantVisitor<ConstantValue> { |
| final IrToElementMap elementMap; |
| |
| ConstantValuefier(this.elementMap); |
| |
| static Never _unexpectedConstant(ir.Constant node) { |
| throw UnsupportedError("Unexpected constant $node (${node.runtimeType})."); |
| } |
| |
| @override |
| ConstantValue visitUnevaluatedConstant(ir.UnevaluatedConstant node) { |
| throw UnsupportedError("Unexpected unevaluated constant $node."); |
| } |
| |
| @override |
| ConstantValue visitTypeLiteralConstant(ir.TypeLiteralConstant node) { |
| DartType type = elementMap.getDartType(node.type); |
| return constant_system.createType(elementMap.commonElements, type); |
| } |
| |
| @override |
| ConstantValue visitStaticTearOffConstant(ir.StaticTearOffConstant node) { |
| ir.Procedure member = node.target; |
| FunctionEntity function = elementMap.getMethod(member); |
| final type = elementMap.getFunctionType(member.function); |
| return FunctionConstantValue(function, type); |
| } |
| |
| @override |
| ConstantValue visitInstantiationConstant(ir.InstantiationConstant node) { |
| List<DartType> typeArguments = []; |
| for (ir.DartType type in node.types) { |
| typeArguments.add(elementMap.getDartType(type)); |
| } |
| final function = |
| visitConstant(node.tearOffConstant) as FunctionConstantValue; |
| return InstantiationConstantValue(typeArguments, function); |
| } |
| |
| @override |
| ConstantValue visitInstanceConstant(ir.InstanceConstant node) { |
| InterfaceType type = elementMap.createInterfaceType( |
| node.classNode, |
| node.typeArguments, |
| ); |
| Map<FieldEntity, ConstantValue> fields = {}; |
| node.fieldValues.forEach((ir.Reference reference, ir.Constant value) { |
| FieldEntity field = elementMap.getField(reference.asField); |
| fields[field] = visitConstant(value); |
| }); |
| return ConstructedConstantValue(type, fields); |
| } |
| |
| @override |
| ConstantValue visitListConstant(ir.ListConstant node) { |
| List<ConstantValue> elements = []; |
| for (ir.Constant element in node.entries) { |
| elements.add(visitConstant(element)); |
| } |
| final type = elementMap.commonElements.listType( |
| elementMap.getDartType(node.typeArgument), |
| ); |
| return constant_system.createList( |
| elementMap.commonElements, |
| type, |
| elements, |
| ); |
| } |
| |
| @override |
| ConstantValue visitSetConstant(ir.SetConstant node) { |
| List<ConstantValue> elements = []; |
| for (ir.Constant element in node.entries) { |
| elements.add(visitConstant(element)); |
| } |
| final type = elementMap.commonElements.setType( |
| elementMap.getDartType(node.typeArgument), |
| ); |
| return constant_system.createSet(elementMap.commonElements, type, elements); |
| } |
| |
| @override |
| ConstantValue visitMapConstant(ir.MapConstant node) { |
| List<ConstantValue> keys = []; |
| List<ConstantValue> values = []; |
| for (ir.ConstantMapEntry element in node.entries) { |
| keys.add(visitConstant(element.key)); |
| values.add(visitConstant(element.value)); |
| } |
| final type = elementMap.commonElements.mapType( |
| elementMap.getDartType(node.keyType), |
| elementMap.getDartType(node.valueType), |
| ); |
| return constant_system.createMap( |
| elementMap.commonElements, |
| type, |
| keys, |
| values, |
| ); |
| } |
| |
| @override |
| ConstantValue visitRecordConstant(ir.RecordConstant node) { |
| final shape = recordShapeOfRecordType(node.recordType); |
| final fieldValues = [ |
| for (final value in node.positional) visitConstant(value), |
| for (final value in node.named.values) visitConstant(value), |
| ]; |
| return RecordConstantValue(shape, fieldValues); |
| } |
| |
| @override |
| ConstantValue visitSymbolConstant(ir.SymbolConstant node) { |
| return constant_system.createSymbol(elementMap.commonElements, node.name); |
| } |
| |
| @override |
| ConstantValue visitStringConstant(ir.StringConstant node) { |
| return constant_system.createString(node.value); |
| } |
| |
| @override |
| ConstantValue visitDoubleConstant(ir.DoubleConstant node) { |
| return constant_system.createDouble(node.value); |
| } |
| |
| @override |
| ConstantValue visitIntConstant(ir.IntConstant node) { |
| return constant_system.createIntFromInt(node.value); |
| } |
| |
| @override |
| ConstantValue visitBoolConstant(ir.BoolConstant node) { |
| return constant_system.createBool(node.value); |
| } |
| |
| @override |
| ConstantValue visitNullConstant(ir.NullConstant node) { |
| return constant_system.createNull(); |
| } |
| |
| @override |
| Never visitConstructorTearOffConstant(ir.ConstructorTearOffConstant node) => |
| _unexpectedConstant(node); |
| |
| @override |
| Never visitRedirectingFactoryTearOffConstant( |
| ir.RedirectingFactoryTearOffConstant node, |
| ) => _unexpectedConstant(node); |
| |
| @override |
| Never visitTypedefTearOffConstant(ir.TypedefTearOffConstant node) => |
| _unexpectedConstant(node); |
| |
| @override |
| Never visitAuxiliaryConstant(ir.AuxiliaryConstant node) => |
| _unexpectedConstant(node); |
| } |