Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 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 | 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 'package:kernel/ast.dart' as ir; | 5 import 'package:kernel/ast.dart' as ir; |
| 6 | 6 |
| 7 import '../common.dart'; | |
| 8 import '../common/names.dart'; | |
| 9 import '../core_types.dart'; | |
| 7 import '../elements/elements.dart'; | 10 import '../elements/elements.dart'; |
| 8 import '../elements/entities.dart'; | 11 import '../elements/entities.dart'; |
| 9 import '../elements/types.dart'; | 12 import '../elements/types.dart'; |
| 13 import '../js_backend/backend_helpers.dart'; | |
| 10 import '../native/native.dart' as native; | 14 import '../native/native.dart' as native; |
| 11 import '../universe/call_structure.dart'; | 15 import '../universe/call_structure.dart'; |
| 12 import '../universe/selector.dart'; | 16 import '../universe/selector.dart'; |
| 13 | 17 |
| 14 /// Interface that translates between Kernel IR nodes and entities. | 18 /// Interface that translates between Kernel IR nodes and entities. |
| 15 abstract class KernelElementAdapter { | 19 abstract class KernelElementAdapter { |
| 20 /// Access to the commonly used elements and types. | |
| 21 CommonElements get commonElements; | |
| 22 | |
| 16 /// Returns the [DartType] corresponding to [type]. | 23 /// Returns the [DartType] corresponding to [type]. |
| 17 DartType getDartType(ir.DartType type); | 24 DartType getDartType(ir.DartType type); |
| 18 | 25 |
| 19 /// Returns the list of [DartType]s corresponding to [types]. | 26 /// Returns the list of [DartType]s corresponding to [types]. |
| 20 List<DartType> getDartTypes(List<ir.DartType> types); | 27 List<DartType> getDartTypes(List<ir.DartType> types); |
| 21 | 28 |
| 22 /// Returns the [InterfaceType] corresponding to [type]. | 29 /// Returns the [InterfaceType] corresponding to [type]. |
| 23 InterfaceType getInterfaceType(ir.InterfaceType type); | 30 InterfaceType getInterfaceType(ir.InterfaceType type); |
| 24 | 31 |
| 25 /// Return the [InterfaceType] corresponding to the [cls] with the given | 32 /// Return the [InterfaceType] corresponding to the [cls] with the given |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 45 FunctionEntity getMethod(ir.Procedure node); | 52 FunctionEntity getMethod(ir.Procedure node); |
| 46 | 53 |
| 47 /// Returns the [FieldEntity] corresponding to the field [node]. | 54 /// Returns the [FieldEntity] corresponding to the field [node]. |
| 48 FieldEntity getField(ir.Field node); | 55 FieldEntity getField(ir.Field node); |
| 49 | 56 |
| 50 /// Returns the [ClassEntity] corresponding to the class [node]. | 57 /// Returns the [ClassEntity] corresponding to the class [node]. |
| 51 ClassEntity getClass(ir.Class node); | 58 ClassEntity getClass(ir.Class node); |
| 52 | 59 |
| 53 /// Returns the [Local] corresponding to the [node]. The node must be either | 60 /// Returns the [Local] corresponding to the [node]. The node must be either |
| 54 /// a [ir.FunctionDeclaration] or [ir.FunctionExpression]. | 61 /// a [ir.FunctionDeclaration] or [ir.FunctionExpression]. |
| 55 Local getLocalFunction(ir.Node node); | 62 Local getLocalFunction(ir.TreeNode node); |
| 56 | 63 |
| 57 /// Returns the [LibraryEntity] corresponding to the library [node]. | 64 /// Returns the [LibraryEntity] corresponding to the library [node]. |
| 58 LibraryEntity getLibrary(ir.Library node); | 65 LibraryEntity getLibrary(ir.Library node); |
| 59 | 66 |
| 60 /// Returns the [Name] corresponding to [name]. | 67 /// Returns the [Name] corresponding to [name]. |
| 61 Name getName(ir.Name name); | 68 Name getName(ir.Name name); |
| 62 | 69 |
| 63 /// Returns `true` is [node] has a `@Native(...)` annotation. | 70 /// Returns `true` is [node] has a `@Native(...)` annotation. |
| 64 bool isNativeClass(ir.Class node); | 71 bool isNativeClass(ir.Class node); |
| 65 | 72 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 } | 104 } |
| 98 | 105 |
| 99 /// Kinds of foreign functions. | 106 /// Kinds of foreign functions. |
| 100 enum ForeignKind { | 107 enum ForeignKind { |
| 101 JS, | 108 JS, |
| 102 JS_BUILTIN, | 109 JS_BUILTIN, |
| 103 JS_EMBEDDED_GLOBAL, | 110 JS_EMBEDDED_GLOBAL, |
| 104 JS_INTERCEPTOR_CONSTANT, | 111 JS_INTERCEPTOR_CONSTANT, |
| 105 NONE, | 112 NONE, |
| 106 } | 113 } |
| 114 | |
| 115 abstract class KernelElementAdapterMixin implements KernelElementAdapter { | |
| 116 DiagnosticReporter get reporter; | |
| 117 CommonElements get commonElements; | |
| 118 | |
| 119 LibraryEntity lookupLibrary(Uri uri); | |
| 120 ClassEntity lookupClass(LibraryEntity library, String name); | |
| 121 InterfaceType getRawType(ClassEntity cls); | |
| 122 InterfaceType getThisType(ClassEntity cls); | |
| 123 | |
| 124 @override | |
| 125 Name getName(ir.Name name) { | |
| 126 return new Name( | |
| 127 name.name, name.isPrivate ? getLibrary(name.library) : null); | |
| 128 } | |
| 129 | |
| 130 @override | |
| 131 CallStructure getCallStructure(ir.Arguments arguments) { | |
| 132 int argumentCount = arguments.positional.length + arguments.named.length; | |
| 133 List<String> namedArguments = arguments.named.map((e) => e.name).toList(); | |
| 134 return new CallStructure(argumentCount, namedArguments); | |
| 135 } | |
| 136 | |
| 137 @override | |
| 138 Selector getSelector(ir.Expression node) { | |
| 139 if (node is ir.PropertyGet) return getGetterSelector(node); | |
| 140 if (node is ir.PropertySet) return getSetterSelector(node); | |
| 141 if (node is ir.InvocationExpression) return getInvocationSelector(node); | |
| 142 throw new SpannableAssertionFailure(CURRENT_ELEMENT_SPANNABLE, | |
| 143 "Can only get the selector for a property get or an invocation: ${node}" ); | |
|
Johnni Winther
2017/01/30 14:40:00
Fixed in later CL (dartfmt doesn't handle this)
| |
| 144 } | |
| 145 | |
| 146 Selector getInvocationSelector(ir.InvocationExpression invocation) { | |
| 147 Name name = getName(invocation.name); | |
| 148 SelectorKind kind; | |
| 149 if (Elements.isOperatorName(invocation.name.name)) { | |
| 150 if (name == Names.INDEX_NAME || name == Names.INDEX_SET_NAME) { | |
| 151 kind = SelectorKind.INDEX; | |
| 152 } else { | |
| 153 kind = SelectorKind.OPERATOR; | |
| 154 } | |
| 155 } else { | |
| 156 kind = SelectorKind.CALL; | |
| 157 } | |
| 158 | |
| 159 CallStructure callStructure = getCallStructure(invocation.arguments); | |
| 160 return new Selector(kind, name, callStructure); | |
| 161 } | |
| 162 | |
| 163 Selector getGetterSelector(ir.PropertyGet getter) { | |
| 164 ir.Name irName = getter.name; | |
| 165 Name name = new Name( | |
| 166 irName.name, irName.isPrivate ? getLibrary(irName.library) : null); | |
| 167 return new Selector.getter(name); | |
| 168 } | |
| 169 | |
| 170 Selector getSetterSelector(ir.PropertySet setter) { | |
| 171 ir.Name irName = setter.name; | |
| 172 Name name = new Name( | |
| 173 irName.name, irName.isPrivate ? getLibrary(irName.library) : null); | |
| 174 return new Selector.setter(name); | |
| 175 } | |
| 176 | |
| 177 /// Returns `true` is [node] has a `@Native(...)` annotation. | |
| 178 // TODO(johnniwinther): Cache this for later use. | |
| 179 bool isNativeClass(ir.Class node) { | |
| 180 for (ir.Expression annotation in node.annotations) { | |
| 181 if (annotation is ir.ConstructorInvocation) { | |
| 182 FunctionEntity target = getConstructor(annotation.target); | |
| 183 if (target.enclosingClass == commonElements.nativeAnnotationClass) { | |
| 184 return true; | |
| 185 } | |
| 186 } | |
| 187 } | |
| 188 return false; | |
| 189 } | |
| 190 | |
| 191 /// Compute the kind of foreign helper function called by [node], if any. | |
| 192 ForeignKind getForeignKind(ir.StaticInvocation node) { | |
| 193 if (isForeignLibrary(node.target.enclosingLibrary)) { | |
| 194 switch (node.target.name.name) { | |
| 195 case BackendHelpers.JS: | |
| 196 return ForeignKind.JS; | |
| 197 case BackendHelpers.JS_BUILTIN: | |
| 198 return ForeignKind.JS_BUILTIN; | |
| 199 case BackendHelpers.JS_EMBEDDED_GLOBAL: | |
| 200 return ForeignKind.JS_EMBEDDED_GLOBAL; | |
| 201 case BackendHelpers.JS_INTERCEPTOR_CONSTANT: | |
| 202 return ForeignKind.JS_INTERCEPTOR_CONSTANT; | |
| 203 } | |
| 204 } | |
| 205 return ForeignKind.NONE; | |
| 206 } | |
| 207 | |
| 208 /// Return `true` if [node] is the `dart:_foreign_helper` library. | |
| 209 bool isForeignLibrary(ir.Library node) { | |
| 210 return node.importUri == BackendHelpers.DART_FOREIGN_HELPER; | |
| 211 } | |
| 212 | |
| 213 /// Looks up [typeName] for use in the spec-string of a `JS` called. | |
| 214 // TODO(johnniwinther): Use this in [native.NativeBehavior] instead of calling | |
| 215 // the `ForeignResolver`. | |
| 216 // TODO(johnniwinther): Cache the result to avoid redundant lookups? | |
| 217 native.TypeLookup typeLookup({bool resolveAsRaw: true}) { | |
| 218 return (String typeName) { | |
| 219 DartType findIn(Uri uri) { | |
| 220 LibraryEntity library = lookupLibrary(uri); | |
| 221 if (library != null) { | |
| 222 ClassEntity cls = lookupClass(library, typeName); | |
| 223 if (cls != null) { | |
| 224 // TODO(johnniwinther): Align semantics. | |
| 225 return resolveAsRaw ? getRawType(cls) : getThisType(cls); | |
| 226 } | |
| 227 } | |
| 228 return null; | |
| 229 } | |
| 230 | |
| 231 DartType type = findIn(Uris.dart_core); | |
| 232 type ??= findIn(BackendHelpers.DART_JS_HELPER); | |
| 233 type ??= findIn(BackendHelpers.DART_INTERCEPTORS); | |
| 234 type ??= findIn(BackendHelpers.DART_ISOLATE_HELPER); | |
| 235 type ??= findIn(Uris.dart_collection); | |
| 236 type ??= findIn(Uris.dart_html); | |
| 237 type ??= findIn(Uris.dart_svg); | |
| 238 type ??= findIn(Uris.dart_web_audio); | |
| 239 type ??= findIn(Uris.dart_web_gl); | |
| 240 return type; | |
| 241 }; | |
| 242 } | |
| 243 | |
| 244 String _getStringArgument(ir.StaticInvocation node, int index) { | |
| 245 return node.arguments.positional[index].accept(new Stringifier()); | |
| 246 } | |
| 247 | |
| 248 /// Computes the [native.NativeBehavior] for a call to the [JS] function. | |
| 249 // TODO(johnniwinther): Cache this for later use. | |
| 250 native.NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node) { | |
| 251 if (node.arguments.positional.length < 2 || | |
| 252 node.arguments.named.isNotEmpty) { | |
| 253 reporter.reportErrorMessage( | |
| 254 CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS); | |
| 255 return new native.NativeBehavior(); | |
| 256 } | |
| 257 String specString = _getStringArgument(node, 0); | |
| 258 if (specString == null) { | |
| 259 reporter.reportErrorMessage( | |
| 260 CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST); | |
| 261 return new native.NativeBehavior(); | |
| 262 } | |
| 263 | |
| 264 String codeString = _getStringArgument(node, 1); | |
| 265 if (codeString == null) { | |
| 266 reporter.reportErrorMessage( | |
| 267 CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND); | |
| 268 return new native.NativeBehavior(); | |
| 269 } | |
| 270 | |
| 271 return native.NativeBehavior.ofJsCall( | |
| 272 specString, | |
| 273 codeString, | |
| 274 typeLookup(resolveAsRaw: true), | |
| 275 CURRENT_ELEMENT_SPANNABLE, | |
| 276 reporter, | |
| 277 commonElements); | |
| 278 } | |
| 279 | |
| 280 /// Computes the [native.NativeBehavior] for a call to the [JS_BUILTIN] | |
| 281 /// function. | |
| 282 // TODO(johnniwinther): Cache this for later use. | |
| 283 native.NativeBehavior getNativeBehaviorForJsBuiltinCall( | |
| 284 ir.StaticInvocation node) { | |
| 285 if (node.arguments.positional.length < 1) { | |
| 286 reporter.internalError( | |
| 287 CURRENT_ELEMENT_SPANNABLE, "JS builtin expression has no type."); | |
| 288 return new native.NativeBehavior(); | |
| 289 } | |
| 290 if (node.arguments.positional.length < 2) { | |
| 291 reporter.internalError( | |
| 292 CURRENT_ELEMENT_SPANNABLE, "JS builtin is missing name."); | |
| 293 return new native.NativeBehavior(); | |
| 294 } | |
| 295 String specString = _getStringArgument(node, 0); | |
| 296 if (specString == null) { | |
| 297 reporter.internalError( | |
| 298 CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument."); | |
| 299 return new native.NativeBehavior(); | |
| 300 } | |
| 301 return native.NativeBehavior.ofJsBuiltinCall( | |
| 302 specString, | |
| 303 typeLookup(resolveAsRaw: true), | |
| 304 CURRENT_ELEMENT_SPANNABLE, | |
| 305 reporter, | |
| 306 commonElements); | |
| 307 } | |
| 308 | |
| 309 /// Computes the [native.NativeBehavior] for a call to the | |
| 310 /// [JS_EMBEDDED_GLOBAL] function. | |
| 311 // TODO(johnniwinther): Cache this for later use. | |
| 312 native.NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall( | |
| 313 ir.StaticInvocation node) { | |
| 314 if (node.arguments.positional.length < 1) { | |
| 315 reporter.internalError(CURRENT_ELEMENT_SPANNABLE, | |
| 316 "JS embedded global expression has no type."); | |
| 317 return new native.NativeBehavior(); | |
| 318 } | |
| 319 if (node.arguments.positional.length < 2) { | |
| 320 reporter.internalError( | |
| 321 CURRENT_ELEMENT_SPANNABLE, "JS embedded global is missing name."); | |
| 322 return new native.NativeBehavior(); | |
| 323 } | |
| 324 if (node.arguments.positional.length > 2 || | |
| 325 node.arguments.named.isNotEmpty) { | |
| 326 reporter.internalError(CURRENT_ELEMENT_SPANNABLE, | |
| 327 "JS embedded global has more than 2 arguments."); | |
| 328 return new native.NativeBehavior(); | |
| 329 } | |
| 330 String specString = _getStringArgument(node, 0); | |
| 331 if (specString == null) { | |
| 332 reporter.internalError( | |
| 333 CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument."); | |
| 334 return new native.NativeBehavior(); | |
| 335 } | |
| 336 return native.NativeBehavior.ofJsEmbeddedGlobalCall( | |
| 337 specString, | |
| 338 typeLookup(resolveAsRaw: true), | |
| 339 CURRENT_ELEMENT_SPANNABLE, | |
| 340 reporter, | |
| 341 commonElements); | |
| 342 } | |
| 343 | |
| 344 /// Computes the [InterfaceType] referenced by a call to the | |
| 345 /// [JS_INTERCEPTOR_CONSTANT] function, if any. | |
| 346 InterfaceType getInterfaceTypeForJsInterceptorCall(ir.StaticInvocation node) { | |
| 347 if (node.arguments.positional.length != 1 || | |
| 348 node.arguments.named.isNotEmpty) { | |
| 349 reporter.reportErrorMessage(CURRENT_ELEMENT_SPANNABLE, | |
| 350 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); | |
| 351 } | |
| 352 ir.Node argument = node.arguments.positional.first; | |
| 353 if (argument is ir.TypeLiteral && argument.type is ir.InterfaceType) { | |
| 354 return getInterfaceType(argument.type); | |
| 355 } | |
| 356 return null; | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 /// Visitor that converts string literals and concatenations of string literals | |
| 361 /// into the string value. | |
| 362 class Stringifier extends ir.ExpressionVisitor<String> { | |
| 363 @override | |
| 364 String visitStringLiteral(ir.StringLiteral node) => node.value; | |
| 365 | |
| 366 @override | |
| 367 String visitStringConcatenation(ir.StringConcatenation node) { | |
| 368 StringBuffer sb = new StringBuffer(); | |
| 369 for (ir.Expression expression in node.expressions) { | |
| 370 String value = expression.accept(this); | |
| 371 if (value == null) return null; | |
| 372 sb.write(value); | |
| 373 } | |
| 374 return sb.toString(); | |
| 375 } | |
| 376 } | |
| OLD | NEW |