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 // 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 |
3 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
4 | 5 |
5 import 'dart:collection' show HashMap, HashSet; | 6 import 'dart:collection' show HashMap, HashSet; |
6 import 'dart:math' show min, max; | 7 import 'dart:math' show min, max; |
7 | 8 |
8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
9 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 11 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
11 import 'package:analyzer/dart/element/element.dart'; | 12 import 'package:analyzer/dart/element/element.dart'; |
(...skipping 10 matching lines...) Expand all Loading... |
22 show StrongTypeSystemImpl; | 23 show StrongTypeSystemImpl; |
23 import 'package:analyzer/src/summary/idl.dart' show UnlinkedUnit; | 24 import 'package:analyzer/src/summary/idl.dart' show UnlinkedUnit; |
24 import 'package:analyzer/src/summary/link.dart' as summary_link; | 25 import 'package:analyzer/src/summary/link.dart' as summary_link; |
25 import 'package:analyzer/src/summary/package_bundle_reader.dart'; | 26 import 'package:analyzer/src/summary/package_bundle_reader.dart'; |
26 import 'package:analyzer/src/summary/summarize_ast.dart' | 27 import 'package:analyzer/src/summary/summarize_ast.dart' |
27 show serializeAstUnlinked; | 28 show serializeAstUnlinked; |
28 import 'package:analyzer/src/summary/summarize_elements.dart' | 29 import 'package:analyzer/src/summary/summarize_elements.dart' |
29 show PackageBundleAssembler; | 30 show PackageBundleAssembler; |
30 import 'package:analyzer/src/summary/summary_sdk.dart'; | 31 import 'package:analyzer/src/summary/summary_sdk.dart'; |
31 import 'package:analyzer/src/task/strong/ast_properties.dart' | 32 import 'package:analyzer/src/task/strong/ast_properties.dart' |
32 show isDynamicInvoke, setIsDynamicInvoke; | 33 show isDynamicInvoke, setIsDynamicInvoke, getImplicitAssignmentCast; |
33 import 'package:path/path.dart' show separator; | 34 import 'package:path/path.dart' show separator; |
34 | 35 |
35 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 36 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
36 import '../js_ast/js_ast.dart' as JS; | 37 import '../js_ast/js_ast.dart' as JS; |
37 import '../js_ast/js_ast.dart' show js; | 38 import '../js_ast/js_ast.dart' show js; |
38 import 'ast_builder.dart' show AstBuilder; | 39 import 'ast_builder.dart' show AstBuilder; |
39 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; | 40 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; |
40 import 'element_helpers.dart'; | 41 import 'element_helpers.dart'; |
41 import 'element_loader.dart' show ElementLoader; | 42 import 'element_loader.dart' show ElementLoader; |
42 import 'extension_types.dart' show ExtensionTypeSet; | 43 import 'extension_types.dart' show ExtensionTypeSet; |
(...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
552 return js.call('dart.asInt(#)', jsFrom); | 553 return js.call('dart.asInt(#)', jsFrom); |
553 } | 554 } |
554 | 555 |
555 // A no-op in JavaScript. | 556 // A no-op in JavaScript. |
556 return jsFrom; | 557 return jsFrom; |
557 } | 558 } |
558 | 559 |
559 var type = _emitType(to, | 560 var type = _emitType(to, |
560 nameType: options.nameTypeTests || options.hoistTypeTests, | 561 nameType: options.nameTypeTests || options.hoistTypeTests, |
561 hoistType: options.hoistTypeTests); | 562 hoistType: options.hoistTypeTests); |
562 if (isReifiedCoercion(node)) { | 563 if (CoercionReifier.isImplicitCast(node)) { |
563 return js.call('#._check(#)', [type, jsFrom]); | 564 return js.call('#._check(#)', [type, jsFrom]); |
564 } else { | 565 } else { |
565 return js.call('#.as(#)', [type, jsFrom]); | 566 return js.call('#.as(#)', [type, jsFrom]); |
566 } | 567 } |
567 } | 568 } |
568 | 569 |
569 bool isReifiedCoercion(AstNode node) { | |
570 // TODO(sra): Find a better way to recognize reified coercion, since we | |
571 // can't set the isSynthetic attribute. | |
572 return (node is AsExpression) && (node.asOperator.offset == 0); | |
573 } | |
574 | |
575 @override | 570 @override |
576 visitIsExpression(IsExpression node) { | 571 visitIsExpression(IsExpression node) { |
577 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. | 572 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
578 JS.Expression result; | 573 JS.Expression result; |
579 var type = node.type.type; | 574 var type = node.type.type; |
580 var lhs = _visit(node.expression); | 575 var lhs = _visit(node.expression); |
581 var typeofName = _jsTypeofName(type); | 576 var typeofName = _jsTypeofName(type); |
582 if (typeofName != null) { | 577 if (typeofName != null) { |
583 result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]); | 578 result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]); |
584 } else { | 579 } else { |
(...skipping 1338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1923 if (redirect != null) { | 1918 if (redirect != null) { |
1924 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; | 1919 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; |
1925 // Pass along all arguments verbatim, and let the callee handle them. | 1920 // Pass along all arguments verbatim, and let the callee handle them. |
1926 // TODO(jmesserly): we'll need something different once we have | 1921 // TODO(jmesserly): we'll need something different once we have |
1927 // rest/spread support, but this should work for now. | 1922 // rest/spread support, but this should work for now. |
1928 var params = | 1923 var params = |
1929 visitFormalParameterList(node.parameters, destructure: false); | 1924 visitFormalParameterList(node.parameters, destructure: false); |
1930 | 1925 |
1931 var fun = new JS.Fun( | 1926 var fun = new JS.Fun( |
1932 params, | 1927 params, |
1933 js.statement( | 1928 js.statement('{ return $newKeyword #(#); }', |
1934 '{ return $newKeyword #(#); }', [_visit(redirect), params]), | 1929 [_visit(redirect) as JS.Node, params]), |
1935 returnType: returnType); | 1930 returnType: returnType); |
1936 return annotate( | 1931 return annotate( |
1937 new JS.Method(name, fun, isStatic: true), node, node.element); | 1932 new JS.Method(name, fun, isStatic: true), node, node.element); |
1938 } | 1933 } |
1939 | 1934 |
1940 // For const constructors we need to ensure default values are | 1935 // For const constructors we need to ensure default values are |
1941 // available for use by top-level constant initializers. | 1936 // available for use by top-level constant initializers. |
1942 ClassDeclaration cls = node.parent; | 1937 ClassDeclaration cls = node.parent; |
1943 if (node.constKeyword != null) _loader.startTopLevel(cls.element); | 1938 if (node.constKeyword != null) _loader.startTopLevel(cls.element); |
1944 var params = visitFormalParameterList(node.parameters); | 1939 var params = visitFormalParameterList(node.parameters); |
(...skipping 1062 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3007 var t = _bindValue(vars, 't', x, context: x); | 3002 var t = _bindValue(vars, 't', x, context: x); |
3008 return new JS.MetaLet(vars, [ | 3003 return new JS.MetaLet(vars, [ |
3009 js.call('# == null ? # : #', [_visit(t), _emitSet(x, right), _visit(t)]) | 3004 js.call('# == null ? # : #', [_visit(t), _emitSet(x, right), _visit(t)]) |
3010 ]); | 3005 ]); |
3011 } | 3006 } |
3012 | 3007 |
3013 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions | 3008 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions |
3014 // (for example, x is IndexExpression) we evaluate those once. | 3009 // (for example, x is IndexExpression) we evaluate those once. |
3015 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 3010 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
3016 var lhs = _bindLeftHandSide(vars, left, context: context); | 3011 var lhs = _bindLeftHandSide(vars, left, context: context); |
3017 var inc = AstBuilder.binaryExpression(lhs, op, right); | 3012 Expression inc = AstBuilder.binaryExpression(lhs, op, right) |
3018 inc.staticElement = element; | 3013 ..staticElement = element |
3019 inc.staticType = getStaticType(left); | 3014 ..staticType = getStaticType(lhs); |
| 3015 |
| 3016 var castTo = getImplicitAssignmentCast(left); |
| 3017 if (castTo != null) inc = CoercionReifier.castExpression(inc, castTo); |
3020 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); | 3018 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); |
3021 } | 3019 } |
3022 | 3020 |
3023 JS.Expression _emitSet(Expression lhs, Expression rhs) { | 3021 JS.Expression _emitSet(Expression lhs, Expression rhs) { |
3024 if (lhs is IndexExpression) { | 3022 if (lhs is IndexExpression) { |
3025 var target = _getTarget(lhs); | 3023 var target = _getTarget(lhs); |
3026 if (_useNativeJsIndexer(target.staticType)) { | 3024 if (_useNativeJsIndexer(target.staticType)) { |
3027 return js | 3025 return js |
3028 .call('#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); | 3026 .call('#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); |
3029 } | 3027 } |
(...skipping 1507 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4537 _createTemporary('_', nodeTarget.staticType, nullable: false); | 4535 _createTemporary('_', nodeTarget.staticType, nullable: false); |
4538 var baseNode = _stripNullAwareOp(node, param); | 4536 var baseNode = _stripNullAwareOp(node, param); |
4539 tail.add( | 4537 tail.add( |
4540 new JS.ArrowFun(<JS.Parameter>[_visit(param)], _visit(baseNode))); | 4538 new JS.ArrowFun(<JS.Parameter>[_visit(param)], _visit(baseNode))); |
4541 node = nodeTarget; | 4539 node = nodeTarget; |
4542 } else { | 4540 } else { |
4543 break; | 4541 break; |
4544 } | 4542 } |
4545 } | 4543 } |
4546 if (tail.isEmpty) return _visit(node); | 4544 if (tail.isEmpty) return _visit(node); |
4547 return js.call('dart.nullSafe(#, #)', [_visit(node), tail.reversed]); | 4545 return js.call( |
| 4546 'dart.nullSafe(#, #)', [_visit(node) as JS.Expression, tail.reversed]); |
4548 } | 4547 } |
4549 | 4548 |
4550 static Token _getOperator(Expression node) { | 4549 static Token _getOperator(Expression node) { |
4551 if (node is PropertyAccess) return node.operator; | 4550 if (node is PropertyAccess) return node.operator; |
4552 if (node is MethodInvocation) return node.operator; | 4551 if (node is MethodInvocation) return node.operator; |
4553 return null; | 4552 return null; |
4554 } | 4553 } |
4555 | 4554 |
4556 // TODO(jmesserly): this is dropping source location. | 4555 // TODO(jmesserly): this is dropping source location. |
4557 Expression _stripNullAwareOp(Expression node, Expression newTarget) { | 4556 Expression _stripNullAwareOp(Expression node, Expression newTarget) { |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4690 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 4689 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
4691 var l = _visit(_bindValue(vars, 'l', target)); | 4690 var l = _visit(_bindValue(vars, 'l', target)); |
4692 return new JS.MetaLet(vars, [ | 4691 return new JS.MetaLet(vars, [ |
4693 js.call('(#[(#[dart._extensionType]) ? dartx[#] : #]).call(#, #)', | 4692 js.call('(#[(#[dart._extensionType]) ? dartx[#] : #]).call(#, #)', |
4694 [l, l, memberName, memberName, l, _visitList(args)]) | 4693 [l, l, memberName, memberName, l, _visitList(args)]) |
4695 ]); | 4694 ]); |
4696 } | 4695 } |
4697 // dynamic dispatch | 4696 // dynamic dispatch |
4698 var dynamicHelper = const {'[]': 'dindex', '[]=': 'dsetindex'}[name]; | 4697 var dynamicHelper = const {'[]': 'dindex', '[]=': 'dsetindex'}[name]; |
4699 if (dynamicHelper != null) { | 4698 if (dynamicHelper != null) { |
4700 return js.call( | 4699 return js.call('dart.$dynamicHelper(#, #)', |
4701 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); | 4700 [_visit(target) as JS.Expression, _visitList(args)]); |
4702 } else { | 4701 } else { |
4703 return js.call('dart.dsend(#, #, #)', | 4702 return js.call('dart.dsend(#, #, #)', |
4704 [_visit(target), memberName, _visitList(args)]); | 4703 [_visit(target), memberName, _visitList(args)]); |
4705 } | 4704 } |
4706 } | 4705 } |
4707 | 4706 |
4708 // Generic dispatch to a statically known method. | 4707 // Generic dispatch to a statically known method. |
4709 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | 4708 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); |
4710 } | 4709 } |
4711 | 4710 |
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5045 | 5044 |
5046 if (isConst) return _cacheConst(emitList); | 5045 if (isConst) return _cacheConst(emitList); |
5047 return emitList(); | 5046 return emitList(); |
5048 } | 5047 } |
5049 | 5048 |
5050 @override | 5049 @override |
5051 visitMapLiteral(MapLiteral node) { | 5050 visitMapLiteral(MapLiteral node) { |
5052 // TODO(jmesserly): we can likely make these faster. | 5051 // TODO(jmesserly): we can likely make these faster. |
5053 JS.Expression emitMap() { | 5052 JS.Expression emitMap() { |
5054 var entries = node.entries; | 5053 var entries = node.entries; |
5055 var mapArguments = null; | 5054 Object mapArguments = null; |
5056 var type = node.staticType as InterfaceType; | 5055 var type = node.staticType as InterfaceType; |
5057 var typeArgs = type.typeArguments; | 5056 var typeArgs = type.typeArguments; |
5058 var reifyTypeArgs = typeArgs.any((t) => !t.isDynamic); | 5057 var reifyTypeArgs = typeArgs.any((t) => !t.isDynamic); |
5059 if (entries.isEmpty && !reifyTypeArgs) { | 5058 if (entries.isEmpty && !reifyTypeArgs) { |
5060 mapArguments = []; | 5059 mapArguments = []; |
5061 } else if (entries.every((e) => e.key is StringLiteral)) { | 5060 } else if (entries.every((e) => e.key is StringLiteral)) { |
5062 // Use JS object literal notation if possible, otherwise use an array. | 5061 // Use JS object literal notation if possible, otherwise use an array. |
5063 // We could do this any time all keys are non-nullable String type. | 5062 // We could do this any time all keys are non-nullable String type. |
5064 // For now, support StringLiteral as the common non-nullable String case
. | 5063 // For now, support StringLiteral as the common non-nullable String case
. |
5065 var props = <JS.Property>[]; | 5064 var props = <JS.Property>[]; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5167 if (node is BinaryExpression) { | 5166 if (node is BinaryExpression) { |
5168 JS.Expression shortCircuit(String code) { | 5167 JS.Expression shortCircuit(String code) { |
5169 return finish(js.call(code, | 5168 return finish(js.call(code, |
5170 [_visitTest(node.leftOperand), _visitTest(node.rightOperand)])); | 5169 [_visitTest(node.leftOperand), _visitTest(node.rightOperand)])); |
5171 } | 5170 } |
5172 | 5171 |
5173 var op = node.operator.type.lexeme; | 5172 var op = node.operator.type.lexeme; |
5174 if (op == '&&') return shortCircuit('# && #'); | 5173 if (op == '&&') return shortCircuit('# && #'); |
5175 if (op == '||') return shortCircuit('# || #'); | 5174 if (op == '||') return shortCircuit('# || #'); |
5176 } | 5175 } |
5177 if (isReifiedCoercion(node)) { | 5176 if (node is AsExpression && CoercionReifier.isImplicitCast(node)) { |
5178 AsExpression asNode = node; | 5177 assert(node.staticType == types.boolType); |
5179 assert(asNode.staticType == types.boolType); | 5178 return js.call('dart.test(#)', _visit(node.expression)); |
5180 return js.call('dart.test(#)', _visit(asNode.expression)); | |
5181 } | 5179 } |
5182 JS.Expression result = _visit(node); | 5180 JS.Expression result = _visit(node); |
5183 if (isNullable(node)) result = js.call('dart.test(#)', result); | 5181 if (isNullable(node)) result = js.call('dart.test(#)', result); |
5184 return result; | 5182 return result; |
5185 } | 5183 } |
5186 | 5184 |
5187 /// Like [_emitMemberName], but for declaration sites. | 5185 /// Like [_emitMemberName], but for declaration sites. |
5188 /// | 5186 /// |
5189 /// Unlike call sites, we always have an element available, so we can use it | 5187 /// Unlike call sites, we always have an element available, so we can use it |
5190 /// directly rather than computing the relevant options for [_emitMemberName]. | 5188 /// directly rather than computing the relevant options for [_emitMemberName]. |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5451 if (uri.scheme == 'package') { | 5449 if (uri.scheme == 'package') { |
5452 // Strip the package name. | 5450 // Strip the package name. |
5453 // TODO(vsm): This is not unique if an escaped '/'appears in a filename. | 5451 // TODO(vsm): This is not unique if an escaped '/'appears in a filename. |
5454 // E.g., "foo/bar.dart" and "foo$47bar.dart" would collide. | 5452 // E.g., "foo/bar.dart" and "foo$47bar.dart" would collide. |
5455 qualifiedPath = uri.pathSegments.skip(1).join(separator); | 5453 qualifiedPath = uri.pathSegments.skip(1).join(separator); |
5456 } else if (uri.toFilePath().startsWith(libraryRoot)) { | 5454 } else if (uri.toFilePath().startsWith(libraryRoot)) { |
5457 qualifiedPath = | 5455 qualifiedPath = |
5458 uri.path.substring(libraryRoot.length).replaceAll('/', separator); | 5456 uri.path.substring(libraryRoot.length).replaceAll('/', separator); |
5459 } else { | 5457 } else { |
5460 // We don't have a unique name. | 5458 // We don't have a unique name. |
5461 throw 'Invalid library root. $libraryRoot does not contain ${uri.toFilePath(
)}'; | 5459 throw 'Invalid library root. $libraryRoot does not contain ${uri |
| 5460 .toFilePath()}'; |
5462 } | 5461 } |
5463 return pathToJSIdentifier(qualifiedPath); | 5462 return pathToJSIdentifier(qualifiedPath); |
5464 } | 5463 } |
5465 | 5464 |
5466 /// Shorthand for identifier-like property names. | 5465 /// Shorthand for identifier-like property names. |
5467 /// For now, we emit them as strings and the printer restores them to | 5466 /// For now, we emit them as strings and the printer restores them to |
5468 /// identifiers if it can. | 5467 /// identifiers if it can. |
5469 // TODO(jmesserly): avoid the round tripping through quoted form. | 5468 // TODO(jmesserly): avoid the round tripping through quoted form. |
5470 JS.LiteralString _propertyName(String name) => js.string(name, "'"); | 5469 JS.LiteralString _propertyName(String name) => js.string(name, "'"); |
5471 | 5470 |
5472 // TODO(jacobr): we would like to do something like the following | 5471 // TODO(jacobr): we would like to do something like the following |
5473 // but we don't have summary support yet. | 5472 // but we don't have summary support yet. |
5474 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 5473 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
5475 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 5474 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
5476 | 5475 |
5477 /// A special kind of element created by the compiler, signifying a temporary | 5476 /// A special kind of element created by the compiler, signifying a temporary |
5478 /// variable. These objects use instance equality, and should be shared | 5477 /// variable. These objects use instance equality, and should be shared |
5479 /// everywhere in the tree where they are treated as the same variable. | 5478 /// everywhere in the tree where they are treated as the same variable. |
5480 class TemporaryVariableElement extends LocalVariableElementImpl { | 5479 class TemporaryVariableElement extends LocalVariableElementImpl { |
5481 final JS.Expression jsVariable; | 5480 final JS.Expression jsVariable; |
| 5481 |
5482 TemporaryVariableElement.forNode(Identifier name, this.jsVariable) | 5482 TemporaryVariableElement.forNode(Identifier name, this.jsVariable) |
5483 : super.forNode(name); | 5483 : super.forNode(name); |
5484 | 5484 |
5485 int get hashCode => identityHashCode(this); | 5485 int get hashCode => identityHashCode(this); |
| 5486 |
5486 bool operator ==(Object other) => identical(this, other); | 5487 bool operator ==(Object other) => identical(this, other); |
5487 } | 5488 } |
5488 | 5489 |
5489 bool isLibraryPrefix(Expression node) => | 5490 bool isLibraryPrefix(Expression node) => |
5490 node is SimpleIdentifier && node.staticElement is PrefixElement; | 5491 node is SimpleIdentifier && node.staticElement is PrefixElement; |
5491 | 5492 |
5492 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 5493 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
5493 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 5494 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
5494 | 5495 |
5495 bool _isDartRuntime(LibraryElement l) => | 5496 bool _isDartRuntime(LibraryElement l) => |
5496 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 5497 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |