| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 '../common.dart'; | 5 import '../common.dart'; |
| 6 import '../common/backend_api.dart' show ForeignResolver; | 6 import '../common/backend_api.dart' show ForeignResolver; |
| 7 import '../common/resolution.dart' show Resolution; | 7 import '../common/resolution.dart' show Resolution; |
| 8 import '../compiler.dart' show Compiler; | 8 import '../compiler.dart' show Compiler; |
| 9 import '../constants/values.dart'; | 9 import '../constants/values.dart'; |
| 10 import '../common_elements.dart' show CommonElements; | 10 import '../common_elements.dart' show CommonElements; |
| 11 import '../elements/elements.dart'; | 11 import '../elements/elements.dart'; |
| 12 import '../elements/entities.dart'; | 12 import '../elements/entities.dart'; |
| 13 import '../elements/modelx.dart' show FunctionElementX; | 13 import '../elements/modelx.dart' show FunctionElementX; |
| 14 import '../elements/resolution_types.dart'; | 14 import '../elements/resolution_types.dart'; |
| 15 import '../elements/types.dart'; | 15 import '../elements/types.dart'; |
| 16 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 16 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
| 17 import '../js_backend/backend_usage.dart' show BackendUsageBuilder; | 17 import '../js_backend/backend_usage.dart' show BackendUsageBuilder; |
| 18 import '../js_backend/js_backend.dart'; | 18 import '../js_backend/js_backend.dart'; |
| 19 import '../js_backend/native_data.dart' show NativeClassDataBuilder; |
| 19 import '../js_emitter/js_emitter.dart' show CodeEmitterTask, NativeEmitter; | 20 import '../js_emitter/js_emitter.dart' show CodeEmitterTask, NativeEmitter; |
| 20 import 'package:front_end/src/fasta/scanner.dart' show BeginGroupToken, Token; | 21 import 'package:front_end/src/fasta/scanner.dart' show BeginGroupToken, Token; |
| 21 import 'package:front_end/src/fasta/scanner.dart' as Tokens show EOF_TOKEN; | 22 import 'package:front_end/src/fasta/scanner.dart' as Tokens show EOF_TOKEN; |
| 22 import '../tree/tree.dart'; | 23 import '../tree/tree.dart'; |
| 23 import '../universe/use.dart' show StaticUse, TypeUse; | 24 import '../universe/use.dart' show StaticUse, TypeUse; |
| 24 import '../universe/world_impact.dart' | 25 import '../universe/world_impact.dart' |
| 25 show WorldImpact, WorldImpactBuilder, WorldImpactBuilderImpl; | 26 show WorldImpact, WorldImpactBuilder, WorldImpactBuilderImpl; |
| 26 import 'behavior.dart'; | 27 import 'behavior.dart'; |
| 27 | 28 |
| 28 /** | 29 /** |
| 29 * This could be an abstract class but we use it as a stub for the dart_backend. | 30 * This could be an abstract class but we use it as a stub for the dart_backend. |
| 30 */ | 31 */ |
| 31 class NativeEnqueuer { | 32 class NativeEnqueuer { |
| 32 /// Called when a [type] has been instantiated natively. | 33 /// Called when a [type] has been instantiated natively. |
| 33 void onInstantiatedType(InterfaceType type) {} | 34 void onInstantiatedType(InterfaceType type) {} |
| 34 | 35 |
| 35 /// Initial entry point to native enqueuer. | 36 /// Initial entry point to native enqueuer. |
| 36 WorldImpact processNativeClasses(Iterable<LibraryElement> libraries) => | 37 WorldImpact processNativeClasses(Iterable<LibraryElement> libraries) => |
| 37 const WorldImpact(); | 38 const WorldImpact(); |
| 38 | 39 |
| 39 /// Registers the [nativeBehavior]. Adds the liveness of its instantiated | 40 /// Registers the [nativeBehavior]. Adds the liveness of its instantiated |
| 40 /// types to the world. | 41 /// types to the world. |
| 41 void registerNativeBehavior( | 42 void registerNativeBehavior( |
| 42 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) {} | 43 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) {} |
| 43 | 44 |
| 44 // TODO(johnniwinther): Move [handleFieldAnnotations] and | |
| 45 // [handleMethodAnnotations] to [JavaScriptBackend] or [NativeData]. | |
| 46 // TODO(johnniwinther): Change the return type to 'bool' and rename them to | |
| 47 // something like `computeNativeField`. | |
| 48 /// Process the potentially native [field]. Adds information from metadata | |
| 49 /// attributes. | |
| 50 void handleFieldAnnotations(Element field) {} | |
| 51 | |
| 52 /// Process the potentially native [method]. Adds information from metadata | |
| 53 /// attributes. | |
| 54 void handleMethodAnnotations(Element method) {} | |
| 55 | |
| 56 /// Returns whether native classes are being used. | 45 /// Returns whether native classes are being used. |
| 57 bool get hasInstantiatedNativeClasses => false; | 46 bool get hasInstantiatedNativeClasses => false; |
| 58 | 47 |
| 59 /// Emits a summary information using the [log] function. | 48 /// Emits a summary information using the [log] function. |
| 60 void logSummary(log(message)) {} | 49 void logSummary(log(message)) {} |
| 61 } | 50 } |
| 62 | 51 |
| 63 abstract class NativeEnqueuerBase implements NativeEnqueuer { | 52 abstract class NativeEnqueuerBase implements NativeEnqueuer { |
| 64 static final RegExp _identifier = new RegExp(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$'); | |
| 65 | |
| 66 /// The set of all native classes. Each native class is in [nativeClasses] | 53 /// The set of all native classes. Each native class is in [nativeClasses] |
| 67 /// and exactly one of [unusedClasses] and [registeredClasses]. | 54 /// and exactly one of [unusedClasses] and [registeredClasses]. |
| 68 final Set<ClassElement> _nativeClasses = new Set<ClassElement>(); | 55 final Set<ClassElement> _nativeClasses = new Set<ClassElement>(); |
| 69 | 56 |
| 70 final Set<ClassElement> _registeredClasses = new Set<ClassElement>(); | 57 final Set<ClassElement> _registeredClasses = new Set<ClassElement>(); |
| 71 final Set<ClassElement> _unusedClasses = new Set<ClassElement>(); | 58 final Set<ClassElement> _unusedClasses = new Set<ClassElement>(); |
| 72 | 59 |
| 73 bool get hasInstantiatedNativeClasses => !_registeredClasses.isEmpty; | 60 bool get hasInstantiatedNativeClasses => !_registeredClasses.isEmpty; |
| 74 | 61 |
| 75 final Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); | 62 final Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 } | 233 } |
| 247 // Should be at '{', 'with', 'implements', '<' or 'native'. | 234 // Should be at '{', 'with', 'implements', '<' or 'native'. |
| 248 return id.value; | 235 return id.value; |
| 249 } | 236 } |
| 250 | 237 |
| 251 return reporter.withCurrentElement(classElement, () { | 238 return reporter.withCurrentElement(classElement, () { |
| 252 return scanForExtendsName(classElement.position); | 239 return scanForExtendsName(classElement.position); |
| 253 }); | 240 }); |
| 254 } | 241 } |
| 255 | 242 |
| 256 /// Returns the JSName annotation string or `null` if no JSName annotation is | |
| 257 /// present. | |
| 258 String findJsNameFromAnnotation(Element element) { | |
| 259 String name = null; | |
| 260 ClassElement annotationClass = backend.helpers.annotationJSNameClass; | |
| 261 for (MetadataAnnotation annotation in element.implementation.metadata) { | |
| 262 annotation.ensureResolved(resolution); | |
| 263 ConstantValue value = | |
| 264 compiler.constants.getConstantValue(annotation.constant); | |
| 265 if (!value.isConstructedObject) continue; | |
| 266 ConstructedConstantValue constructedObject = value; | |
| 267 if (constructedObject.type.element != annotationClass) continue; | |
| 268 | |
| 269 Iterable<ConstantValue> fields = constructedObject.fields.values; | |
| 270 // TODO(sra): Better validation of the constant. | |
| 271 if (fields.length != 1 || fields.single is! StringConstantValue) { | |
| 272 reporter.internalError( | |
| 273 annotation, 'Annotations needs one string: ${annotation}'); | |
| 274 } | |
| 275 StringConstantValue specStringConstant = fields.single; | |
| 276 String specString = specStringConstant.toDartString().slowToString(); | |
| 277 if (name == null) { | |
| 278 name = specString; | |
| 279 } else { | |
| 280 reporter.internalError( | |
| 281 annotation, 'Too many JSName annotations: ${annotation}'); | |
| 282 } | |
| 283 } | |
| 284 return name; | |
| 285 } | |
| 286 | |
| 287 /// Register [classes] as natively instantiated in [impactBuilder]. | 243 /// Register [classes] as natively instantiated in [impactBuilder]. |
| 288 void _registerTypeUses( | 244 void _registerTypeUses( |
| 289 WorldImpactBuilder impactBuilder, Set<ClassElement> classes, cause) { | 245 WorldImpactBuilder impactBuilder, Set<ClassElement> classes, cause) { |
| 290 for (ClassElement cls in classes) { | 246 for (ClassElement cls in classes) { |
| 291 if (!_unusedClasses.contains(cls)) { | 247 if (!_unusedClasses.contains(cls)) { |
| 292 // No need to add [classElement] to [impactBuilder]: it has already been | 248 // No need to add [classElement] to [impactBuilder]: it has already been |
| 293 // instantiated and we don't track origins of native instantiations | 249 // instantiated and we don't track origins of native instantiations |
| 294 // precisely. | 250 // precisely. |
| 295 continue; | 251 continue; |
| 296 } | 252 } |
| 297 cls.ensureResolved(resolution); | 253 cls.ensureResolved(resolution); |
| 298 impactBuilder | 254 impactBuilder |
| 299 .registerTypeUse(new TypeUse.nativeInstantiation(cls.rawType)); | 255 .registerTypeUse(new TypeUse.nativeInstantiation(cls.rawType)); |
| 300 } | 256 } |
| 301 } | 257 } |
| 302 | 258 |
| 303 void handleFieldAnnotations(Element element) { | |
| 304 if (compiler.serialization.isDeserialized(element)) { | |
| 305 return; | |
| 306 } | |
| 307 if (element.isInstanceMember && | |
| 308 backend.nativeClassData.isNativeClass(element.enclosingClass)) { | |
| 309 // Exclude non-instance (static) fields - they are not really native and | |
| 310 // are compiled as isolate globals. Access of a property of a constructor | |
| 311 // function or a non-method property in the prototype chain, must be coded | |
| 312 // using a JS-call. | |
| 313 _setNativeName(element); | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 void handleMethodAnnotations(Element method) { | |
| 318 if (compiler.serialization.isDeserialized(method)) { | |
| 319 return; | |
| 320 } | |
| 321 if (isNativeMethod(method)) { | |
| 322 if (method.isStatic) { | |
| 323 _setNativeNameForStaticMethod(method); | |
| 324 } else { | |
| 325 _setNativeName(method); | |
| 326 } | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 /// Sets the native name of [element], either from an annotation, or | |
| 331 /// defaulting to the Dart name. | |
| 332 void _setNativeName(MemberElement element) { | |
| 333 String name = findJsNameFromAnnotation(element); | |
| 334 if (name == null) name = element.name; | |
| 335 backend.nativeClassDataBuilder.setNativeMemberName(element, name); | |
| 336 } | |
| 337 | |
| 338 /// Sets the native name of the static native method [element], using the | |
| 339 /// following rules: | |
| 340 /// 1. If [element] has a @JSName annotation that is an identifier, qualify | |
| 341 /// that identifier to the @Native name of the enclosing class | |
| 342 /// 2. If [element] has a @JSName annotation that is not an identifier, | |
| 343 /// use the declared @JSName as the expression | |
| 344 /// 3. If [element] does not have a @JSName annotation, qualify the name of | |
| 345 /// the method with the @Native name of the enclosing class. | |
| 346 void _setNativeNameForStaticMethod(MethodElement element) { | |
| 347 String name = findJsNameFromAnnotation(element); | |
| 348 if (name == null) name = element.name; | |
| 349 if (isIdentifier(name)) { | |
| 350 List<String> nativeNames = backend.nativeClassDataBuilder | |
| 351 .getNativeTagsOfClassRaw(element.enclosingClass); | |
| 352 if (nativeNames.length != 1) { | |
| 353 reporter.internalError( | |
| 354 element, | |
| 355 'Unable to determine a native name for the enclosing class, ' | |
| 356 'options: $nativeNames'); | |
| 357 } | |
| 358 backend.nativeClassDataBuilder | |
| 359 .setNativeMemberName(element, '${nativeNames[0]}.$name'); | |
| 360 } else { | |
| 361 backend.nativeClassDataBuilder.setNativeMemberName(element, name); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 bool isIdentifier(String s) => _identifier.hasMatch(s); | |
| 366 | |
| 367 bool isNativeMethod(FunctionElementX element) { | |
| 368 if (!backend.canLibraryUseNative(element.library)) return false; | |
| 369 // Native method? | |
| 370 return reporter.withCurrentElement(element, () { | |
| 371 Node node = element.parseNode(resolution.parsingContext); | |
| 372 if (node is! FunctionExpression) return false; | |
| 373 FunctionExpression functionExpression = node; | |
| 374 node = functionExpression.body; | |
| 375 Token token = node.getBeginToken(); | |
| 376 if (identical(token.stringValue, 'native')) return true; | |
| 377 return false; | |
| 378 }); | |
| 379 } | |
| 380 | |
| 381 void registerNativeBehavior( | 259 void registerNativeBehavior( |
| 382 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) { | 260 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) { |
| 383 _processNativeBehavior(impactBuilder, nativeBehavior, cause); | 261 _processNativeBehavior(impactBuilder, nativeBehavior, cause); |
| 384 } | 262 } |
| 385 | 263 |
| 386 void _processNativeBehavior( | 264 void _processNativeBehavior( |
| 387 WorldImpactBuilder impactBuilder, NativeBehavior behavior, cause) { | 265 WorldImpactBuilder impactBuilder, NativeBehavior behavior, cause) { |
| 388 void registerInstantiation(ResolutionInterfaceType type) { | 266 void registerInstantiation(ResolutionInterfaceType type) { |
| 389 impactBuilder.registerTypeUse(new TypeUse.nativeInstantiation(type)); | 267 impactBuilder.registerTypeUse(new TypeUse.nativeInstantiation(type)); |
| 390 } | 268 } |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 482 | 360 |
| 483 void registerBackendUse(MethodElement element) { | 361 void registerBackendUse(MethodElement element) { |
| 484 _backendUsageBuilder.registerBackendFunctionUse(element); | 362 _backendUsageBuilder.registerBackendFunctionUse(element); |
| 485 _backendUsageBuilder.registerGlobalFunctionDependency(element); | 363 _backendUsageBuilder.registerGlobalFunctionDependency(element); |
| 486 } | 364 } |
| 487 | 365 |
| 488 void processNativeClass(ClassElement classElement) { | 366 void processNativeClass(ClassElement classElement) { |
| 489 super.processNativeClass(classElement); | 367 super.processNativeClass(classElement); |
| 490 | 368 |
| 491 // Js Interop interfaces do not have tags. | 369 // Js Interop interfaces do not have tags. |
| 492 if (backend.nativeData.isJsInterop(classElement)) return; | 370 if (backend.nativeData.isJsInteropClass(classElement)) return; |
| 493 // Since we map from dispatch tags to classes, a dispatch tag must be used | 371 // Since we map from dispatch tags to classes, a dispatch tag must be used |
| 494 // on only one native class. | 372 // on only one native class. |
| 495 for (String tag in backend.nativeData.getNativeTagsOfClass(classElement)) { | 373 for (String tag in backend.nativeData.getNativeTagsOfClass(classElement)) { |
| 496 ClassElement owner = tagOwner[tag]; | 374 ClassElement owner = tagOwner[tag]; |
| 497 if (owner != null) { | 375 if (owner != null) { |
| 498 if (owner != classElement) { | 376 if (owner != classElement) { |
| 499 reporter.internalError( | 377 reporter.internalError( |
| 500 classElement, "Tag '$tag' already in use by '${owner.name}'"); | 378 classElement, "Tag '$tag' already in use by '${owner.name}'"); |
| 501 } | 379 } |
| 502 } else { | 380 } else { |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 620 List<ClassEntity> directSubtypes = | 498 List<ClassEntity> directSubtypes = |
| 621 emitter.directSubtypes.putIfAbsent(superclass, () => <ClassEntity>[]); | 499 emitter.directSubtypes.putIfAbsent(superclass, () => <ClassEntity>[]); |
| 622 directSubtypes.add(cls); | 500 directSubtypes.add(cls); |
| 623 } | 501 } |
| 624 | 502 |
| 625 void logSummary(log(message)) { | 503 void logSummary(log(message)) { |
| 626 log('Compiled ${_registeredClasses.length} native classes, ' | 504 log('Compiled ${_registeredClasses.length} native classes, ' |
| 627 '${_unusedClasses.length} native classes omitted.'); | 505 '${_unusedClasses.length} native classes omitted.'); |
| 628 } | 506 } |
| 629 } | 507 } |
| OLD | NEW |