OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 | 2 |
3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
5 | 5 |
6 import 'dart:collection' show HashMap, HashSet; | 6 import 'dart:collection' show HashMap, HashSet; |
7 import 'dart:math' show min, max; | 7 import 'dart:math' show min, max; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 var libraryPrefix = <String>[]; | 317 var libraryPrefix = <String>[]; |
318 if (libraryJSName != null && libraryJSName.isNotEmpty) { | 318 if (libraryJSName != null && libraryJSName.isNotEmpty) { |
319 libraryPrefix.addAll(libraryJSName.split('.')); | 319 libraryPrefix.addAll(libraryJSName.split('.')); |
320 } | 320 } |
321 | 321 |
322 String elementJSName; | 322 String elementJSName; |
323 if (findAnnotation(e, isPublicJSAnnotation) != null) { | 323 if (findAnnotation(e, isPublicJSAnnotation) != null) { |
324 elementJSName = getAnnotationName(e, isPublicJSAnnotation) ?? ''; | 324 elementJSName = getAnnotationName(e, isPublicJSAnnotation) ?? ''; |
325 } | 325 } |
326 | 326 |
327 if (e is TopLevelVariableElement && | 327 if (e is TopLevelVariableElement) { |
328 e.getter != null && | 328 elementJSName = _jsInteropStaticMemberName(e); |
329 (e.getter.isExternal || | |
330 findAnnotation(e.getter, isPublicJSAnnotation) != null)) { | |
331 elementJSName = getAnnotationName(e.getter, isPublicJSAnnotation) ?? ''; | |
332 } | 329 } |
333 if (elementJSName == null) return null; | 330 if (elementJSName == null) return null; |
334 | 331 |
335 var elementJSParts = <String>[]; | 332 var elementJSParts = <String>[]; |
336 if (elementJSName.isNotEmpty) { | 333 if (elementJSName.isNotEmpty) { |
337 elementJSParts.addAll(elementJSName.split('.')); | 334 elementJSParts.addAll(elementJSName.split('.')); |
338 } else { | 335 } else { |
339 elementJSParts.add(e.name); | 336 elementJSParts.add(e.name); |
340 } | 337 } |
341 | 338 |
342 return libraryPrefix..addAll(elementJSParts); | 339 return libraryPrefix..addAll(elementJSParts); |
343 } | 340 } |
344 | 341 |
345 JS.Expression _emitJSInterop(Element e) { | 342 JS.Expression _emitJSInterop(Element e) { |
346 var jsName = _getJSName(e); | 343 var jsName = _getJSName(e); |
347 if (jsName == null) return null; | 344 if (jsName == null) return null; |
348 var fullName = ['global']..addAll(jsName); | 345 var fullName = ['global']..addAll(jsName); |
349 JS.Expression access = _runtimeModule; | 346 JS.Expression access = _runtimeModule; |
350 for (var part in fullName) { | 347 for (var part in fullName) { |
351 access = new JS.PropertyAccess(access, js.string(part)); | 348 access = new JS.PropertyAccess(access, js.string(part)); |
352 } | 349 } |
353 return access; | 350 return access; |
354 } | 351 } |
355 | 352 |
| 353 String _jsInteropStaticMemberName(Element e, {String name}) { |
| 354 if (e == null || |
| 355 e.library == null || |
| 356 findAnnotation(e.library, isPublicJSAnnotation) == null) { |
| 357 return null; |
| 358 } |
| 359 if (e is PropertyInducingElement) { |
| 360 // Assume properties have consistent JS names for getters and setters. |
| 361 return _jsInteropStaticMemberName(e.getter, name: e.name) ?? |
| 362 _jsInteropStaticMemberName(e.setter, name: e.name); |
| 363 } |
| 364 if (e is ExecutableElement && |
| 365 e.isExternal && |
| 366 findAnnotation(e, isPublicJSAnnotation) != null) { |
| 367 return getAnnotationName(e, isPublicJSAnnotation) ?? name ?? e.name; |
| 368 } |
| 369 return null; |
| 370 } |
| 371 |
| 372 JS.Expression _emitJSInteropStaticMemberName(Element e) { |
| 373 var name = _jsInteropStaticMemberName(e); |
| 374 if (name == null) return null; |
| 375 // We do not support statics names with JS annotations containing dots. |
| 376 // See https://github.com/dart-lang/sdk/issues/27926 |
| 377 if (name.contains('.')) { |
| 378 throw new UnimplementedError( |
| 379 'We do not support JS annotations containing dots on static members. ' |
| 380 'See https://github.com/dart-lang/sdk/issues/27926'); |
| 381 } |
| 382 return js.string(name); |
| 383 } |
| 384 |
356 /// Flattens blocks in [items] to a single list. | 385 /// Flattens blocks in [items] to a single list. |
357 /// | 386 /// |
358 /// This will not flatten blocks that are marked as being scopes. | 387 /// This will not flatten blocks that are marked as being scopes. |
359 void _copyAndFlattenBlocks( | 388 void _copyAndFlattenBlocks( |
360 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { | 389 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { |
361 for (var item in items) { | 390 for (var item in items) { |
362 if (item is JS.Block && !item.isScope) { | 391 if (item is JS.Block && !item.isScope) { |
363 _copyAndFlattenBlocks(result, item.statements); | 392 _copyAndFlattenBlocks(result, item.statements); |
364 } else { | 393 } else { |
365 result.add(item); | 394 result.add(item); |
(...skipping 2332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2698 return _emitTopLevelName(element); | 2727 return _emitTopLevelName(element); |
2699 } | 2728 } |
2700 | 2729 |
2701 var name = element.name; | 2730 var name = element.name; |
2702 | 2731 |
2703 // Unqualified class member. This could mean implicit-this, or implicit | 2732 // Unqualified class member. This could mean implicit-this, or implicit |
2704 // call to a static from the same class. | 2733 // call to a static from the same class. |
2705 if (element is ClassMemberElement && element is! ConstructorElement) { | 2734 if (element is ClassMemberElement && element is! ConstructorElement) { |
2706 bool isStatic = element.isStatic; | 2735 bool isStatic = element.isStatic; |
2707 var type = element.enclosingElement.type; | 2736 var type = element.enclosingElement.type; |
2708 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 2737 var member = _emitMemberName(name, |
| 2738 isStatic: isStatic, type: type, element: element); |
2709 | 2739 |
2710 if (isStatic) { | 2740 if (isStatic) { |
2711 var dynType = _emitStaticAccess(type); | 2741 var dynType = _emitStaticAccess(type); |
2712 return new JS.PropertyAccess(dynType, member); | 2742 return new JS.PropertyAccess(dynType, member); |
2713 } | 2743 } |
2714 | 2744 |
2715 // For instance members, we add implicit-this. | 2745 // For instance members, we add implicit-this. |
2716 // For method tear-offs, we ensure it's a bound method. | 2746 // For method tear-offs, we ensure it's a bound method. |
2717 var tearOff = element is MethodElement && !inInvocationContext(node); | 2747 var tearOff = element is MethodElement && !inInvocationContext(node); |
2718 if (tearOff) return _callHelper('bind(this, #)', member); | 2748 if (tearOff) return _callHelper('bind(this, #)', member); |
(...skipping 545 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3264 | 3294 |
3265 /// Emits assignment to a static field element or property. | 3295 /// Emits assignment to a static field element or property. |
3266 JS.Expression _emitSetStaticProperty( | 3296 JS.Expression _emitSetStaticProperty( |
3267 Expression lhs, Element element, Expression rhs) { | 3297 Expression lhs, Element element, Expression rhs) { |
3268 // For static methods, we add the raw type name, without generics or | 3298 // For static methods, we add the raw type name, without generics or |
3269 // library prefix. We don't need those because static calls can't use | 3299 // library prefix. We don't need those because static calls can't use |
3270 // the generic type. | 3300 // the generic type. |
3271 ClassElement classElement = element.enclosingElement; | 3301 ClassElement classElement = element.enclosingElement; |
3272 var type = classElement.type; | 3302 var type = classElement.type; |
3273 var dynType = _emitStaticAccess(type); | 3303 var dynType = _emitStaticAccess(type); |
3274 var member = _emitMemberName(element.name, isStatic: true, type: type); | 3304 var member = _emitMemberName(element.name, |
| 3305 isStatic: true, type: type, element: element); |
3275 return _visit(rhs).toAssignExpression( | 3306 return _visit(rhs).toAssignExpression( |
3276 annotate(new JS.PropertyAccess(dynType, member), lhs)); | 3307 annotate(new JS.PropertyAccess(dynType, member), lhs)); |
3277 } | 3308 } |
3278 | 3309 |
3279 /// Emits an assignment to the [element] property of instance referenced by | 3310 /// Emits an assignment to the [element] property of instance referenced by |
3280 /// [jsTarget]. | 3311 /// [jsTarget]. |
3281 JS.Expression _emitWriteInstanceProperty(Expression lhs, | 3312 JS.Expression _emitWriteInstanceProperty(Expression lhs, |
3282 JS.Expression jsTarget, Element element, JS.Expression value) { | 3313 JS.Expression jsTarget, Element element, JS.Expression value) { |
3283 String memberName = element.name; | 3314 String memberName = element.name; |
3284 var type = (element.enclosingElement as ClassElement).type; | 3315 var type = (element.enclosingElement as ClassElement).type; |
3285 var name = _emitMemberName(memberName, type: type); | 3316 var name = _emitMemberName(memberName, type: type, element: element); |
3286 return value.toAssignExpression( | 3317 return value.toAssignExpression( |
3287 annotate(new JS.PropertyAccess(jsTarget, name), lhs)); | 3318 annotate(new JS.PropertyAccess(jsTarget, name), lhs)); |
3288 } | 3319 } |
3289 | 3320 |
3290 JS.Expression _emitSetSuper(Expression lhs, SuperExpression target, | 3321 JS.Expression _emitSetSuper(Expression lhs, SuperExpression target, |
3291 SimpleIdentifier id, Expression rhs) { | 3322 SimpleIdentifier id, Expression rhs) { |
3292 // TODO(sra): Determine whether and access helper is required for the | 3323 // TODO(sra): Determine whether and access helper is required for the |
3293 // setter. For now fall back on the r-value path. | 3324 // setter. For now fall back on the r-value path. |
3294 return _visit(rhs).toAssignExpression(_visit(lhs)); | 3325 return _visit(rhs).toAssignExpression(_visit(lhs)); |
3295 } | 3326 } |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3423 | 3454 |
3424 JS.Expression _emitTarget(Expression target, Element element, bool isStatic) { | 3455 JS.Expression _emitTarget(Expression target, Element element, bool isStatic) { |
3425 if (isStatic) { | 3456 if (isStatic) { |
3426 if (element is ConstructorElement) { | 3457 if (element is ConstructorElement) { |
3427 return _emitConstructorAccess(element.enclosingElement.type); | 3458 return _emitConstructorAccess(element.enclosingElement.type); |
3428 } | 3459 } |
3429 if (element is ExecutableElement) { | 3460 if (element is ExecutableElement) { |
3430 return _emitStaticAccess( | 3461 return _emitStaticAccess( |
3431 (element.enclosingElement as ClassElement).type); | 3462 (element.enclosingElement as ClassElement).type); |
3432 } | 3463 } |
| 3464 if (element is FieldElement) { |
| 3465 return _emitStaticAccess(element.enclosingElement.type); |
| 3466 } |
3433 } | 3467 } |
3434 return _visit(target); | 3468 return _visit(target); |
3435 } | 3469 } |
3436 | 3470 |
3437 /// Emits a (possibly generic) instance method call. | 3471 /// Emits a (possibly generic) instance, or static method call. |
3438 JS.Expression _emitMethodCallInternal( | 3472 JS.Expression _emitMethodCallInternal( |
3439 Expression target, | 3473 Expression target, |
3440 MethodInvocation node, | 3474 MethodInvocation node, |
3441 List<JS.Expression> args, | 3475 List<JS.Expression> args, |
3442 List<JS.Expression> typeArgs) { | 3476 List<JS.Expression> typeArgs) { |
3443 var type = getStaticType(target); | 3477 var type = getStaticType(target); |
3444 var name = node.methodName.name; | |
3445 var element = node.methodName.staticElement; | 3478 var element = node.methodName.staticElement; |
3446 bool isStatic = element is ExecutableElement && element.isStatic; | 3479 bool isStatic = element is ExecutableElement && element.isStatic; |
3447 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); | 3480 var name = node.methodName.name; |
| 3481 var memberName = |
| 3482 _emitMemberName(name, type: type, isStatic: isStatic, element: element); |
3448 | 3483 |
3449 JS.Expression jsTarget = _emitTarget(target, element, isStatic); | 3484 JS.Expression jsTarget = _emitTarget(target, element, isStatic); |
3450 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { | 3485 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { |
3451 if (_inWhitelistCode(target)) { | 3486 if (_inWhitelistCode(target)) { |
3452 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 3487 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
3453 var l = _visit(_bindValue(vars, 'l', target)); | 3488 var l = _visit(_bindValue(vars, 'l', target)); |
3454 jsTarget = new JS.MetaLet(vars, [ | 3489 jsTarget = new JS.MetaLet(vars, [ |
3455 js.call('(#[(#[#._extensionType]) ? #[#] : #]).bind(#)', [ | 3490 js.call('(#[(#[#._extensionType]) ? #[#] : #]).bind(#)', [ |
3456 l, | 3491 l, |
3457 l, | 3492 l, |
(...skipping 1342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4800 return _emitFunctionTypeArguments(type, instantiated); | 4835 return _emitFunctionTypeArguments(type, instantiated); |
4801 } | 4836 } |
4802 | 4837 |
4803 JS.LiteralString _emitDynamicOperationName(String name) => | 4838 JS.LiteralString _emitDynamicOperationName(String name) => |
4804 js.string(options.replCompile ? '${name}Repl' : name); | 4839 js.string(options.replCompile ? '${name}Repl' : name); |
4805 | 4840 |
4806 JS.Expression _emitAccessInternal(Expression target, Element member, | 4841 JS.Expression _emitAccessInternal(Expression target, Element member, |
4807 String memberName, List<JS.Expression> typeArgs) { | 4842 String memberName, List<JS.Expression> typeArgs) { |
4808 bool isStatic = member is ClassMemberElement && member.isStatic; | 4843 bool isStatic = member is ClassMemberElement && member.isStatic; |
4809 var name = _emitMemberName(memberName, | 4844 var name = _emitMemberName(memberName, |
4810 type: getStaticType(target), isStatic: isStatic); | 4845 type: getStaticType(target), isStatic: isStatic, element: member); |
4811 if (isDynamicInvoke(target)) { | 4846 if (isDynamicInvoke(target)) { |
4812 if (_inWhitelistCode(target)) { | 4847 if (_inWhitelistCode(target)) { |
4813 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 4848 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
4814 var l = _visit(_bindValue(vars, 'l', target)); | 4849 var l = _visit(_bindValue(vars, 'l', target)); |
4815 return new JS.MetaLet(vars, [ | 4850 return new JS.MetaLet(vars, [ |
4816 js.call('(#[#._extensionType]) ? #[#[#]] : #.#', | 4851 js.call('(#[#._extensionType]) ? #[#[#]] : #.#', |
4817 [l, _runtimeModule, l, _extensionSymbolsModule, name, l, name]) | 4852 [l, _runtimeModule, l, _extensionSymbolsModule, name, l, name]) |
4818 ]); | 4853 ]); |
4819 } | 4854 } |
4820 return _callHelper('#(#, #)', | 4855 return _callHelper('#(#, #)', |
(...skipping 598 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5419 /// | 5454 /// |
5420 /// This follows the same pattern as ECMAScript 6 Map: | 5455 /// This follows the same pattern as ECMAScript 6 Map: |
5421 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> | 5456 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> |
5422 /// | 5457 /// |
5423 /// Unary minus looks like: `x._negate()`. | 5458 /// Unary minus looks like: `x._negate()`. |
5424 /// | 5459 /// |
5425 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 5460 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
5426 /// helper, that checks for null. The user defined method is called '=='. | 5461 /// helper, that checks for null. The user defined method is called '=='. |
5427 /// | 5462 /// |
5428 JS.Expression _emitMemberName(String name, | 5463 JS.Expression _emitMemberName(String name, |
5429 {DartType type, bool isStatic: false, bool useExtension}) { | 5464 {DartType type, |
5430 // Static members skip the rename steps. | 5465 bool isStatic: false, |
5431 if (isStatic) return _propertyName(name); | 5466 bool useExtension, |
| 5467 Element element}) { |
| 5468 // Static members skip the rename steps and may require JS interop renames. |
| 5469 if (isStatic) { |
| 5470 return _emitJSInteropStaticMemberName(element) ?? _propertyName(name); |
| 5471 } |
5432 | 5472 |
5433 if (name.startsWith('_')) { | 5473 if (name.startsWith('_')) { |
5434 return _emitPrivateNameSymbol(currentLibrary, name); | 5474 return _emitPrivateNameSymbol(currentLibrary, name); |
5435 } | 5475 } |
5436 | 5476 |
5437 // When generating synthetic names, we use _ as the prefix, since Dart names | 5477 // When generating synthetic names, we use _ as the prefix, since Dart names |
5438 // won't have this (eliminated above), nor will static names reach here. | 5478 // won't have this (eliminated above), nor will static names reach here. |
5439 switch (name) { | 5479 switch (name) { |
5440 case '[]': | 5480 case '[]': |
5441 name = '_get'; | 5481 name = '_get'; |
(...skipping 275 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5717 var targetIdentifier = target as SimpleIdentifier; | 5757 var targetIdentifier = target as SimpleIdentifier; |
5718 | 5758 |
5719 if (targetIdentifier.staticElement is! PrefixElement) return false; | 5759 if (targetIdentifier.staticElement is! PrefixElement) return false; |
5720 var prefix = targetIdentifier.staticElement as PrefixElement; | 5760 var prefix = targetIdentifier.staticElement as PrefixElement; |
5721 | 5761 |
5722 // The library the prefix is referring to must come from a deferred import. | 5762 // The library the prefix is referring to must come from a deferred import. |
5723 var containingLibrary = (target.root as CompilationUnit).element.library; | 5763 var containingLibrary = (target.root as CompilationUnit).element.library; |
5724 var imports = containingLibrary.getImportsWithPrefix(prefix); | 5764 var imports = containingLibrary.getImportsWithPrefix(prefix); |
5725 return imports.length == 1 && imports[0].isDeferred; | 5765 return imports.length == 1 && imports[0].isDeferred; |
5726 } | 5766 } |
OLD | NEW |