Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 965033002: add source maps support, fixes #50 (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: format Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/devc.dart ('k') | lib/src/js/nodes.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « lib/devc.dart ('k') | lib/src/js/nodes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698