| 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 // 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 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
| 6 | 6 |
| 7 import 'dart:collection' show HashSet, HashMap; | 7 import 'dart:collection' show HashSet, HashMap; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 JS.Program emitLibrary(LibraryUnit library) { | 103 JS.Program emitLibrary(LibraryUnit library) { |
| 104 String jsDefaultValue = null; | 104 String jsDefaultValue = null; |
| 105 | 105 |
| 106 // Modify the AST to make coercions explicit. | 106 // Modify the AST to make coercions explicit. |
| 107 new CoercionReifier(library, compiler).reify(); | 107 new CoercionReifier(library, compiler).reify(); |
| 108 | 108 |
| 109 var unit = library.library; | 109 var unit = library.library; |
| 110 if (unit.directives.isNotEmpty) { | 110 if (unit.directives.isNotEmpty) { |
| 111 var libraryDir = unit.directives.first; | 111 var libraryDir = unit.directives.first; |
| 112 if (libraryDir is LibraryDirective) { | 112 if (libraryDir is LibraryDirective) { |
| 113 var jsName = findNodeAnnotation(libraryDir, _isJsNameAnnotation); | 113 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); |
| 114 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); | 114 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); |
| 115 } | 115 } |
| 116 } | 116 } |
| 117 if (jsDefaultValue == null) jsDefaultValue = '{}'; | 117 if (jsDefaultValue == null) jsDefaultValue = '{}'; |
| 118 | 118 |
| 119 // TODO(jmesserly): visit scriptTag, directives? | 119 // TODO(jmesserly): visit scriptTag, directives? |
| 120 | 120 |
| 121 _loader.collectElements(currentLibrary, library.partsThenLibrary); | 121 _loader.collectElements(currentLibrary, library.partsThenLibrary); |
| 122 | 122 |
| 123 for (var unit in library.partsThenLibrary) { | 123 for (var unit in library.partsThenLibrary) { |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 return js.statement('let # = #;', [dartClassName, jsTypeName]); | 322 return js.statement('let # = #;', [dartClassName, jsTypeName]); |
| 323 } | 323 } |
| 324 return null; | 324 return null; |
| 325 } | 325 } |
| 326 | 326 |
| 327 @override | 327 @override |
| 328 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 328 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
| 329 // If we've already emitted this class, skip it. | 329 // If we've already emitted this class, skip it. |
| 330 var classElem = node.element; | 330 var classElem = node.element; |
| 331 var type = classElem.type; | 331 var type = classElem.type; |
| 332 var jsName = findNodeAnnotation(node, _isJsNameAnnotation); | 332 var jsName = getAnnotationValue(node, _isJsNameAnnotation); |
| 333 | 333 |
| 334 if (jsName != null) return _emitJsType(node.name.name, jsName); | 334 if (jsName != null) return _emitJsType(node.name.name, jsName); |
| 335 | 335 |
| 336 var ctors = <ConstructorDeclaration>[]; | 336 var ctors = <ConstructorDeclaration>[]; |
| 337 var fields = <FieldDeclaration>[]; | 337 var fields = <FieldDeclaration>[]; |
| 338 var methods = <MethodDeclaration>[]; | 338 var methods = <MethodDeclaration>[]; |
| 339 for (var member in node.members) { | 339 for (var member in node.members) { |
| 340 if (member is ConstructorDeclaration) { | 340 if (member is ConstructorDeclaration) { |
| 341 ctors.add(member); | 341 ctors.add(member); |
| 342 } else if (member is FieldDeclaration && !member.isStatic) { | 342 } else if (member is FieldDeclaration && !member.isStatic) { |
| 343 fields.add(member); | 343 fields.add(member); |
| 344 } else if (member is MethodDeclaration) { | 344 } else if (member is MethodDeclaration) { |
| 345 methods.add(member); | 345 methods.add(member); |
| 346 } | 346 } |
| 347 } | 347 } |
| 348 | 348 |
| 349 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 349 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
| 350 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); | 350 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); |
| 351 | 351 |
| 352 String jsPeerName; | 352 String jsPeerName; |
| 353 var jsPeer = findNodeAnnotation(node, _isJsPeerInterface); | 353 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); |
| 354 if (jsPeer != null) { | 354 if (jsPeer != null) { |
| 355 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); | 355 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); |
| 356 } | 356 } |
| 357 | 357 |
| 358 var body = _finishClassMembers( | 358 var body = _finishClassMembers( |
| 359 classElem, classExpr, ctors, fields, methods, jsPeerName); | 359 classElem, classExpr, ctors, fields, methods, jsPeerName); |
| 360 | 360 |
| 361 var result = _finishClassDef(type, body); | 361 var result = _finishClassDef(type, body); |
| 362 | 362 |
| 363 if (jsPeerName != null) { | 363 if (jsPeerName != null) { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 var type = element.type; | 468 var type = element.type; |
| 469 var isObject = type.isObject; | 469 var isObject = type.isObject; |
| 470 | 470 |
| 471 // Iff no constructor is specified for a class C, it implicitly has a | 471 // Iff no constructor is specified for a class C, it implicitly has a |
| 472 // default constructor `C() : super() {}`, unless C is class Object. | 472 // default constructor `C() : super() {}`, unless C is class Object. |
| 473 var jsMethods = <JS.Method>[]; | 473 var jsMethods = <JS.Method>[]; |
| 474 if (ctors.isEmpty && !isObject) { | 474 if (ctors.isEmpty && !isObject) { |
| 475 jsMethods.add(_emitImplicitConstructor(node, fields)); | 475 jsMethods.add(_emitImplicitConstructor(node, fields)); |
| 476 } | 476 } |
| 477 | 477 |
| 478 bool hasJsPeer = findNodeAnnotation(node, _isJsPeerInterface) != null; | 478 bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null; |
| 479 | 479 |
| 480 bool hasIterator = false; | 480 bool hasIterator = false; |
| 481 for (var m in node.members) { | 481 for (var m in node.members) { |
| 482 if (m is ConstructorDeclaration) { | 482 if (m is ConstructorDeclaration) { |
| 483 jsMethods.add(_emitConstructor(m, type, fields, isObject)); | 483 jsMethods.add(_emitConstructor(m, type, fields, isObject)); |
| 484 } else if (m is MethodDeclaration) { | 484 } else if (m is MethodDeclaration) { |
| 485 jsMethods.add(_emitMethodDeclaration(type, m)); | 485 jsMethods.add(_emitMethodDeclaration(type, m)); |
| 486 | 486 |
| 487 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { | 487 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { |
| 488 hasIterator = true; | 488 hasIterator = true; |
| (...skipping 853 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1342 var vars = {}; | 1342 var vars = {}; |
| 1343 var lhs = _bindLeftHandSide(vars, left, context: context); | 1343 var lhs = _bindLeftHandSide(vars, left, context: context); |
| 1344 var inc = AstBuilder.binaryExpression(lhs, op, right); | 1344 var inc = AstBuilder.binaryExpression(lhs, op, right); |
| 1345 inc.staticElement = element; | 1345 inc.staticElement = element; |
| 1346 inc.staticType = getStaticType(left); | 1346 inc.staticType = getStaticType(left); |
| 1347 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); | 1347 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); |
| 1348 } | 1348 } |
| 1349 | 1349 |
| 1350 JS.Expression _emitSet(Expression lhs, Expression rhs) { | 1350 JS.Expression _emitSet(Expression lhs, Expression rhs) { |
| 1351 if (lhs is IndexExpression) { | 1351 if (lhs is IndexExpression) { |
| 1352 var target = _getTarget(lhs); | 1352 return _emitSend(_getTarget(lhs), '[]=', [lhs.index, rhs]); |
| 1353 if (_useNativeJsIndexer(target.staticType)) { | |
| 1354 return js.call( | |
| 1355 '#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); | |
| 1356 } | |
| 1357 return _emitSend(target, '[]=', [lhs.index, rhs]); | |
| 1358 } | 1353 } |
| 1359 | 1354 |
| 1360 Expression target = null; | 1355 Expression target = null; |
| 1361 SimpleIdentifier id; | 1356 SimpleIdentifier id; |
| 1362 if (lhs is PropertyAccess) { | 1357 if (lhs is PropertyAccess) { |
| 1363 target = _getTarget(lhs); | 1358 target = _getTarget(lhs); |
| 1364 id = lhs.propertyName; | 1359 id = lhs.propertyName; |
| 1365 } else if (lhs is PrefixedIdentifier) { | 1360 } else if (lhs is PrefixedIdentifier) { |
| 1366 target = lhs.prefix; | 1361 target = lhs.prefix; |
| 1367 id = lhs.identifier; | 1362 id = lhs.identifier; |
| (...skipping 746 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2114 _visitList(args) | 2109 _visitList(args) |
| 2115 ]); | 2110 ]); |
| 2116 } | 2111 } |
| 2117 | 2112 |
| 2118 // Generic dispatch to a statically known method. | 2113 // Generic dispatch to a statically known method. |
| 2119 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | 2114 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); |
| 2120 } | 2115 } |
| 2121 | 2116 |
| 2122 @override | 2117 @override |
| 2123 visitIndexExpression(IndexExpression node) { | 2118 visitIndexExpression(IndexExpression node) { |
| 2124 var target = _getTarget(node); | 2119 return _emitSend(_getTarget(node), '[]', [node.index]); |
| 2125 if (_useNativeJsIndexer(target.staticType)) { | |
| 2126 return new JS.PropertyAccess(_visit(target), _visit(node.index)); | |
| 2127 } | |
| 2128 return _emitSend(target, '[]', [node.index]); | |
| 2129 } | 2120 } |
| 2130 | 2121 |
| 2131 // TODO(jmesserly): ideally we'd check the method and see if it is marked | |
| 2132 // `external`, but that doesn't work because it isn't in the element model. | |
| 2133 bool _useNativeJsIndexer(DartType type) => | |
| 2134 findElementAnnotation(type.element, _isJsIndexerAnnotation) != null; | |
| 2135 | |
| 2136 /// Gets the target of a [PropertyAccess] or [IndexExpression]. | 2122 /// Gets the target of a [PropertyAccess] or [IndexExpression]. |
| 2137 /// Those two nodes are special because they're both allowed on left side of | 2123 /// Those two nodes are special because they're both allowed on left side of |
| 2138 /// an assignment expression and cascades. | 2124 /// an assignment expression and cascades. |
| 2139 Expression _getTarget(node) { | 2125 Expression _getTarget(node) { |
| 2140 assert(node is IndexExpression || node is PropertyAccess); | 2126 assert(node is IndexExpression || node is PropertyAccess); |
| 2141 return node.isCascaded ? _cascadeTarget : node.target; | 2127 return node.isCascaded ? _cascadeTarget : node.target; |
| 2142 } | 2128 } |
| 2143 | 2129 |
| 2144 @override | 2130 @override |
| 2145 visitConditionalExpression(ConditionalExpression node) { | 2131 visitConditionalExpression(ConditionalExpression node) { |
| (...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2643 assert(uri.scheme == 'package'); | 2629 assert(uri.scheme == 'package'); |
| 2644 // filepath is good here, we want the output to start with a directory | 2630 // filepath is good here, we want the output to start with a directory |
| 2645 // matching the package name. | 2631 // matching the package name. |
| 2646 } | 2632 } |
| 2647 return filepath; | 2633 return filepath; |
| 2648 } | 2634 } |
| 2649 | 2635 |
| 2650 // TODO(jmesserly): validate the library. See issue #135. | 2636 // TODO(jmesserly): validate the library. See issue #135. |
| 2651 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2637 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
| 2652 | 2638 |
| 2653 bool _isJsIndexerAnnotation(DartObjectImpl value) => | |
| 2654 value.type.name == 'JsIndexer'; | |
| 2655 | |
| 2656 bool _isJsPeerInterface(DartObjectImpl value) => | 2639 bool _isJsPeerInterface(DartObjectImpl value) => |
| 2657 value.type.name == 'JsPeerInterface'; | 2640 value.type.name == 'JsPeerInterface'; |
| 2658 | 2641 |
| 2659 // TODO(jacobr): we would like to do something like the following | 2642 // TODO(jacobr): we would like to do something like the following |
| 2660 // but we don't have summary support yet. | 2643 // but we don't have summary support yet. |
| 2661 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2644 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
| 2662 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2645 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
| 2663 | 2646 |
| 2664 /// A special kind of element created by the compiler, signifying a temporary | 2647 /// A special kind of element created by the compiler, signifying a temporary |
| 2665 /// variable. These objects use instance equality, and should be shared | 2648 /// variable. These objects use instance equality, and should be shared |
| 2666 /// everywhere in the tree where they are treated as the same variable. | 2649 /// everywhere in the tree where they are treated as the same variable. |
| 2667 class TemporaryVariableElement extends LocalVariableElementImpl { | 2650 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 2668 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2651 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 2669 | 2652 |
| 2670 int get hashCode => identityHashCode(this); | 2653 int get hashCode => identityHashCode(this); |
| 2671 bool operator ==(Object other) => identical(this, other); | 2654 bool operator ==(Object other) => identical(this, other); |
| 2672 } | 2655 } |
| OLD | NEW |