Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(429)

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/code_generator.dart

Issue 2536823003: Fix handling of static names for JS interop and improve test coverage. (Closed)
Patch Set: \ Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/dev_compiler/test/codegen/lib/html/js_typed_interop_rename_static_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/dev_compiler/test/codegen/lib/html/js_typed_interop_rename_static_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698