Chromium Code Reviews| 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 import 'dart:io' show Directory, File; | 8 import 'dart:io' show Directory, File; |
| 9 | 9 |
| 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 26 | 26 |
| 27 import 'package:dev_compiler/src/checker/rules.dart'; | 27 import 'package:dev_compiler/src/checker/rules.dart'; |
| 28 import 'package:dev_compiler/src/info.dart'; | 28 import 'package:dev_compiler/src/info.dart'; |
| 29 import 'package:dev_compiler/src/options.dart'; | 29 import 'package:dev_compiler/src/options.dart'; |
| 30 import 'package:dev_compiler/src/utils.dart'; | 30 import 'package:dev_compiler/src/utils.dart'; |
| 31 | 31 |
| 32 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
| 33 import 'js_names.dart'; | 33 import 'js_names.dart'; |
| 34 import 'js_metalet.dart'; | 34 import 'js_metalet.dart'; |
| 35 | 35 |
| 36 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; | |
| 37 | |
| 38 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata | |
| 39 .firstWhere((annotation) => _isAnnotationType(annotation, name), | |
| 40 orElse: () => null); | |
| 41 | |
| 42 Annotation _getJsNameAnnotation(AnnotatedNode node) => | |
| 43 _getAnnotation(node, "JsName"); | |
| 44 | |
| 45 // TODO(jacobr): we would like to do something like the following | |
| 46 // but we don't have summary support yet. | |
| 47 // bool _supportJsExtensionMethod(AnnotatedNode node) => | |
| 48 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | |
| 49 | |
| 50 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 36 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| 51 final LibraryInfo libraryInfo; | 37 final LibraryInfo libraryInfo; |
| 52 final TypeRules rules; | 38 final TypeRules rules; |
| 53 | 39 |
| 54 /// The variable for the target of the current `..` cascade expression. | 40 /// The variable for the target of the current `..` cascade expression. |
| 55 SimpleIdentifier _cascadeTarget; | 41 SimpleIdentifier _cascadeTarget; |
| 56 /// The variable for the current catch clause | 42 /// The variable for the current catch clause |
| 57 SimpleIdentifier _catchParameter; | 43 SimpleIdentifier _catchParameter; |
| 58 | 44 |
| 59 ClassDeclaration currentClass; | 45 ClassDeclaration currentClass; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 115 LibraryElement get currentLibrary => libraryInfo.library; | 101 LibraryElement get currentLibrary => libraryInfo.library; |
| 116 | 102 |
| 117 /// The name for the library's exports inside itself. | 103 /// The name for the library's exports inside itself. |
| 118 /// This much be a constant because we interpolate it into template strings, | 104 /// This much be a constant because we interpolate it into template strings, |
| 119 /// and otherwise it would break caching for them. | 105 /// and otherwise it would break caching for them. |
| 120 /// `exports` was chosen as the most similar to ES module patterns. | 106 /// `exports` was chosen as the most similar to ES module patterns. |
| 121 final JSTemporary _exportsVar = new JSTemporary('exports'); | 107 final JSTemporary _exportsVar = new JSTemporary('exports'); |
| 122 final JSTemporary _namedArgTemp = new JSTemporary('opts'); | 108 final JSTemporary _namedArgTemp = new JSTemporary('opts'); |
| 123 | 109 |
| 124 JS.Program emitLibrary(LibraryUnit library) { | 110 JS.Program emitLibrary(LibraryUnit library) { |
| 125 var jsDefaultValue = '{}'; | 111 String jsDefaultValue = null; |
| 126 var unit = library.library; | 112 var unit = library.library; |
| 127 if (unit.directives.isNotEmpty) { | 113 if (unit.directives.isNotEmpty) { |
| 128 var annotation = _getJsNameAnnotation(unit.directives.first); | 114 var libraryDir = unit.directives.first; |
| 129 if (annotation != null) { | 115 if (libraryDir is LibraryDirective) { |
| 130 var arguments = annotation.arguments.arguments; | 116 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); |
| 131 if (!arguments.isEmpty) { | 117 jsDefaultValue = getStringConstantField(jsName, 'name'); |
| 132 var namedExpression = arguments.first as NamedExpression; | |
| 133 var literal = namedExpression.expression as SimpleStringLiteral; | |
| 134 jsDefaultValue = literal.stringValue; | |
| 135 } | |
| 136 } | 118 } |
| 137 } | 119 } |
| 120 if (jsDefaultValue == null) jsDefaultValue = '{}'; | |
| 121 | |
| 138 var body = <JS.Statement>[]; | 122 var body = <JS.Statement>[]; |
| 139 | 123 |
| 140 // Collect classes we need to emit, used for: | 124 // Collect classes we need to emit, used for: |
| 141 // * tracks what we've emitted so we don't emit twice | 125 // * tracks what we've emitted so we don't emit twice |
| 142 // * provides a mapping from ClassElement back to the ClassDeclaration. | 126 // * provides a mapping from ClassElement back to the ClassDeclaration. |
| 143 for (var unit in library.partsThenLibrary) { | 127 for (var unit in library.partsThenLibrary) { |
| 144 for (var decl in unit.declarations) { | 128 for (var decl in unit.declarations) { |
| 145 if (decl is ClassDeclaration || | 129 if (decl is ClassDeclaration || |
| 146 decl is ClassTypeAlias || | 130 decl is ClassTypeAlias || |
| 147 decl is FunctionTypeAlias) { | 131 decl is FunctionTypeAlias) { |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 326 | 310 |
| 327 var name = node.name.name; | 311 var name = node.name.name; |
| 328 var heritage = | 312 var heritage = |
| 329 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); | 313 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); |
| 330 var classDecl = new JS.ClassDeclaration( | 314 var classDecl = new JS.ClassDeclaration( |
| 331 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); | 315 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); |
| 332 | 316 |
| 333 return _finishClassDef(type, classDecl); | 317 return _finishClassDef(type, classDecl); |
| 334 } | 318 } |
| 335 | 319 |
| 336 JS.Statement _emitJsType(ClassDeclaration node, Annotation jsName) { | 320 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { |
| 337 var dartName = node.name.name; | 321 var jsTypeName = getStringConstantField(jsName, 'name'); |
| 338 var jsTypeName = _getLiteralStringNamedArg(jsName, 'name'); | |
| 339 | 322 |
| 340 if (jsTypeName != null && jsTypeName != dartName) { | 323 if (jsTypeName != null && jsTypeName != dartClassName) { |
| 341 // We export the JS type as if it was a Dart type. For example this allows | 324 // We export the JS type as if it was a Dart type. For example this allows |
| 342 // `dom.InputElement` to actually be HTMLInputElement. | 325 // `dom.InputElement` to actually be HTMLInputElement. |
| 343 // TODO(jmesserly): if we had the JsName on the Element, we could just | 326 // TODO(jmesserly): if we had the JsName on the Element, we could just |
| 344 // generate it correctly when we refer to it. | 327 // generate it correctly when we refer to it. |
| 345 if (isPublic(dartName)) _addExport(dartName); | 328 if (isPublic(dartClassName)) _addExport(dartClassName); |
| 346 return js.statement('let # = #;', [dartName, jsTypeName]); | 329 return js.statement('let # = #;', [dartClassName, jsTypeName]); |
| 347 } | 330 } |
| 348 return null; | 331 return null; |
| 349 } | 332 } |
| 350 | 333 |
| 351 @override | 334 @override |
| 352 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 335 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
| 353 // If we've already emitted this class, skip it. | 336 // If we've already emitted this class, skip it. |
| 354 var type = node.element.type; | 337 var type = node.element.type; |
| 355 if (_pendingClasses.remove(node.element) == null) return null; | 338 if (_pendingClasses.remove(node.element) == null) return null; |
| 356 | 339 |
| 357 var jsName = _getJsNameAnnotation(node); | 340 var jsName = getAnnotationValue(node, _isJsNameAnnotation); |
| 358 if (jsName != null) return _emitJsType(node, jsName); | 341 if (jsName != null) return _emitJsType(node.name.name, jsName); |
| 359 | 342 |
| 360 currentClass = node; | 343 currentClass = node; |
| 361 | 344 |
| 362 var ctors = <ConstructorDeclaration>[]; | 345 var ctors = <ConstructorDeclaration>[]; |
| 363 var fields = <FieldDeclaration>[]; | 346 var fields = <FieldDeclaration>[]; |
| 364 var staticFields = <FieldDeclaration>[]; | 347 var staticFields = <FieldDeclaration>[]; |
| 365 for (var member in node.members) { | 348 for (var member in node.members) { |
| 366 if (member is ConstructorDeclaration) { | 349 if (member is ConstructorDeclaration) { |
| 367 ctors.add(member); | 350 ctors.add(member); |
| 368 } else if (member is FieldDeclaration) { | 351 } else if (member is FieldDeclaration) { |
| (...skipping 2218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2587 identical(parent.methodName, node)) return; | 2570 identical(parent.methodName, node)) return; |
| 2588 if (parent is ConstructorName) return; | 2571 if (parent is ConstructorName) return; |
| 2589 if (parent is Label) return; | 2572 if (parent is Label) return; |
| 2590 | 2573 |
| 2591 if (node.inSetterContext() && node.staticElement == _variable) { | 2574 if (node.inSetterContext() && node.staticElement == _variable) { |
| 2592 _potentiallyMutated = true; | 2575 _potentiallyMutated = true; |
| 2593 } | 2576 } |
| 2594 } | 2577 } |
| 2595 } | 2578 } |
| 2596 | 2579 |
| 2597 String _getLiteralStringNamedArg(Annotation annotation, String argName) { | 2580 bool _isJsNameAnnotation(DartObjectImpl value) { |
| 2598 if (annotation.arguments != null) { | 2581 var type = value.type; |
| 2599 var args = annotation.arguments.arguments; | 2582 var library = type.element.library; |
| 2600 if (args.isNotEmpty && args[0] is NamedExpression) { | 2583 // TODO(jmesserly): move JsName to dart:js |
|
Jacob
2015/04/14 20:54:33
until we move it to dart:js can't we just be permi
Jennifer Messerly
2015/04/15 00:21:26
hmmm. The current situation does indeed seem odd.
| |
| 2601 NamedExpression named = args[0]; | 2584 return type.name == 'JsName' && (library.isDartCore || library.name == 'dom'); |
| 2602 if (named.name.label.name == argName && | |
| 2603 named.expression is StringLiteral) { | |
| 2604 return (named.expression as StringLiteral).stringValue; | |
| 2605 } | |
| 2606 } | |
| 2607 } | |
| 2608 return null; | |
| 2609 } | 2585 } |
| 2586 | |
| 2587 // TODO(jacobr): we would like to do something like the following | |
| 2588 // but we don't have summary support yet. | |
| 2589 // bool _supportJsExtensionMethod(AnnotatedNode node) => | |
| 2590 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | |
| OLD | NEW |