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 16 matching lines...) Expand all Loading... | |
| 27 | 27 |
| 28 import 'package:dev_compiler/src/checker/rules.dart'; | 28 import 'package:dev_compiler/src/checker/rules.dart'; |
| 29 import 'package:dev_compiler/src/info.dart'; | 29 import 'package:dev_compiler/src/info.dart'; |
| 30 import 'package:dev_compiler/src/options.dart'; | 30 import 'package:dev_compiler/src/options.dart'; |
| 31 import 'package:dev_compiler/src/utils.dart'; | 31 import 'package:dev_compiler/src/utils.dart'; |
| 32 | 32 |
| 33 import 'code_generator.dart'; | 33 import 'code_generator.dart'; |
| 34 import 'js_names.dart'; | 34 import 'js_names.dart'; |
| 35 import 'js_metalet.dart'; | 35 import 'js_metalet.dart'; |
| 36 | 36 |
| 37 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; | |
| 38 | |
| 39 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata | |
| 40 .firstWhere((annotation) => _isAnnotationType(annotation, name), | |
| 41 orElse: () => null); | |
| 42 | |
| 43 Annotation _getJsNameAnnotation(AnnotatedNode node) => | |
| 44 _getAnnotation(node, "JsName"); | |
| 45 | |
| 46 // TODO(jacobr): we would like to do something like the following | |
| 47 // but we don't have summary support yet. | |
| 48 // bool _supportJsExtensionMethod(AnnotatedNode node) => | |
| 49 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | |
| 50 | |
| 51 // Various dynamic helpers we call. | 37 // Various dynamic helpers we call. |
| 52 // If renaming these, make sure to check other places like the | 38 // If renaming these, make sure to check other places like the |
| 53 // dart_runtime.js file and comments. | 39 // dart_runtime.js file and comments. |
| 54 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 40 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
| 55 // import and generate calls to, rather than dart_runtime.js | 41 // import and generate calls to, rather than dart_runtime.js |
| 56 const DPUT = 'dput'; | 42 const DPUT = 'dput'; |
| 57 const DLOAD = 'dload'; | 43 const DLOAD = 'dload'; |
| 58 const DINDEX = 'dindex'; | 44 const DINDEX = 'dindex'; |
| 59 const DSETINDEX = 'dsetindex'; | 45 const DSETINDEX = 'dsetindex'; |
| 60 const DCALL = 'dcall'; | 46 const DCALL = 'dcall'; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 | 85 |
| 100 /// Memoized results of [_inLibraryCycle]. | 86 /// Memoized results of [_inLibraryCycle]. |
| 101 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | 87 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
| 102 | 88 |
| 103 JSCodegenVisitor(this.libraryInfo, this.rules, this._extensionMethods); | 89 JSCodegenVisitor(this.libraryInfo, this.rules, this._extensionMethods); |
| 104 | 90 |
| 105 LibraryElement get currentLibrary => libraryInfo.library; | 91 LibraryElement get currentLibrary => libraryInfo.library; |
| 106 TypeProvider get types => rules.provider; | 92 TypeProvider get types => rules.provider; |
| 107 | 93 |
| 108 JS.Program emitLibrary(LibraryUnit library) { | 94 JS.Program emitLibrary(LibraryUnit library) { |
| 109 var jsDefaultValue = '{}'; | 95 String jsDefaultValue = null; |
| 110 var unit = library.library; | 96 var unit = library.library; |
| 111 if (unit.directives.isNotEmpty) { | 97 if (unit.directives.isNotEmpty) { |
| 112 var annotation = _getJsNameAnnotation(unit.directives.first); | 98 var libraryDir = unit.directives.first; |
| 113 if (annotation != null) { | 99 if (libraryDir is LibraryDirective) { |
| 114 var arguments = annotation.arguments.arguments; | 100 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); |
| 115 if (!arguments.isEmpty) { | 101 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); |
| 116 var namedExpression = arguments.first as NamedExpression; | |
| 117 var literal = namedExpression.expression as SimpleStringLiteral; | |
| 118 jsDefaultValue = literal.stringValue; | |
| 119 } | |
| 120 } | 102 } |
| 121 } | 103 } |
| 104 if (jsDefaultValue == null) jsDefaultValue = '{}'; | |
| 105 | |
| 122 var body = <JS.Statement>[]; | 106 var body = <JS.Statement>[]; |
| 123 | 107 |
| 124 // Collect classes we need to emit, used for: | 108 // Collect classes we need to emit, used for: |
| 125 // * tracks what we've emitted so we don't emit twice | 109 // * tracks what we've emitted so we don't emit twice |
| 126 // * provides a mapping from ClassElement back to the ClassDeclaration. | 110 // * provides a mapping from ClassElement back to the ClassDeclaration. |
| 127 for (var unit in library.partsThenLibrary) { | 111 for (var unit in library.partsThenLibrary) { |
| 128 for (var decl in unit.declarations) { | 112 for (var decl in unit.declarations) { |
| 129 if (decl is ClassDeclaration || | 113 if (decl is ClassDeclaration || |
| 130 decl is ClassTypeAlias || | 114 decl is ClassTypeAlias || |
| 131 decl is FunctionTypeAlias) { | 115 decl is FunctionTypeAlias) { |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 | 294 |
| 311 var name = node.name.name; | 295 var name = node.name.name; |
| 312 var heritage = | 296 var heritage = |
| 313 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); | 297 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); |
| 314 var classDecl = new JS.ClassDeclaration( | 298 var classDecl = new JS.ClassDeclaration( |
| 315 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); | 299 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); |
| 316 | 300 |
| 317 return _finishClassDef(type, classDecl); | 301 return _finishClassDef(type, classDecl); |
| 318 } | 302 } |
| 319 | 303 |
| 320 JS.Statement _emitJsType(ClassDeclaration node, Annotation jsName) { | 304 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { |
| 321 var dartName = node.name.name; | 305 var jsTypeName = getConstantField(jsName, 'name', types.stringType); |
| 322 var jsTypeName = _getLiteralStringNamedArg(jsName, 'name'); | |
| 323 | 306 |
| 324 if (jsTypeName != null && jsTypeName != dartName) { | 307 if (jsTypeName != null && jsTypeName != dartClassName) { |
| 325 // We export the JS type as if it was a Dart type. For example this allows | 308 // We export the JS type as if it was a Dart type. For example this allows |
| 326 // `dom.InputElement` to actually be HTMLInputElement. | 309 // `dom.InputElement` to actually be HTMLInputElement. |
| 327 // TODO(jmesserly): if we had the JsName on the Element, we could just | 310 // TODO(jmesserly): if we had the JsName on the Element, we could just |
| 328 // generate it correctly when we refer to it. | 311 // generate it correctly when we refer to it. |
| 329 if (isPublic(dartName)) _addExport(dartName); | 312 if (isPublic(dartClassName)) _addExport(dartClassName); |
| 330 return js.statement('let # = #;', [dartName, jsTypeName]); | 313 return js.statement('let # = #;', [dartClassName, jsTypeName]); |
| 331 } | 314 } |
| 332 return null; | 315 return null; |
| 333 } | 316 } |
| 334 | 317 |
| 335 @override | 318 @override |
| 336 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 319 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
| 337 // If we've already emitted this class, skip it. | 320 // If we've already emitted this class, skip it. |
| 338 var type = node.element.type; | 321 var type = node.element.type; |
| 339 if (_pendingClasses.remove(node.element) == null) return null; | 322 if (_pendingClasses.remove(node.element) == null) return null; |
| 340 | 323 |
| 341 var jsName = _getJsNameAnnotation(node); | 324 var jsName = getAnnotationValue(node, _isJsNameAnnotation); |
| 342 if (jsName != null) return _emitJsType(node, jsName); | 325 if (jsName != null) return _emitJsType(node.name.name, jsName); |
| 343 | 326 |
| 344 currentClass = node; | 327 currentClass = node; |
| 345 | 328 |
| 346 var ctors = <ConstructorDeclaration>[]; | 329 var ctors = <ConstructorDeclaration>[]; |
| 347 var fields = <FieldDeclaration>[]; | 330 var fields = <FieldDeclaration>[]; |
| 348 var staticFields = <FieldDeclaration>[]; | 331 var staticFields = <FieldDeclaration>[]; |
| 349 for (var member in node.members) { | 332 for (var member in node.members) { |
| 350 if (member is ConstructorDeclaration) { | 333 if (member is ConstructorDeclaration) { |
| 351 ctors.add(member); | 334 ctors.add(member); |
| 352 } else if (member is FieldDeclaration) { | 335 } else if (member is FieldDeclaration) { |
| (...skipping 2125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2478 /// This method is conservative: it should never return `true` unless it is | 2461 /// This method is conservative: it should never return `true` unless it is |
| 2479 /// certain the [node] is stateless, because generated code may rely on the | 2462 /// certain the [node] is stateless, because generated code may rely on the |
| 2480 /// correctness of a `true` value. However it may return `false` for things | 2463 /// correctness of a `true` value. However it may return `false` for things |
| 2481 /// that are in fact, stateless. | 2464 /// that are in fact, stateless. |
| 2482 bool _isStateless(Expression node, [AstNode context]) { | 2465 bool _isStateless(Expression node, [AstNode context]) { |
| 2483 if (node is SimpleIdentifier) { | 2466 if (node is SimpleIdentifier) { |
| 2484 var e = node.staticElement; | 2467 var e = node.staticElement; |
| 2485 if (e is PropertyAccessorElement) e = e.variable; | 2468 if (e is PropertyAccessorElement) e = e.variable; |
| 2486 if (e is VariableElement && !e.isSynthetic) { | 2469 if (e is VariableElement && !e.isSynthetic) { |
| 2487 if (e.isFinal) return true; | 2470 if (e.isFinal) return true; |
| 2488 | 2471 if (e is LocalVariableElement || e is ParameterElement) { |
| 2489 // TODO(jmesserly): remove this when isPotentiallyMutated* is available | |
| 2490 // without the implementation class. Technically we shouldn't hit the | |
| 2491 // ParameterMember case based on current usage of _isStateless, but this | |
| 2492 // makes it clear we shouldn't rely on *Impl class. | |
| 2493 if (e is Member) e = e.baseElement; | |
| 2494 | |
| 2495 if (e is LocalVariableElementImpl || e is ParameterElementImpl) { | |
| 2496 // make sure the local isn't mutated in the context. | 2472 // make sure the local isn't mutated in the context. |
| 2497 return !_isPotentiallyMutated(e, context); | 2473 return !_isPotentiallyMutated(e, context); |
| 2498 } | 2474 } |
| 2499 } | 2475 } |
| 2500 } | 2476 } |
| 2501 return false; | 2477 return false; |
| 2502 } | 2478 } |
| 2503 | 2479 |
| 2504 /// Returns true if the local variable is potentially mutated within [context]. | 2480 /// Returns true if the local variable is potentially mutated within [context]. |
| 2505 /// This accounts for closures that may have been created outside of [context]. | 2481 /// This accounts for closures that may have been created outside of [context]. |
| 2506 bool _isPotentiallyMutated(VariableElementImpl e, [AstNode context]) { | 2482 bool _isPotentiallyMutated(VariableElement e, [AstNode context]) { |
| 2507 if (e.isPotentiallyMutatedInClosure) { | 2483 if (e.isPotentiallyMutatedInClosure) return true; |
| 2508 // TODO(jmesserly): this returns true incorrectly in some cases, because | |
| 2509 // VariableResolverVisitor only checks that enclosingElement is not the | |
| 2510 // function element, but enclosingElement can be something else in some | |
| 2511 // cases (the block scope?). So it's more conservative than it could be. | |
| 2512 return true; | |
| 2513 } | |
| 2514 if (e.isPotentiallyMutatedInScope) { | 2484 if (e.isPotentiallyMutatedInScope) { |
| 2515 // Need to visit the context looking for assignment to this local. | 2485 // Need to visit the context looking for assignment to this local. |
| 2516 if (context != null) { | 2486 if (context != null) { |
| 2517 var visitor = new _AssignmentFinder(e); | 2487 var visitor = new _AssignmentFinder(e); |
| 2518 context.accept(visitor); | 2488 context.accept(visitor); |
| 2519 return visitor._potentiallyMutated; | 2489 return visitor._potentiallyMutated; |
| 2520 } | 2490 } |
| 2521 return true; | 2491 return true; |
| 2522 } | 2492 } |
| 2523 return false; | 2493 return false; |
| 2524 } | 2494 } |
| 2525 | 2495 |
| 2526 /// Adapted from VariableResolverVisitor. Finds an assignment to a given | 2496 /// Adapted from VariableResolverVisitor. Finds an assignment to a given |
| 2527 /// local variable. | 2497 /// local variable. |
| 2528 // TODO(jmesserly): change type annotation to not be *Impl once | 2498 // TODO(jmesserly): change type annotation to not be *Impl once |
| 2529 // isPotentiallyMutated is available on VariableElement. | 2499 // isPotentiallyMutated is available on VariableElement. |
| 2530 class _AssignmentFinder extends RecursiveAstVisitor { | 2500 class _AssignmentFinder extends RecursiveAstVisitor { |
| 2531 final VariableElementImpl _variable; | 2501 final VariableElement _variable; |
| 2532 bool _potentiallyMutated = false; | 2502 bool _potentiallyMutated = false; |
| 2533 | 2503 |
| 2534 _AssignmentFinder(this._variable); | 2504 _AssignmentFinder(this._variable); |
| 2535 | 2505 |
| 2536 @override | 2506 @override |
| 2537 visitSimpleIdentifier(SimpleIdentifier node) { | 2507 visitSimpleIdentifier(SimpleIdentifier node) { |
| 2538 // Ignore if qualified. | 2508 // Ignore if qualified. |
| 2539 AstNode parent = node.parent; | 2509 AstNode parent = node.parent; |
| 2540 if (parent is PrefixedIdentifier && | 2510 if (parent is PrefixedIdentifier && |
| 2541 identical(parent.identifier, node)) return; | 2511 identical(parent.identifier, node)) return; |
| 2542 if (parent is PropertyAccess && | 2512 if (parent is PropertyAccess && |
| 2543 identical(parent.propertyName, node)) return; | 2513 identical(parent.propertyName, node)) return; |
| 2544 if (parent is MethodInvocation && | 2514 if (parent is MethodInvocation && |
| 2545 identical(parent.methodName, node)) return; | 2515 identical(parent.methodName, node)) return; |
| 2546 if (parent is ConstructorName) return; | 2516 if (parent is ConstructorName) return; |
| 2547 if (parent is Label) return; | 2517 if (parent is Label) return; |
| 2548 | 2518 |
| 2549 if (node.inSetterContext() && node.staticElement == _variable) { | 2519 if (node.inSetterContext() && node.staticElement == _variable) { |
| 2550 _potentiallyMutated = true; | 2520 _potentiallyMutated = true; |
| 2551 } | 2521 } |
| 2552 } | 2522 } |
| 2553 } | 2523 } |
| 2554 | 2524 |
| 2555 String _getLiteralStringNamedArg(Annotation annotation, String argName) { | 2525 // TODO(jmesserly): validate the library. See issue #135. |
|
Jacob
2015/04/15 00:34:41
Is this the style we're following for referencing
Jennifer Messerly
2015/04/15 00:38:39
No idea. I used to paste full links, but other git
| |
| 2556 if (annotation.arguments != null) { | 2526 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
| 2557 var args = annotation.arguments.arguments; | 2527 |
| 2558 if (args.isNotEmpty && args[0] is NamedExpression) { | 2528 // TODO(jacobr): we would like to do something like the following |
| 2559 NamedExpression named = args[0]; | 2529 // but we don't have summary support yet. |
| 2560 if (named.name.label.name == argName && | 2530 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
| 2561 named.expression is StringLiteral) { | 2531 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
| 2562 return (named.expression as StringLiteral).stringValue; | |
| 2563 } | |
| 2564 } | |
| 2565 } | |
| 2566 return null; | |
| 2567 } | |
| OLD | NEW |