|
18 | 18 | /// kernel class, because multiple constructs in Dart may desugar to a tree
|
19 | 19 | /// with the same kind of root node.
|
20 | 20 | import 'package:kernel/ast.dart';
|
| 21 | +import 'package:kernel/clone.dart'; |
21 | 22 | import 'package:kernel/src/printer.dart';
|
22 | 23 | import 'package:kernel/text/ast_to_text.dart' show Precedence, Printer;
|
23 | 24 | import 'package:kernel/type_environment.dart';
|
@@ -5597,6 +5598,15 @@ class ListPattern extends Pattern {
|
5597 | 5598 | .createVariableGet(fileOffset, listElementVariable),
|
5598 | 5599 | inferenceVisitor);
|
5599 | 5600 |
|
| 5601 | + // If the sub-pattern transformation doesn't declare captured variables |
| 5602 | + // and consists of a single empty element, it means that it simply |
| 5603 | + // doesn't have a place where it could refer to the element expression. |
| 5604 | + // In that case we can avoid creating the intermediary variable for the |
| 5605 | + // element expression. |
| 5606 | + // |
| 5607 | + // An example of such sub-pattern is in the following: |
| 5608 | + // |
| 5609 | + // if (x case [var _]) { /* ... */ } |
5600 | 5610 | if (patterns[i].declaredVariables.isNotEmpty ||
|
5601 | 5611 | !(subpatternTransformationResult.elements.length == 1 &&
|
5602 | 5612 | subpatternTransformationResult.elements.single.isEmpty)) {
|
@@ -6001,14 +6011,11 @@ class MapPattern extends Pattern {
|
6001 | 6011 | final DartType? keyType;
|
6002 | 6012 | final DartType? valueType;
|
6003 | 6013 | final List<MapPatternEntry> entries;
|
| 6014 | + late final InterfaceType type; |
6004 | 6015 |
|
6005 | 6016 | @override
|
6006 |
| - List<VariableDeclaration> get declaredVariables => [ |
6007 |
| - for (MapPatternEntry entry in entries) ...[ |
6008 |
| - ...entry.key.declaredVariables, |
6009 |
| - ...entry.value.declaredVariables |
6010 |
| - ] |
6011 |
| - ]; |
| 6017 | + List<VariableDeclaration> get declaredVariables => |
| 6018 | + [for (MapPatternEntry entry in entries) ...entry.value.declaredVariables]; |
6012 | 6019 |
|
6013 | 6020 | MapPattern(this.keyType, this.valueType, this.entries, int fileOffset)
|
6014 | 6021 | : assert((keyType == null) == (valueType == null)),
|
@@ -6050,12 +6057,147 @@ class MapPattern extends Pattern {
|
6050 | 6057 | DartType matchedType,
|
6051 | 6058 | Expression variableInitializingContext,
|
6052 | 6059 | InferenceVisitorBase inferenceVisitor) {
|
6053 |
| - return new PatternTransformationResult([ |
6054 |
| - new PatternTransformationElement( |
6055 |
| - condition: |
6056 |
| - new InvalidExpression("Unimplemented MapPattern.transform"), |
6057 |
| - variableInitializers: []) |
6058 |
| - ]); |
| 6060 | + DartType valueType = type.typeArguments[1]; |
| 6061 | + |
| 6062 | + ObjectAccessTarget containsKeyTarget = inferenceVisitor.findInterfaceMember( |
| 6063 | + type, containsKeyName, fileOffset, |
| 6064 | + callSiteAccessKind: CallSiteAccessKind.methodInvocation); |
| 6065 | + bool typeCheckForTargetMapNeeded = |
| 6066 | + !inferenceVisitor.isAssignable(type, matchedType) || |
| 6067 | + matchedType is DynamicType; |
| 6068 | + |
| 6069 | + // mapVariable: `matchedType` MVAR = `matchedExpression` |
| 6070 | + VariableDeclaration mapVariable = inferenceVisitor.engine.forest |
| 6071 | + .createVariableDeclarationForValue(matchedExpression, |
| 6072 | + type: matchedType); |
| 6073 | + |
| 6074 | + Expression? keysCheck; |
| 6075 | + for (int i = entries.length - 1; i >= 0; i--) { |
| 6076 | + MapPatternEntry entry = entries[i]; |
| 6077 | + ExpressionPattern keyPattern = entry.key as ExpressionPattern; |
| 6078 | + |
| 6079 | + // containsKeyCheck: `mapVariable`.containsKey(`keyPattern.expression`) |
| 6080 | + // ==> MVAR.containsKey(`keyPattern.expression`) |
| 6081 | + Expression containsKeyCheck = new InstanceInvocation( |
| 6082 | + InstanceAccessKind.Instance, |
| 6083 | + inferenceVisitor.engine.forest |
| 6084 | + .createVariableGet(fileOffset, mapVariable) |
| 6085 | + ..promotedType = typeCheckForTargetMapNeeded ? type : null, |
| 6086 | + containsKeyName, |
| 6087 | + inferenceVisitor.engine.forest |
| 6088 | + .createArguments(fileOffset, [keyPattern.expression]), |
| 6089 | + functionType: containsKeyTarget.getFunctionType(inferenceVisitor), |
| 6090 | + interfaceTarget: containsKeyTarget.member as Procedure) |
| 6091 | + ..fileOffset = fileOffset; |
| 6092 | + |
| 6093 | + if (keysCheck == null) { |
| 6094 | + // keyCheck: `containsKeyCheck` |
| 6095 | + keysCheck = containsKeyCheck; |
| 6096 | + } else { |
| 6097 | + // keyCheck: `containsKeyCheck` && `keyCheck` |
| 6098 | + keysCheck = inferenceVisitor.engine.forest.createLogicalExpression( |
| 6099 | + fileOffset, containsKeyCheck, doubleAmpersandName.text, keysCheck); |
| 6100 | + } |
| 6101 | + } |
| 6102 | + |
| 6103 | + Expression? typeCheck; |
| 6104 | + if (typeCheckForTargetMapNeeded) { |
| 6105 | + // typeCheck: `mapVariable` is `targetMapType` |
| 6106 | + // ==> MVAR is Map<`keyType`, `valueType`> |
| 6107 | + typeCheck = inferenceVisitor.engine.forest.createIsExpression( |
| 6108 | + fileOffset, |
| 6109 | + inferenceVisitor.engine.forest |
| 6110 | + .createVariableGet(fileOffset, mapVariable), |
| 6111 | + type, |
| 6112 | + forNonNullableByDefault: inferenceVisitor.isNonNullableByDefault); |
| 6113 | + } |
| 6114 | + |
| 6115 | + Expression? typeAndKeysCheck; |
| 6116 | + if (typeCheck != null && keysCheck != null) { |
| 6117 | + // typeAndKeysCheck: `typeCheck` && `keysCheck` |
| 6118 | + typeAndKeysCheck = inferenceVisitor.engine.forest.createLogicalExpression( |
| 6119 | + fileOffset, typeCheck, doubleAmpersandName.text, keysCheck); |
| 6120 | + } else if (typeCheck != null && keysCheck == null) { |
| 6121 | + typeAndKeysCheck = typeCheck; |
| 6122 | + } else if (typeCheck == null && keysCheck != null) { |
| 6123 | + typeAndKeysCheck = keysCheck; |
| 6124 | + } else { |
| 6125 | + typeAndKeysCheck = null; |
| 6126 | + } |
| 6127 | + |
| 6128 | + ObjectAccessTarget valueAccess = inferenceVisitor.findInterfaceMember( |
| 6129 | + type, indexGetName, fileOffset, |
| 6130 | + callSiteAccessKind: CallSiteAccessKind.operatorInvocation); |
| 6131 | + FunctionType valueAccessFunctionType = |
| 6132 | + valueAccess.getFunctionType(inferenceVisitor); |
| 6133 | + PatternTransformationResult transformationResult = |
| 6134 | + new PatternTransformationResult([]); |
| 6135 | + List<VariableDeclaration> valueAccessVariables = []; |
| 6136 | + CloneVisitorNotMembers cloner = new CloneVisitorNotMembers(); |
| 6137 | + for (MapPatternEntry entry in entries) { |
| 6138 | + ExpressionPattern keyPattern = entry.key as ExpressionPattern; |
| 6139 | + |
| 6140 | + // [keyPattern.expression] can be cloned without caching because it's a |
| 6141 | + // const expression according to the spec, and the constant |
| 6142 | + // canonicalization will eliminate the duplicated code. |
| 6143 | + // |
| 6144 | + // mapValue: `mapVariable`[`keyPattern.expression`] |
| 6145 | + // ==> MVAR[`keyPattern.expression`] |
| 6146 | + Expression mapValue = new InstanceInvocation( |
| 6147 | + InstanceAccessKind.Instance, |
| 6148 | + inferenceVisitor.engine.forest |
| 6149 | + .createVariableGet(fileOffset, mapVariable) |
| 6150 | + ..promotedType = typeCheckForTargetMapNeeded ? type : null, |
| 6151 | + indexGetName, |
| 6152 | + inferenceVisitor.engine.forest.createArguments( |
| 6153 | + fileOffset, [cloner.clone(keyPattern.expression)]), |
| 6154 | + functionType: valueAccessFunctionType, |
| 6155 | + interfaceTarget: valueAccess.member as Procedure); |
| 6156 | + |
| 6157 | + // mapValueVariable: `valueType` VVAR = `mapValue` |
| 6158 | + // ==> `valueType` VVAR = MVAR[`keyPattern.expression`] |
| 6159 | + VariableDeclaration mapValueVariable = inferenceVisitor.engine.forest |
| 6160 | + .createVariableDeclarationForValue(mapValue, type: valueType); |
| 6161 | + |
| 6162 | + PatternTransformationResult subpatternTransformationResult = entry.value |
| 6163 | + .transform( |
| 6164 | + inferenceVisitor.engine.forest |
| 6165 | + .createVariableGet(fileOffset, mapValueVariable), |
| 6166 | + valueType, |
| 6167 | + inferenceVisitor.engine.forest |
| 6168 | + .createVariableGet(fileOffset, mapValueVariable), |
| 6169 | + inferenceVisitor); |
| 6170 | + |
| 6171 | + // If the sub-pattern transformation doesn't declare captured variables |
| 6172 | + // and consists of a single empty element, it means that it simply |
| 6173 | + // doesn't have a place where it could refer to the element expression. |
| 6174 | + // In that case we can avoid creating the intermediary variable for the |
| 6175 | + // element expression. |
| 6176 | + // |
| 6177 | + // An example of such sub-pattern is in the following: |
| 6178 | + // |
| 6179 | + // if (x case {"key": var _}) { /* ... */ } |
| 6180 | + if (entry.value.declaredVariables.isNotEmpty || |
| 6181 | + !(subpatternTransformationResult.elements.length == 1 && |
| 6182 | + subpatternTransformationResult.elements.single.isEmpty)) { |
| 6183 | + valueAccessVariables.add(mapValueVariable); |
| 6184 | + transformationResult = transformationResult.combine( |
| 6185 | + subpatternTransformationResult, inferenceVisitor); |
| 6186 | + } |
| 6187 | + } |
| 6188 | + |
| 6189 | + transformationResult = transformationResult.prependElement( |
| 6190 | + new PatternTransformationElement( |
| 6191 | + condition: typeAndKeysCheck, |
| 6192 | + variableInitializers: valueAccessVariables), |
| 6193 | + inferenceVisitor); |
| 6194 | + |
| 6195 | + transformationResult = transformationResult.prependElement( |
| 6196 | + new PatternTransformationElement( |
| 6197 | + condition: null, variableInitializers: [mapVariable]), |
| 6198 | + inferenceVisitor); |
| 6199 | + |
| 6200 | + return transformationResult; |
6059 | 6201 | }
|
6060 | 6202 | }
|
6061 | 6203 |
|
|
0 commit comments