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

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: Fix handling of static names for JS interop and improve test coverage. 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 || e.library == null ||
355 findAnnotation(e.library, isPublicJSAnnotation) == null) {
356 return null;
357 }
358 if (e is PropertyInducingElement) {
359 // Assume properties have consistent JS names for getters and setters.
360 return _jsInteropStaticMemberName(e.getter, name: e.name) ??
361 _jsInteropStaticMemberName(e.setter, name: e.name);
362 }
363 if (e is ExecutableElement &&
364 e.isExternal &&
365 findAnnotation(e, isPublicJSAnnotation) != null) {
366 return getAnnotationName(e, isPublicJSAnnotation) ?? name ?? e.name;
367 }
368 return null;
369 }
370
371 JS.Expression _emitJSInteropStaticMemberName(Element e) {
372 var name = _jsInteropStaticMemberName(e);
373 if (name == null) return null;
374 // We do not support statics names with JS annotations containing dots.
375 // See https://github.com/dart-lang/sdk/issues/27926
376 assert(!name.contains('.'));
Jennifer Messerly 2016/11/29 20:54:52 This should be an error or a throw, rather than an
Jacob 2016/11/29 21:37:33 Done.
377 return js.string(name);
378 }
379
356 /// Flattens blocks in [items] to a single list. 380 /// Flattens blocks in [items] to a single list.
357 /// 381 ///
358 /// This will not flatten blocks that are marked as being scopes. 382 /// This will not flatten blocks that are marked as being scopes.
359 void _copyAndFlattenBlocks( 383 void _copyAndFlattenBlocks(
360 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { 384 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) {
361 for (var item in items) { 385 for (var item in items) {
362 if (item is JS.Block && !item.isScope) { 386 if (item is JS.Block && !item.isScope) {
363 _copyAndFlattenBlocks(result, item.statements); 387 _copyAndFlattenBlocks(result, item.statements);
364 } else { 388 } else {
365 result.add(item); 389 result.add(item);
(...skipping 2332 matching lines...) Expand 10 before | Expand all | Expand 10 after
2698 return _emitTopLevelName(element); 2722 return _emitTopLevelName(element);
2699 } 2723 }
2700 2724
2701 var name = element.name; 2725 var name = element.name;
2702 2726
2703 // Unqualified class member. This could mean implicit-this, or implicit 2727 // Unqualified class member. This could mean implicit-this, or implicit
2704 // call to a static from the same class. 2728 // call to a static from the same class.
2705 if (element is ClassMemberElement && element is! ConstructorElement) { 2729 if (element is ClassMemberElement && element is! ConstructorElement) {
2706 bool isStatic = element.isStatic; 2730 bool isStatic = element.isStatic;
2707 var type = element.enclosingElement.type; 2731 var type = element.enclosingElement.type;
2708 var member = _emitMemberName(name, isStatic: isStatic, type: type); 2732 var member = _emitMemberName(name,
2733 isStatic: isStatic, type: type, element: element);
2709 2734
2710 if (isStatic) { 2735 if (isStatic) {
2711 var dynType = _emitStaticAccess(type); 2736 var dynType = _emitStaticAccess(type);
2712 return new JS.PropertyAccess(dynType, member); 2737 return new JS.PropertyAccess(dynType, member);
2713 } 2738 }
2714 2739
2715 // For instance members, we add implicit-this. 2740 // For instance members, we add implicit-this.
2716 // For method tear-offs, we ensure it's a bound method. 2741 // For method tear-offs, we ensure it's a bound method.
2717 var tearOff = element is MethodElement && !inInvocationContext(node); 2742 var tearOff = element is MethodElement && !inInvocationContext(node);
2718 if (tearOff) return _callHelper('bind(this, #)', member); 2743 if (tearOff) return _callHelper('bind(this, #)', member);
(...skipping 545 matching lines...) Expand 10 before | Expand all | Expand 10 after
3264 3289
3265 /// Emits assignment to a static field element or property. 3290 /// Emits assignment to a static field element or property.
3266 JS.Expression _emitSetStaticProperty( 3291 JS.Expression _emitSetStaticProperty(
3267 Expression lhs, Element element, Expression rhs) { 3292 Expression lhs, Element element, Expression rhs) {
3268 // For static methods, we add the raw type name, without generics or 3293 // 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 3294 // library prefix. We don't need those because static calls can't use
3270 // the generic type. 3295 // the generic type.
3271 ClassElement classElement = element.enclosingElement; 3296 ClassElement classElement = element.enclosingElement;
3272 var type = classElement.type; 3297 var type = classElement.type;
3273 var dynType = _emitStaticAccess(type); 3298 var dynType = _emitStaticAccess(type);
3274 var member = _emitMemberName(element.name, isStatic: true, type: type); 3299 var member = _emitMemberName(element.name,
3300 isStatic: true, type: type, element: element);
3275 return _visit(rhs).toAssignExpression( 3301 return _visit(rhs).toAssignExpression(
3276 annotate(new JS.PropertyAccess(dynType, member), lhs)); 3302 annotate(new JS.PropertyAccess(dynType, member), lhs));
3277 } 3303 }
3278 3304
3279 /// Emits an assignment to the [element] property of instance referenced by 3305 /// Emits an assignment to the [element] property of instance referenced by
3280 /// [jsTarget]. 3306 /// [jsTarget].
3281 JS.Expression _emitWriteInstanceProperty(Expression lhs, 3307 JS.Expression _emitWriteInstanceProperty(Expression lhs,
3282 JS.Expression jsTarget, Element element, JS.Expression value) { 3308 JS.Expression jsTarget, Element element, JS.Expression value) {
3283 String memberName = element.name; 3309 String memberName = element.name;
3284 var type = (element.enclosingElement as ClassElement).type; 3310 var type = (element.enclosingElement as ClassElement).type;
3285 var name = _emitMemberName(memberName, type: type); 3311 var name = _emitMemberName(memberName, type: type, element: element);
3286 return value.toAssignExpression( 3312 return value.toAssignExpression(
3287 annotate(new JS.PropertyAccess(jsTarget, name), lhs)); 3313 annotate(new JS.PropertyAccess(jsTarget, name), lhs));
3288 } 3314 }
3289 3315
3290 JS.Expression _emitSetSuper(Expression lhs, SuperExpression target, 3316 JS.Expression _emitSetSuper(Expression lhs, SuperExpression target,
3291 SimpleIdentifier id, Expression rhs) { 3317 SimpleIdentifier id, Expression rhs) {
3292 // TODO(sra): Determine whether and access helper is required for the 3318 // TODO(sra): Determine whether and access helper is required for the
3293 // setter. For now fall back on the r-value path. 3319 // setter. For now fall back on the r-value path.
3294 return _visit(rhs).toAssignExpression(_visit(lhs)); 3320 return _visit(rhs).toAssignExpression(_visit(lhs));
3295 } 3321 }
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
3423 3449
3424 JS.Expression _emitTarget(Expression target, Element element, bool isStatic) { 3450 JS.Expression _emitTarget(Expression target, Element element, bool isStatic) {
3425 if (isStatic) { 3451 if (isStatic) {
3426 if (element is ConstructorElement) { 3452 if (element is ConstructorElement) {
3427 return _emitConstructorAccess(element.enclosingElement.type); 3453 return _emitConstructorAccess(element.enclosingElement.type);
3428 } 3454 }
3429 if (element is ExecutableElement) { 3455 if (element is ExecutableElement) {
3430 return _emitStaticAccess( 3456 return _emitStaticAccess(
3431 (element.enclosingElement as ClassElement).type); 3457 (element.enclosingElement as ClassElement).type);
3432 } 3458 }
3459 if (element is FieldElement) {
3460 var enclosingElement = element.enclosingElement;
3461 if (enclosingElement is ClassElement) {
Jennifer Messerly 2016/11/29 20:54:52 the "is" check is unnecessary -- FieldElement is a
Jacob 2016/11/29 21:37:33 Surprised I ever thought that is check was needed.
3462 return _emitStaticAccess(enclosingElement.type);
3463 }
3464 }
3433 } 3465 }
3434 return _visit(target); 3466 return _visit(target);
3435 } 3467 }
3436 3468
3437 /// Emits a (possibly generic) instance method call. 3469 /// Emits a (possibly generic) instance, or static method call.
3438 JS.Expression _emitMethodCallInternal( 3470 JS.Expression _emitMethodCallInternal(
3439 Expression target, 3471 Expression target,
3440 MethodInvocation node, 3472 MethodInvocation node,
3441 List<JS.Expression> args, 3473 List<JS.Expression> args,
3442 List<JS.Expression> typeArgs) { 3474 List<JS.Expression> typeArgs) {
3443 var type = getStaticType(target); 3475 var type = getStaticType(target);
3444 var name = node.methodName.name;
3445 var element = node.methodName.staticElement; 3476 var element = node.methodName.staticElement;
3446 bool isStatic = element is ExecutableElement && element.isStatic; 3477 bool isStatic = element is ExecutableElement && element.isStatic;
3447 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); 3478 var name = node.methodName.name;
3479 var memberName =
3480 _emitMemberName(name, type: type, isStatic: isStatic, element: element);
3448 3481
3449 JS.Expression jsTarget = _emitTarget(target, element, isStatic); 3482 JS.Expression jsTarget = _emitTarget(target, element, isStatic);
3450 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { 3483 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) {
3451 if (_inWhitelistCode(target)) { 3484 if (_inWhitelistCode(target)) {
3452 var vars = <JS.MetaLetVariable, JS.Expression>{}; 3485 var vars = <JS.MetaLetVariable, JS.Expression>{};
3453 var l = _visit(_bindValue(vars, 'l', target)); 3486 var l = _visit(_bindValue(vars, 'l', target));
3454 jsTarget = new JS.MetaLet(vars, [ 3487 jsTarget = new JS.MetaLet(vars, [
3455 js.call('(#[(#[#._extensionType]) ? #[#] : #]).bind(#)', [ 3488 js.call('(#[(#[#._extensionType]) ? #[#] : #]).bind(#)', [
3456 l, 3489 l,
3457 l, 3490 l,
(...skipping 1342 matching lines...) Expand 10 before | Expand all | Expand 10 after
4800 return _emitFunctionTypeArguments(type, instantiated); 4833 return _emitFunctionTypeArguments(type, instantiated);
4801 } 4834 }
4802 4835
4803 JS.LiteralString _emitDynamicOperationName(String name) => 4836 JS.LiteralString _emitDynamicOperationName(String name) =>
4804 js.string(options.replCompile ? '${name}Repl' : name); 4837 js.string(options.replCompile ? '${name}Repl' : name);
4805 4838
4806 JS.Expression _emitAccessInternal(Expression target, Element member, 4839 JS.Expression _emitAccessInternal(Expression target, Element member,
4807 String memberName, List<JS.Expression> typeArgs) { 4840 String memberName, List<JS.Expression> typeArgs) {
4808 bool isStatic = member is ClassMemberElement && member.isStatic; 4841 bool isStatic = member is ClassMemberElement && member.isStatic;
4809 var name = _emitMemberName(memberName, 4842 var name = _emitMemberName(memberName,
4810 type: getStaticType(target), isStatic: isStatic); 4843 type: getStaticType(target), isStatic: isStatic, element: member);
4811 if (isDynamicInvoke(target)) { 4844 if (isDynamicInvoke(target)) {
4812 if (_inWhitelistCode(target)) { 4845 if (_inWhitelistCode(target)) {
4813 var vars = <JS.MetaLetVariable, JS.Expression>{}; 4846 var vars = <JS.MetaLetVariable, JS.Expression>{};
4814 var l = _visit(_bindValue(vars, 'l', target)); 4847 var l = _visit(_bindValue(vars, 'l', target));
4815 return new JS.MetaLet(vars, [ 4848 return new JS.MetaLet(vars, [
4816 js.call('(#[#._extensionType]) ? #[#[#]] : #.#', 4849 js.call('(#[#._extensionType]) ? #[#[#]] : #.#',
4817 [l, _runtimeModule, l, _extensionSymbolsModule, name, l, name]) 4850 [l, _runtimeModule, l, _extensionSymbolsModule, name, l, name])
4818 ]); 4851 ]);
4819 } 4852 }
4820 return _callHelper('#(#, #)', 4853 return _callHelper('#(#, #)',
(...skipping 598 matching lines...) Expand 10 before | Expand all | Expand 10 after
5419 /// 5452 ///
5420 /// This follows the same pattern as ECMAScript 6 Map: 5453 /// This follows the same pattern as ECMAScript 6 Map:
5421 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ Objects/Map> 5454 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ Objects/Map>
5422 /// 5455 ///
5423 /// Unary minus looks like: `x._negate()`. 5456 /// Unary minus looks like: `x._negate()`.
5424 /// 5457 ///
5425 /// Equality is a bit special, it is generated via the Dart `equals` runtime 5458 /// 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 '=='. 5459 /// helper, that checks for null. The user defined method is called '=='.
5427 /// 5460 ///
5428 JS.Expression _emitMemberName(String name, 5461 JS.Expression _emitMemberName(String name,
5429 {DartType type, bool isStatic: false, bool useExtension}) { 5462 {DartType type,
5430 // Static members skip the rename steps. 5463 bool isStatic: false,
5431 if (isStatic) return _propertyName(name); 5464 bool useExtension,
5465 Element element}) {
5466 // Static members skip the rename steps and may require JS interop renames.
5467 if (isStatic) {
5468 return _emitJSInteropStaticMemberName(element) ?? _propertyName(name);
5469 }
5432 5470
5433 if (name.startsWith('_')) { 5471 if (name.startsWith('_')) {
5434 return _emitPrivateNameSymbol(currentLibrary, name); 5472 return _emitPrivateNameSymbol(currentLibrary, name);
5435 } 5473 }
5436 5474
5437 // When generating synthetic names, we use _ as the prefix, since Dart names 5475 // 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. 5476 // won't have this (eliminated above), nor will static names reach here.
5439 switch (name) { 5477 switch (name) {
5440 case '[]': 5478 case '[]':
5441 name = '_get'; 5479 name = '_get';
(...skipping 275 matching lines...) Expand 10 before | Expand all | Expand 10 after
5717 var targetIdentifier = target as SimpleIdentifier; 5755 var targetIdentifier = target as SimpleIdentifier;
5718 5756
5719 if (targetIdentifier.staticElement is! PrefixElement) return false; 5757 if (targetIdentifier.staticElement is! PrefixElement) return false;
5720 var prefix = targetIdentifier.staticElement as PrefixElement; 5758 var prefix = targetIdentifier.staticElement as PrefixElement;
5721 5759
5722 // The library the prefix is referring to must come from a deferred import. 5760 // The library the prefix is referring to must come from a deferred import.
5723 var containingLibrary = (target.root as CompilationUnit).element.library; 5761 var containingLibrary = (target.root as CompilationUnit).element.library;
5724 var imports = containingLibrary.getImportsWithPrefix(prefix); 5762 var imports = containingLibrary.getImportsWithPrefix(prefix);
5725 return imports.length == 1 && imports[0].isDeferred; 5763 return imports.length == 1 && imports[0].isDeferred;
5726 } 5764 }
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