| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import '../compiler.dart' show Compiler; | 5 import '../common.dart'; |
| 6 import '../elements/resolution_types.dart'; | 6 import '../core_types.dart'; |
| 7 import '../elements/elements.dart'; | 7 import '../elements/elements.dart' show ErroneousElement; |
| 8 import '../elements/entities.dart'; |
| 9 import '../elements/resolution_types.dart' show MalformedType; |
| 10 import '../elements/types.dart'; |
| 8 import '../js/js.dart' as jsAst; | 11 import '../js/js.dart' as jsAst; |
| 9 import '../js/js.dart' show js; | 12 import '../js/js.dart' show js; |
| 10 import '../ssa/codegen.dart' show SsaCodeGenerator; | 13 import '../ssa/codegen.dart' show SsaCodeGenerator; |
| 11 import '../ssa/nodes.dart' show HTypeConversion; | 14 import '../ssa/nodes.dart' show HTypeConversion; |
| 12 import '../universe/call_structure.dart' show CallStructure; | 15 import '../universe/call_structure.dart' show CallStructure; |
| 13 import '../universe/use.dart' show StaticUse; | 16 import '../universe/use.dart' show StaticUse; |
| 14 import 'backend.dart'; | 17 import 'backend_helpers.dart'; |
| 15 | 18 |
| 16 class CheckedModeHelper { | 19 class CheckedModeHelper { |
| 17 final String name; | 20 final String name; |
| 18 | 21 |
| 19 const CheckedModeHelper(String this.name); | 22 const CheckedModeHelper(String this.name); |
| 20 | 23 |
| 21 StaticUse getStaticUse(Compiler compiler) { | 24 StaticUse getStaticUse(BackendHelpers helpers) { |
| 22 JavaScriptBackend backend = compiler.backend; | |
| 23 // TODO(johnniwinther): Refactor this to avoid looking up directly in the | 25 // TODO(johnniwinther): Refactor this to avoid looking up directly in the |
| 24 // js helper library but instead access helpers directly on backend helpers. | 26 // js helper library but instead access helpers directly on backend helpers. |
| 25 LibraryElement jsHelperLibrary = backend.helpers.jsHelperLibrary; | 27 return new StaticUse.staticInvoke( |
| 26 MethodElement method = jsHelperLibrary.find(name); | 28 helpers.findHelperFunction(name), callStructure); |
| 27 return new StaticUse.staticInvoke(method, callStructure); | |
| 28 } | 29 } |
| 29 | 30 |
| 30 CallStructure get callStructure => CallStructure.ONE_ARG; | 31 CallStructure get callStructure => CallStructure.ONE_ARG; |
| 31 | 32 |
| 32 jsAst.Expression generateCall( | 33 jsAst.Expression generateCall( |
| 33 SsaCodeGenerator codegen, HTypeConversion node) { | 34 SsaCodeGenerator codegen, HTypeConversion node) { |
| 34 StaticUse staticUse = getStaticUse(codegen.compiler); | 35 StaticUse staticUse = getStaticUse(codegen.backend.helpers); |
| 35 codegen.registry.registerStaticUse(staticUse); | 36 codegen.registry.registerStaticUse(staticUse); |
| 36 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 37 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
| 37 codegen.use(node.checkedInput); | 38 codegen.use(node.checkedInput); |
| 38 arguments.add(codegen.pop()); | 39 arguments.add(codegen.pop()); |
| 39 generateAdditionalArguments(codegen, node, arguments); | 40 generateAdditionalArguments(codegen, node, arguments); |
| 40 jsAst.Expression helper = | 41 jsAst.Expression helper = |
| 41 codegen.backend.emitter.staticFunctionAccess(staticUse.element); | 42 codegen.backend.emitter.staticFunctionAccess(staticUse.element); |
| 42 return new jsAst.Call(helper, arguments); | 43 return new jsAst.Call(helper, arguments); |
| 43 } | 44 } |
| 44 | 45 |
| 45 void generateAdditionalArguments(SsaCodeGenerator codegen, | 46 void generateAdditionalArguments(SsaCodeGenerator codegen, |
| 46 HTypeConversion node, List<jsAst.Expression> arguments) { | 47 HTypeConversion node, List<jsAst.Expression> arguments) { |
| 47 // No additional arguments needed. | 48 // No additional arguments needed. |
| 48 } | 49 } |
| 49 | |
| 50 static const List<CheckedModeHelper> helpers = const <CheckedModeHelper>[ | |
| 51 const MalformedCheckedModeHelper('checkMalformedType'), | |
| 52 const CheckedModeHelper('voidTypeCheck'), | |
| 53 const CheckedModeHelper('stringTypeCast'), | |
| 54 const CheckedModeHelper('stringTypeCheck'), | |
| 55 const CheckedModeHelper('doubleTypeCast'), | |
| 56 const CheckedModeHelper('doubleTypeCheck'), | |
| 57 const CheckedModeHelper('numTypeCast'), | |
| 58 const CheckedModeHelper('numTypeCheck'), | |
| 59 const CheckedModeHelper('boolTypeCast'), | |
| 60 const CheckedModeHelper('boolTypeCheck'), | |
| 61 const CheckedModeHelper('intTypeCast'), | |
| 62 const CheckedModeHelper('intTypeCheck'), | |
| 63 const PropertyCheckedModeHelper('numberOrStringSuperNativeTypeCast'), | |
| 64 const PropertyCheckedModeHelper('numberOrStringSuperNativeTypeCheck'), | |
| 65 const PropertyCheckedModeHelper('numberOrStringSuperTypeCast'), | |
| 66 const PropertyCheckedModeHelper('numberOrStringSuperTypeCheck'), | |
| 67 const PropertyCheckedModeHelper('stringSuperNativeTypeCast'), | |
| 68 const PropertyCheckedModeHelper('stringSuperNativeTypeCheck'), | |
| 69 const PropertyCheckedModeHelper('stringSuperTypeCast'), | |
| 70 const PropertyCheckedModeHelper('stringSuperTypeCheck'), | |
| 71 const CheckedModeHelper('listTypeCast'), | |
| 72 const CheckedModeHelper('listTypeCheck'), | |
| 73 const PropertyCheckedModeHelper('listSuperNativeTypeCast'), | |
| 74 const PropertyCheckedModeHelper('listSuperNativeTypeCheck'), | |
| 75 const PropertyCheckedModeHelper('listSuperTypeCast'), | |
| 76 const PropertyCheckedModeHelper('listSuperTypeCheck'), | |
| 77 const PropertyCheckedModeHelper('interceptedTypeCast'), | |
| 78 const PropertyCheckedModeHelper('interceptedTypeCheck'), | |
| 79 const SubtypeCheckedModeHelper('subtypeCast'), | |
| 80 const SubtypeCheckedModeHelper('assertSubtype'), | |
| 81 const TypeVariableCheckedModeHelper('subtypeOfRuntimeTypeCast'), | |
| 82 const TypeVariableCheckedModeHelper('assertSubtypeOfRuntimeType'), | |
| 83 const PropertyCheckedModeHelper('propertyTypeCast'), | |
| 84 const PropertyCheckedModeHelper('propertyTypeCheck') | |
| 85 ]; | |
| 86 } | 50 } |
| 87 | 51 |
| 88 class MalformedCheckedModeHelper extends CheckedModeHelper { | 52 class MalformedCheckedModeHelper extends CheckedModeHelper { |
| 89 const MalformedCheckedModeHelper(String name) : super(name); | 53 const MalformedCheckedModeHelper(String name) : super(name); |
| 90 | 54 |
| 91 CallStructure get callStructure => CallStructure.TWO_ARGS; | 55 CallStructure get callStructure => CallStructure.TWO_ARGS; |
| 92 | 56 |
| 93 void generateAdditionalArguments(SsaCodeGenerator codegen, | 57 void generateAdditionalArguments(SsaCodeGenerator codegen, |
| 94 HTypeConversion node, List<jsAst.Expression> arguments) { | 58 HTypeConversion node, List<jsAst.Expression> arguments) { |
| 59 // TODO(johnniwinther): Support malformed types in [types.dart]. |
| 95 MalformedType type = node.typeExpression; | 60 MalformedType type = node.typeExpression; |
| 96 ErroneousElement element = type.element; | 61 ErroneousElement element = type.element; |
| 97 arguments.add(js.escapedString(element.message)); | 62 arguments.add(js.escapedString(element.message)); |
| 98 } | 63 } |
| 99 } | 64 } |
| 100 | 65 |
| 101 class PropertyCheckedModeHelper extends CheckedModeHelper { | 66 class PropertyCheckedModeHelper extends CheckedModeHelper { |
| 102 const PropertyCheckedModeHelper(String name) : super(name); | 67 const PropertyCheckedModeHelper(String name) : super(name); |
| 103 | 68 |
| 104 CallStructure get callStructure => CallStructure.TWO_ARGS; | 69 CallStructure get callStructure => CallStructure.TWO_ARGS; |
| 105 | 70 |
| 106 void generateAdditionalArguments(SsaCodeGenerator codegen, | 71 void generateAdditionalArguments(SsaCodeGenerator codegen, |
| 107 HTypeConversion node, List<jsAst.Expression> arguments) { | 72 HTypeConversion node, List<jsAst.Expression> arguments) { |
| 108 ResolutionDartType type = node.typeExpression; | 73 DartType type = node.typeExpression; |
| 109 jsAst.Name additionalArgument = codegen.backend.namer.operatorIsType(type); | 74 jsAst.Name additionalArgument = codegen.backend.namer.operatorIsType(type); |
| 110 arguments.add(js.quoteName(additionalArgument)); | 75 arguments.add(js.quoteName(additionalArgument)); |
| 111 } | 76 } |
| 112 } | 77 } |
| 113 | 78 |
| 114 class TypeVariableCheckedModeHelper extends CheckedModeHelper { | 79 class TypeVariableCheckedModeHelper extends CheckedModeHelper { |
| 115 const TypeVariableCheckedModeHelper(String name) : super(name); | 80 const TypeVariableCheckedModeHelper(String name) : super(name); |
| 116 | 81 |
| 117 CallStructure get callStructure => CallStructure.TWO_ARGS; | 82 CallStructure get callStructure => CallStructure.TWO_ARGS; |
| 118 | 83 |
| 119 void generateAdditionalArguments(SsaCodeGenerator codegen, | 84 void generateAdditionalArguments(SsaCodeGenerator codegen, |
| 120 HTypeConversion node, List<jsAst.Expression> arguments) { | 85 HTypeConversion node, List<jsAst.Expression> arguments) { |
| 121 assert(node.typeExpression.isTypeVariable); | 86 assert(node.typeExpression.isTypeVariable); |
| 122 codegen.use(node.typeRepresentation); | 87 codegen.use(node.typeRepresentation); |
| 123 arguments.add(codegen.pop()); | 88 arguments.add(codegen.pop()); |
| 124 } | 89 } |
| 125 } | 90 } |
| 126 | 91 |
| 127 class SubtypeCheckedModeHelper extends CheckedModeHelper { | 92 class SubtypeCheckedModeHelper extends CheckedModeHelper { |
| 128 const SubtypeCheckedModeHelper(String name) : super(name); | 93 const SubtypeCheckedModeHelper(String name) : super(name); |
| 129 | 94 |
| 130 CallStructure get callStructure => const CallStructure.unnamed(4); | 95 CallStructure get callStructure => const CallStructure.unnamed(4); |
| 131 | 96 |
| 132 void generateAdditionalArguments(SsaCodeGenerator codegen, | 97 void generateAdditionalArguments(SsaCodeGenerator codegen, |
| 133 HTypeConversion node, List<jsAst.Expression> arguments) { | 98 HTypeConversion node, List<jsAst.Expression> arguments) { |
| 134 ResolutionDartType type = node.typeExpression; | 99 InterfaceType type = node.typeExpression; |
| 135 Element element = type.element; | 100 ClassEntity element = type.element; |
| 136 jsAst.Name isField = codegen.backend.namer.operatorIs(element); | 101 jsAst.Name isField = codegen.backend.namer.operatorIs(element); |
| 137 arguments.add(js.quoteName(isField)); | 102 arguments.add(js.quoteName(isField)); |
| 138 codegen.use(node.typeRepresentation); | 103 codegen.use(node.typeRepresentation); |
| 139 arguments.add(codegen.pop()); | 104 arguments.add(codegen.pop()); |
| 140 jsAst.Name asField = codegen.backend.namer.substitutionName(element); | 105 jsAst.Name asField = codegen.backend.namer.substitutionName(element); |
| 141 arguments.add(js.quoteName(asField)); | 106 arguments.add(js.quoteName(asField)); |
| 142 } | 107 } |
| 143 } | 108 } |
| 109 |
| 110 class CheckedModeHelpers { |
| 111 final CommonElements _commonElements; |
| 112 final BackendHelpers _helpers; |
| 113 |
| 114 CheckedModeHelpers(this._commonElements, this._helpers); |
| 115 |
| 116 /// All the checked mode helpers. |
| 117 static const List<CheckedModeHelper> helpers = const <CheckedModeHelper>[ |
| 118 const MalformedCheckedModeHelper('checkMalformedType'), |
| 119 const CheckedModeHelper('voidTypeCheck'), |
| 120 const CheckedModeHelper('stringTypeCast'), |
| 121 const CheckedModeHelper('stringTypeCheck'), |
| 122 const CheckedModeHelper('doubleTypeCast'), |
| 123 const CheckedModeHelper('doubleTypeCheck'), |
| 124 const CheckedModeHelper('numTypeCast'), |
| 125 const CheckedModeHelper('numTypeCheck'), |
| 126 const CheckedModeHelper('boolTypeCast'), |
| 127 const CheckedModeHelper('boolTypeCheck'), |
| 128 const CheckedModeHelper('intTypeCast'), |
| 129 const CheckedModeHelper('intTypeCheck'), |
| 130 const PropertyCheckedModeHelper('numberOrStringSuperNativeTypeCast'), |
| 131 const PropertyCheckedModeHelper('numberOrStringSuperNativeTypeCheck'), |
| 132 const PropertyCheckedModeHelper('numberOrStringSuperTypeCast'), |
| 133 const PropertyCheckedModeHelper('numberOrStringSuperTypeCheck'), |
| 134 const PropertyCheckedModeHelper('stringSuperNativeTypeCast'), |
| 135 const PropertyCheckedModeHelper('stringSuperNativeTypeCheck'), |
| 136 const PropertyCheckedModeHelper('stringSuperTypeCast'), |
| 137 const PropertyCheckedModeHelper('stringSuperTypeCheck'), |
| 138 const CheckedModeHelper('listTypeCast'), |
| 139 const CheckedModeHelper('listTypeCheck'), |
| 140 const PropertyCheckedModeHelper('listSuperNativeTypeCast'), |
| 141 const PropertyCheckedModeHelper('listSuperNativeTypeCheck'), |
| 142 const PropertyCheckedModeHelper('listSuperTypeCast'), |
| 143 const PropertyCheckedModeHelper('listSuperTypeCheck'), |
| 144 const PropertyCheckedModeHelper('interceptedTypeCast'), |
| 145 const PropertyCheckedModeHelper('interceptedTypeCheck'), |
| 146 const SubtypeCheckedModeHelper('subtypeCast'), |
| 147 const SubtypeCheckedModeHelper('assertSubtype'), |
| 148 const TypeVariableCheckedModeHelper('subtypeOfRuntimeTypeCast'), |
| 149 const TypeVariableCheckedModeHelper('assertSubtypeOfRuntimeType'), |
| 150 const PropertyCheckedModeHelper('propertyTypeCast'), |
| 151 const PropertyCheckedModeHelper('propertyTypeCheck'), |
| 152 ]; |
| 153 |
| 154 // Checked mode helpers indexed by name. |
| 155 static final Map<String, CheckedModeHelper> checkedModeHelperByName = |
| 156 new Map<String, CheckedModeHelper>.fromIterable(helpers, |
| 157 key: (helper) => helper.name); |
| 158 |
| 159 /** |
| 160 * Returns the checked mode helper that will be needed to do a type check/type |
| 161 * cast on [type] at runtime. Note that this method is being called both by |
| 162 * the resolver with interface types (int, String, ...), and by the SSA |
| 163 * backend with implementation types (JSInt, JSString, ...). |
| 164 */ |
| 165 CheckedModeHelper getCheckedModeHelper(DartType type, {bool typeCast}) { |
| 166 return getCheckedModeHelperInternal(type, |
| 167 typeCast: typeCast, nativeCheckOnly: false); |
| 168 } |
| 169 |
| 170 /** |
| 171 * Returns the native checked mode helper that will be needed to do a type |
| 172 * check/type cast on [type] at runtime. If no native helper exists for |
| 173 * [type], [:null:] is returned. |
| 174 */ |
| 175 CheckedModeHelper getNativeCheckedModeHelper(DartType type, {bool typeCast}) { |
| 176 return getCheckedModeHelperInternal(type, |
| 177 typeCast: typeCast, nativeCheckOnly: true); |
| 178 } |
| 179 |
| 180 /** |
| 181 * Returns the checked mode helper for the type check/type cast for [type]. If |
| 182 * [nativeCheckOnly] is [:true:], only names for native helpers are returned. |
| 183 */ |
| 184 CheckedModeHelper getCheckedModeHelperInternal(DartType type, |
| 185 {bool typeCast, bool nativeCheckOnly}) { |
| 186 String name = getCheckedModeHelperNameInternal(type, |
| 187 typeCast: typeCast, nativeCheckOnly: nativeCheckOnly); |
| 188 if (name == null) return null; |
| 189 CheckedModeHelper helper = checkedModeHelperByName[name]; |
| 190 assert(helper != null); |
| 191 return helper; |
| 192 } |
| 193 |
| 194 String getCheckedModeHelperNameInternal(DartType type, |
| 195 {bool typeCast, bool nativeCheckOnly}) { |
| 196 assert(!type.isTypedef); |
| 197 if (type.isMalformed) { |
| 198 // The same error is thrown for type test and type cast of a malformed |
| 199 // type so we only need one check method. |
| 200 return 'checkMalformedType'; |
| 201 } |
| 202 |
| 203 if (type.isVoid) { |
| 204 assert(!typeCast); // Cannot cast to void. |
| 205 if (nativeCheckOnly) return null; |
| 206 return 'voidTypeCheck'; |
| 207 } |
| 208 |
| 209 if (type.isTypeVariable) { |
| 210 return typeCast |
| 211 ? 'subtypeOfRuntimeTypeCast' |
| 212 : 'assertSubtypeOfRuntimeType'; |
| 213 } |
| 214 |
| 215 if (type.isFunctionType) return null; |
| 216 |
| 217 assert(invariant(NO_LOCATION_SPANNABLE, type.isInterfaceType, |
| 218 message: "Unexpected type: $type")); |
| 219 InterfaceType interfaceType = type; |
| 220 ClassEntity element = interfaceType.element; |
| 221 bool nativeCheck = true; |
| 222 // TODO(13955), TODO(9731). The test for non-primitive types should use an |
| 223 // interceptor. The interceptor should be an argument to HTypeConversion so |
| 224 // that it can be optimized by standard interceptor optimizations. |
| 225 // nativeCheckOnly || emitter.nativeEmitter.requiresNativeIsCheck(element); |
| 226 |
| 227 var suffix = typeCast ? 'TypeCast' : 'TypeCheck'; |
| 228 if (element == _helpers.jsStringClass || |
| 229 element == _commonElements.stringClass) { |
| 230 if (nativeCheckOnly) return null; |
| 231 return 'string$suffix'; |
| 232 } |
| 233 |
| 234 if (element == _helpers.jsDoubleClass || |
| 235 element == _commonElements.doubleClass) { |
| 236 if (nativeCheckOnly) return null; |
| 237 return 'double$suffix'; |
| 238 } |
| 239 |
| 240 if (element == _helpers.jsNumberClass || |
| 241 element == _commonElements.numClass) { |
| 242 if (nativeCheckOnly) return null; |
| 243 return 'num$suffix'; |
| 244 } |
| 245 |
| 246 if (element == _helpers.jsBoolClass || |
| 247 element == _commonElements.boolClass) { |
| 248 if (nativeCheckOnly) return null; |
| 249 return 'bool$suffix'; |
| 250 } |
| 251 |
| 252 if (element == _helpers.jsIntClass || |
| 253 element == _commonElements.intClass || |
| 254 element == _helpers.jsUInt32Class || |
| 255 element == _helpers.jsUInt31Class || |
| 256 element == _helpers.jsPositiveIntClass) { |
| 257 if (nativeCheckOnly) return null; |
| 258 return 'int$suffix'; |
| 259 } |
| 260 |
| 261 if (_commonElements.isNumberOrStringSupertype(element)) { |
| 262 return nativeCheck |
| 263 ? 'numberOrStringSuperNative$suffix' |
| 264 : 'numberOrStringSuper$suffix'; |
| 265 } |
| 266 |
| 267 if (_commonElements.isStringOnlySupertype(element)) { |
| 268 return nativeCheck ? 'stringSuperNative$suffix' : 'stringSuper$suffix'; |
| 269 } |
| 270 |
| 271 if ((element == _commonElements.listClass || |
| 272 element == _helpers.jsArrayClass) && |
| 273 type.treatAsRaw) { |
| 274 if (nativeCheckOnly) return null; |
| 275 return 'list$suffix'; |
| 276 } |
| 277 |
| 278 if (_commonElements.isListSupertype(element)) { |
| 279 return nativeCheck ? 'listSuperNative$suffix' : 'listSuper$suffix'; |
| 280 } |
| 281 |
| 282 if (type.isInterfaceType && !type.treatAsRaw) { |
| 283 return typeCast ? 'subtypeCast' : 'assertSubtype'; |
| 284 } |
| 285 |
| 286 if (nativeCheck) { |
| 287 // TODO(karlklose): can we get rid of this branch when we use |
| 288 // interceptors? |
| 289 return 'intercepted$suffix'; |
| 290 } else { |
| 291 return 'property$suffix'; |
| 292 } |
| 293 } |
| 294 } |
| OLD | NEW |