| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 import 'package:kernel/ast.dart' as ir; | |
| 6 | |
| 7 import '../common.dart'; | |
| 8 import '../common/names.dart'; | |
| 9 import '../constants/constructors.dart'; | |
| 10 import '../constants/expressions.dart'; | |
| 11 import '../constants/values.dart'; | |
| 12 import '../common_elements.dart'; | |
| 13 import '../elements/elements.dart'; | |
| 14 import '../elements/entities.dart'; | |
| 15 import '../elements/operators.dart'; | |
| 16 import '../elements/types.dart'; | |
| 17 import '../js_backend/backend.dart' show JavaScriptBackend; | |
| 18 import '../native/native.dart' as native; | |
| 19 import '../universe/call_structure.dart'; | |
| 20 import '../universe/selector.dart'; | |
| 21 import 'kernel_debug.dart'; | |
| 22 | |
| 23 /// Interface that translates between Kernel IR nodes and entities. | |
| 24 abstract class KernelElementAdapter { | |
| 25 /// Access to the commonly used elements and types. | |
| 26 CommonElements get commonElements; | |
| 27 | |
| 28 /// [ElementEnvironment] for library, class and member lookup. | |
| 29 ElementEnvironment get elementEnvironment; | |
| 30 | |
| 31 /// Returns the [DartType] corresponding to [type]. | |
| 32 DartType getDartType(ir.DartType type); | |
| 33 | |
| 34 /// Returns the list of [DartType]s corresponding to [types]. | |
| 35 List<DartType> getDartTypes(List<ir.DartType> types); | |
| 36 | |
| 37 /// Returns the [InterfaceType] corresponding to [type]. | |
| 38 InterfaceType getInterfaceType(ir.InterfaceType type); | |
| 39 | |
| 40 /// Return the [InterfaceType] corresponding to the [cls] with the given | |
| 41 /// [typeArguments]. | |
| 42 InterfaceType createInterfaceType( | |
| 43 ir.Class cls, List<ir.DartType> typeArguments); | |
| 44 | |
| 45 /// Returns the [CallStructure] corresponding to the [arguments]. | |
| 46 CallStructure getCallStructure(ir.Arguments arguments); | |
| 47 | |
| 48 /// Returns the [Selector] corresponding to the invocation or getter/setter | |
| 49 /// access of [node]. | |
| 50 Selector getSelector(ir.Expression node); | |
| 51 | |
| 52 /// Returns the [ConstructorEntity] corresponding to the generative or factory | |
| 53 /// constructor [node]. | |
| 54 ConstructorEntity getConstructor(ir.Member node); | |
| 55 | |
| 56 /// Returns the [MemberEntity] corresponding to the member [node]. | |
| 57 MemberEntity getMember(ir.Member node); | |
| 58 | |
| 59 /// Returns the [FunctionEntity] corresponding to the procedure [node]. | |
| 60 FunctionEntity getMethod(ir.Procedure node); | |
| 61 | |
| 62 /// Returns the [FieldEntity] corresponding to the field [node]. | |
| 63 FieldEntity getField(ir.Field node); | |
| 64 | |
| 65 /// Returns the [ClassEntity] corresponding to the class [node]. | |
| 66 ClassEntity getClass(ir.Class node); | |
| 67 | |
| 68 /// Returns the [Local] corresponding to the [node]. The node must be either | |
| 69 /// a [ir.FunctionDeclaration] or [ir.FunctionExpression]. | |
| 70 Local getLocalFunction(ir.TreeNode node); | |
| 71 | |
| 72 /// Returns the [LibraryEntity] corresponding to the library [node]. | |
| 73 LibraryEntity getLibrary(ir.Library node); | |
| 74 | |
| 75 /// Returns the [Name] corresponding to [name]. | |
| 76 Name getName(ir.Name name); | |
| 77 | |
| 78 /// Returns `true` is [node] has a `@Native(...)` annotation. | |
| 79 bool isNativeClass(ir.Class node); | |
| 80 | |
| 81 /// Return `true` if [node] is the `dart:_foreign_helper` library. | |
| 82 bool isForeignLibrary(ir.Library node); | |
| 83 | |
| 84 /// Computes the native behavior for reading the native [field]. | |
| 85 native.NativeBehavior getNativeBehaviorForFieldLoad(ir.Field field); | |
| 86 | |
| 87 /// Computes the native behavior for writing to the native [field]. | |
| 88 native.NativeBehavior getNativeBehaviorForFieldStore(ir.Field field); | |
| 89 | |
| 90 /// Computes the native behavior for calling [procedure]. | |
| 91 native.NativeBehavior getNativeBehaviorForMethod(ir.Procedure procedure); | |
| 92 | |
| 93 /// Computes the [native.NativeBehavior] for a call to the [JS] function. | |
| 94 native.NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node); | |
| 95 | |
| 96 /// Computes the [native.NativeBehavior] for a call to the [JS_BUILTIN] | |
| 97 /// function. | |
| 98 native.NativeBehavior getNativeBehaviorForJsBuiltinCall( | |
| 99 ir.StaticInvocation node); | |
| 100 | |
| 101 /// Computes the [native.NativeBehavior] for a call to the | |
| 102 /// [JS_EMBEDDED_GLOBAL] function. | |
| 103 native.NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall( | |
| 104 ir.StaticInvocation node); | |
| 105 | |
| 106 /// Compute the kind of foreign helper function called by [node], if any. | |
| 107 ForeignKind getForeignKind(ir.StaticInvocation node); | |
| 108 | |
| 109 /// Computes the [InterfaceType] referenced by a call to the | |
| 110 /// [JS_INTERCEPTOR_CONSTANT] function, if any. | |
| 111 InterfaceType getInterfaceTypeForJsInterceptorCall(ir.StaticInvocation node); | |
| 112 | |
| 113 /// Computes the [ConstantValue] for the constant [expression]. | |
| 114 ConstantValue getConstantValue(ir.Expression expression); | |
| 115 } | |
| 116 | |
| 117 /// Kinds of foreign functions. | |
| 118 enum ForeignKind { | |
| 119 JS, | |
| 120 JS_BUILTIN, | |
| 121 JS_EMBEDDED_GLOBAL, | |
| 122 JS_INTERCEPTOR_CONSTANT, | |
| 123 NONE, | |
| 124 } | |
| 125 | |
| 126 abstract class KernelElementAdapterMixin implements KernelElementAdapter { | |
| 127 DiagnosticReporter get reporter; | |
| 128 FunctionType getFunctionType(ir.FunctionNode node); | |
| 129 native.BehaviorBuilder get nativeBehaviorBuilder; | |
| 130 ConstantValue computeConstantValue(ConstantExpression constant); | |
| 131 | |
| 132 @override | |
| 133 Name getName(ir.Name name) { | |
| 134 return new Name( | |
| 135 name.name, name.isPrivate ? getLibrary(name.library) : null); | |
| 136 } | |
| 137 | |
| 138 @override | |
| 139 CallStructure getCallStructure(ir.Arguments arguments) { | |
| 140 int argumentCount = arguments.positional.length + arguments.named.length; | |
| 141 List<String> namedArguments = arguments.named.map((e) => e.name).toList(); | |
| 142 return new CallStructure(argumentCount, namedArguments); | |
| 143 } | |
| 144 | |
| 145 @override | |
| 146 Selector getSelector(ir.Expression node) { | |
| 147 // TODO(efortuna): This is screaming for a common interface between | |
| 148 // PropertyGet and SuperPropertyGet (and same for *Get). Talk to kernel | |
| 149 // folks. | |
| 150 if (node is ir.PropertyGet) { | |
| 151 return getGetterSelector(node.name); | |
| 152 } | |
| 153 if (node is ir.SuperPropertyGet) { | |
| 154 return getGetterSelector(node.name); | |
| 155 } | |
| 156 if (node is ir.PropertySet) { | |
| 157 return getSetterSelector(node.name); | |
| 158 } | |
| 159 if (node is ir.SuperPropertySet) { | |
| 160 return getSetterSelector(node.name); | |
| 161 } | |
| 162 if (node is ir.InvocationExpression) { | |
| 163 return getInvocationSelector(node); | |
| 164 } | |
| 165 throw new SpannableAssertionFailure( | |
| 166 CURRENT_ELEMENT_SPANNABLE, | |
| 167 "Can only get the selector for a property get or an invocation: " | |
| 168 "${node}"); | |
| 169 } | |
| 170 | |
| 171 Selector getInvocationSelector(ir.InvocationExpression invocation) { | |
| 172 Name name = getName(invocation.name); | |
| 173 SelectorKind kind; | |
| 174 if (Elements.isOperatorName(invocation.name.name)) { | |
| 175 if (name == Names.INDEX_NAME || name == Names.INDEX_SET_NAME) { | |
| 176 kind = SelectorKind.INDEX; | |
| 177 } else { | |
| 178 kind = SelectorKind.OPERATOR; | |
| 179 } | |
| 180 } else { | |
| 181 kind = SelectorKind.CALL; | |
| 182 } | |
| 183 | |
| 184 CallStructure callStructure = getCallStructure(invocation.arguments); | |
| 185 return new Selector(kind, name, callStructure); | |
| 186 } | |
| 187 | |
| 188 Selector getGetterSelector(ir.Name irName) { | |
| 189 Name name = new Name( | |
| 190 irName.name, irName.isPrivate ? getLibrary(irName.library) : null); | |
| 191 return new Selector.getter(name); | |
| 192 } | |
| 193 | |
| 194 Selector getSetterSelector(ir.Name irName) { | |
| 195 Name name = new Name( | |
| 196 irName.name, irName.isPrivate ? getLibrary(irName.library) : null); | |
| 197 return new Selector.setter(name); | |
| 198 } | |
| 199 | |
| 200 ConstantValue getConstantValue(ir.Expression node) { | |
| 201 ConstantExpression constant = new Constantifier(this).visit(node); | |
| 202 if (constant == null) { | |
| 203 throw new UnsupportedError( | |
| 204 'No constant for ${DebugPrinter.prettyPrint(node)}'); | |
| 205 } | |
| 206 return computeConstantValue(constant); | |
| 207 } | |
| 208 | |
| 209 /// Converts [annotations] into a list of [ConstantValue]s. | |
| 210 List<ConstantValue> getMetadata(List<ir.Expression> annotations) { | |
| 211 if (annotations.isEmpty) return const <ConstantValue>[]; | |
| 212 List<ConstantValue> metadata = <ConstantValue>[]; | |
| 213 annotations.forEach((ir.Expression node) { | |
| 214 metadata.add(getConstantValue(node)); | |
| 215 }); | |
| 216 return metadata; | |
| 217 } | |
| 218 | |
| 219 /// Returns `true` is [node] has a `@Native(...)` annotation. | |
| 220 // TODO(johnniwinther): Cache this for later use. | |
| 221 bool isNativeClass(ir.Class node) { | |
| 222 for (ir.Expression annotation in node.annotations) { | |
| 223 if (annotation is ir.ConstructorInvocation) { | |
| 224 FunctionEntity target = getConstructor(annotation.target); | |
| 225 if (target.enclosingClass == commonElements.nativeAnnotationClass) { | |
| 226 return true; | |
| 227 } | |
| 228 } | |
| 229 } | |
| 230 return false; | |
| 231 } | |
| 232 | |
| 233 /// Compute the kind of foreign helper function called by [node], if any. | |
| 234 ForeignKind getForeignKind(ir.StaticInvocation node) { | |
| 235 if (isForeignLibrary(node.target.enclosingLibrary)) { | |
| 236 switch (node.target.name.name) { | |
| 237 case JavaScriptBackend.JS: | |
| 238 return ForeignKind.JS; | |
| 239 case JavaScriptBackend.JS_BUILTIN: | |
| 240 return ForeignKind.JS_BUILTIN; | |
| 241 case JavaScriptBackend.JS_EMBEDDED_GLOBAL: | |
| 242 return ForeignKind.JS_EMBEDDED_GLOBAL; | |
| 243 case JavaScriptBackend.JS_INTERCEPTOR_CONSTANT: | |
| 244 return ForeignKind.JS_INTERCEPTOR_CONSTANT; | |
| 245 } | |
| 246 } | |
| 247 return ForeignKind.NONE; | |
| 248 } | |
| 249 | |
| 250 /// Return `true` if [node] is the `dart:_foreign_helper` library. | |
| 251 bool isForeignLibrary(ir.Library node) { | |
| 252 return node.importUri == Uris.dart__foreign_helper; | |
| 253 } | |
| 254 | |
| 255 /// Looks up [typeName] for use in the spec-string of a `JS` called. | |
| 256 // TODO(johnniwinther): Use this in [native.NativeBehavior] instead of calling | |
| 257 // the `ForeignResolver`. | |
| 258 // TODO(johnniwinther): Cache the result to avoid redundant lookups? | |
| 259 native.TypeLookup typeLookup({bool resolveAsRaw: true}) { | |
| 260 DartType lookup(String typeName, {bool required}) { | |
| 261 DartType findIn(Uri uri) { | |
| 262 LibraryEntity library = elementEnvironment.lookupLibrary(uri); | |
| 263 if (library != null) { | |
| 264 ClassEntity cls = elementEnvironment.lookupClass(library, typeName); | |
| 265 if (cls != null) { | |
| 266 // TODO(johnniwinther): Align semantics. | |
| 267 return resolveAsRaw | |
| 268 ? elementEnvironment.getRawType(cls) | |
| 269 : elementEnvironment.getThisType(cls); | |
| 270 } | |
| 271 } | |
| 272 return null; | |
| 273 } | |
| 274 | |
| 275 // TODO(johnniwinther): Narrow the set of lookups base on the depending | |
| 276 // library. | |
| 277 DartType type = findIn(Uris.dart_core); | |
| 278 type ??= findIn(Uris.dart__js_helper); | |
| 279 type ??= findIn(Uris.dart__interceptors); | |
| 280 type ??= findIn(Uris.dart__isolate_helper); | |
| 281 type ??= findIn(Uris.dart__native_typed_data); | |
| 282 type ??= findIn(Uris.dart_collection); | |
| 283 type ??= findIn(Uris.dart_math); | |
| 284 type ??= findIn(Uris.dart_html); | |
| 285 type ??= findIn(Uris.dart_html_common); | |
| 286 type ??= findIn(Uris.dart_svg); | |
| 287 type ??= findIn(Uris.dart_web_audio); | |
| 288 type ??= findIn(Uris.dart_web_gl); | |
| 289 type ??= findIn(Uris.dart_web_sql); | |
| 290 type ??= findIn(Uris.dart_indexed_db); | |
| 291 type ??= findIn(Uris.dart_typed_data); | |
| 292 if (type == null && required) { | |
| 293 reporter.reportErrorMessage(CURRENT_ELEMENT_SPANNABLE, | |
| 294 MessageKind.GENERIC, {'text': "Type '$typeName' not found."}); | |
| 295 } | |
| 296 return type; | |
| 297 } | |
| 298 | |
| 299 return lookup; | |
| 300 } | |
| 301 | |
| 302 String _getStringArgument(ir.StaticInvocation node, int index) { | |
| 303 return node.arguments.positional[index].accept(new Stringifier()); | |
| 304 } | |
| 305 | |
| 306 /// Computes the [native.NativeBehavior] for a call to the [JS] function. | |
| 307 // TODO(johnniwinther): Cache this for later use. | |
| 308 native.NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node) { | |
| 309 if (node.arguments.positional.length < 2 || | |
| 310 node.arguments.named.isNotEmpty) { | |
| 311 reporter.reportErrorMessage( | |
| 312 CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS); | |
| 313 return new native.NativeBehavior(); | |
| 314 } | |
| 315 String specString = _getStringArgument(node, 0); | |
| 316 if (specString == null) { | |
| 317 reporter.reportErrorMessage( | |
| 318 CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST); | |
| 319 return new native.NativeBehavior(); | |
| 320 } | |
| 321 | |
| 322 String codeString = _getStringArgument(node, 1); | |
| 323 if (codeString == null) { | |
| 324 reporter.reportErrorMessage( | |
| 325 CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND); | |
| 326 return new native.NativeBehavior(); | |
| 327 } | |
| 328 | |
| 329 return native.NativeBehavior.ofJsCall( | |
| 330 specString, | |
| 331 codeString, | |
| 332 typeLookup(resolveAsRaw: true), | |
| 333 CURRENT_ELEMENT_SPANNABLE, | |
| 334 reporter, | |
| 335 commonElements); | |
| 336 } | |
| 337 | |
| 338 /// Computes the [native.NativeBehavior] for a call to the [JS_BUILTIN] | |
| 339 /// function. | |
| 340 // TODO(johnniwinther): Cache this for later use. | |
| 341 native.NativeBehavior getNativeBehaviorForJsBuiltinCall( | |
| 342 ir.StaticInvocation node) { | |
| 343 if (node.arguments.positional.length < 1) { | |
| 344 reporter.internalError( | |
| 345 CURRENT_ELEMENT_SPANNABLE, "JS builtin expression has no type."); | |
| 346 return new native.NativeBehavior(); | |
| 347 } | |
| 348 if (node.arguments.positional.length < 2) { | |
| 349 reporter.internalError( | |
| 350 CURRENT_ELEMENT_SPANNABLE, "JS builtin is missing name."); | |
| 351 return new native.NativeBehavior(); | |
| 352 } | |
| 353 String specString = _getStringArgument(node, 0); | |
| 354 if (specString == null) { | |
| 355 reporter.internalError( | |
| 356 CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument."); | |
| 357 return new native.NativeBehavior(); | |
| 358 } | |
| 359 return native.NativeBehavior.ofJsBuiltinCall( | |
| 360 specString, | |
| 361 typeLookup(resolveAsRaw: true), | |
| 362 CURRENT_ELEMENT_SPANNABLE, | |
| 363 reporter, | |
| 364 commonElements); | |
| 365 } | |
| 366 | |
| 367 /// Computes the [native.NativeBehavior] for a call to the | |
| 368 /// [JS_EMBEDDED_GLOBAL] function. | |
| 369 // TODO(johnniwinther): Cache this for later use. | |
| 370 native.NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall( | |
| 371 ir.StaticInvocation node) { | |
| 372 if (node.arguments.positional.length < 1) { | |
| 373 reporter.internalError(CURRENT_ELEMENT_SPANNABLE, | |
| 374 "JS embedded global expression has no type."); | |
| 375 return new native.NativeBehavior(); | |
| 376 } | |
| 377 if (node.arguments.positional.length < 2) { | |
| 378 reporter.internalError( | |
| 379 CURRENT_ELEMENT_SPANNABLE, "JS embedded global is missing name."); | |
| 380 return new native.NativeBehavior(); | |
| 381 } | |
| 382 if (node.arguments.positional.length > 2 || | |
| 383 node.arguments.named.isNotEmpty) { | |
| 384 reporter.internalError(CURRENT_ELEMENT_SPANNABLE, | |
| 385 "JS embedded global has more than 2 arguments."); | |
| 386 return new native.NativeBehavior(); | |
| 387 } | |
| 388 String specString = _getStringArgument(node, 0); | |
| 389 if (specString == null) { | |
| 390 reporter.internalError( | |
| 391 CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument."); | |
| 392 return new native.NativeBehavior(); | |
| 393 } | |
| 394 return native.NativeBehavior.ofJsEmbeddedGlobalCall( | |
| 395 specString, | |
| 396 typeLookup(resolveAsRaw: true), | |
| 397 CURRENT_ELEMENT_SPANNABLE, | |
| 398 reporter, | |
| 399 commonElements); | |
| 400 } | |
| 401 | |
| 402 /// Computes the [InterfaceType] referenced by a call to the | |
| 403 /// [JS_INTERCEPTOR_CONSTANT] function, if any. | |
| 404 InterfaceType getInterfaceTypeForJsInterceptorCall(ir.StaticInvocation node) { | |
| 405 if (node.arguments.positional.length != 1 || | |
| 406 node.arguments.named.isNotEmpty) { | |
| 407 reporter.reportErrorMessage(CURRENT_ELEMENT_SPANNABLE, | |
| 408 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); | |
| 409 } | |
| 410 ir.Node argument = node.arguments.positional.first; | |
| 411 if (argument is ir.TypeLiteral && argument.type is ir.InterfaceType) { | |
| 412 return getInterfaceType(argument.type); | |
| 413 } | |
| 414 return null; | |
| 415 } | |
| 416 | |
| 417 /// Computes the native behavior for reading the native [field]. | |
| 418 // TODO(johnniwinther): Cache this for later use. | |
| 419 native.NativeBehavior getNativeBehaviorForFieldLoad(ir.Field field) { | |
| 420 DartType type = getDartType(field.type); | |
| 421 List<ConstantValue> metadata = getMetadata(field.annotations); | |
| 422 // TODO(johnniwinther): Provide the correct value for [isJsInterop]. | |
| 423 return nativeBehaviorBuilder.buildFieldLoadBehavior( | |
| 424 type, metadata, typeLookup(resolveAsRaw: false), | |
| 425 isJsInterop: false); | |
| 426 } | |
| 427 | |
| 428 /// Computes the native behavior for writing to the native [field]. | |
| 429 // TODO(johnniwinther): Cache this for later use. | |
| 430 native.NativeBehavior getNativeBehaviorForFieldStore(ir.Field field) { | |
| 431 DartType type = getDartType(field.type); | |
| 432 return nativeBehaviorBuilder.buildFieldStoreBehavior(type); | |
| 433 } | |
| 434 | |
| 435 /// Computes the native behavior for calling [procedure]. | |
| 436 // TODO(johnniwinther): Cache this for later use. | |
| 437 native.NativeBehavior getNativeBehaviorForMethod(ir.Procedure procedure) { | |
| 438 DartType type = getFunctionType(procedure.function); | |
| 439 List<ConstantValue> metadata = getMetadata(procedure.annotations); | |
| 440 // TODO(johnniwinther): Provide the correct value for [isJsInterop]. | |
| 441 return nativeBehaviorBuilder.buildMethodBehavior( | |
| 442 type, metadata, typeLookup(resolveAsRaw: false), | |
| 443 isJsInterop: false); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 /// Visitor that converts string literals and concatenations of string literals | |
| 448 /// into the string value. | |
| 449 class Stringifier extends ir.ExpressionVisitor<String> { | |
| 450 @override | |
| 451 String visitStringLiteral(ir.StringLiteral node) => node.value; | |
| 452 | |
| 453 @override | |
| 454 String visitStringConcatenation(ir.StringConcatenation node) { | |
| 455 StringBuffer sb = new StringBuffer(); | |
| 456 for (ir.Expression expression in node.expressions) { | |
| 457 String value = expression.accept(this); | |
| 458 if (value == null) return null; | |
| 459 sb.write(value); | |
| 460 } | |
| 461 return sb.toString(); | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 /// Visitor that converts a kernel constant expression into a | |
| 466 /// [ConstantExpression]. | |
| 467 class Constantifier extends ir.ExpressionVisitor<ConstantExpression> { | |
| 468 final bool requireConstant; | |
| 469 final KernelElementAdapterMixin elementAdapter; | |
| 470 | |
| 471 Constantifier(this.elementAdapter, {this.requireConstant: true}); | |
| 472 | |
| 473 CommonElements get _commonElements => elementAdapter.commonElements; | |
| 474 | |
| 475 ConstantExpression visit(ir.Expression node) { | |
| 476 ConstantExpression constant = node.accept(this); | |
| 477 if (constant == null && requireConstant) { | |
| 478 throw new UnsupportedError( | |
| 479 "No constant computed for $node (${node.runtimeType})"); | |
| 480 } | |
| 481 return constant; | |
| 482 } | |
| 483 | |
| 484 ConstantExpression defaultExpression(ir.Expression node) { | |
| 485 throw new UnimplementedError( | |
| 486 'Unimplemented constant expression $node (${node.runtimeType})'); | |
| 487 } | |
| 488 | |
| 489 List<ConstantExpression> _computeList(List<ir.Expression> expressions) { | |
| 490 List<ConstantExpression> list = <ConstantExpression>[]; | |
| 491 for (ir.Expression expression in expressions) { | |
| 492 ConstantExpression constant = visit(expression); | |
| 493 if (constant == null) return null; | |
| 494 list.add(constant); | |
| 495 } | |
| 496 return list; | |
| 497 } | |
| 498 | |
| 499 List<ConstantExpression> _computeArguments(ir.Arguments node) { | |
| 500 List<ConstantExpression> arguments = <ConstantExpression>[]; | |
| 501 for (ir.Expression argument in node.positional) { | |
| 502 ConstantExpression constant = visit(argument); | |
| 503 if (constant == null) return null; | |
| 504 arguments.add(constant); | |
| 505 } | |
| 506 for (ir.NamedExpression argument in node.named) { | |
| 507 ConstantExpression constant = visit(argument.value); | |
| 508 if (constant == null) return null; | |
| 509 arguments.add(constant); | |
| 510 } | |
| 511 return arguments; | |
| 512 } | |
| 513 | |
| 514 ConstructedConstantExpression _computeConstructorInvocation( | |
| 515 ir.Constructor target, ir.Arguments arguments) { | |
| 516 return new ConstructedConstantExpression( | |
| 517 elementAdapter.createInterfaceType( | |
| 518 target.enclosingClass, arguments.types), | |
| 519 elementAdapter.getConstructor(target), | |
| 520 elementAdapter.getCallStructure(arguments), | |
| 521 _computeArguments(arguments)); | |
| 522 } | |
| 523 | |
| 524 @override | |
| 525 ConstantExpression visitConstructorInvocation(ir.ConstructorInvocation node) { | |
| 526 return _computeConstructorInvocation(node.target, node.arguments); | |
| 527 } | |
| 528 | |
| 529 @override | |
| 530 ConstantExpression visitVariableGet(ir.VariableGet node) { | |
| 531 if (node.variable.parent is ir.FunctionNode) { | |
| 532 ir.FunctionNode function = node.variable.parent; | |
| 533 int index = function.positionalParameters.indexOf(node.variable); | |
| 534 if (index != -1) { | |
| 535 return new PositionalArgumentReference(index); | |
| 536 } else { | |
| 537 assert(function.namedParameters.contains(node.variable)); | |
| 538 return new NamedArgumentReference(node.variable.name); | |
| 539 } | |
| 540 } | |
| 541 throw new UnimplementedError( | |
| 542 'Unimplemented constant expression $node (${node.runtimeType})'); | |
| 543 } | |
| 544 | |
| 545 @override | |
| 546 ConstantExpression visitStaticGet(ir.StaticGet node) { | |
| 547 if (node.target is ir.Field) { | |
| 548 return new FieldConstantExpression(elementAdapter.getField(node.target)); | |
| 549 } else if (node.target is ir.Procedure) { | |
| 550 FunctionEntity function = elementAdapter.getMethod(node.target); | |
| 551 DartType type = elementAdapter.getFunctionType(node.target.function); | |
| 552 return new FunctionConstantExpression(function, type); | |
| 553 } | |
| 554 throw new UnimplementedError( | |
| 555 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 556 } | |
| 557 | |
| 558 @override | |
| 559 ConstantExpression visitNullLiteral(ir.NullLiteral node) { | |
| 560 return new NullConstantExpression(); | |
| 561 } | |
| 562 | |
| 563 @override | |
| 564 ConstantExpression visitBoolLiteral(ir.BoolLiteral node) { | |
| 565 return new BoolConstantExpression(node.value); | |
| 566 } | |
| 567 | |
| 568 @override | |
| 569 ConstantExpression visitIntLiteral(ir.IntLiteral node) { | |
| 570 return new IntConstantExpression(node.value); | |
| 571 } | |
| 572 | |
| 573 @override | |
| 574 ConstantExpression visitDoubleLiteral(ir.DoubleLiteral node) { | |
| 575 return new DoubleConstantExpression(node.value); | |
| 576 } | |
| 577 | |
| 578 @override | |
| 579 ConstantExpression visitStringLiteral(ir.StringLiteral node) { | |
| 580 return new StringConstantExpression(node.value); | |
| 581 } | |
| 582 | |
| 583 @override | |
| 584 ConstantExpression visitSymbolLiteral(ir.SymbolLiteral node) { | |
| 585 return new SymbolConstantExpression(node.value); | |
| 586 } | |
| 587 | |
| 588 @override | |
| 589 ConstantExpression visitStringConcatenation(ir.StringConcatenation node) { | |
| 590 return new ConcatenateConstantExpression(_computeList(node.expressions)); | |
| 591 } | |
| 592 | |
| 593 @override | |
| 594 ConstantExpression visitMapLiteral(ir.MapLiteral node) { | |
| 595 if (!node.isConst) { | |
| 596 throw new UnimplementedError( | |
| 597 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 598 } | |
| 599 DartType keyType = elementAdapter.getDartType(node.keyType); | |
| 600 DartType valueType = elementAdapter.getDartType(node.valueType); | |
| 601 List<ConstantExpression> keys = <ConstantExpression>[]; | |
| 602 List<ConstantExpression> values = <ConstantExpression>[]; | |
| 603 for (ir.MapEntry entry in node.entries) { | |
| 604 keys.add(visit(entry.key)); | |
| 605 values.add(visit(entry.value)); | |
| 606 } | |
| 607 return new MapConstantExpression( | |
| 608 _commonElements.mapType(keyType, valueType), keys, values); | |
| 609 } | |
| 610 | |
| 611 @override | |
| 612 ConstantExpression visitListLiteral(ir.ListLiteral node) { | |
| 613 if (!node.isConst) { | |
| 614 throw new UnimplementedError( | |
| 615 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 616 } | |
| 617 DartType elementType = elementAdapter.getDartType(node.typeArgument); | |
| 618 List<ConstantExpression> values = <ConstantExpression>[]; | |
| 619 for (ir.Expression value in node.expressions) { | |
| 620 values.add(visit(value)); | |
| 621 } | |
| 622 return new ListConstantExpression( | |
| 623 _commonElements.listType(elementType), values); | |
| 624 } | |
| 625 | |
| 626 @override | |
| 627 ConstantExpression visitConditionalExpression(ir.ConditionalExpression node) { | |
| 628 ConstantExpression condition = visit(node.condition); | |
| 629 ConstantExpression trueExp = visit(node.then); | |
| 630 ConstantExpression falseExp = visit(node.otherwise); | |
| 631 return new ConditionalConstantExpression(condition, trueExp, falseExp); | |
| 632 } | |
| 633 | |
| 634 @override | |
| 635 ConstantExpression visitPropertyGet(ir.PropertyGet node) { | |
| 636 if (node.name.name != 'length') { | |
| 637 throw new UnimplementedError( | |
| 638 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 639 } | |
| 640 ConstantExpression receiver = visit(node.receiver); | |
| 641 return new StringLengthConstantExpression(receiver); | |
| 642 } | |
| 643 | |
| 644 @override | |
| 645 ConstantExpression visitMethodInvocation(ir.MethodInvocation node) { | |
| 646 // Method invocations are generally not constant expressions but unary | |
| 647 // and binary expressions are encoded as method invocations in kernel. | |
| 648 if (node.arguments.named.isNotEmpty) { | |
| 649 throw new UnimplementedError( | |
| 650 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 651 } | |
| 652 if (node.arguments.positional.length == 0) { | |
| 653 UnaryOperator operator; | |
| 654 if (node.name.name == UnaryOperator.NEGATE.selectorName) { | |
| 655 operator = UnaryOperator.NEGATE; | |
| 656 } else { | |
| 657 operator = UnaryOperator.parse(node.name.name); | |
| 658 } | |
| 659 if (operator != null) { | |
| 660 ConstantExpression expression = visit(node.receiver); | |
| 661 return new UnaryConstantExpression(operator, expression); | |
| 662 } | |
| 663 } | |
| 664 if (node.arguments.positional.length == 1) { | |
| 665 BinaryOperator operator = BinaryOperator.parse(node.name.name); | |
| 666 if (operator != null) { | |
| 667 ConstantExpression left = visit(node.receiver); | |
| 668 ConstantExpression right = visit(node.arguments.positional.single); | |
| 669 return new BinaryConstantExpression(left, operator, right); | |
| 670 } | |
| 671 } | |
| 672 throw new UnimplementedError( | |
| 673 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 674 } | |
| 675 | |
| 676 @override | |
| 677 ConstantExpression visitStaticInvocation(ir.StaticInvocation node) { | |
| 678 MemberEntity member = elementAdapter.getMember(node.target); | |
| 679 if (member == _commonElements.identicalFunction) { | |
| 680 if (node.arguments.positional.length == 2 && | |
| 681 node.arguments.named.isEmpty) { | |
| 682 ConstantExpression left = visit(node.arguments.positional[0]); | |
| 683 ConstantExpression right = visit(node.arguments.positional[1]); | |
| 684 return new IdenticalConstantExpression(left, right); | |
| 685 } | |
| 686 } else if (member.name == 'fromEnvironment' && | |
| 687 node.arguments.positional.length == 1) { | |
| 688 ConstantExpression name = visit(node.arguments.positional.single); | |
| 689 ConstantExpression defaultValue; | |
| 690 if (node.arguments.named.length == 1) { | |
| 691 if (node.arguments.named.single.name != 'defaultValue') { | |
| 692 throw new UnimplementedError( | |
| 693 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 694 } | |
| 695 defaultValue = visit(node.arguments.named.single.value); | |
| 696 } | |
| 697 if (member.enclosingClass == _commonElements.boolClass) { | |
| 698 return new BoolFromEnvironmentConstantExpression(name, defaultValue); | |
| 699 } else if (member.enclosingClass == _commonElements.intClass) { | |
| 700 return new IntFromEnvironmentConstantExpression(name, defaultValue); | |
| 701 } else if (member.enclosingClass == _commonElements.stringClass) { | |
| 702 return new StringFromEnvironmentConstantExpression(name, defaultValue); | |
| 703 } | |
| 704 } | |
| 705 throw new UnimplementedError( | |
| 706 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 707 } | |
| 708 | |
| 709 @override | |
| 710 ConstantExpression visitLogicalExpression(ir.LogicalExpression node) { | |
| 711 BinaryOperator operator = BinaryOperator.parse(node.operator); | |
| 712 if (operator != null) { | |
| 713 ConstantExpression left = visit(node.left); | |
| 714 ConstantExpression right = visit(node.right); | |
| 715 return new BinaryConstantExpression(left, operator, right); | |
| 716 } | |
| 717 throw new UnimplementedError( | |
| 718 'Unexpected constant expression $node (${node.runtimeType})'); | |
| 719 } | |
| 720 | |
| 721 /// Compute the [ConstantConstructor] corresponding to the const constructor | |
| 722 /// [node]. | |
| 723 ConstantConstructor computeConstantConstructor(ir.Constructor node) { | |
| 724 assert(node.isConst); | |
| 725 ir.Class cls = node.enclosingClass; | |
| 726 InterfaceType type = elementAdapter.elementEnvironment | |
| 727 .getThisType(elementAdapter.getClass(cls)); | |
| 728 | |
| 729 Map<dynamic, ConstantExpression> defaultValues = | |
| 730 <dynamic, ConstantExpression>{}; | |
| 731 int parameterIndex = 0; | |
| 732 node.function.positionalParameters | |
| 733 .forEach((ir.VariableDeclaration parameter) { | |
| 734 if (parameterIndex >= node.function.requiredParameterCount) { | |
| 735 if (parameter.initializer != null) { | |
| 736 defaultValues[parameterIndex] = parameter.initializer.accept(this); | |
| 737 } else { | |
| 738 defaultValues[parameterIndex] = new NullConstantExpression(); | |
| 739 } | |
| 740 } | |
| 741 parameterIndex++; | |
| 742 }); | |
| 743 node.function.namedParameters.forEach((ir.VariableDeclaration parameter) { | |
| 744 defaultValues[parameter.name] = parameter.initializer.accept(this); | |
| 745 }); | |
| 746 | |
| 747 bool isRedirecting = node.initializers.length == 1 && | |
| 748 node.initializers.single is ir.RedirectingInitializer; | |
| 749 | |
| 750 Map<FieldEntity, ConstantExpression> fieldMap = | |
| 751 <FieldEntity, ConstantExpression>{}; | |
| 752 | |
| 753 void registerField(ir.Field field, ConstantExpression constant) { | |
| 754 fieldMap[elementAdapter.getField(field)] = constant; | |
| 755 } | |
| 756 | |
| 757 if (!isRedirecting) { | |
| 758 for (ir.Field field in cls.fields) { | |
| 759 if (field.initializer != null) { | |
| 760 registerField(field, field.initializer.accept(this)); | |
| 761 } | |
| 762 } | |
| 763 } | |
| 764 | |
| 765 ConstructedConstantExpression superConstructorInvocation; | |
| 766 for (ir.Initializer initializer in node.initializers) { | |
| 767 if (initializer is ir.FieldInitializer) { | |
| 768 registerField(initializer.field, initializer.value.accept(this)); | |
| 769 } else if (initializer is ir.SuperInitializer) { | |
| 770 superConstructorInvocation = _computeConstructorInvocation( | |
| 771 initializer.target, initializer.arguments); | |
| 772 } else if (initializer is ir.RedirectingInitializer) { | |
| 773 superConstructorInvocation = _computeConstructorInvocation( | |
| 774 initializer.target, initializer.arguments); | |
| 775 } else { | |
| 776 throw new UnsupportedError( | |
| 777 'Unexpected initializer $node (${node.runtimeType})'); | |
| 778 } | |
| 779 } | |
| 780 if (isRedirecting) { | |
| 781 return new RedirectingGenerativeConstantConstructor( | |
| 782 defaultValues, superConstructorInvocation); | |
| 783 } else { | |
| 784 return new GenerativeConstantConstructor( | |
| 785 type, defaultValues, fieldMap, superConstructorInvocation); | |
| 786 } | |
| 787 } | |
| 788 } | |
| OLD | NEW |