| 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, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
| 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 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 JS.Program emitLibrary(LibraryUnit library) { | 102 JS.Program emitLibrary(LibraryUnit library) { |
| 103 String jsDefaultValue = null; | 103 String jsDefaultValue = null; |
| 104 | 104 |
| 105 // Modify the AST to make coercions explicit. | 105 // Modify the AST to make coercions explicit. |
| 106 new CoercionReifier(library, compiler).reify(); | 106 new CoercionReifier(library, compiler).reify(); |
| 107 | 107 |
| 108 var unit = library.library; | 108 var unit = library.library; |
| 109 if (unit.directives.isNotEmpty) { | 109 if (unit.directives.isNotEmpty) { |
| 110 var libraryDir = unit.directives.first; | 110 var libraryDir = unit.directives.first; |
| 111 if (libraryDir is LibraryDirective) { | 111 if (libraryDir is LibraryDirective) { |
| 112 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); | 112 var jsName = findAnnotation(libraryDir.element, _isJsNameAnnotation); |
| 113 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); | 113 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); |
| 114 } | 114 } |
| 115 } | 115 } |
| 116 if (jsDefaultValue == null) jsDefaultValue = '{}'; | 116 if (jsDefaultValue == null) jsDefaultValue = '{}'; |
| 117 | 117 |
| 118 // TODO(jmesserly): visit scriptTag, directives? | 118 // TODO(jmesserly): visit scriptTag, directives? |
| 119 | 119 |
| 120 _loader.collectElements(currentLibrary, library.partsThenLibrary); | 120 _loader.collectElements(currentLibrary, library.partsThenLibrary); |
| 121 | 121 |
| 122 for (var unit in library.partsThenLibrary) { | 122 for (var unit in library.partsThenLibrary) { |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 return js.statement('let # = #;', [dartClassName, jsTypeName]); | 321 return js.statement('let # = #;', [dartClassName, jsTypeName]); |
| 322 } | 322 } |
| 323 return null; | 323 return null; |
| 324 } | 324 } |
| 325 | 325 |
| 326 @override | 326 @override |
| 327 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 327 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
| 328 // If we've already emitted this class, skip it. | 328 // If we've already emitted this class, skip it. |
| 329 var classElem = node.element; | 329 var classElem = node.element; |
| 330 var type = classElem.type; | 330 var type = classElem.type; |
| 331 var jsName = getAnnotationValue(node, _isJsNameAnnotation); | 331 var jsName = findAnnotation(classElem, _isJsNameAnnotation); |
| 332 | 332 |
| 333 if (jsName != null) return _emitJsType(node.name.name, jsName); | 333 if (jsName != null) return _emitJsType(node.name.name, jsName); |
| 334 | 334 |
| 335 var ctors = <ConstructorDeclaration>[]; | 335 var ctors = <ConstructorDeclaration>[]; |
| 336 var fields = <FieldDeclaration>[]; | 336 var fields = <FieldDeclaration>[]; |
| 337 var methods = <MethodDeclaration>[]; | 337 var methods = <MethodDeclaration>[]; |
| 338 for (var member in node.members) { | 338 for (var member in node.members) { |
| 339 if (member is ConstructorDeclaration) { | 339 if (member is ConstructorDeclaration) { |
| 340 ctors.add(member); | 340 ctors.add(member); |
| 341 } else if (member is FieldDeclaration && !member.isStatic) { | 341 } else if (member is FieldDeclaration && !member.isStatic) { |
| 342 fields.add(member); | 342 fields.add(member); |
| 343 } else if (member is MethodDeclaration) { | 343 } else if (member is MethodDeclaration) { |
| 344 methods.add(member); | 344 methods.add(member); |
| 345 } | 345 } |
| 346 } | 346 } |
| 347 | 347 |
| 348 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 348 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
| 349 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); | 349 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); |
| 350 | 350 |
| 351 String jsPeerName; | 351 String jsPeerName; |
| 352 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); | 352 var jsPeer = findAnnotation(classElem, _isJsPeerInterface); |
| 353 if (jsPeer != null) { | 353 if (jsPeer != null) { |
| 354 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); | 354 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); |
| 355 } | 355 } |
| 356 | 356 |
| 357 var body = _finishClassMembers( | 357 var body = _finishClassMembers( |
| 358 classElem, classExpr, ctors, fields, methods, jsPeerName); | 358 classElem, classExpr, ctors, fields, methods, jsPeerName); |
| 359 | 359 |
| 360 var result = _finishClassDef(type, body); | 360 var result = _finishClassDef(type, body); |
| 361 | 361 |
| 362 if (jsPeerName != null) { | 362 if (jsPeerName != null) { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 var type = element.type; | 467 var type = element.type; |
| 468 var isObject = type.isObject; | 468 var isObject = type.isObject; |
| 469 | 469 |
| 470 // Iff no constructor is specified for a class C, it implicitly has a | 470 // Iff no constructor is specified for a class C, it implicitly has a |
| 471 // default constructor `C() : super() {}`, unless C is class Object. | 471 // default constructor `C() : super() {}`, unless C is class Object. |
| 472 var jsMethods = <JS.Method>[]; | 472 var jsMethods = <JS.Method>[]; |
| 473 if (ctors.isEmpty && !isObject) { | 473 if (ctors.isEmpty && !isObject) { |
| 474 jsMethods.add(_emitImplicitConstructor(node, fields)); | 474 jsMethods.add(_emitImplicitConstructor(node, fields)); |
| 475 } | 475 } |
| 476 | 476 |
| 477 bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null; | 477 bool hasJsPeer = findAnnotation(element, _isJsPeerInterface) != null; |
| 478 | 478 |
| 479 bool hasIterator = false; | 479 bool hasIterator = false; |
| 480 for (var m in node.members) { | 480 for (var m in node.members) { |
| 481 if (m is ConstructorDeclaration) { | 481 if (m is ConstructorDeclaration) { |
| 482 jsMethods.add(_emitConstructor(m, type, fields, isObject)); | 482 jsMethods.add(_emitConstructor(m, type, fields, isObject)); |
| 483 } else if (m is MethodDeclaration) { | 483 } else if (m is MethodDeclaration) { |
| 484 jsMethods.add(_emitMethodDeclaration(type, m)); | 484 jsMethods.add(_emitMethodDeclaration(type, m)); |
| 485 | 485 |
| 486 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { | 486 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { |
| 487 hasIterator = true; | 487 hasIterator = true; |
| (...skipping 909 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1397 var vars = {}; | 1397 var vars = {}; |
| 1398 var lhs = _bindLeftHandSide(vars, left, context: context); | 1398 var lhs = _bindLeftHandSide(vars, left, context: context); |
| 1399 var inc = AstBuilder.binaryExpression(lhs, op, right); | 1399 var inc = AstBuilder.binaryExpression(lhs, op, right); |
| 1400 inc.staticElement = element; | 1400 inc.staticElement = element; |
| 1401 inc.staticType = getStaticType(left); | 1401 inc.staticType = getStaticType(left); |
| 1402 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); | 1402 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); |
| 1403 } | 1403 } |
| 1404 | 1404 |
| 1405 JS.Expression _emitSet(Expression lhs, Expression rhs) { | 1405 JS.Expression _emitSet(Expression lhs, Expression rhs) { |
| 1406 if (lhs is IndexExpression) { | 1406 if (lhs is IndexExpression) { |
| 1407 return _emitSend(_getTarget(lhs), '[]=', [lhs.index, rhs]); | 1407 var target = _getTarget(lhs); |
| 1408 if (_useNativeJsIndexer(target.staticType)) { |
| 1409 return js.call( |
| 1410 '#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 1411 } |
| 1412 return _emitSend(target, '[]=', [lhs.index, rhs]); |
| 1408 } | 1413 } |
| 1409 | 1414 |
| 1410 Expression target = null; | 1415 Expression target = null; |
| 1411 SimpleIdentifier id; | 1416 SimpleIdentifier id; |
| 1412 if (lhs is PropertyAccess) { | 1417 if (lhs is PropertyAccess) { |
| 1413 target = _getTarget(lhs); | 1418 target = _getTarget(lhs); |
| 1414 id = lhs.propertyName; | 1419 id = lhs.propertyName; |
| 1415 } else if (lhs is PrefixedIdentifier) { | 1420 } else if (lhs is PrefixedIdentifier) { |
| 1416 target = lhs.prefix; | 1421 target = lhs.prefix; |
| 1417 id = lhs.identifier; | 1422 id = lhs.identifier; |
| (...skipping 770 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2188 _visitList(args) | 2193 _visitList(args) |
| 2189 ]); | 2194 ]); |
| 2190 } | 2195 } |
| 2191 | 2196 |
| 2192 // Generic dispatch to a statically known method. | 2197 // Generic dispatch to a statically known method. |
| 2193 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | 2198 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); |
| 2194 } | 2199 } |
| 2195 | 2200 |
| 2196 @override | 2201 @override |
| 2197 visitIndexExpression(IndexExpression node) { | 2202 visitIndexExpression(IndexExpression node) { |
| 2198 return _emitSend(_getTarget(node), '[]', [node.index]); | 2203 var target = _getTarget(node); |
| 2204 if (_useNativeJsIndexer(target.staticType)) { |
| 2205 return new JS.PropertyAccess(_visit(target), _visit(node.index)); |
| 2206 } |
| 2207 return _emitSend(target, '[]', [node.index]); |
| 2199 } | 2208 } |
| 2200 | 2209 |
| 2210 // TODO(jmesserly): ideally we'd check the method and see if it is marked |
| 2211 // `external`, but that doesn't work because it isn't in the element model. |
| 2212 bool _useNativeJsIndexer(DartType type) => |
| 2213 findAnnotation(type.element, _isJsNameAnnotation) != null; |
| 2214 |
| 2201 /// Gets the target of a [PropertyAccess] or [IndexExpression]. | 2215 /// Gets the target of a [PropertyAccess] or [IndexExpression]. |
| 2202 /// Those two nodes are special because they're both allowed on left side of | 2216 /// Those two nodes are special because they're both allowed on left side of |
| 2203 /// an assignment expression and cascades. | 2217 /// an assignment expression and cascades. |
| 2204 Expression _getTarget(node) { | 2218 Expression _getTarget(node) { |
| 2205 assert(node is IndexExpression || node is PropertyAccess); | 2219 assert(node is IndexExpression || node is PropertyAccess); |
| 2206 return node.isCascaded ? _cascadeTarget : node.target; | 2220 return node.isCascaded ? _cascadeTarget : node.target; |
| 2207 } | 2221 } |
| 2208 | 2222 |
| 2209 @override | 2223 @override |
| 2210 visitConditionalExpression(ConditionalExpression node) { | 2224 visitConditionalExpression(ConditionalExpression node) { |
| (...skipping 495 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2706 | 2720 |
| 2707 /// A special kind of element created by the compiler, signifying a temporary | 2721 /// A special kind of element created by the compiler, signifying a temporary |
| 2708 /// variable. These objects use instance equality, and should be shared | 2722 /// variable. These objects use instance equality, and should be shared |
| 2709 /// everywhere in the tree where they are treated as the same variable. | 2723 /// everywhere in the tree where they are treated as the same variable. |
| 2710 class TemporaryVariableElement extends LocalVariableElementImpl { | 2724 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 2711 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2725 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 2712 | 2726 |
| 2713 int get hashCode => identityHashCode(this); | 2727 int get hashCode => identityHashCode(this); |
| 2714 bool operator ==(Object other) => identical(this, other); | 2728 bool operator ==(Object other) => identical(this, other); |
| 2715 } | 2729 } |
| OLD | NEW |