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 |