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 = getAnnotationValue(libraryDir, _isJsNameAnnotation); | 113 var jsName = findNodeAnnotation(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 = getAnnotationValue(node, _isJsNameAnnotation); | 332 var jsName = findNodeAnnotation(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 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 } | 343 } |
344 } | 344 } |
345 | 345 |
346 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 346 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
347 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); | 347 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); |
348 | 348 |
349 String jsPeerName; | 349 String jsPeerName; |
350 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); | 350 var jsPeer = findNodeAnnotation(node, _isJsPeerInterface); |
351 if (jsPeer != null) { | 351 if (jsPeer != null) { |
352 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); | 352 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); |
353 } | 353 } |
354 | 354 |
355 var body = | 355 var body = |
356 _finishClassMembers(classElem, classExpr, ctors, fields, jsPeerName); | 356 _finishClassMembers(classElem, classExpr, ctors, fields, jsPeerName); |
357 | 357 |
358 var result = _finishClassDef(type, body); | 358 var result = _finishClassDef(type, body); |
359 | 359 |
360 if (jsPeerName != null) { | 360 if (jsPeerName != null) { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
466 var isObject = type.isObject; | 466 var isObject = type.isObject; |
467 var name = node.name.name; | 467 var name = node.name.name; |
468 | 468 |
469 // Iff no constructor is specified for a class C, it implicitly has a | 469 // Iff no constructor is specified for a class C, it implicitly has a |
470 // default constructor `C() : super() {}`, unless C is class Object. | 470 // default constructor `C() : super() {}`, unless C is class Object. |
471 var jsMethods = <JS.Method>[]; | 471 var jsMethods = <JS.Method>[]; |
472 if (ctors.isEmpty && !isObject) { | 472 if (ctors.isEmpty && !isObject) { |
473 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 473 jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
474 } | 474 } |
475 | 475 |
476 bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null; | 476 bool hasJsPeer = findNodeAnnotation(node, _isJsPeerInterface) != null; |
477 | 477 |
478 bool hasIterator = false; | 478 bool hasIterator = false; |
479 for (var m in node.members) { | 479 for (var m in node.members) { |
480 if (m is ConstructorDeclaration) { | 480 if (m is ConstructorDeclaration) { |
481 jsMethods.add(_emitConstructor(m, name, fields, isObject)); | 481 jsMethods.add(_emitConstructor(m, name, fields, isObject)); |
482 } else if (m is MethodDeclaration) { | 482 } else if (m is MethodDeclaration) { |
483 jsMethods.add(_emitMethodDeclaration(type, m)); | 483 jsMethods.add(_emitMethodDeclaration(type, m)); |
484 | 484 |
485 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { | 485 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { |
486 hasIterator = true; | 486 hasIterator = true; |
(...skipping 690 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1177 var vars = {}; | 1177 var vars = {}; |
1178 var lhs = _bindLeftHandSide(vars, left, context: context); | 1178 var lhs = _bindLeftHandSide(vars, left, context: context); |
1179 var inc = AstBuilder.binaryExpression(lhs, op, right); | 1179 var inc = AstBuilder.binaryExpression(lhs, op, right); |
1180 inc.staticElement = element; | 1180 inc.staticElement = element; |
1181 inc.staticType = getStaticType(left); | 1181 inc.staticType = getStaticType(left); |
1182 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); | 1182 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); |
1183 } | 1183 } |
1184 | 1184 |
1185 JS.Expression _emitSet(Expression lhs, Expression rhs) { | 1185 JS.Expression _emitSet(Expression lhs, Expression rhs) { |
1186 if (lhs is IndexExpression) { | 1186 if (lhs is IndexExpression) { |
1187 return _emitSend(_getTarget(lhs), '[]=', [lhs.index, rhs]); | 1187 var target = _getTarget(lhs); |
| 1188 if (_useNativeJsIndexer(target.staticType)) { |
| 1189 return js.call( |
| 1190 '#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 1191 } |
| 1192 return _emitSend(target, '[]=', [lhs.index, rhs]); |
1188 } | 1193 } |
1189 | 1194 |
1190 Expression target = null; | 1195 Expression target = null; |
1191 SimpleIdentifier id; | 1196 SimpleIdentifier id; |
1192 if (lhs is PropertyAccess) { | 1197 if (lhs is PropertyAccess) { |
1193 target = _getTarget(lhs); | 1198 target = _getTarget(lhs); |
1194 id = lhs.propertyName; | 1199 id = lhs.propertyName; |
1195 } else if (lhs is PrefixedIdentifier) { | 1200 } else if (lhs is PrefixedIdentifier) { |
1196 target = lhs.prefix; | 1201 target = lhs.prefix; |
1197 id = lhs.identifier; | 1202 id = lhs.identifier; |
(...skipping 740 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1938 _visitList(args) | 1943 _visitList(args) |
1939 ]); | 1944 ]); |
1940 } | 1945 } |
1941 | 1946 |
1942 // Generic dispatch to a statically known method. | 1947 // Generic dispatch to a statically known method. |
1943 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | 1948 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); |
1944 } | 1949 } |
1945 | 1950 |
1946 @override | 1951 @override |
1947 visitIndexExpression(IndexExpression node) { | 1952 visitIndexExpression(IndexExpression node) { |
1948 return _emitSend(_getTarget(node), '[]', [node.index]); | 1953 var target = _getTarget(node); |
| 1954 if (_useNativeJsIndexer(target.staticType)) { |
| 1955 return new JS.PropertyAccess(_visit(target), _visit(node.index)); |
| 1956 } |
| 1957 return _emitSend(target, '[]', [node.index]); |
1949 } | 1958 } |
1950 | 1959 |
| 1960 // TODO(jmesserly): ideally we'd check the method and see if it is marked |
| 1961 // `external`, but that doesn't work because it isn't in the element model. |
| 1962 bool _useNativeJsIndexer(DartType type) => |
| 1963 findElementAnnotation(type.element, _isJsIndexerAnnotation) != null; |
| 1964 |
1951 /// Gets the target of a [PropertyAccess] or [IndexExpression]. | 1965 /// Gets the target of a [PropertyAccess] or [IndexExpression]. |
1952 /// Those two nodes are special because they're both allowed on left side of | 1966 /// Those two nodes are special because they're both allowed on left side of |
1953 /// an assignment expression and cascades. | 1967 /// an assignment expression and cascades. |
1954 Expression _getTarget(node) { | 1968 Expression _getTarget(node) { |
1955 assert(node is IndexExpression || node is PropertyAccess); | 1969 assert(node is IndexExpression || node is PropertyAccess); |
1956 return node.isCascaded ? _cascadeTarget : node.target; | 1970 return node.isCascaded ? _cascadeTarget : node.target; |
1957 } | 1971 } |
1958 | 1972 |
1959 @override | 1973 @override |
1960 visitConditionalExpression(ConditionalExpression node) { | 1974 visitConditionalExpression(ConditionalExpression node) { |
(...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2458 assert(uri.scheme == 'package'); | 2472 assert(uri.scheme == 'package'); |
2459 // filepath is good here, we want the output to start with a directory | 2473 // filepath is good here, we want the output to start with a directory |
2460 // matching the package name. | 2474 // matching the package name. |
2461 } | 2475 } |
2462 return filepath; | 2476 return filepath; |
2463 } | 2477 } |
2464 | 2478 |
2465 // TODO(jmesserly): validate the library. See issue #135. | 2479 // TODO(jmesserly): validate the library. See issue #135. |
2466 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2480 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2467 | 2481 |
| 2482 bool _isJsIndexerAnnotation(DartObjectImpl value) => |
| 2483 value.type.name == 'JsIndexer'; |
| 2484 |
2468 bool _isJsPeerInterface(DartObjectImpl value) => | 2485 bool _isJsPeerInterface(DartObjectImpl value) => |
2469 value.type.name == 'JsPeerInterface'; | 2486 value.type.name == 'JsPeerInterface'; |
2470 | 2487 |
2471 // TODO(jacobr): we would like to do something like the following | 2488 // TODO(jacobr): we would like to do something like the following |
2472 // but we don't have summary support yet. | 2489 // but we don't have summary support yet. |
2473 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2490 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2474 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2491 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
2475 | 2492 |
2476 /// A special kind of element created by the compiler, signifying a temporary | 2493 /// A special kind of element created by the compiler, signifying a temporary |
2477 /// variable. These objects use instance equality, and should be shared | 2494 /// variable. These objects use instance equality, and should be shared |
2478 /// everywhere in the tree where they are treated as the same variable. | 2495 /// everywhere in the tree where they are treated as the same variable. |
2479 class TemporaryVariableElement extends LocalVariableElementImpl { | 2496 class TemporaryVariableElement extends LocalVariableElementImpl { |
2480 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2497 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
2481 | 2498 |
2482 int get hashCode => identityHashCode(this); | 2499 int get hashCode => identityHashCode(this); |
2483 bool operator ==(Object other) => identical(this, other); | 2500 bool operator ==(Object other) => identical(this, other); |
2484 } | 2501 } |
OLD | NEW |