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 ddc.src.codegen.js_codegen; | 5 library ddc.src.codegen.js_codegen; |
| 6 | 6 |
| 7 import 'dart:io' show Directory, File; | 7 import 'dart:io' show Directory, File; |
| 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; |
| 11 import 'package:analyzer/src/generated/constant.dart'; | 11 import 'package:analyzer/src/generated/constant.dart'; |
| 12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/src/generated/element.dart'; |
| 13 import 'package:analyzer/src/generated/scanner.dart' | 13 import 'package:analyzer/src/generated/scanner.dart' |
| 14 show StringToken, Token, TokenType; | 14 show StringToken, Token, TokenType; |
| 15 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; | |
| 16 import 'package:source_maps/source_maps.dart' show SourceMapSpan; | |
| 17 import 'package:source_span/source_span.dart' show SourceLocation; | |
| 15 import 'package:path/path.dart' as path; | 18 import 'package:path/path.dart' as path; |
| 16 | 19 |
| 17 // TODO(jmesserly): import from its own package | 20 // TODO(jmesserly): import from its own package |
| 18 import 'package:dev_compiler/src/js/js_ast.dart' as JS; | 21 import 'package:dev_compiler/src/js/js_ast.dart' as JS; |
| 19 import 'package:dev_compiler/src/js/js_ast.dart' show js; | 22 import 'package:dev_compiler/src/js/js_ast.dart' show js; |
| 20 | 23 |
| 21 import 'package:dev_compiler/src/checker/rules.dart'; | 24 import 'package:dev_compiler/src/checker/rules.dart'; |
| 22 import 'package:dev_compiler/src/info.dart'; | 25 import 'package:dev_compiler/src/info.dart'; |
| 26 import 'package:dev_compiler/src/options.dart'; | |
| 23 import 'package:dev_compiler/src/report.dart'; | 27 import 'package:dev_compiler/src/report.dart'; |
| 24 import 'package:dev_compiler/src/utils.dart'; | 28 import 'package:dev_compiler/src/utils.dart'; |
| 25 import 'code_generator.dart'; | 29 import 'code_generator.dart'; |
| 26 | 30 |
| 27 // This must match the optional parameter name used in runtime.js | 31 // This must match the optional parameter name used in runtime.js |
| 28 const String _jsNamedParameterName = r'opt$'; | 32 const String _jsNamedParameterName = r'opt$'; |
| 29 | 33 |
| 30 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 34 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| 31 final LibraryInfo libraryInfo; | 35 final LibraryInfo libraryInfo; |
| 32 final TypeRules rules; | 36 final TypeRules rules; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 52 JS.Block generateLibrary( | 56 JS.Block generateLibrary( |
| 53 Iterable<CompilationUnit> units, CheckerReporter reporter) { | 57 Iterable<CompilationUnit> units, CheckerReporter reporter) { |
| 54 var body = <JS.Statement>[]; | 58 var body = <JS.Statement>[]; |
| 55 for (var unit in units) { | 59 for (var unit in units) { |
| 56 // TODO(jmesserly): this is needed because RestrictedTypeRules can send | 60 // TODO(jmesserly): this is needed because RestrictedTypeRules can send |
| 57 // messages to CheckerReporter, for things like missing types. | 61 // messages to CheckerReporter, for things like missing types. |
| 58 // We should probably refactor so this can't happen. | 62 // We should probably refactor so this can't happen. |
| 59 var source = unit.element.source; | 63 var source = unit.element.source; |
| 60 _constEvaluator = new ConstantEvaluator(source, rules.provider); | 64 _constEvaluator = new ConstantEvaluator(source, rules.provider); |
| 61 reporter.enterSource(source); | 65 reporter.enterSource(source); |
| 62 body.add(unit.accept(this)); | 66 body.add(_visit(unit)); |
| 63 reporter.leaveSource(); | 67 reporter.leaveSource(); |
| 64 } | 68 } |
| 65 | 69 |
| 66 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); | 70 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); |
| 67 | 71 |
| 68 // TODO(jmesserly): make these immutable in JS? | 72 // TODO(jmesserly): make these immutable in JS? |
| 69 for (var name in _exports) { | 73 for (var name in _exports) { |
| 70 body.add(js.statement('#.# = #;', [_libraryName, name, name])); | 74 body.add(js.statement('#.# = #;', [_libraryName, name, name])); |
| 71 } | 75 } |
| 72 | 76 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 84 | 88 |
| 85 @override | 89 @override |
| 86 JS.Statement visitCompilationUnit(CompilationUnit node) { | 90 JS.Statement visitCompilationUnit(CompilationUnit node) { |
| 87 // TODO(jmesserly): scriptTag, directives. | 91 // TODO(jmesserly): scriptTag, directives. |
| 88 var body = <JS.Statement>[]; | 92 var body = <JS.Statement>[]; |
| 89 for (var child in node.declarations) { | 93 for (var child in node.declarations) { |
| 90 // Attempt to group adjacent fields/properties. | 94 // Attempt to group adjacent fields/properties. |
| 91 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 95 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
| 92 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 96 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
| 93 | 97 |
| 94 var code = child.accept(this); | 98 var code = _visit(child); |
| 95 if (code != null) body.add(code); | 99 if (code != null) body.add(code); |
| 96 } | 100 } |
| 97 // Flush any unwritten fields/properties. | 101 // Flush any unwritten fields/properties. |
| 98 _flushLazyFields(body); | 102 _flushLazyFields(body); |
| 99 _flushLibraryProperties(body); | 103 _flushLibraryProperties(body); |
| 100 return _statement(body); | 104 return _statement(body); |
| 101 } | 105 } |
| 102 | 106 |
| 103 bool isPublic(String name) => !name.startsWith('_'); | 107 bool isPublic(String name) => !name.startsWith('_'); |
| 104 | 108 |
| 105 /// Conversions that we don't handle end up here. | 109 /// Conversions that we don't handle end up here. |
| 106 @override | 110 @override |
| 107 visitConversion(Conversion node) { | 111 visitConversion(Conversion node) { |
| 108 var from = node.baseType; | 112 var from = node.baseType; |
| 109 var to = node.convertedType; | 113 var to = node.convertedType; |
| 110 | 114 |
| 111 // All Dart number types map to a JS double. | 115 // All Dart number types map to a JS double. |
| 112 if (rules.isNumType(from) && | 116 if (rules.isNumType(from) && |
| 113 (rules.isIntType(to) || rules.isDoubleType(to))) { | 117 (rules.isIntType(to) || rules.isDoubleType(to))) { |
| 114 // TODO(jmesserly): a lot of these checks are meaningless, as people use | 118 // TODO(jmesserly): a lot of these checks are meaningless, as people use |
| 115 // `num` to mean "any kind of number" rather than "could be null". | 119 // `num` to mean "any kind of number" rather than "could be null". |
| 116 // The core libraries especially suffer from this problem, with many of | 120 // The core libraries especially suffer from this problem, with many of |
| 117 // the `num` methods returning `num`. | 121 // the `num` methods returning `num`. |
| 118 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { | 122 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { |
| 119 // Converting from a nullable number to a non-nullable number | 123 // Converting from a nullable number to a non-nullable number |
| 120 // only requires a null check. | 124 // only requires a null check. |
| 121 return js.call('dart.notNull(#)', node.expression.accept(this)); | 125 return js.call('dart.notNull(#)', _visit(node.expression)); |
| 122 } else { | 126 } else { |
| 123 // A no-op in JavaScript. | 127 // A no-op in JavaScript. |
| 124 return node.expression.accept(this); | 128 return _visit(node.expression); |
| 125 } | 129 } |
| 126 } | 130 } |
| 127 | 131 |
| 128 return _emitCast(node.expression, to); | 132 return _emitCast(node.expression, to); |
| 129 } | 133 } |
| 130 | 134 |
| 131 @override | 135 @override |
| 132 visitAsExpression(AsExpression node) => | 136 visitAsExpression(AsExpression node) => |
| 133 _emitCast(node.expression, node.type.type); | 137 _emitCast(node.expression, node.type.type); |
| 134 | 138 |
| 135 _emitCast(Expression node, DartType type) => | 139 _emitCast(Expression node, DartType type) => |
| 136 js.call('dart.as(#)', [[node.accept(this), _emitTypeName(type)]]); | 140 js.call('dart.as(#)', [[_visit(node), _emitTypeName(type)]]); |
| 137 | 141 |
| 138 @override | 142 @override |
| 139 visitIsExpression(IsExpression node) { | 143 visitIsExpression(IsExpression node) { |
| 140 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. | 144 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
| 141 JS.Expression result; | 145 JS.Expression result; |
| 142 var type = node.type.type; | 146 var type = node.type.type; |
| 143 var lhs = node.expression.accept(this); | 147 var lhs = _visit(node.expression); |
| 144 var typeofName = _jsTypeofName(type); | 148 var typeofName = _jsTypeofName(type); |
| 145 if (typeofName != null) { | 149 if (typeofName != null) { |
| 146 result = js.call('typeof # == #', [lhs, typeofName]); | 150 result = js.call('typeof # == #', [lhs, typeofName]); |
| 147 } else { | 151 } else { |
| 148 // Always go through a runtime helper, because implicit interfaces. | 152 // Always go through a runtime helper, because implicit interfaces. |
| 149 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); | 153 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); |
| 150 } | 154 } |
| 151 | 155 |
| 152 if (node.notOperator != null) { | 156 if (node.notOperator != null) { |
| 153 return js.call('!#', result); | 157 return js.call('!#', result); |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 233 // Iff no constructor is specified for a class C, it implicitly has a | 237 // Iff no constructor is specified for a class C, it implicitly has a |
| 234 // default constructor `C() : super() {}`, unless C is class Object. | 238 // default constructor `C() : super() {}`, unless C is class Object. |
| 235 if (ctors.isEmpty && !node.element.type.isObject) { | 239 if (ctors.isEmpty && !node.element.type.isObject) { |
| 236 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 240 jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
| 237 } | 241 } |
| 238 | 242 |
| 239 for (var member in node.members) { | 243 for (var member in node.members) { |
| 240 if (member is ConstructorDeclaration) { | 244 if (member is ConstructorDeclaration) { |
| 241 jsMethods.add(_emitConstructor(member, name, fields)); | 245 jsMethods.add(_emitConstructor(member, name, fields)); |
| 242 } else if (member is MethodDeclaration) { | 246 } else if (member is MethodDeclaration) { |
| 243 jsMethods.add(member.accept(this)); | 247 jsMethods.add(_visit(member)); |
| 244 } | 248 } |
| 245 } | 249 } |
| 246 | 250 |
| 247 // Support for adapting dart:core Iterator/Iterable to ES6 versions. | 251 // Support for adapting dart:core Iterator/Iterable to ES6 versions. |
| 248 // This lets them use for-of loops transparently. | 252 // This lets them use for-of loops transparently. |
| 249 // https://github.com/lukehoban/es6features#iterators--forof | 253 // https://github.com/lukehoban/es6features#iterators--forof |
| 250 if (node.element.library.isDartCore && node.element.name == 'Iterable') { | 254 if (node.element.library.isDartCore && node.element.name == 'Iterable') { |
| 251 JS.Fun body = js.call('''function() { | 255 JS.Fun body = js.call('''function() { |
| 252 var iterator = this.iterator; | 256 var iterator = this.iterator; |
| 253 return { | 257 return { |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 324 | 328 |
| 325 JS.Method _emitConstructor(ConstructorDeclaration node, String className, | 329 JS.Method _emitConstructor(ConstructorDeclaration node, String className, |
| 326 List<FieldDeclaration> fields) { | 330 List<FieldDeclaration> fields) { |
| 327 if (_externalOrNative(node)) return null; | 331 if (_externalOrNative(node)) return null; |
| 328 | 332 |
| 329 var name = _constructorName(className, node.name); | 333 var name = _constructorName(className, node.name); |
| 330 | 334 |
| 331 // We generate constructors as initializer methods in the class; | 335 // We generate constructors as initializer methods in the class; |
| 332 // this allows use of `super` for instance methods/properties. | 336 // this allows use of `super` for instance methods/properties. |
| 333 // It also avoids V8 restrictions on `super` in default constructors. | 337 // It also avoids V8 restrictions on `super` in default constructors. |
| 334 return new JS.Method(new JS.PropertyName(name), new JS.Fun( | 338 return new JS.Method(new JS.PropertyName(name), |
| 335 node.parameters.accept(this), _emitConstructorBody(node, fields))); | 339 new JS.Fun(_visit(node.parameters), _emitConstructorBody(node, fields))) |
| 340 ..sourceInformation = node; | |
| 336 } | 341 } |
| 337 | 342 |
| 338 String _constructorName(String className, SimpleIdentifier name) { | 343 String _constructorName(String className, SimpleIdentifier name) { |
| 339 if (name == null) return className; | 344 if (name == null) return className; |
| 340 return '$className\$${name.name}'; | 345 return '$className\$${name.name}'; |
| 341 } | 346 } |
| 342 | 347 |
| 343 JS.Block _emitConstructorBody( | 348 JS.Block _emitConstructorBody( |
| 344 ConstructorDeclaration node, List<FieldDeclaration> fields) { | 349 ConstructorDeclaration node, List<FieldDeclaration> fields) { |
| 345 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | 350 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
| 346 if (node.redirectedConstructor != null) { | 351 if (node.redirectedConstructor != null) { |
| 347 return js.statement('{ return new #(#); }', [ | 352 return js.statement('{ return new #(#); }', [ |
| 348 node.redirectedConstructor.accept(this), | 353 _visit(node.redirectedConstructor), |
| 349 node.parameters.accept(this) | 354 _visit(node.parameters) |
| 350 ]); | 355 ]); |
| 351 } | 356 } |
| 352 | 357 |
| 353 var body = <JS.Statement>[]; | 358 var body = <JS.Statement>[]; |
| 354 | 359 |
| 355 // Generate optional/named argument value assignment. These can not have | 360 // Generate optional/named argument value assignment. These can not have |
| 356 // side effects, and may be used by the constructor's initializers, so it's | 361 // side effects, and may be used by the constructor's initializers, so it's |
| 357 // nice to do them first. | 362 // nice to do them first. |
| 358 var init = _emitArgumentInitializers(node.parameters); | 363 var init = _emitArgumentInitializers(node.parameters); |
| 359 if (init != null) body.add(init); | 364 if (init != null) body.add(init); |
| 360 | 365 |
| 361 // Redirecting constructors: these are not allowed to have initializers, | 366 // Redirecting constructors: these are not allowed to have initializers, |
| 362 // and the redirecting ctor invocation runs before field initializers. | 367 // and the redirecting ctor invocation runs before field initializers. |
| 363 var redirectCall = node.initializers.firstWhere( | 368 var redirectCall = node.initializers.firstWhere( |
| 364 (i) => i is RedirectingConstructorInvocation, orElse: () => null); | 369 (i) => i is RedirectingConstructorInvocation, orElse: () => null); |
| 365 | 370 |
| 366 if (redirectCall != null) { | 371 if (redirectCall != null) { |
| 367 body.add(redirectCall.accept(this)); | 372 body.add(_visit(redirectCall)); |
| 368 return new JS.Block(body); | 373 return new JS.Block(body); |
| 369 } | 374 } |
| 370 | 375 |
| 371 // Initializers only run for non-factory constructors. | 376 // Initializers only run for non-factory constructors. |
| 372 if (node.factoryKeyword == null) { | 377 if (node.factoryKeyword == null) { |
| 373 // Generate field initializers. | 378 // Generate field initializers. |
| 374 // These are expanded into each non-redirecting constructor. | 379 // These are expanded into each non-redirecting constructor. |
| 375 // In the future we may want to create an initializer function if we have | 380 // In the future we may want to create an initializer function if we have |
| 376 // multiple constructors, but it needs to be balanced against readability. | 381 // multiple constructors, but it needs to be balanced against readability. |
| 377 body.add(_initializeFields(fields, node.parameters, node.initializers)); | 382 body.add(_initializeFields(fields, node.parameters, node.initializers)); |
| 378 | 383 |
| 379 var superCall = node.initializers.firstWhere( | 384 var superCall = node.initializers.firstWhere( |
| 380 (i) => i is SuperConstructorInvocation, orElse: () => null); | 385 (i) => i is SuperConstructorInvocation, orElse: () => null); |
| 381 | 386 |
| 382 // If no superinitializer is provided, an implicit superinitializer of the | 387 // If no superinitializer is provided, an implicit superinitializer of the |
| 383 // form `super()` is added at the end of the initializer list, unless the | 388 // form `super()` is added at the end of the initializer list, unless the |
| 384 // enclosing class is class Object. | 389 // enclosing class is class Object. |
| 385 var jsSuper = _superConstructorCall(node.parent, superCall); | 390 var jsSuper = _superConstructorCall(node.parent, superCall); |
| 386 if (jsSuper != null) body.add(jsSuper); | 391 if (jsSuper != null) body.add(jsSuper); |
| 387 } | 392 } |
| 388 | 393 |
| 389 body.add(node.body.accept(this)); | 394 body.add(_visit(node.body)); |
| 390 return new JS.Block(body); | 395 return new JS.Block(body)..sourceInformation = node; |
| 391 } | 396 } |
| 392 | 397 |
| 393 @override | 398 @override |
| 394 JS.Statement visitRedirectingConstructorInvocation( | 399 JS.Statement visitRedirectingConstructorInvocation( |
| 395 RedirectingConstructorInvocation node) { | 400 RedirectingConstructorInvocation node) { |
| 396 ClassDeclaration classDecl = node.parent.parent; | 401 ClassDeclaration classDecl = node.parent.parent; |
| 397 var className = classDecl.name.name; | 402 var className = classDecl.name.name; |
| 398 | 403 |
| 399 var name = _constructorName(className, node.constructorName); | 404 var name = _constructorName(className, node.constructorName); |
| 400 return js.statement('this.#(#);', [name, node.argumentList.accept(this)]); | 405 return js.statement('this.#(#);', [name, _visit(node.argumentList)]); |
| 401 } | 406 } |
| 402 | 407 |
| 403 JS.Statement _superConstructorCall(ClassDeclaration clazz, | 408 JS.Statement _superConstructorCall(ClassDeclaration clazz, |
| 404 [SuperConstructorInvocation node]) { | 409 [SuperConstructorInvocation node]) { |
| 405 var superCtorName = node != null ? node.constructorName : null; | 410 var superCtorName = node != null ? node.constructorName : null; |
| 406 | 411 |
| 407 var element = clazz.element; | 412 var element = clazz.element; |
| 408 if (superCtorName == null && | 413 if (superCtorName == null && |
| 409 (element.type.isObject || element.supertype.isObject)) { | 414 (element.type.isObject || element.supertype.isObject)) { |
| 410 return null; | 415 return null; |
| 411 } | 416 } |
| 412 | 417 |
| 413 var supertypeName = element.supertype.name; | 418 var supertypeName = element.supertype.name; |
| 414 var name = _constructorName(supertypeName, superCtorName); | 419 var name = _constructorName(supertypeName, superCtorName); |
| 415 | 420 |
| 416 var args = node != null ? node.argumentList.accept(this) : []; | 421 var args = node != null ? _visit(node.argumentList) : []; |
| 417 return js.statement('super.#(#);', [name, args]); | 422 return js.statement('super.#(#);', [name, args])..sourceInformation = node; |
| 418 } | 423 } |
| 419 | 424 |
| 420 /// Initialize fields. They follow the sequence: | 425 /// Initialize fields. They follow the sequence: |
| 421 /// | 426 /// |
| 422 /// 1. field declaration initializer if non-const, | 427 /// 1. field declaration initializer if non-const, |
| 423 /// 2. field initializing parameters, | 428 /// 2. field initializing parameters, |
| 424 /// 3. constructor field initializers, | 429 /// 3. constructor field initializers, |
| 425 /// 4. initialize fields not covered in 1-3 | 430 /// 4. initialize fields not covered in 1-3 |
| 426 JS.Statement _initializeFields(List<FieldDeclaration> fields, | 431 JS.Statement _initializeFields(List<FieldDeclaration> fields, |
| 427 [FormalParameterList parameters, | 432 [FormalParameterList parameters, |
| 428 NodeList<ConstructorInitializer> initializers]) { | 433 NodeList<ConstructorInitializer> initializers]) { |
| 429 var body = <JS.Statement>[]; | 434 var body = <JS.Statement>[]; |
| 430 | 435 |
| 431 // Run field initializers if they can have side-effects. | 436 // Run field initializers if they can have side-effects. |
| 432 var unsetFields = new Map<String, VariableDeclaration>(); | 437 var unsetFields = new Map<String, VariableDeclaration>(); |
| 433 for (var declaration in fields) { | 438 for (var declaration in fields) { |
| 434 for (var field in declaration.fields.variables) { | 439 for (var field in declaration.fields.variables) { |
| 435 if (_isFieldInitConstant(field)) { | 440 if (_isFieldInitConstant(field)) { |
| 436 unsetFields[field.name.name] = field; | 441 unsetFields[field.name.name] = field; |
| 437 } else { | 442 } else { |
| 438 body.add(js.statement( | 443 body.add(js.statement( |
| 439 '# = #;', [field.name.accept(this), _visitInitializer(field)])); | 444 '# = #;', [_visit(field.name), _visitInitializer(field)])); |
| 440 } | 445 } |
| 441 } | 446 } |
| 442 } | 447 } |
| 443 | 448 |
| 444 // Initialize fields from `this.fieldName` parameters. | 449 // Initialize fields from `this.fieldName` parameters. |
| 445 if (parameters != null) { | 450 if (parameters != null) { |
| 446 for (var p in parameters.parameters) { | 451 for (var p in parameters.parameters) { |
| 447 if (p is DefaultFormalParameter) p = p.parameter; | 452 if (p is DefaultFormalParameter) p = p.parameter; |
| 448 if (p is FieldFormalParameter) { | 453 if (p is FieldFormalParameter) { |
| 449 var name = p.identifier.name; | 454 var name = p.identifier.name; |
| 450 body.add(js.statement('this.# = #;', [name, name])); | 455 body.add(js.statement('this.# = #;', [name, name])); |
| 451 unsetFields.remove(name); | 456 unsetFields.remove(name); |
| 452 } | 457 } |
| 453 } | 458 } |
| 454 } | 459 } |
| 455 | 460 |
| 456 // Run constructor field initializers such as `: foo = bar.baz` | 461 // Run constructor field initializers such as `: foo = bar.baz` |
| 457 if (initializers != null) { | 462 if (initializers != null) { |
| 458 for (var init in initializers) { | 463 for (var init in initializers) { |
| 459 if (init is ConstructorFieldInitializer) { | 464 if (init is ConstructorFieldInitializer) { |
| 460 body.add(js.statement('# = #;', [ | 465 body.add(js.statement( |
| 461 init.fieldName.accept(this), | 466 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); |
| 462 init.expression.accept(this) | |
| 463 ])); | |
| 464 unsetFields.remove(init.fieldName.name); | 467 unsetFields.remove(init.fieldName.name); |
| 465 } | 468 } |
| 466 } | 469 } |
| 467 } | 470 } |
| 468 | 471 |
| 469 // Initialize all remaining fields | 472 // Initialize all remaining fields |
| 470 unsetFields.forEach((name, field) { | 473 unsetFields.forEach((name, field) { |
| 471 JS.Expression value; | 474 JS.Expression value; |
| 472 if (field.initializer != null) { | 475 if (field.initializer != null) { |
| 473 value = field.initializer.accept(this); | 476 value = _visit(field.initializer); |
| 474 } else { | 477 } else { |
| 475 var type = rules.elementType(field.element); | 478 var type = rules.elementType(field.element); |
| 476 if (rules.maybeNonNullableType(type)) { | 479 if (rules.maybeNonNullableType(type)) { |
| 477 value = js.call('dart.as(null, #)', _emitTypeName(type)); | 480 value = js.call('dart.as(null, #)', _emitTypeName(type)); |
| 478 } else { | 481 } else { |
| 479 value = new JS.LiteralNull(); | 482 value = new JS.LiteralNull(); |
| 480 } | 483 } |
| 481 } | 484 } |
| 482 body.add(js.statement('this.# = #;', [name, value])); | 485 body.add(js.statement('this.# = #;', [name, value])); |
| 483 }); | 486 }); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 525 name, | 528 name, |
| 526 _defaultParamValue(param) | 529 _defaultParamValue(param) |
| 527 ])); | 530 ])); |
| 528 } | 531 } |
| 529 } | 532 } |
| 530 return _statement(body); | 533 return _statement(body); |
| 531 } | 534 } |
| 532 | 535 |
| 533 JS.Expression _defaultParamValue(FormalParameter param) { | 536 JS.Expression _defaultParamValue(FormalParameter param) { |
| 534 if (param is DefaultFormalParameter && param.defaultValue != null) { | 537 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 535 return param.defaultValue.accept(this); | 538 return _visit(param.defaultValue); |
| 536 } else { | 539 } else { |
| 537 return new JS.LiteralNull(); | 540 return new JS.LiteralNull(); |
| 538 } | 541 } |
| 539 } | 542 } |
| 540 | 543 |
| 541 @override | 544 @override |
| 542 JS.Method visitMethodDeclaration(MethodDeclaration node) { | 545 JS.Method visitMethodDeclaration(MethodDeclaration node) { |
| 543 if (node.isAbstract || _externalOrNative(node)) { | 546 if (node.isAbstract || _externalOrNative(node)) { |
| 544 return null; | 547 return null; |
| 545 } | 548 } |
| 546 | 549 |
| 547 var params = _visit(node.parameters); | 550 var params = _visit(node.parameters); |
| 548 if (params == null) params = []; | 551 if (params == null) params = []; |
| 549 | 552 |
| 550 return new JS.Method(new JS.PropertyName(_jsMethodName(node.name.name)), | 553 return new JS.Method(new JS.PropertyName(_jsMethodName(node.name.name)), |
| 551 new JS.Fun(params, node.body.accept(this)), | 554 new JS.Fun(params, _visit(node.body)), |
| 552 isGetter: node.isGetter, | 555 isGetter: node.isGetter, |
| 553 isSetter: node.isSetter, | 556 isSetter: node.isSetter, |
| 554 isStatic: node.isStatic); | 557 isStatic: node.isStatic); |
| 555 } | 558 } |
| 556 | 559 |
| 557 @override | 560 @override |
| 558 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 561 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
| 559 assert(node.parent is CompilationUnit); | 562 assert(node.parent is CompilationUnit); |
| 560 | 563 |
| 561 if (_externalOrNative(node)) return null; | 564 if (_externalOrNative(node)) return null; |
| 562 | 565 |
| 563 if (node.isGetter || node.isSetter) { | 566 if (node.isGetter || node.isSetter) { |
| 564 // Add these later so we can use getter/setter syntax. | 567 // Add these later so we can use getter/setter syntax. |
| 565 _properties.add(node); | 568 _properties.add(node); |
| 566 return null; | 569 return null; |
| 567 } | 570 } |
| 568 | 571 |
| 569 var body = <JS.Statement>[]; | 572 var body = <JS.Statement>[]; |
| 570 _flushLibraryProperties(body); | 573 _flushLibraryProperties(body); |
| 571 | 574 |
| 572 var name = node.name.name; | 575 var name = node.name.name; |
| 573 body.add(js.comment('Function $name: ${node.element.type}')); | 576 body.add(js.comment('Function $name: ${node.element.type}')); |
| 574 | 577 |
| 575 body.add(new JS.FunctionDeclaration(new JS.VariableDeclaration(name), | 578 body.add(new JS.FunctionDeclaration( |
| 576 node.functionExpression.accept(this))); | 579 new JS.VariableDeclaration(name), _visit(node.functionExpression))); |
| 577 | 580 |
| 578 if (isPublic(name)) _exports.add(name); | 581 if (isPublic(name)) _exports.add(name); |
| 579 return _statement(body); | 582 return _statement(body); |
| 580 } | 583 } |
| 581 | 584 |
| 582 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 585 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
| 583 var name = node.name.name; | 586 var name = node.name.name; |
| 584 if (isPublic(name)) _exports.add(name); | 587 if (isPublic(name)) _exports.add(name); |
| 585 return new JS.Method( | 588 return new JS.Method( |
| 586 new JS.PropertyName(name), node.functionExpression.accept(this), | 589 new JS.PropertyName(name), _visit(node.functionExpression), |
| 587 isGetter: node.isGetter, isSetter: node.isSetter); | 590 isGetter: node.isGetter, isSetter: node.isSetter); |
| 588 } | 591 } |
| 589 | 592 |
| 590 @override | 593 @override |
| 591 JS.Expression visitFunctionExpression(FunctionExpression node) { | 594 JS.Expression visitFunctionExpression(FunctionExpression node) { |
| 592 var params = _visit(node.parameters); | 595 var params = _visit(node.parameters); |
| 593 if (params == null) params = []; | 596 if (params == null) params = []; |
| 594 | 597 |
| 595 if (node.parent is FunctionDeclaration) { | 598 if (node.parent is FunctionDeclaration) { |
| 596 return new JS.Fun(params, node.body.accept(this)); | 599 return new JS.Fun(params, _visit(node.body)); |
| 597 } else { | 600 } else { |
| 598 var bindThis = _maybeBindThis(node.body); | 601 var bindThis = _maybeBindThis(node.body); |
| 599 | 602 |
| 600 String code; | 603 String code; |
| 601 AstNode body; | 604 AstNode body; |
| 602 var nodeBody = node.body; | 605 var nodeBody = node.body; |
| 603 if (nodeBody is ExpressionFunctionBody) { | 606 if (nodeBody is ExpressionFunctionBody) { |
| 604 code = '(#) => #'; | 607 code = '(#) => #'; |
| 605 body = nodeBody.expression; | 608 body = nodeBody.expression; |
| 606 } else { | 609 } else { |
| 607 code = '(#) => { #; }'; | 610 code = '(#) => { #; }'; |
| 608 body = nodeBody; | 611 body = nodeBody; |
| 609 } | 612 } |
| 610 return js.call('($code)$bindThis', [params, body.accept(this)]); | 613 return js.call('($code)$bindThis', [params, _visit(body)]); |
| 611 } | 614 } |
| 612 } | 615 } |
| 613 | 616 |
| 614 @override | 617 @override |
| 615 JS.Statement visitFunctionDeclarationStatement( | 618 JS.Statement visitFunctionDeclarationStatement( |
| 616 FunctionDeclarationStatement node) { | 619 FunctionDeclarationStatement node) { |
| 617 var func = node.functionDeclaration; | 620 var func = node.functionDeclaration; |
| 618 if (func.isGetter || func.isSetter) { | 621 if (func.isGetter || func.isSetter) { |
| 619 return js.comment('Unimplemented function get/set statement: $node'); | 622 return js.comment('Unimplemented function get/set statement: $node'); |
| 620 } | 623 } |
| 621 | 624 |
| 622 var name = new JS.VariableDeclaration(func.name.name); | 625 var name = new JS.VariableDeclaration(func.name.name); |
| 623 return new JS.Block([ | 626 return new JS.Block([ |
| 624 js.comment("// Function ${func.name.name}: ${func.element.type}\n"), | 627 js.comment("// Function ${func.name.name}: ${func.element.type}\n"), |
| 625 new JS.FunctionDeclaration(name, func.functionExpression.accept(this)) | 628 new JS.FunctionDeclaration(name, _visit(func.functionExpression)) |
| 626 ]); | 629 ]); |
| 627 } | 630 } |
| 628 | 631 |
| 629 /// Writes a simple identifier. This can handle implicit `this` as well as | 632 /// Writes a simple identifier. This can handle implicit `this` as well as |
| 630 /// going through the qualified library name if necessary. | 633 /// going through the qualified library name if necessary. |
| 631 @override | 634 @override |
| 632 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { | 635 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { |
| 633 var e = node.staticElement; | 636 var e = node.staticElement; |
| 634 if (e == null) { | 637 if (e == null) { |
| 635 return js.commentExpression( | 638 return js.commentExpression( |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 684 var lhs = node.leftHandSide; | 687 var lhs = node.leftHandSide; |
| 685 var rhs = node.rightHandSide; | 688 var rhs = node.rightHandSide; |
| 686 if (lhs is IndexExpression) { | 689 if (lhs is IndexExpression) { |
| 687 String code; | 690 String code; |
| 688 var target = _getTarget(lhs); | 691 var target = _getTarget(lhs); |
| 689 if (rules.isDynamicTarget(target)) { | 692 if (rules.isDynamicTarget(target)) { |
| 690 code = 'dart.dsetindex(#, #, #)'; | 693 code = 'dart.dsetindex(#, #, #)'; |
| 691 } else { | 694 } else { |
| 692 code = '#.set(#, #)'; | 695 code = '#.set(#, #)'; |
| 693 } | 696 } |
| 694 return js.call(code, [ | 697 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 695 target.accept(this), | |
| 696 lhs.index.accept(this), | |
| 697 rhs.accept(this) | |
| 698 ]); | |
| 699 } | 698 } |
| 700 | 699 |
| 701 if (lhs is PropertyAccess) { | 700 if (lhs is PropertyAccess) { |
| 702 var target = _getTarget(lhs); | 701 var target = _getTarget(lhs); |
| 703 if (rules.isDynamicTarget(target)) { | 702 if (rules.isDynamicTarget(target)) { |
| 704 return js.call('dart.dput(#, #, #)', [ | 703 return js.call('dart.dput(#, #, #)', [ |
| 705 target.accept(this), | 704 _visit(target), |
| 706 js.string(lhs.propertyName.name, "'"), | 705 js.string(lhs.propertyName.name, "'"), |
| 707 rhs.accept(this) | 706 _visit(rhs) |
| 708 ]); | 707 ]); |
| 709 } | 708 } |
| 710 } | 709 } |
| 711 | 710 |
| 712 if (node.parent is ExpressionStatement && | 711 if (node.parent is ExpressionStatement && |
| 713 rhs is CascadeExpression && | 712 rhs is CascadeExpression && |
| 714 _isStateless(lhs, rhs)) { | 713 _isStateless(lhs, rhs)) { |
| 715 // Special case: cascade assignment to a variable in a statement. | 714 // Special case: cascade assignment to a variable in a statement. |
| 716 // We can reuse the variable to desugar it: | 715 // We can reuse the variable to desugar it: |
| 717 // result = []..length = length; | 716 // result = []..length = length; |
| 718 // becomes: | 717 // becomes: |
| 719 // result = []; | 718 // result = []; |
| 720 // result.length = length; | 719 // result.length = length; |
| 721 var savedCascadeTemp = _cascadeTarget; | 720 var savedCascadeTemp = _cascadeTarget; |
| 722 _cascadeTarget = lhs; | 721 _cascadeTarget = lhs; |
| 723 | 722 |
| 724 var body = []; | 723 var body = []; |
| 725 body.add( | 724 body.add(js.statement('# = #;', [_visit(lhs), _visit(rhs.target)])); |
| 726 js.statement('# = #;', [lhs.accept(this), rhs.target.accept(this)])); | |
| 727 for (var section in rhs.cascadeSections) { | 725 for (var section in rhs.cascadeSections) { |
| 728 body.add(new JS.ExpressionStatement(section.accept(this))); | 726 body.add(new JS.ExpressionStatement(_visit(section))); |
| 729 } | 727 } |
| 730 | 728 |
| 731 _cascadeTarget = savedCascadeTemp; | 729 _cascadeTarget = savedCascadeTemp; |
| 732 return _statement(body); | 730 return _statement(body); |
| 733 } | 731 } |
| 734 | 732 |
| 735 return js.call('# = #', [lhs.accept(this), rhs.accept(this)]); | 733 return js.call('# = #', [_visit(lhs), _visit(rhs)]); |
| 736 } | 734 } |
| 737 | 735 |
| 738 @override | 736 @override |
| 739 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { | 737 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 740 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); | 738 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); |
| 741 var ret = new JS.Return(node.expression.accept(this)); | 739 var ret = new JS.Return(_visit(node.expression)); |
| 742 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); | 740 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); |
| 743 } | 741 } |
| 744 | 742 |
| 745 @override | 743 @override |
| 746 JS.Block visitEmptyFunctionBody(EmptyFunctionBody node) => new JS.Block([]); | 744 JS.Block visitEmptyFunctionBody(EmptyFunctionBody node) => new JS.Block([]); |
| 747 | 745 |
| 748 @override | 746 @override |
| 749 JS.Block visitBlockFunctionBody(BlockFunctionBody node) { | 747 JS.Block visitBlockFunctionBody(BlockFunctionBody node) { |
| 750 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); | 748 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); |
| 751 var block = visitBlock(node.block); | 749 var block = visitBlock(node.block); |
| 752 if (initArgs != null) return new JS.Block([initArgs, block]); | 750 if (initArgs != null) return new JS.Block([initArgs, block]); |
| 753 return block; | 751 return block; |
| 754 } | 752 } |
| 755 | 753 |
| 756 @override | 754 @override |
| 757 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements)); | 755 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements)); |
| 758 | 756 |
| 759 @override | 757 @override |
| 760 visitMethodInvocation(MethodInvocation node) { | 758 visitMethodInvocation(MethodInvocation node) { |
| 761 var target = node.isCascaded ? _cascadeTarget : node.target; | 759 var target = node.isCascaded ? _cascadeTarget : node.target; |
| 762 | 760 |
| 763 var result = _emitForeignJS(node); | 761 var result = _emitForeignJS(node); |
| 764 if (result != null) return result; | 762 if (result != null) return result; |
| 765 | 763 |
| 766 if (rules.isDynamicCall(node.methodName)) { | 764 if (rules.isDynamicCall(node.methodName)) { |
| 767 var args = node.argumentList.accept(this); | 765 var args = _visit(node.argumentList); |
| 768 if (target != null) { | 766 if (target != null) { |
| 769 return js.call('dart.dinvoke(#, #, #)', [ | 767 return js.call('dart.dinvoke(#, #, #)', [ |
| 770 target.accept(this), | 768 _visit(target), |
| 771 js.string(node.methodName.name, "'"), | 769 js.string(node.methodName.name, "'"), |
| 772 args | 770 args |
| 773 ]); | 771 ]); |
| 774 } else { | 772 } else { |
| 775 return js.call( | 773 return js.call('dart.dinvokef(#, #)', [_visit(node.methodName), args]); |
| 776 'dart.dinvokef(#, #)', [node.methodName.accept(this), args]); | |
| 777 } | 774 } |
| 778 } | 775 } |
| 779 | 776 |
| 780 // TODO(jmesserly): if this resolves to a getter returning a function with | 777 // TODO(jmesserly): if this resolves to a getter returning a function with |
| 781 // a call method, we don't generate the `.call` correctly. | 778 // a call method, we don't generate the `.call` correctly. |
| 782 | 779 |
| 783 var targetJs; | 780 var targetJs; |
| 784 if (target != null) { | 781 if (target != null) { |
| 785 targetJs = js.call('#.#', [target.accept(this), node.methodName.name]); | 782 targetJs = js.call('#.#', [_visit(target), node.methodName.name]); |
| 786 } else { | 783 } else { |
| 787 targetJs = node.methodName.accept(this); | 784 targetJs = _visit(node.methodName); |
| 788 } | 785 } |
| 789 | 786 |
| 790 return js.call('#(#)', [targetJs, node.argumentList.accept(this)]); | 787 return js.call('#(#)', [targetJs, _visit(node.argumentList)]); |
| 791 } | 788 } |
| 792 | 789 |
| 793 /// Emits code for the `JS(...)` builtin. | 790 /// Emits code for the `JS(...)` builtin. |
| 794 _emitForeignJS(MethodInvocation node) { | 791 _emitForeignJS(MethodInvocation node) { |
| 795 var e = node.methodName.staticElement; | 792 var e = node.methodName.staticElement; |
| 796 if (e is FunctionElement && | 793 if (e is FunctionElement && |
| 797 e.library.name == '_foreign_helper' && | 794 e.library.name == '_foreign_helper' && |
| 798 e.name == 'JS') { | 795 e.name == 'JS') { |
| 799 var args = node.argumentList.arguments; | 796 var args = node.argumentList.arguments; |
| 800 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 797 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 811 | 808 |
| 812 @override | 809 @override |
| 813 JS.Expression visitFunctionExpressionInvocation( | 810 JS.Expression visitFunctionExpressionInvocation( |
| 814 FunctionExpressionInvocation node) { | 811 FunctionExpressionInvocation node) { |
| 815 var code; | 812 var code; |
| 816 if (rules.isDynamicCall(node.function)) { | 813 if (rules.isDynamicCall(node.function)) { |
| 817 code = 'dart.dinvokef(#, #)'; | 814 code = 'dart.dinvokef(#, #)'; |
| 818 } else { | 815 } else { |
| 819 code = '#(#)'; | 816 code = '#(#)'; |
| 820 } | 817 } |
| 821 return js.call( | 818 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
| 822 code, [node.function.accept(this), node.argumentList.accept(this)]); | |
| 823 } | 819 } |
| 824 | 820 |
| 825 @override | 821 @override |
| 826 List<JS.Expression> visitArgumentList(ArgumentList node) { | 822 List<JS.Expression> visitArgumentList(ArgumentList node) { |
| 827 var args = <JS.Expression>[]; | 823 var args = <JS.Expression>[]; |
| 828 var named = <JS.Property>[]; | 824 var named = <JS.Property>[]; |
| 829 for (var arg in node.arguments) { | 825 for (var arg in node.arguments) { |
| 830 if (arg is NamedExpression) { | 826 if (arg is NamedExpression) { |
| 831 named.add(visitNamedExpression(arg)); | 827 named.add(visitNamedExpression(arg)); |
| 832 } else { | 828 } else { |
| 833 args.add(arg.accept(this)); | 829 args.add(_visit(arg)); |
| 834 } | 830 } |
| 835 } | 831 } |
| 836 if (named.isNotEmpty) { | 832 if (named.isNotEmpty) { |
| 837 args.add(new JS.ObjectInitializer(named)); | 833 args.add(new JS.ObjectInitializer(named)); |
| 838 } | 834 } |
| 839 return args; | 835 return args; |
| 840 } | 836 } |
| 841 | 837 |
| 842 @override | 838 @override |
| 843 JS.Property visitNamedExpression(NamedExpression node) { | 839 JS.Property visitNamedExpression(NamedExpression node) { |
| 844 assert(node.parent is ArgumentList); | 840 assert(node.parent is ArgumentList); |
| 845 return new JS.Property(new JS.PropertyName(node.name.label.name), | 841 return new JS.Property( |
| 846 node.expression.accept(this)); | 842 new JS.PropertyName(node.name.label.name), _visit(node.expression)); |
| 847 } | 843 } |
| 848 | 844 |
| 849 @override | 845 @override |
| 850 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { | 846 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { |
| 851 var result = <JS.Parameter>[]; | 847 var result = <JS.Parameter>[]; |
| 852 for (FormalParameter param in node.parameters) { | 848 for (FormalParameter param in node.parameters) { |
| 853 if (param.kind == ParameterKind.NAMED) { | 849 if (param.kind == ParameterKind.NAMED) { |
| 854 result.add(new JS.Parameter(_jsNamedParameterName)); | 850 result.add(new JS.Parameter(_jsNamedParameterName)); |
| 855 break; | 851 break; |
| 856 } | 852 } |
| 857 result.add(new JS.Parameter(param.identifier.name)); | 853 result.add(new JS.Parameter(param.identifier.name)); |
| 858 } | 854 } |
| 859 return result; | 855 return result; |
| 860 } | 856 } |
| 861 | 857 |
| 862 @override | 858 @override |
| 863 JS.Statement visitExpressionStatement(ExpressionStatement node) => | 859 JS.Statement visitExpressionStatement(ExpressionStatement node) => |
| 864 _expressionStatement(node.expression.accept(this)); | 860 _expressionStatement(_visit(node.expression)); |
| 865 | 861 |
| 866 // Some expressions may choose to generate themselves as JS statements | 862 // Some expressions may choose to generate themselves as JS statements |
| 867 // if their parent is in a statement context. | 863 // if their parent is in a statement context. |
| 868 // TODO(jmesserly): refactor so we handle the special cases here, and | 864 // TODO(jmesserly): refactor so we handle the special cases here, and |
| 869 // can use better return types on the expression visit methods. | 865 // can use better return types on the expression visit methods. |
| 870 JS.Statement _expressionStatement(expr) => | 866 JS.Statement _expressionStatement(expr) => |
| 871 expr is JS.Statement ? expr : new JS.ExpressionStatement(expr); | 867 expr is JS.Statement ? expr : new JS.ExpressionStatement(expr); |
| 872 | 868 |
| 873 @override | 869 @override |
| 874 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => | 870 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => |
| 875 new JS.EmptyStatement(); | 871 new JS.EmptyStatement(); |
| 876 | 872 |
| 877 @override | 873 @override |
| 878 JS.Statement visitAssertStatement(AssertStatement node) => | 874 JS.Statement visitAssertStatement(AssertStatement node) => |
| 879 // TODO(jmesserly): only emit in checked mode. | 875 // TODO(jmesserly): only emit in checked mode. |
| 880 js.statement('dart.assert(#);', node.condition.accept(this)); | 876 js.statement('dart.assert(#);', _visit(node.condition)); |
| 881 | 877 |
| 882 @override | 878 @override |
| 883 JS.Return visitReturnStatement(ReturnStatement node) => | 879 JS.Return visitReturnStatement(ReturnStatement node) => |
| 884 new JS.Return(_visit(node.expression)); | 880 new JS.Return(_visit(node.expression)); |
| 885 | 881 |
| 886 @override | 882 @override |
| 887 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 883 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| 888 var body = <JS.Statement>[]; | 884 var body = <JS.Statement>[]; |
| 889 | 885 |
| 890 for (var field in node.variables.variables) { | 886 for (var field in node.variables.variables) { |
| 891 if (field.isConst) { | 887 if (field.isConst) { |
| 892 // constant fields don't change, so we can generate them as `let` | 888 // constant fields don't change, so we can generate them as `let` |
| 893 // but add them to the module's exports | 889 // but add them to the module's exports |
| 894 var name = field.name.name; | 890 var name = field.name.name; |
| 895 body.add(js.statement('let # = #;', [ | 891 body.add(js.statement('let # = #;', [ |
| 896 new JS.VariableDeclaration(name), | 892 new JS.VariableDeclaration(name), |
| 897 _visitInitializer(field) | 893 _visitInitializer(field) |
| 898 ])); | 894 ])); |
| 899 if (isPublic(name)) _exports.add(name); | 895 if (isPublic(name)) _exports.add(name); |
| 900 } else if (_isFieldInitConstant(field)) { | 896 } else if (_isFieldInitConstant(field)) { |
| 901 body.add(js.statement( | 897 body.add(js.statement( |
| 902 '# = #;', [field.name.accept(this), _visitInitializer(field)])); | 898 '# = #;', [_visit(field.name), _visitInitializer(field)])); |
| 903 } else { | 899 } else { |
| 904 _lazyFields.add(field); | 900 _lazyFields.add(field); |
| 905 } | 901 } |
| 906 } | 902 } |
| 907 | 903 |
| 908 return _statement(body); | 904 return _statement(body); |
| 909 } | 905 } |
| 910 | 906 |
| 911 @override | 907 @override |
| 912 visitVariableDeclarationList(VariableDeclarationList node) { | 908 visitVariableDeclarationList(VariableDeclarationList node) { |
| 913 var last = node.variables.last; | 909 var last = node.variables.last; |
| 914 var lastInitializer = last.initializer; | 910 var lastInitializer = last.initializer; |
| 915 | 911 |
| 916 List<JS.VariableInitialization> variables; | 912 List<JS.VariableInitialization> variables; |
| 917 if (lastInitializer is CascadeExpression && | 913 if (lastInitializer is CascadeExpression && |
| 918 node.parent is VariableDeclarationStatement) { | 914 node.parent is VariableDeclarationStatement) { |
| 919 // Special case: cascade as variable initializer | 915 // Special case: cascade as variable initializer |
| 920 // | 916 // |
| 921 // We can reuse the variable to desugar it: | 917 // We can reuse the variable to desugar it: |
| 922 // var result = []..length = length; | 918 // var result = []..length = length; |
| 923 // becomes: | 919 // becomes: |
| 924 // var result = []; | 920 // var result = []; |
| 925 // result.length = length; | 921 // result.length = length; |
| 926 var savedCascadeTemp = _cascadeTarget; | 922 var savedCascadeTemp = _cascadeTarget; |
| 927 _cascadeTarget = last.name; | 923 _cascadeTarget = last.name; |
| 928 | 924 |
| 929 variables = _visitList(node.variables.take(node.variables.length - 1)); | 925 variables = _visitList(node.variables.take(node.variables.length - 1)); |
| 930 variables.add(new JS.VariableInitialization( | 926 variables.add(new JS.VariableInitialization( |
| 931 new JS.VariableDeclaration(last.name.name), | 927 new JS.VariableDeclaration(last.name.name), |
| 932 lastInitializer.target.accept(this))); | 928 _visit(lastInitializer.target))); |
| 933 | 929 |
| 934 var result = <JS.Expression>[ | 930 var result = <JS.Expression>[ |
| 935 new JS.VariableDeclarationList('let', variables) | 931 new JS.VariableDeclarationList('let', variables) |
| 936 ]; | 932 ]; |
| 937 result.addAll(_visitList(lastInitializer.cascadeSections)); | 933 result.addAll(_visitList(lastInitializer.cascadeSections)); |
| 938 _cascadeTarget = savedCascadeTemp; | 934 _cascadeTarget = savedCascadeTemp; |
| 939 return _statement(result.map((e) => new JS.ExpressionStatement(e))); | 935 return _statement(result.map((e) => new JS.ExpressionStatement(e))); |
| 940 } else { | 936 } else { |
| 941 variables = _visitList(node.variables); | 937 variables = _visitList(node.variables); |
| 942 } | 938 } |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 964 } | 960 } |
| 965 | 961 |
| 966 JS.Statement _emitLazyFields( | 962 JS.Statement _emitLazyFields( |
| 967 String objExpr, List<VariableDeclaration> fields) { | 963 String objExpr, List<VariableDeclaration> fields) { |
| 968 if (fields.isEmpty) return null; | 964 if (fields.isEmpty) return null; |
| 969 | 965 |
| 970 var methods = []; | 966 var methods = []; |
| 971 for (var node in fields) { | 967 for (var node in fields) { |
| 972 var name = node.name.name; | 968 var name = node.name.name; |
| 973 methods.add(new JS.Method(new JS.PropertyName(name), | 969 methods.add(new JS.Method(new JS.PropertyName(name), |
| 974 js.call('function() { return #; }', node.initializer.accept(this)), | 970 js.call('function() { return #; }', _visit(node.initializer)), |
| 975 isGetter: true)); | 971 isGetter: true)); |
| 976 | 972 |
| 977 // TODO(jmesserly): use a dummy setter to indicate writable. | 973 // TODO(jmesserly): use a dummy setter to indicate writable. |
| 978 if (!node.isFinal) { | 974 if (!node.isFinal) { |
| 979 methods.add(new JS.Method( | 975 methods.add(new JS.Method( |
| 980 new JS.PropertyName(name), js.call('function() {}'), | 976 new JS.PropertyName(name), js.call('function() {}'), |
| 981 isSetter: true)); | 977 isSetter: true)); |
| 982 } | 978 } |
| 983 } | 979 } |
| 984 | 980 |
| 985 return js.statement( | 981 return js.statement( |
| 986 'dart.defineLazyProperties(#, { # })', [objExpr, methods]); | 982 'dart.defineLazyProperties(#, { # })', [objExpr, methods]); |
| 987 } | 983 } |
| 988 | 984 |
| 989 void _flushLibraryProperties(List<JS.Statement> body) { | 985 void _flushLibraryProperties(List<JS.Statement> body) { |
| 990 if (_properties.isEmpty) return; | 986 if (_properties.isEmpty) return; |
| 991 body.add(js.statement('dart.copyProperties(#, { # });', [ | 987 body.add(js.statement('dart.copyProperties(#, { # });', [ |
| 992 _libraryName, | 988 _libraryName, |
| 993 _properties.map(_emitTopLevelProperty) | 989 _properties.map(_emitTopLevelProperty) |
| 994 ])); | 990 ])); |
| 995 _properties.clear(); | 991 _properties.clear(); |
| 996 } | 992 } |
| 997 | 993 |
| 998 @override | 994 @override |
| 999 JS.Statement visitVariableDeclarationStatement( | 995 JS.Statement visitVariableDeclarationStatement( |
| 1000 VariableDeclarationStatement node) => | 996 VariableDeclarationStatement node) => |
| 1001 _expressionStatement(node.variables.accept(this)); | 997 _expressionStatement(_visit(node.variables)); |
| 1002 | 998 |
| 1003 @override | 999 @override |
| 1004 visitConstructorName(ConstructorName node) { | 1000 visitConstructorName(ConstructorName node) { |
| 1005 var typeName = node.type.name.accept(this); | 1001 var typeName = _visit(node.type.name); |
| 1006 if (node.name != null) { | 1002 if (node.name != null) { |
| 1007 return js.call('#.#', [typeName, node.name.name]); | 1003 return js.call('#.#', [typeName, node.name.name]); |
| 1008 } | 1004 } |
| 1009 return typeName; | 1005 return typeName; |
| 1010 } | 1006 } |
| 1011 | 1007 |
| 1012 @override | 1008 @override |
| 1013 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1009 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 1014 return js.call('new #(#)', [ | 1010 return js.call( |
| 1015 node.constructorName.accept(this), | 1011 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); |
| 1016 node.argumentList.accept(this) | |
| 1017 ]); | |
| 1018 } | 1012 } |
| 1019 | 1013 |
| 1020 /// True if this type is built-in to JS, and we use the values unwrapped. | 1014 /// True if this type is built-in to JS, and we use the values unwrapped. |
| 1021 /// For these types we generate a calling convention via static | 1015 /// For these types we generate a calling convention via static |
| 1022 /// "extension methods". This allows types to be extended without adding | 1016 /// "extension methods". This allows types to be extended without adding |
| 1023 /// extensions directly on the prototype. | 1017 /// extensions directly on the prototype. |
| 1024 bool _isJSBuiltinType(DartType t) => | 1018 bool _isJSBuiltinType(DartType t) => |
| 1025 rules.isNumType(t) || rules.isStringType(t) || rules.isBoolType(t); | 1019 rules.isNumType(t) || rules.isStringType(t) || rules.isBoolType(t); |
| 1026 | 1020 |
| 1027 bool typeIsPrimitiveInJS(DartType t) => !rules.isDynamic(t) && | 1021 bool typeIsPrimitiveInJS(DartType t) => !rules.isDynamic(t) && |
| 1028 (rules.isIntType(t) || | 1022 (rules.isIntType(t) || |
| 1029 rules.isDoubleType(t) || | 1023 rules.isDoubleType(t) || |
| 1030 rules.isBoolType(t) || | 1024 rules.isBoolType(t) || |
| 1031 rules.isNumType(t)); | 1025 rules.isNumType(t)); |
| 1032 | 1026 |
| 1033 bool typeIsNonNullablePrimitiveInJS(DartType t) => | 1027 bool typeIsNonNullablePrimitiveInJS(DartType t) => |
| 1034 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); | 1028 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); |
| 1035 | 1029 |
| 1036 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 1030 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
| 1037 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 1031 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
| 1038 | 1032 |
| 1039 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 1033 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
| 1040 | 1034 |
| 1041 JS.Expression notNull(Expression expr) { | 1035 JS.Expression notNull(Expression expr) { |
| 1042 var type = rules.getStaticType(expr); | 1036 var type = rules.getStaticType(expr); |
| 1043 if (rules.isNonNullableType(type)) { | 1037 if (rules.isNonNullableType(type)) { |
| 1044 return expr.accept(this); | 1038 return _visit(expr); |
| 1045 } else { | 1039 } else { |
| 1046 return js.call('dart.notNull(#)', expr.accept(this)); | 1040 return js.call('dart.notNull(#)', _visit(expr)); |
| 1047 } | 1041 } |
| 1048 } | 1042 } |
| 1049 | 1043 |
| 1050 @override | 1044 @override |
| 1051 JS.Expression visitBinaryExpression(BinaryExpression node) { | 1045 JS.Expression visitBinaryExpression(BinaryExpression node) { |
| 1052 var op = node.operator; | 1046 var op = node.operator; |
| 1053 var left = node.leftOperand; | 1047 var left = node.leftOperand; |
| 1054 var right = node.rightOperand; | 1048 var right = node.rightOperand; |
| 1055 var leftType = rules.getStaticType(left); | 1049 var leftType = rules.getStaticType(left); |
| 1056 var rightType = rules.getStaticType(right); | 1050 var rightType = rules.getStaticType(right); |
| 1057 | 1051 |
| 1058 var code; | 1052 var code; |
| 1059 if (op.type.isEqualityOperator) { | 1053 if (op.type.isEqualityOperator) { |
| 1060 // If we statically know LHS or RHS is null we can generate a clean check. | 1054 // If we statically know LHS or RHS is null we can generate a clean check. |
| 1061 // We can also do this if the left hand side is a primitive type, because | 1055 // We can also do this if the left hand side is a primitive type, because |
| 1062 // we know then it doesn't have an overridden. | 1056 // we know then it doesn't have an overridden. |
| 1063 if (_isNull(left) || _isNull(right) || typeIsPrimitiveInJS(leftType)) { | 1057 if (_isNull(left) || _isNull(right) || typeIsPrimitiveInJS(leftType)) { |
| 1064 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-strict-equa lity-comparison | 1058 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-strict-equa lity-comparison |
| 1065 code = op.type == TokenType.EQ_EQ ? '# === #' : '# !== #'; | 1059 code = op.type == TokenType.EQ_EQ ? '# === #' : '# !== #'; |
| 1066 } else { | 1060 } else { |
| 1067 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; | 1061 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; |
| 1068 code = '${bang}dart.equals(#, #)'; | 1062 code = '${bang}dart.equals(#, #)'; |
| 1069 } | 1063 } |
| 1070 return js.call(code, [left.accept(this), right.accept(this)]); | 1064 return js.call(code, [_visit(left), _visit(right)]); |
| 1071 } else if (binaryOperationIsPrimitive(leftType, rightType)) { | 1065 } else if (binaryOperationIsPrimitive(leftType, rightType)) { |
| 1072 // special cases where we inline the operation | 1066 // special cases where we inline the operation |
| 1073 // these values are assumed to be non-null (determined by the checker) | 1067 // these values are assumed to be non-null (determined by the checker) |
| 1074 // TODO(jmesserly): it would be nice to just inline the method from core, | 1068 // TODO(jmesserly): it would be nice to just inline the method from core, |
| 1075 // instead of special cases here. | 1069 // instead of special cases here. |
| 1076 if (op.type == TokenType.TILDE_SLASH) { | 1070 if (op.type == TokenType.TILDE_SLASH) { |
| 1077 // `a ~/ b` is equivalent to `(a / b).truncate()` | 1071 // `a ~/ b` is equivalent to `(a / b).truncate()` |
| 1078 code = '(# / #).truncate()'; | 1072 code = '(# / #).truncate()'; |
| 1079 } else { | 1073 } else { |
| 1080 // TODO(vsm): When do Dart ops not map to JS? | 1074 // TODO(vsm): When do Dart ops not map to JS? |
| 1081 code = '# $op #'; | 1075 code = '# $op #'; |
| 1082 } | 1076 } |
| 1083 return js.call(code, [notNull(left), notNull(right)]); | 1077 return js.call(code, [notNull(left), notNull(right)]); |
| 1084 } else { | 1078 } else { |
| 1085 var opString = js.string(op.lexeme, "'"); | 1079 var opString = js.string(op.lexeme, "'"); |
| 1086 if (rules.isDynamicTarget(left)) { | 1080 if (rules.isDynamicTarget(left)) { |
| 1087 // dynamic dispatch | 1081 // dynamic dispatch |
| 1088 return js.call('dart.dbinary(#, #, #)', [ | 1082 return js.call( |
| 1089 left.accept(this), | 1083 'dart.dbinary(#, #, #)', [_visit(left), opString, _visit(right)]); |
| 1090 opString, | |
| 1091 right.accept(this) | |
| 1092 ]); | |
| 1093 } else if (_isJSBuiltinType(leftType)) { | 1084 } else if (_isJSBuiltinType(leftType)) { |
| 1094 // TODO(jmesserly): we'd get better readability from the static-dispatch | 1085 // TODO(jmesserly): we'd get better readability from the static-dispatch |
| 1095 // pattern below. Consider: | 1086 // pattern below. Consider: |
| 1096 // | 1087 // |
| 1097 // "hello"['+']"world" | 1088 // "hello"['+']"world" |
| 1098 // vs | 1089 // vs |
| 1099 // core.String['+']("hello", "world") | 1090 // core.String['+']("hello", "world") |
| 1100 // | 1091 // |
| 1101 // Infix notation is much more readable, which is a bit part of why | 1092 // Infix notation is much more readable, which is a bit part of why |
| 1102 // C# added its extension methods feature. However this would require | 1093 // C# added its extension methods feature. However this would require |
| 1103 // adding these methods to String.prototype/Number.prototype in JS. | 1094 // adding these methods to String.prototype/Number.prototype in JS. |
| 1104 return js.call('#.#(#, #)', [ | 1095 return js.call('#.#(#, #)', [ |
| 1105 _emitTypeName(leftType), | 1096 _emitTypeName(leftType), |
| 1106 opString, | 1097 opString, |
| 1107 left.accept(this), | 1098 _visit(left), |
| 1108 right.accept(this) | 1099 _visit(right) |
| 1109 ]); | 1100 ]); |
| 1110 } else { | 1101 } else { |
| 1111 // Generic static-dispatch, user-defined operator code path. | 1102 // Generic static-dispatch, user-defined operator code path. |
| 1112 return js.call( | 1103 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); |
| 1113 '#.#(#)', [left.accept(this), opString, right.accept(this)]); | |
| 1114 } | 1104 } |
| 1115 } | 1105 } |
| 1116 } | 1106 } |
| 1117 | 1107 |
| 1118 bool _isNull(Expression expr) => expr is NullLiteral; | 1108 bool _isNull(Expression expr) => expr is NullLiteral; |
| 1119 | 1109 |
| 1120 @override | 1110 @override |
| 1121 JS.Expression visitPostfixExpression(PostfixExpression node) { | 1111 JS.Expression visitPostfixExpression(PostfixExpression node) { |
| 1122 var op = node.operator; | 1112 var op = node.operator; |
| 1123 var expr = node.operand; | 1113 var expr = node.operand; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1160 // Special case: target is stateless, so we can just reuse it. | 1150 // Special case: target is stateless, so we can just reuse it. |
| 1161 _cascadeTarget = node.target; | 1151 _cascadeTarget = node.target; |
| 1162 | 1152 |
| 1163 if (parent is ExpressionStatement) { | 1153 if (parent is ExpressionStatement) { |
| 1164 var sections = _visitList(node.cascadeSections); | 1154 var sections = _visitList(node.cascadeSections); |
| 1165 result = _statement(sections.map((e) => new JS.ExpressionStatement(e))); | 1155 result = _statement(sections.map((e) => new JS.ExpressionStatement(e))); |
| 1166 } else { | 1156 } else { |
| 1167 // Use comma expression. For example: | 1157 // Use comma expression. For example: |
| 1168 // (sb.write(1), sb.write(2), sb) | 1158 // (sb.write(1), sb.write(2), sb) |
| 1169 var sections = _visitListToBinary(node.cascadeSections, ','); | 1159 var sections = _visitListToBinary(node.cascadeSections, ','); |
| 1170 result = new JS.Binary(',', sections, _cascadeTarget.accept(this)); | 1160 result = new JS.Binary(',', sections, _visit(_cascadeTarget)); |
| 1171 } | 1161 } |
| 1172 } else { | 1162 } else { |
| 1173 // In the general case we need to capture the target expression into | 1163 // In the general case we need to capture the target expression into |
| 1174 // a temporary. This uses a lambda to get a temporary scope, and it also | 1164 // a temporary. This uses a lambda to get a temporary scope, and it also |
| 1175 // remains valid in an expression context. | 1165 // remains valid in an expression context. |
| 1176 // TODO(jmesserly): need a better way to handle temps. | 1166 // TODO(jmesserly): need a better way to handle temps. |
| 1177 // TODO(jmesserly): special case for parent is ExpressionStatement? | 1167 // TODO(jmesserly): special case for parent is ExpressionStatement? |
| 1178 _cascadeTarget = | 1168 _cascadeTarget = |
| 1179 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, '_', 0)); | 1169 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, '_', 0)); |
| 1180 _cascadeTarget.staticElement = | 1170 _cascadeTarget.staticElement = |
| 1181 new LocalVariableElementImpl.forNode(_cascadeTarget); | 1171 new LocalVariableElementImpl.forNode(_cascadeTarget); |
| 1182 _cascadeTarget.staticType = node.target.staticType; | 1172 _cascadeTarget.staticType = node.target.staticType; |
| 1183 | 1173 |
| 1184 var body = _visitList(node.cascadeSections); | 1174 var body = _visitList(node.cascadeSections); |
| 1185 if (node.parent is! ExpressionStatement) { | 1175 if (node.parent is! ExpressionStatement) { |
| 1186 body.add(js.statement('return #;', _cascadeTarget.name)); | 1176 body.add(js.statement('return #;', _cascadeTarget.name)); |
| 1187 } | 1177 } |
| 1188 | 1178 |
| 1189 var bindThis = _maybeBindThis(node.cascadeSections); | 1179 var bindThis = _maybeBindThis(node.cascadeSections); |
| 1190 result = js.call('((#) => { # })$bindThis(#)', [ | 1180 result = js.call('((#) => { # })$bindThis(#)', [ |
| 1191 _cascadeTarget.name, | 1181 _cascadeTarget.name, |
| 1192 body, | 1182 body, |
| 1193 node.target.accept(this) | 1183 _visit(node.target) |
| 1194 ]); | 1184 ]); |
| 1195 } | 1185 } |
| 1196 | 1186 |
| 1197 _cascadeTarget = savedCascadeTemp; | 1187 _cascadeTarget = savedCascadeTemp; |
| 1198 return result; | 1188 return result; |
| 1199 } | 1189 } |
| 1200 | 1190 |
| 1201 /// True is the expression can be evaluated multiple times without causing | 1191 /// True is the expression can be evaluated multiple times without causing |
| 1202 /// code execution. This is true for final fields. This can be true for local | 1192 /// code execution. This is true for final fields. This can be true for local |
| 1203 /// variables, if: | 1193 /// variables, if: |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 1214 return !_isPotentiallyMutated(e, context); | 1204 return !_isPotentiallyMutated(e, context); |
| 1215 } | 1205 } |
| 1216 } | 1206 } |
| 1217 } | 1207 } |
| 1218 return false; | 1208 return false; |
| 1219 } | 1209 } |
| 1220 | 1210 |
| 1221 @override | 1211 @override |
| 1222 visitParenthesizedExpression(ParenthesizedExpression node) => | 1212 visitParenthesizedExpression(ParenthesizedExpression node) => |
| 1223 // The printer handles precedence so we don't need to. | 1213 // The printer handles precedence so we don't need to. |
| 1224 node.expression.accept(this); | 1214 _visit(node.expression); |
| 1225 | 1215 |
| 1226 @override | 1216 @override |
| 1227 visitSimpleFormalParameter(SimpleFormalParameter node) => | 1217 visitSimpleFormalParameter(SimpleFormalParameter node) => |
| 1228 node.identifier.accept(this); | 1218 _visit(node.identifier); |
| 1229 | 1219 |
| 1230 @override | 1220 @override |
| 1231 visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) => | 1221 visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) => |
| 1232 node.identifier.accept(this); | 1222 _visit(node.identifier); |
| 1233 | 1223 |
| 1234 @override | 1224 @override |
| 1235 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1225 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
| 1236 | 1226 |
| 1237 @override | 1227 @override |
| 1238 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1228 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
| 1239 | 1229 |
| 1240 @override | 1230 @override |
| 1241 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1231 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 1242 if (node.prefix.staticElement is PrefixElement) { | 1232 if (node.prefix.staticElement is PrefixElement) { |
| 1243 return node.identifier.accept(this); | 1233 return _visit(node.identifier); |
| 1244 } else { | 1234 } else { |
| 1245 return _visitGet(node.prefix, node.identifier); | 1235 return _visitGet(node.prefix, node.identifier); |
| 1246 } | 1236 } |
| 1247 } | 1237 } |
| 1248 | 1238 |
| 1249 @override | 1239 @override |
| 1250 visitPropertyAccess(PropertyAccess node) => | 1240 visitPropertyAccess(PropertyAccess node) => |
| 1251 _visitGet(_getTarget(node), node.propertyName); | 1241 _visitGet(_getTarget(node), node.propertyName); |
| 1252 | 1242 |
| 1253 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1243 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
| 1254 _visitGet(Expression target, SimpleIdentifier name) { | 1244 _visitGet(Expression target, SimpleIdentifier name) { |
| 1255 if (rules.isDynamicTarget(target)) { | 1245 if (rules.isDynamicTarget(target)) { |
| 1256 return js.call( | 1246 return js.call( |
| 1257 'dart.dload(#, #)', [target.accept(this), js.string(name.name, "'")]); | 1247 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); |
| 1258 } else { | 1248 } else { |
| 1259 return js.call('#.#', [target.accept(this), name.name]); | 1249 return js.call('#.#', [_visit(target), name.name]); |
| 1260 } | 1250 } |
| 1261 } | 1251 } |
| 1262 | 1252 |
| 1263 @override | 1253 @override |
| 1264 visitIndexExpression(IndexExpression node) { | 1254 visitIndexExpression(IndexExpression node) { |
| 1265 var target = _getTarget(node); | 1255 var target = _getTarget(node); |
| 1266 var code; | 1256 var code; |
| 1267 if (rules.isDynamicTarget(target)) { | 1257 if (rules.isDynamicTarget(target)) { |
| 1268 code = 'dart.dindex(#, #)'; | 1258 code = 'dart.dindex(#, #)'; |
| 1269 } else { | 1259 } else { |
| 1270 code = '#.get(#)'; | 1260 code = '#.get(#)'; |
| 1271 } | 1261 } |
| 1272 return js.call(code, [target.accept(this), node.index.accept(this)]); | 1262 return js.call(code, [_visit(target), _visit(node.index)]); |
| 1273 } | 1263 } |
| 1274 | 1264 |
| 1275 /// Gets the target of a [PropertyAccess] or [IndexExpression]. | 1265 /// Gets the target of a [PropertyAccess] or [IndexExpression]. |
| 1276 /// Those two nodes are special because they're both allowed on left side of | 1266 /// Those two nodes are special because they're both allowed on left side of |
| 1277 /// an assignment expression and cascades. | 1267 /// an assignment expression and cascades. |
| 1278 Expression _getTarget(node) { | 1268 Expression _getTarget(node) { |
| 1279 assert(node is IndexExpression || node is PropertyAccess); | 1269 assert(node is IndexExpression || node is PropertyAccess); |
| 1280 return node.isCascaded ? _cascadeTarget : node.target; | 1270 return node.isCascaded ? _cascadeTarget : node.target; |
| 1281 } | 1271 } |
| 1282 | 1272 |
| 1283 @override | 1273 @override |
| 1284 visitConditionalExpression(ConditionalExpression node) { | 1274 visitConditionalExpression(ConditionalExpression node) { |
| 1285 return js.call('# ? # : #', [ | 1275 return js.call('# ? # : #', [ |
| 1286 node.condition.accept(this), | 1276 _visit(node.condition), |
| 1287 node.thenExpression.accept(this), | 1277 _visit(node.thenExpression), |
| 1288 node.elseExpression.accept(this) | 1278 _visit(node.elseExpression) |
| 1289 ]); | 1279 ]); |
| 1290 } | 1280 } |
| 1291 | 1281 |
| 1292 @override | 1282 @override |
| 1293 visitThrowExpression(ThrowExpression node) { | 1283 visitThrowExpression(ThrowExpression node) { |
| 1294 var expr = node.expression.accept(this); | 1284 var expr = _visit(node.expression); |
| 1295 if (node.parent is ExpressionStatement) { | 1285 if (node.parent is ExpressionStatement) { |
| 1296 return js.statement('throw #;', expr); | 1286 return js.statement('throw #;', expr); |
| 1297 } else { | 1287 } else { |
| 1298 return js.call('dart.throw_(#)', expr); | 1288 return js.call('dart.throw_(#)', expr); |
| 1299 } | 1289 } |
| 1300 } | 1290 } |
| 1301 | 1291 |
| 1302 @override | 1292 @override |
| 1303 JS.If visitIfStatement(IfStatement node) { | 1293 JS.If visitIfStatement(IfStatement node) { |
| 1304 return new JS.If(node.condition.accept(this), _visit(node.thenStatement), | 1294 return new JS.If(_visit(node.condition), _visit(node.thenStatement), |
| 1305 _visitOrEmpty(node.elseStatement)); | 1295 _visitOrEmpty(node.elseStatement)); |
| 1306 } | 1296 } |
| 1307 | 1297 |
| 1308 @override | 1298 @override |
| 1309 JS.For visitForStatement(ForStatement node) { | 1299 JS.For visitForStatement(ForStatement node) { |
| 1310 var init = _visit(node.initialization); | 1300 var init = _visit(node.initialization); |
| 1311 if (init == null) init = _visit(node.variables); | 1301 if (init == null) init = _visit(node.variables); |
| 1312 return new JS.For(init, _visit(node.condition), | 1302 return new JS.For(init, _visit(node.condition), |
| 1313 _visitListToBinary(node.updaters, ','), _visit(node.body)); | 1303 _visitListToBinary(node.updaters, ','), _visit(node.body)); |
| 1314 } | 1304 } |
| 1315 | 1305 |
| 1316 @override | 1306 @override |
| 1317 JS.While visitWhileStatement(WhileStatement node) { | 1307 JS.While visitWhileStatement(WhileStatement node) { |
| 1318 return new JS.While(node.condition.accept(this), node.body.accept(this)); | 1308 return new JS.While(_visit(node.condition), _visit(node.body)); |
| 1319 } | 1309 } |
| 1320 | 1310 |
| 1321 @override | 1311 @override |
| 1322 JS.Do visitDoStatement(DoStatement node) { | 1312 JS.Do visitDoStatement(DoStatement node) { |
| 1323 return new JS.Do(node.body.accept(this), node.condition.accept(this)); | 1313 return new JS.Do(_visit(node.body), _visit(node.condition)); |
| 1324 } | 1314 } |
| 1325 | 1315 |
| 1326 @override | 1316 @override |
| 1327 JS.ForOf visitForEachStatement(ForEachStatement node) { | 1317 JS.ForOf visitForEachStatement(ForEachStatement node) { |
| 1328 var init = _visit(node.identifier); | 1318 var init = _visit(node.identifier); |
| 1329 if (init == null) { | 1319 if (init == null) { |
| 1330 init = js.call('let #', node.loopVariable.identifier.name); | 1320 init = js.call('let #', node.loopVariable.identifier.name); |
| 1331 } | 1321 } |
| 1332 return new JS.ForOf( | 1322 return new JS.ForOf(init, _visit(node.iterable), _visit(node.body)); |
| 1333 init, node.iterable.accept(this), node.body.accept(this)); | |
| 1334 } | 1323 } |
| 1335 | 1324 |
| 1336 @override | 1325 @override |
| 1337 visitBreakStatement(BreakStatement node) { | 1326 visitBreakStatement(BreakStatement node) { |
| 1338 var label = node.label; | 1327 var label = node.label; |
| 1339 return new JS.Break(label != null ? label.name : null); | 1328 return new JS.Break(label != null ? label.name : null); |
| 1340 } | 1329 } |
| 1341 | 1330 |
| 1342 @override | 1331 @override |
| 1343 visitContinueStatement(ContinueStatement node) { | 1332 visitContinueStatement(ContinueStatement node) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1377 if (s.length == 0) return new JS.Block([]); | 1366 if (s.length == 0) return new JS.Block([]); |
| 1378 if (s.length == 1) return s[0]; | 1367 if (s.length == 1) return s[0]; |
| 1379 return new JS.Block(s); | 1368 return new JS.Block(s); |
| 1380 } | 1369 } |
| 1381 | 1370 |
| 1382 JS.Statement _visitCatchClause(CatchClause node, String varName) { | 1371 JS.Statement _visitCatchClause(CatchClause node, String varName) { |
| 1383 var body = []; | 1372 var body = []; |
| 1384 if (node.catchKeyword != null) { | 1373 if (node.catchKeyword != null) { |
| 1385 var name = node.exceptionParameter; | 1374 var name = node.exceptionParameter; |
| 1386 if (name != null && name.name != varName) { | 1375 if (name != null && name.name != varName) { |
| 1387 body.add(js.statement('let # = #;', [name.accept(this), varName])); | 1376 body.add(js.statement('let # = #;', [_visit(name), varName])); |
| 1388 } | 1377 } |
| 1389 if (node.stackTraceParameter != null) { | 1378 if (node.stackTraceParameter != null) { |
| 1390 var stackVar = node.stackTraceParameter.name; | 1379 var stackVar = node.stackTraceParameter.name; |
| 1391 body.add(js.statement( | 1380 body.add(js.statement( |
| 1392 'let # = dart.stackTrace(#);', [stackVar, name.accept(this)])); | 1381 'let # = dart.stackTrace(#);', [stackVar, _visit(name)])); |
| 1393 } | 1382 } |
| 1394 } | 1383 } |
| 1395 | 1384 |
| 1396 body.add(node.body.accept(this)); | 1385 body.add(_visit(node.body)); |
| 1397 | 1386 |
| 1398 if (node.exceptionType != null) { | 1387 if (node.exceptionType != null) { |
| 1399 return js.statement('if (dart.is(#, #)) #;', [ | 1388 return js.statement('if (dart.is(#, #)) #;', [ |
| 1400 varName, | 1389 varName, |
| 1401 _emitTypeName(node.exceptionType.type), | 1390 _emitTypeName(node.exceptionType.type), |
| 1402 _statement(body) | 1391 _statement(body) |
| 1403 ]); | 1392 ]); |
| 1404 } | 1393 } |
| 1405 return _statement(body); | 1394 return _statement(body); |
| 1406 } | 1395 } |
| 1407 | 1396 |
| 1408 @override | 1397 @override |
| 1409 JS.Case visitSwitchCase(SwitchCase node) { | 1398 JS.Case visitSwitchCase(SwitchCase node) { |
| 1410 var expr = node.expression.accept(this); | 1399 var expr = _visit(node.expression); |
| 1411 var body = _visitList(node.statements); | 1400 var body = _visitList(node.statements); |
| 1412 if (node.labels.isNotEmpty) { | 1401 if (node.labels.isNotEmpty) { |
| 1413 body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); | 1402 body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); |
| 1414 } | 1403 } |
| 1415 // TODO(jmesserly): make sure we are statically checking fall through | 1404 // TODO(jmesserly): make sure we are statically checking fall through |
| 1416 return new JS.Case(expr, new JS.Block(body)); | 1405 return new JS.Case(expr, new JS.Block(body)); |
| 1417 } | 1406 } |
| 1418 | 1407 |
| 1419 @override | 1408 @override |
| 1420 JS.Default visitSwitchDefault(SwitchDefault node) { | 1409 JS.Default visitSwitchDefault(SwitchDefault node) { |
| 1421 var body = _visitList(node.statements); | 1410 var body = _visitList(node.statements); |
| 1422 if (node.labels.isNotEmpty) { | 1411 if (node.labels.isNotEmpty) { |
| 1423 body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); | 1412 body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); |
| 1424 } | 1413 } |
| 1425 // TODO(jmesserly): make sure we are statically checking fall through | 1414 // TODO(jmesserly): make sure we are statically checking fall through |
| 1426 return new JS.Default(new JS.Block(body)); | 1415 return new JS.Default(new JS.Block(body)); |
| 1427 } | 1416 } |
| 1428 | 1417 |
| 1429 @override | 1418 @override |
| 1430 JS.Switch visitSwitchStatement(SwitchStatement node) => | 1419 JS.Switch visitSwitchStatement(SwitchStatement node) => |
| 1431 new JS.Switch(node.expression.accept(this), _visitList(node.members)); | 1420 new JS.Switch(_visit(node.expression), _visitList(node.members)); |
| 1432 | 1421 |
| 1433 @override | 1422 @override |
| 1434 JS.Statement visitLabeledStatement(LabeledStatement node) { | 1423 JS.Statement visitLabeledStatement(LabeledStatement node) { |
| 1435 var result = _visit(node.statement); | 1424 var result = _visit(node.statement); |
| 1436 for (var label in node.labels.reversed) { | 1425 for (var label in node.labels.reversed) { |
| 1437 result = new JS.LabeledStatement(label.label.name, result); | 1426 result = new JS.LabeledStatement(label.label.name, result); |
| 1438 } | 1427 } |
| 1439 return result; | 1428 return result; |
| 1440 } | 1429 } |
| 1441 | 1430 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 1464 visitMapLiteral(MapLiteral node) { | 1453 visitMapLiteral(MapLiteral node) { |
| 1465 var entries = node.entries; | 1454 var entries = node.entries; |
| 1466 var mapArguments = null; | 1455 var mapArguments = null; |
| 1467 if (entries.isEmpty) return js.call('dart.map()'); | 1456 if (entries.isEmpty) return js.call('dart.map()'); |
| 1468 | 1457 |
| 1469 // Use JS object literal notation if possible, otherwise use an array. | 1458 // Use JS object literal notation if possible, otherwise use an array. |
| 1470 if (entries.every((e) => e.key is SimpleStringLiteral)) { | 1459 if (entries.every((e) => e.key is SimpleStringLiteral)) { |
| 1471 var props = []; | 1460 var props = []; |
| 1472 for (var e in entries) { | 1461 for (var e in entries) { |
| 1473 var key = (e.key as SimpleStringLiteral).value; | 1462 var key = (e.key as SimpleStringLiteral).value; |
| 1474 var value = e.value.accept(this); | 1463 var value = _visit(e.value); |
| 1475 props.add(new JS.Property(js.escapedString(key), value)); | 1464 props.add(new JS.Property(js.escapedString(key), value)); |
| 1476 } | 1465 } |
| 1477 mapArguments = new JS.ObjectInitializer(props); | 1466 mapArguments = new JS.ObjectInitializer(props); |
| 1478 } else { | 1467 } else { |
| 1479 var values = []; | 1468 var values = []; |
| 1480 for (var e in entries) { | 1469 for (var e in entries) { |
| 1481 values.add(e.key.accept(this)); | 1470 values.add(_visit(e.key)); |
| 1482 values.add(e.value.accept(this)); | 1471 values.add(_visit(e.value)); |
| 1483 } | 1472 } |
| 1484 mapArguments = new JS.ArrayInitializer(values); | 1473 mapArguments = new JS.ArrayInitializer(values); |
| 1485 } | 1474 } |
| 1486 return js.call('dart.map(#)', [mapArguments]); | 1475 return js.call('dart.map(#)', [mapArguments]); |
| 1487 } | 1476 } |
| 1488 | 1477 |
| 1489 @override | 1478 @override |
| 1490 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) => | 1479 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) => |
| 1491 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"'); | 1480 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"'); |
| 1492 | 1481 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 1504 | 1493 |
| 1505 @override | 1494 @override |
| 1506 String visitInterpolationString(InterpolationString node) { | 1495 String visitInterpolationString(InterpolationString node) { |
| 1507 // TODO(jmesserly): this call adds quotes, and then we strip them off. | 1496 // TODO(jmesserly): this call adds quotes, and then we strip them off. |
| 1508 var str = js.escapedString(node.value, '`').value; | 1497 var str = js.escapedString(node.value, '`').value; |
| 1509 return str.substring(1, str.length - 1); | 1498 return str.substring(1, str.length - 1); |
| 1510 } | 1499 } |
| 1511 | 1500 |
| 1512 @override | 1501 @override |
| 1513 visitInterpolationExpression(InterpolationExpression node) => | 1502 visitInterpolationExpression(InterpolationExpression node) => |
| 1514 node.expression.accept(this); | 1503 _visit(node.expression); |
| 1515 | 1504 |
| 1516 @override | 1505 @override |
| 1517 visitBooleanLiteral(BooleanLiteral node) => js.boolean(node.value); | 1506 visitBooleanLiteral(BooleanLiteral node) => js.boolean(node.value); |
| 1518 | 1507 |
| 1519 @override | 1508 @override |
| 1520 JS.Statement visitDeclaration(Declaration node) => | 1509 JS.Statement visitDeclaration(Declaration node) => |
| 1521 js.comment('Unimplemented ${node.runtimeType}: $node'); | 1510 js.comment('Unimplemented ${node.runtimeType}: $node'); |
| 1522 | 1511 |
| 1523 @override | 1512 @override |
| 1524 JS.Statement visitStatement(Statement node) => | 1513 JS.Statement visitStatement(Statement node) => |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1563 | 1552 |
| 1564 /// Returns true if [element] is a getter in JS, therefore needs | 1553 /// Returns true if [element] is a getter in JS, therefore needs |
| 1565 /// `lib.topLevel` syntax instead of just `topLevel`. | 1554 /// `lib.topLevel` syntax instead of just `topLevel`. |
| 1566 bool _needsModuleGetter(Element element) { | 1555 bool _needsModuleGetter(Element element) { |
| 1567 if (element is PropertyAccessorElement) { | 1556 if (element is PropertyAccessorElement) { |
| 1568 element = (element as PropertyAccessorElement).variable; | 1557 element = (element as PropertyAccessorElement).variable; |
| 1569 } | 1558 } |
| 1570 return element is TopLevelVariableElement && !element.isConst; | 1559 return element is TopLevelVariableElement && !element.isConst; |
| 1571 } | 1560 } |
| 1572 | 1561 |
| 1573 _visit(AstNode node) => node != null ? node.accept(this) : null; | 1562 _visit(AstNode node) { |
| 1563 if (node == null) return null; | |
| 1564 var result = node.accept(this); | |
| 1565 if (result is JS.Node) result.sourceInformation = node; | |
| 1566 return result; | |
| 1567 } | |
| 1574 | 1568 |
| 1575 JS.Statement _visitOrEmpty(AstNode node) { | 1569 JS.Statement _visitOrEmpty(Statement node) { |
| 1576 if (node == null) return new JS.EmptyStatement(); | 1570 if (node == null) return new JS.EmptyStatement(); |
| 1577 return node.accept(this); | 1571 return _visit(node); |
| 1578 } | 1572 } |
| 1579 | 1573 |
| 1580 List _visitList(Iterable<AstNode> nodes) { | 1574 List _visitList(Iterable<AstNode> nodes) { |
| 1581 if (nodes == null) return null; | 1575 if (nodes == null) return null; |
| 1582 var result = []; | 1576 var result = []; |
| 1583 for (var node in nodes) result.add(node.accept(this)); | 1577 for (var node in nodes) result.add(_visit(node)); |
| 1584 return result; | 1578 return result; |
| 1585 } | 1579 } |
| 1586 | 1580 |
| 1587 /// Visits a list of expressions, creating a comma expression if needed in JS. | 1581 /// Visits a list of expressions, creating a comma expression if needed in JS. |
| 1588 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 1582 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
| 1589 if (nodes == null || nodes.isEmpty) return null; | 1583 if (nodes == null || nodes.isEmpty) return null; |
| 1590 | 1584 |
| 1591 JS.Expression result = null; | 1585 JS.Expression result = null; |
| 1592 for (var node in nodes) { | 1586 for (var node in nodes) { |
| 1593 var jsExpr = node.accept(this); | 1587 var jsExpr = _visit(node); |
| 1594 if (result == null) { | 1588 if (result == null) { |
| 1595 result = jsExpr; | 1589 result = jsExpr; |
| 1596 } else { | 1590 } else { |
| 1597 result = new JS.Binary(operator, result, jsExpr); | 1591 result = new JS.Binary(operator, result, jsExpr); |
| 1598 } | 1592 } |
| 1599 } | 1593 } |
| 1600 return result; | 1594 return result; |
| 1601 } | 1595 } |
| 1602 | 1596 |
| 1603 /// The following names are allowed for user-defined operators: | 1597 /// The following names are allowed for user-defined operators: |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1705 } | 1699 } |
| 1706 } | 1700 } |
| 1707 | 1701 |
| 1708 @override | 1702 @override |
| 1709 visitThisExpression(ThisExpression node) { | 1703 visitThisExpression(ThisExpression node) { |
| 1710 _bindThis = true; | 1704 _bindThis = true; |
| 1711 } | 1705 } |
| 1712 } | 1706 } |
| 1713 | 1707 |
| 1714 class JSGenerator extends CodeGenerator { | 1708 class JSGenerator extends CodeGenerator { |
| 1715 JSGenerator(String outDir, Uri root, TypeRules rules) | 1709 final JSCodeOptions options; |
| 1710 | |
| 1711 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) | |
| 1716 : super(outDir, root, rules); | 1712 : super(outDir, root, rules); |
| 1717 | 1713 |
| 1718 void generateLibrary(Iterable<CompilationUnit> units, LibraryInfo info, | 1714 void generateLibrary(Iterable<CompilationUnit> units, LibraryInfo info, |
| 1719 CheckerReporter reporter) { | 1715 CheckerReporter reporter) { |
| 1720 JS.Block jsTree = | 1716 JS.Block jsTree = |
| 1721 new JSCodegenVisitor(info, rules).generateLibrary(units, reporter); | 1717 new JSCodegenVisitor(info, rules).generateLibrary(units, reporter); |
| 1722 | 1718 |
| 1723 var outputPath = path.join(outDir, jsOutputPath(info)); | 1719 var outputPath = path.join(outDir, jsOutputPath(info)); |
| 1724 new Directory(path.dirname(outputPath)).createSync(recursive: true); | 1720 new Directory(path.dirname(outputPath)).createSync(recursive: true); |
| 1725 | 1721 |
| 1726 var context = new JS.SimpleJavaScriptPrintingContext(); | 1722 if (options.emitSourceMaps) { |
| 1723 var outFilename = path.basename(outputPath); | |
| 1724 var outDir = path.dirname(outputPath); | |
|
Siggi Cherem (dart-lang)
2015/02/27 22:47:09
I'm inclined to just inline this in the call two l
Jennifer Messerly
2015/02/27 22:49:02
sounds good
| |
| 1725 var printer = new srcmaps.Printer(outFilename); | |
| 1726 var context = new SourceMapPrintingContext(printer, outDir); | |
| 1727 _writeLibrary(context, jsTree); | |
| 1728 printer.add('//# sourceMappingURL=$outFilename.map'); | |
| 1729 // Write output file and source map | |
| 1730 new File(outputPath).writeAsStringSync(printer.text); | |
| 1731 new File('$outputPath.map').writeAsStringSync(printer.map); | |
| 1732 } else { | |
| 1733 var context = new JS.SimpleJavaScriptPrintingContext(); | |
| 1734 _writeLibrary(context, jsTree); | |
| 1735 // Write output file and source map | |
| 1736 new File(outputPath).writeAsStringSync(context.getText()); | |
| 1737 } | |
| 1738 } | |
| 1739 | |
| 1740 void _writeLibrary(JS.JavaScriptPrintingContext context, JS.Block jsTree) { | |
| 1727 var opts = | 1741 var opts = |
| 1728 new JS.JavaScriptPrintingOptions(avoidKeywordsInIdentifiers: true); | 1742 new JS.JavaScriptPrintingOptions(avoidKeywordsInIdentifiers: true); |
| 1729 var printer = new JS.Printer(opts, context); | 1743 new JS.Printer(opts, context).blockOutWithoutBraces(jsTree); |
| 1730 printer.blockOutWithoutBraces(jsTree); | |
| 1731 new File(outputPath).writeAsStringSync(context.getText()); | |
| 1732 } | 1744 } |
| 1733 } | 1745 } |
| 1734 | 1746 |
| 1747 String jsToString(JS.Node node) { | |
|
Siggi Cherem (dart-lang)
2015/02/27 22:47:09
is this used anywhere? (I couldn't find it)
Jennifer Messerly
2015/02/27 22:49:02
no, but I keep adding it back for debugging and it
Jennifer Messerly
2015/02/27 22:56:10
I could add this in a comment. It could also be ad
| |
| 1748 var context = new JS.SimpleJavaScriptPrintingContext(); | |
| 1749 var opts = new JS.JavaScriptPrintingOptions(avoidKeywordsInIdentifiers: true); | |
| 1750 new JS.Printer(opts, context).visit(node); | |
| 1751 // Write output file and source map | |
| 1752 return context.getText(); | |
| 1753 } | |
| 1754 | |
| 1735 /// Choose a canonical name from the library element. | 1755 /// Choose a canonical name from the library element. |
| 1736 /// This never uses the library's name (the identifier in the `library` | 1756 /// This never uses the library's name (the identifier in the `library` |
| 1737 /// declaration) as it doesn't have any meaningful rules enforced. | 1757 /// declaration) as it doesn't have any meaningful rules enforced. |
| 1738 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 1758 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
| 1739 | 1759 |
| 1740 /// Path to file that will be generated for [info]. | 1760 /// Path to file that will be generated for [info]. |
| 1741 // TODO(jmesserly): library directory should be relative to its package | 1761 // TODO(jmesserly): library directory should be relative to its package |
| 1742 // root. For example, "package:dev_compiler/src/codegen/js_codegen.dart" would b e: | 1762 // root. For example, "package:dev_compiler/src/codegen/js_codegen.dart" would b e: |
| 1743 // "ddc/src/codegen/js_codegen.js" under the output directory. | 1763 // "ddc/src/codegen/js_codegen.js" under the output directory. |
| 1744 String jsOutputPath(LibraryInfo info) => '${info.name}/${info.name}.js'; | 1764 String jsOutputPath(LibraryInfo info) => '${info.name}/${info.name}.js'; |
| 1765 | |
| 1766 class SourceMapPrintingContext extends JS.JavaScriptPrintingContext { | |
| 1767 final srcmaps.Printer printer; | |
| 1768 final String outputDir; | |
| 1769 | |
| 1770 CompilationUnit unit; | |
| 1771 Uri uri; | |
| 1772 | |
| 1773 SourceMapPrintingContext(this.printer, this.outputDir); | |
| 1774 | |
| 1775 void emit(String string) { | |
| 1776 printer.add(string); | |
| 1777 } | |
| 1778 | |
| 1779 void enterNode(JS.Node jsNode) { | |
| 1780 AstNode node = jsNode.sourceInformation; | |
| 1781 if (node is CompilationUnit) { | |
| 1782 unit = node; | |
| 1783 uri = _makeRelativeUri(unit.element.source.uri); | |
| 1784 return; | |
| 1785 } | |
| 1786 if (unit == null || node == null || node.offset == -1) return; | |
| 1787 | |
| 1788 var loc = _location(node.offset); | |
| 1789 var name = _getIdentifier(node); | |
| 1790 if (name != null) { | |
| 1791 // TODO(jmesserly): mark only uses the beginning of the span, but | |
| 1792 // we're required to pass this as a valid span. | |
| 1793 var end = _location(node.end); | |
| 1794 printer.mark(new SourceMapSpan(loc, end, name, isIdentifier: true)); | |
| 1795 } else { | |
| 1796 printer.mark(loc); | |
| 1797 } | |
| 1798 } | |
| 1799 | |
| 1800 SourceLocation _location(int offset) { | |
| 1801 var lineInfo = unit.lineInfo.getLocation(offset); | |
| 1802 return new SourceLocation(offset, | |
| 1803 sourceUrl: uri, | |
| 1804 line: lineInfo.lineNumber - 1, | |
| 1805 column: lineInfo.columnNumber - 1); | |
| 1806 } | |
| 1807 | |
| 1808 Uri _makeRelativeUri(Uri src) { | |
| 1809 return new Uri(path: path.relative(src.path, from: outputDir)); | |
| 1810 } | |
| 1811 | |
| 1812 void exitNode(JS.Node jsNode) { | |
| 1813 AstNode node = jsNode.sourceInformation; | |
| 1814 if (node is CompilationUnit) { | |
| 1815 unit = null; | |
| 1816 uri = null; | |
| 1817 return; | |
| 1818 } | |
| 1819 if (unit == null || node == null || node.offset == -1) return; | |
| 1820 | |
| 1821 // TODO(jmesserly): in many cases marking the end will be unncessary. | |
| 1822 printer.mark(_location(node.end)); | |
| 1823 } | |
| 1824 | |
| 1825 String _getIdentifier(AstNode node) { | |
| 1826 if (node is SimpleIdentifier) return node.name; | |
| 1827 return null; | |
| 1828 } | |
| 1829 } | |
| OLD | NEW |