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 'package:front_end/src/fasta/scanner.dart' show StringToken, Token; | 5 import 'package:front_end/src/fasta/scanner.dart' |
| 6 show BeginGroupToken, StringToken, Token; |
| 7 import 'package:front_end/src/fasta/scanner.dart' as Tokens show EOF_TOKEN; |
6 | 8 |
7 import '../common.dart'; | 9 import '../common.dart'; |
8 import '../common/backend_api.dart'; | 10 import '../common/backend_api.dart'; |
| 11 import '../common/resolution.dart'; |
9 import '../compiler.dart' show Compiler; | 12 import '../compiler.dart' show Compiler; |
10 import '../constants/values.dart'; | 13 import '../constants/values.dart'; |
11 import '../elements/elements.dart' | 14 import '../elements/elements.dart' |
12 show | 15 show |
13 ClassElement, | 16 ClassElement, |
14 Element, | 17 Element, |
15 FieldElement, | 18 FieldElement, |
16 LibraryElement, | 19 LibraryElement, |
17 MemberElement, | 20 MemberElement, |
18 MetadataAnnotation, | 21 MetadataAnnotation, |
19 MethodElement; | 22 MethodElement; |
| 23 import '../elements/entities.dart'; |
20 import '../elements/modelx.dart' show FunctionElementX, MetadataAnnotationX; | 24 import '../elements/modelx.dart' show FunctionElementX, MetadataAnnotationX; |
21 import '../elements/resolution_types.dart' show ResolutionDartType; | 25 import '../elements/resolution_types.dart' show ResolutionDartType; |
| 26 import '../js_backend/backend_helpers.dart'; |
22 import '../js_backend/js_backend.dart'; | 27 import '../js_backend/js_backend.dart'; |
23 import '../js_backend/native_data.dart'; | 28 import '../js_backend/native_data.dart'; |
24 import '../patch_parser.dart'; | 29 import '../patch_parser.dart'; |
25 import '../tree/tree.dart'; | 30 import '../tree/tree.dart'; |
26 import 'behavior.dart'; | 31 import 'behavior.dart'; |
27 | 32 |
28 /// Interface for computing native members and [NativeBehavior]s in member code | 33 /// Interface for computing native members and [NativeBehavior]s in member code |
29 /// based on the AST. | 34 /// based on the AST. |
30 abstract class NativeDataResolver { | 35 abstract class NativeDataResolver { |
31 /// Returns `true` if [element] is a JsInterop member. | 36 /// Returns `true` if [element] is a JsInterop member. |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 JavaScriptBackend backend = compiler.backend; | 376 JavaScriptBackend backend = compiler.backend; |
372 ResolutionDartType type = constant.getType(compiler.commonElements); | 377 ResolutionDartType type = constant.getType(compiler.commonElements); |
373 if (type.element != backend.helpers.jsAnnotationClass) { | 378 if (type.element != backend.helpers.jsAnnotationClass) { |
374 compiler.reporter | 379 compiler.reporter |
375 .internalError(annotation, 'Invalid @JS(...) annotation.'); | 380 .internalError(annotation, 'Invalid @JS(...) annotation.'); |
376 } | 381 } |
377 } | 382 } |
378 | 383 |
379 bool get defaultResult => false; | 384 bool get defaultResult => false; |
380 } | 385 } |
| 386 |
| 387 /// Interface for computing all native classes in a set of libraries. |
| 388 abstract class NativeClassResolver { |
| 389 Iterable<ClassEntity> computeNativeClasses(Iterable<LibraryEntity> libraries); |
| 390 } |
| 391 |
| 392 class NativeClassResolverImpl implements NativeClassResolver { |
| 393 final DiagnosticReporter _reporter; |
| 394 final Resolution _resolution; |
| 395 final BackendHelpers _helpers; |
| 396 final NativeBasicData _nativeBasicData; |
| 397 |
| 398 Map<String, ClassElement> _tagOwner = new Map<String, ClassElement>(); |
| 399 |
| 400 NativeClassResolverImpl( |
| 401 this._resolution, this._reporter, this._helpers, this._nativeBasicData); |
| 402 |
| 403 Iterable<ClassElement> computeNativeClasses( |
| 404 Iterable<LibraryElement> libraries) { |
| 405 Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
| 406 libraries.forEach((l) => _processNativeClassesInLibrary(l, nativeClasses)); |
| 407 if (_helpers.isolateHelperLibrary != null) { |
| 408 _processNativeClassesInLibrary( |
| 409 _helpers.isolateHelperLibrary, nativeClasses); |
| 410 } |
| 411 _processSubclassesOfNativeClasses(libraries, nativeClasses); |
| 412 return nativeClasses; |
| 413 } |
| 414 |
| 415 void _processNativeClassesInLibrary( |
| 416 LibraryElement library, Set<ClassElement> nativeClasses) { |
| 417 // Use implementation to ensure the inclusion of injected members. |
| 418 library.implementation.forEachLocalMember((Element element) { |
| 419 if (element.isClass) { |
| 420 ClassElement cls = element; |
| 421 if (_nativeBasicData.isNativeClass(cls)) { |
| 422 _processNativeClass(element, nativeClasses); |
| 423 } |
| 424 } |
| 425 }); |
| 426 } |
| 427 |
| 428 void _processNativeClass( |
| 429 ClassElement classElement, Set<ClassElement> nativeClasses) { |
| 430 nativeClasses.add(classElement); |
| 431 // Resolve class to ensure the class has valid inheritance info. |
| 432 classElement.ensureResolved(_resolution); |
| 433 // Js Interop interfaces do not have tags. |
| 434 if (_nativeBasicData.isJsInteropClass(classElement)) return; |
| 435 // Since we map from dispatch tags to classes, a dispatch tag must be used |
| 436 // on only one native class. |
| 437 for (String tag in _nativeBasicData.getNativeTagsOfClass(classElement)) { |
| 438 ClassElement owner = _tagOwner[tag]; |
| 439 if (owner != null) { |
| 440 if (owner != classElement) { |
| 441 _reporter.internalError( |
| 442 classElement, "Tag '$tag' already in use by '${owner.name}'"); |
| 443 } |
| 444 } else { |
| 445 _tagOwner[tag] = classElement; |
| 446 } |
| 447 } |
| 448 } |
| 449 |
| 450 void _processSubclassesOfNativeClasses( |
| 451 Iterable<LibraryElement> libraries, Set<ClassElement> nativeClasses) { |
| 452 Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); |
| 453 // Collect potential subclasses, e.g. |
| 454 // |
| 455 // class B extends foo.A {} |
| 456 // |
| 457 // String "A" has a potential subclass B. |
| 458 |
| 459 var potentialExtends = new Map<String, Set<ClassElement>>(); |
| 460 |
| 461 libraries.forEach((library) { |
| 462 library.implementation.forEachLocalMember((element) { |
| 463 if (element.isClass) { |
| 464 String extendsName = _findExtendsNameOfClass(element); |
| 465 if (extendsName != null) { |
| 466 Set<ClassElement> potentialSubclasses = potentialExtends |
| 467 .putIfAbsent(extendsName, () => new Set<ClassElement>()); |
| 468 potentialSubclasses.add(element); |
| 469 } |
| 470 } |
| 471 }); |
| 472 }); |
| 473 |
| 474 // Resolve all the native classes and any classes that might extend them in |
| 475 // [potentialExtends], and then check that the properly resolved class is in |
| 476 // fact a subclass of a native class. |
| 477 |
| 478 ClassElement nativeSuperclassOf(ClassElement classElement) { |
| 479 if (_nativeBasicData.isNativeClass(classElement)) return classElement; |
| 480 if (classElement.superclass == null) return null; |
| 481 return nativeSuperclassOf(classElement.superclass); |
| 482 } |
| 483 |
| 484 void walkPotentialSubclasses(ClassElement element) { |
| 485 if (nativeClassesAndSubclasses.contains(element)) return; |
| 486 element.ensureResolved(_resolution); |
| 487 ClassElement nativeSuperclass = nativeSuperclassOf(element); |
| 488 if (nativeSuperclass != null) { |
| 489 nativeClassesAndSubclasses.add(element); |
| 490 Set<ClassElement> potentialSubclasses = potentialExtends[element.name]; |
| 491 if (potentialSubclasses != null) { |
| 492 potentialSubclasses.forEach(walkPotentialSubclasses); |
| 493 } |
| 494 } |
| 495 } |
| 496 |
| 497 nativeClasses.forEach(walkPotentialSubclasses); |
| 498 nativeClasses.addAll(nativeClassesAndSubclasses); |
| 499 } |
| 500 |
| 501 /** |
| 502 * Returns the source string of the class named in the extends clause, or |
| 503 * `null` if there is no extends clause. |
| 504 */ |
| 505 String _findExtendsNameOfClass(ClassElement classElement) { |
| 506 if (classElement.isResolved) { |
| 507 ClassElement superClass = classElement.superclass; |
| 508 while (superClass != null) { |
| 509 if (!superClass.isUnnamedMixinApplication) { |
| 510 return superClass.name; |
| 511 } |
| 512 superClass = superClass.superclass; |
| 513 } |
| 514 return null; |
| 515 } |
| 516 |
| 517 // "class B extends A ... {}" --> "A" |
| 518 // "class B extends foo.A ... {}" --> "A" |
| 519 // "class B<T> extends foo.A<T,T> with M1, M2 ... {}" --> "A" |
| 520 |
| 521 // We want to avoid calling classElement.parseNode on every class. Doing so |
| 522 // will slightly increase parse time and size and cause compiler errors and |
| 523 // warnings to me emitted in more unused code. |
| 524 |
| 525 // An alternative to this code is to extend the API of ClassElement to |
| 526 // expose the name of the extended element. |
| 527 |
| 528 // Pattern match the above cases in the token stream. |
| 529 // [abstract] class X extends [id.]* id |
| 530 |
| 531 Token skipTypeParameters(Token token) { |
| 532 BeginGroupToken beginGroupToken = token; |
| 533 Token endToken = beginGroupToken.endGroup; |
| 534 return endToken.next; |
| 535 //for (;;) { |
| 536 // token = token.next; |
| 537 // if (token.stringValue == '>') return token.next; |
| 538 // if (token.stringValue == '<') return skipTypeParameters(token); |
| 539 //} |
| 540 } |
| 541 |
| 542 String scanForExtendsName(Token token) { |
| 543 if (token.stringValue == 'abstract') token = token.next; |
| 544 if (token.stringValue != 'class') return null; |
| 545 token = token.next; |
| 546 if (!token.isIdentifier()) return null; |
| 547 token = token.next; |
| 548 // class F<X extends B<X>> extends ... |
| 549 if (token.stringValue == '<') { |
| 550 token = skipTypeParameters(token); |
| 551 } |
| 552 if (token.stringValue != 'extends') return null; |
| 553 token = token.next; |
| 554 Token id = token; |
| 555 while (token.kind != Tokens.EOF_TOKEN) { |
| 556 token = token.next; |
| 557 if (token.stringValue != '.') break; |
| 558 token = token.next; |
| 559 if (!token.isIdentifier()) return null; |
| 560 id = token; |
| 561 } |
| 562 // Should be at '{', 'with', 'implements', '<' or 'native'. |
| 563 return id.lexeme; |
| 564 } |
| 565 |
| 566 return _reporter.withCurrentElement(classElement, () { |
| 567 return scanForExtendsName(classElement.position); |
| 568 }); |
| 569 } |
| 570 } |
OLD | NEW |