Index: lib/src/codegen/js_codegen.dart |
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
index 81166e316a183f91531a59a2153e2b305fa155f5..1f69fefc77ace646444ca3f6ddc6c8f4f11fbdbb 100644 |
--- a/lib/src/codegen/js_codegen.dart |
+++ b/lib/src/codegen/js_codegen.dart |
@@ -12,6 +12,9 @@ import 'package:analyzer/src/generated/constant.dart'; |
import 'package:analyzer/src/generated/element.dart'; |
import 'package:analyzer/src/generated/scanner.dart' |
show StringToken, Token, TokenType; |
+import 'package:source_maps/source_maps.dart' as srcmaps show Printer; |
+import 'package:source_maps/source_maps.dart' show SourceMapSpan; |
+import 'package:source_span/source_span.dart' show SourceLocation; |
import 'package:path/path.dart' as path; |
// TODO(jmesserly): import from its own package |
@@ -20,6 +23,7 @@ import 'package:dev_compiler/src/js/js_ast.dart' show js; |
import 'package:dev_compiler/src/checker/rules.dart'; |
import 'package:dev_compiler/src/info.dart'; |
+import 'package:dev_compiler/src/options.dart'; |
import 'package:dev_compiler/src/report.dart'; |
import 'package:dev_compiler/src/utils.dart'; |
import 'code_generator.dart'; |
@@ -59,7 +63,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var source = unit.element.source; |
_constEvaluator = new ConstantEvaluator(source, rules.provider); |
reporter.enterSource(source); |
- body.add(unit.accept(this)); |
+ body.add(_visit(unit)); |
reporter.leaveSource(); |
} |
@@ -91,7 +95,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
- var code = child.accept(this); |
+ var code = _visit(child); |
if (code != null) body.add(code); |
} |
// Flush any unwritten fields/properties. |
@@ -118,10 +122,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { |
// Converting from a nullable number to a non-nullable number |
// only requires a null check. |
- return js.call('dart.notNull(#)', node.expression.accept(this)); |
+ return js.call('dart.notNull(#)', _visit(node.expression)); |
} else { |
// A no-op in JavaScript. |
- return node.expression.accept(this); |
+ return _visit(node.expression); |
} |
} |
@@ -133,14 +137,14 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
_emitCast(node.expression, node.type.type); |
_emitCast(Expression node, DartType type) => |
- js.call('dart.as(#)', [[node.accept(this), _emitTypeName(type)]]); |
+ js.call('dart.as(#)', [[_visit(node), _emitTypeName(type)]]); |
@override |
visitIsExpression(IsExpression node) { |
// Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
JS.Expression result; |
var type = node.type.type; |
- var lhs = node.expression.accept(this); |
+ var lhs = _visit(node.expression); |
var typeofName = _jsTypeofName(type); |
if (typeofName != null) { |
result = js.call('typeof # == #', [lhs, typeofName]); |
@@ -240,7 +244,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (member is ConstructorDeclaration) { |
jsMethods.add(_emitConstructor(member, name, fields)); |
} else if (member is MethodDeclaration) { |
- jsMethods.add(member.accept(this)); |
+ jsMethods.add(_visit(member)); |
} |
} |
@@ -331,8 +335,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
// We generate constructors as initializer methods in the class; |
// this allows use of `super` for instance methods/properties. |
// It also avoids V8 restrictions on `super` in default constructors. |
- return new JS.Method(new JS.PropertyName(name), new JS.Fun( |
- node.parameters.accept(this), _emitConstructorBody(node, fields))); |
+ return new JS.Method(new JS.PropertyName(name), |
+ new JS.Fun(_visit(node.parameters), _emitConstructorBody(node, fields))) |
+ ..sourceInformation = node; |
} |
String _constructorName(String className, SimpleIdentifier name) { |
@@ -345,8 +350,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
// Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
if (node.redirectedConstructor != null) { |
return js.statement('{ return new #(#); }', [ |
- node.redirectedConstructor.accept(this), |
- node.parameters.accept(this) |
+ _visit(node.redirectedConstructor), |
+ _visit(node.parameters) |
]); |
} |
@@ -364,7 +369,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
(i) => i is RedirectingConstructorInvocation, orElse: () => null); |
if (redirectCall != null) { |
- body.add(redirectCall.accept(this)); |
+ body.add(_visit(redirectCall)); |
return new JS.Block(body); |
} |
@@ -386,8 +391,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (jsSuper != null) body.add(jsSuper); |
} |
- body.add(node.body.accept(this)); |
- return new JS.Block(body); |
+ body.add(_visit(node.body)); |
+ return new JS.Block(body)..sourceInformation = node; |
} |
@override |
@@ -397,7 +402,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var className = classDecl.name.name; |
var name = _constructorName(className, node.constructorName); |
- return js.statement('this.#(#);', [name, node.argumentList.accept(this)]); |
+ return js.statement('this.#(#);', [name, _visit(node.argumentList)]); |
} |
JS.Statement _superConstructorCall(ClassDeclaration clazz, |
@@ -413,8 +418,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var supertypeName = element.supertype.name; |
var name = _constructorName(supertypeName, superCtorName); |
- var args = node != null ? node.argumentList.accept(this) : []; |
- return js.statement('super.#(#);', [name, args]); |
+ var args = node != null ? _visit(node.argumentList) : []; |
+ return js.statement('super.#(#);', [name, args])..sourceInformation = node; |
} |
/// Initialize fields. They follow the sequence: |
@@ -436,7 +441,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
unsetFields[field.name.name] = field; |
} else { |
body.add(js.statement( |
- '# = #;', [field.name.accept(this), _visitInitializer(field)])); |
+ '# = #;', [_visit(field.name), _visitInitializer(field)])); |
} |
} |
} |
@@ -457,10 +462,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (initializers != null) { |
for (var init in initializers) { |
if (init is ConstructorFieldInitializer) { |
- body.add(js.statement('# = #;', [ |
- init.fieldName.accept(this), |
- init.expression.accept(this) |
- ])); |
+ body.add(js.statement( |
+ '# = #;', [_visit(init.fieldName), _visit(init.expression)])); |
unsetFields.remove(init.fieldName.name); |
} |
} |
@@ -470,7 +473,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
unsetFields.forEach((name, field) { |
JS.Expression value; |
if (field.initializer != null) { |
- value = field.initializer.accept(this); |
+ value = _visit(field.initializer); |
} else { |
var type = rules.elementType(field.element); |
if (rules.maybeNonNullableType(type)) { |
@@ -532,7 +535,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
JS.Expression _defaultParamValue(FormalParameter param) { |
if (param is DefaultFormalParameter && param.defaultValue != null) { |
- return param.defaultValue.accept(this); |
+ return _visit(param.defaultValue); |
} else { |
return new JS.LiteralNull(); |
} |
@@ -548,7 +551,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (params == null) params = []; |
return new JS.Method(new JS.PropertyName(_jsMethodName(node.name.name)), |
- new JS.Fun(params, node.body.accept(this)), |
+ new JS.Fun(params, _visit(node.body)), |
isGetter: node.isGetter, |
isSetter: node.isSetter, |
isStatic: node.isStatic); |
@@ -572,8 +575,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var name = node.name.name; |
body.add(js.comment('Function $name: ${node.element.type}')); |
- body.add(new JS.FunctionDeclaration(new JS.VariableDeclaration(name), |
- node.functionExpression.accept(this))); |
+ body.add(new JS.FunctionDeclaration( |
+ new JS.VariableDeclaration(name), _visit(node.functionExpression))); |
if (isPublic(name)) _exports.add(name); |
return _statement(body); |
@@ -583,7 +586,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var name = node.name.name; |
if (isPublic(name)) _exports.add(name); |
return new JS.Method( |
- new JS.PropertyName(name), node.functionExpression.accept(this), |
+ new JS.PropertyName(name), _visit(node.functionExpression), |
isGetter: node.isGetter, isSetter: node.isSetter); |
} |
@@ -593,7 +596,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (params == null) params = []; |
if (node.parent is FunctionDeclaration) { |
- return new JS.Fun(params, node.body.accept(this)); |
+ return new JS.Fun(params, _visit(node.body)); |
} else { |
var bindThis = _maybeBindThis(node.body); |
@@ -607,7 +610,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
code = '(#) => { #; }'; |
body = nodeBody; |
} |
- return js.call('($code)$bindThis', [params, body.accept(this)]); |
+ return js.call('($code)$bindThis', [params, _visit(body)]); |
} |
} |
@@ -622,7 +625,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var name = new JS.VariableDeclaration(func.name.name); |
return new JS.Block([ |
js.comment("// Function ${func.name.name}: ${func.element.type}\n"), |
- new JS.FunctionDeclaration(name, func.functionExpression.accept(this)) |
+ new JS.FunctionDeclaration(name, _visit(func.functionExpression)) |
]); |
} |
@@ -691,20 +694,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
} else { |
code = '#.set(#, #)'; |
} |
- return js.call(code, [ |
- target.accept(this), |
- lhs.index.accept(this), |
- rhs.accept(this) |
- ]); |
+ return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); |
} |
if (lhs is PropertyAccess) { |
var target = _getTarget(lhs); |
if (rules.isDynamicTarget(target)) { |
return js.call('dart.dput(#, #, #)', [ |
- target.accept(this), |
+ _visit(target), |
js.string(lhs.propertyName.name, "'"), |
- rhs.accept(this) |
+ _visit(rhs) |
]); |
} |
} |
@@ -722,23 +721,22 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
_cascadeTarget = lhs; |
var body = []; |
- body.add( |
- js.statement('# = #;', [lhs.accept(this), rhs.target.accept(this)])); |
+ body.add(js.statement('# = #;', [_visit(lhs), _visit(rhs.target)])); |
for (var section in rhs.cascadeSections) { |
- body.add(new JS.ExpressionStatement(section.accept(this))); |
+ body.add(new JS.ExpressionStatement(_visit(section))); |
} |
_cascadeTarget = savedCascadeTemp; |
return _statement(body); |
} |
- return js.call('# = #', [lhs.accept(this), rhs.accept(this)]); |
+ return js.call('# = #', [_visit(lhs), _visit(rhs)]); |
} |
@override |
JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { |
var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); |
- var ret = new JS.Return(node.expression.accept(this)); |
+ var ret = new JS.Return(_visit(node.expression)); |
return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); |
} |
@@ -764,16 +762,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (result != null) return result; |
if (rules.isDynamicCall(node.methodName)) { |
- var args = node.argumentList.accept(this); |
+ var args = _visit(node.argumentList); |
if (target != null) { |
return js.call('dart.dinvoke(#, #, #)', [ |
- target.accept(this), |
+ _visit(target), |
js.string(node.methodName.name, "'"), |
args |
]); |
} else { |
- return js.call( |
- 'dart.dinvokef(#, #)', [node.methodName.accept(this), args]); |
+ return js.call('dart.dinvokef(#, #)', [_visit(node.methodName), args]); |
} |
} |
@@ -782,12 +779,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var targetJs; |
if (target != null) { |
- targetJs = js.call('#.#', [target.accept(this), node.methodName.name]); |
+ targetJs = js.call('#.#', [_visit(target), node.methodName.name]); |
} else { |
- targetJs = node.methodName.accept(this); |
+ targetJs = _visit(node.methodName); |
} |
- return js.call('#(#)', [targetJs, node.argumentList.accept(this)]); |
+ return js.call('#(#)', [targetJs, _visit(node.argumentList)]); |
} |
/// Emits code for the `JS(...)` builtin. |
@@ -818,8 +815,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
} else { |
code = '#(#)'; |
} |
- return js.call( |
- code, [node.function.accept(this), node.argumentList.accept(this)]); |
+ return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
} |
@override |
@@ -830,7 +826,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (arg is NamedExpression) { |
named.add(visitNamedExpression(arg)); |
} else { |
- args.add(arg.accept(this)); |
+ args.add(_visit(arg)); |
} |
} |
if (named.isNotEmpty) { |
@@ -842,8 +838,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.Property visitNamedExpression(NamedExpression node) { |
assert(node.parent is ArgumentList); |
- return new JS.Property(new JS.PropertyName(node.name.label.name), |
- node.expression.accept(this)); |
+ return new JS.Property( |
+ new JS.PropertyName(node.name.label.name), _visit(node.expression)); |
} |
@override |
@@ -861,7 +857,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.Statement visitExpressionStatement(ExpressionStatement node) => |
- _expressionStatement(node.expression.accept(this)); |
+ _expressionStatement(_visit(node.expression)); |
// Some expressions may choose to generate themselves as JS statements |
// if their parent is in a statement context. |
@@ -877,7 +873,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.Statement visitAssertStatement(AssertStatement node) => |
// TODO(jmesserly): only emit in checked mode. |
- js.statement('dart.assert(#);', node.condition.accept(this)); |
+ js.statement('dart.assert(#);', _visit(node.condition)); |
@override |
JS.Return visitReturnStatement(ReturnStatement node) => |
@@ -899,7 +895,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (isPublic(name)) _exports.add(name); |
} else if (_isFieldInitConstant(field)) { |
body.add(js.statement( |
- '# = #;', [field.name.accept(this), _visitInitializer(field)])); |
+ '# = #;', [_visit(field.name), _visitInitializer(field)])); |
} else { |
_lazyFields.add(field); |
} |
@@ -929,7 +925,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
variables = _visitList(node.variables.take(node.variables.length - 1)); |
variables.add(new JS.VariableInitialization( |
new JS.VariableDeclaration(last.name.name), |
- lastInitializer.target.accept(this))); |
+ _visit(lastInitializer.target))); |
var result = <JS.Expression>[ |
new JS.VariableDeclarationList('let', variables) |
@@ -971,7 +967,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
for (var node in fields) { |
var name = node.name.name; |
methods.add(new JS.Method(new JS.PropertyName(name), |
- js.call('function() { return #; }', node.initializer.accept(this)), |
+ js.call('function() { return #; }', _visit(node.initializer)), |
isGetter: true)); |
// TODO(jmesserly): use a dummy setter to indicate writable. |
@@ -998,11 +994,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.Statement visitVariableDeclarationStatement( |
VariableDeclarationStatement node) => |
- _expressionStatement(node.variables.accept(this)); |
+ _expressionStatement(_visit(node.variables)); |
@override |
visitConstructorName(ConstructorName node) { |
- var typeName = node.type.name.accept(this); |
+ var typeName = _visit(node.type.name); |
if (node.name != null) { |
return js.call('#.#', [typeName, node.name.name]); |
} |
@@ -1011,10 +1007,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
visitInstanceCreationExpression(InstanceCreationExpression node) { |
- return js.call('new #(#)', [ |
- node.constructorName.accept(this), |
- node.argumentList.accept(this) |
- ]); |
+ return js.call( |
+ 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); |
} |
/// True if this type is built-in to JS, and we use the values unwrapped. |
@@ -1041,9 +1035,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
JS.Expression notNull(Expression expr) { |
var type = rules.getStaticType(expr); |
if (rules.isNonNullableType(type)) { |
- return expr.accept(this); |
+ return _visit(expr); |
} else { |
- return js.call('dart.notNull(#)', expr.accept(this)); |
+ return js.call('dart.notNull(#)', _visit(expr)); |
} |
} |
@@ -1067,7 +1061,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var bang = op.type == TokenType.BANG_EQ ? '!' : ''; |
code = '${bang}dart.equals(#, #)'; |
} |
- return js.call(code, [left.accept(this), right.accept(this)]); |
+ return js.call(code, [_visit(left), _visit(right)]); |
} else if (binaryOperationIsPrimitive(leftType, rightType)) { |
// special cases where we inline the operation |
// these values are assumed to be non-null (determined by the checker) |
@@ -1085,11 +1079,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var opString = js.string(op.lexeme, "'"); |
if (rules.isDynamicTarget(left)) { |
// dynamic dispatch |
- return js.call('dart.dbinary(#, #, #)', [ |
- left.accept(this), |
- opString, |
- right.accept(this) |
- ]); |
+ return js.call( |
+ 'dart.dbinary(#, #, #)', [_visit(left), opString, _visit(right)]); |
} else if (_isJSBuiltinType(leftType)) { |
// TODO(jmesserly): we'd get better readability from the static-dispatch |
// pattern below. Consider: |
@@ -1104,13 +1095,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
return js.call('#.#(#, #)', [ |
_emitTypeName(leftType), |
opString, |
- left.accept(this), |
- right.accept(this) |
+ _visit(left), |
+ _visit(right) |
]); |
} else { |
// Generic static-dispatch, user-defined operator code path. |
- return js.call( |
- '#.#(#)', [left.accept(this), opString, right.accept(this)]); |
+ return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); |
} |
} |
} |
@@ -1167,7 +1157,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
// Use comma expression. For example: |
// (sb.write(1), sb.write(2), sb) |
var sections = _visitListToBinary(node.cascadeSections, ','); |
- result = new JS.Binary(',', sections, _cascadeTarget.accept(this)); |
+ result = new JS.Binary(',', sections, _visit(_cascadeTarget)); |
} |
} else { |
// In the general case we need to capture the target expression into |
@@ -1190,7 +1180,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
result = js.call('((#) => { # })$bindThis(#)', [ |
_cascadeTarget.name, |
body, |
- node.target.accept(this) |
+ _visit(node.target) |
]); |
} |
@@ -1221,15 +1211,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
visitParenthesizedExpression(ParenthesizedExpression node) => |
// The printer handles precedence so we don't need to. |
- node.expression.accept(this); |
+ _visit(node.expression); |
@override |
visitSimpleFormalParameter(SimpleFormalParameter node) => |
- node.identifier.accept(this); |
+ _visit(node.identifier); |
@override |
visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) => |
- node.identifier.accept(this); |
+ _visit(node.identifier); |
@override |
JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
@@ -1240,7 +1230,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
visitPrefixedIdentifier(PrefixedIdentifier node) { |
if (node.prefix.staticElement is PrefixElement) { |
- return node.identifier.accept(this); |
+ return _visit(node.identifier); |
} else { |
return _visitGet(node.prefix, node.identifier); |
} |
@@ -1254,9 +1244,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
_visitGet(Expression target, SimpleIdentifier name) { |
if (rules.isDynamicTarget(target)) { |
return js.call( |
- 'dart.dload(#, #)', [target.accept(this), js.string(name.name, "'")]); |
+ 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); |
} else { |
- return js.call('#.#', [target.accept(this), name.name]); |
+ return js.call('#.#', [_visit(target), name.name]); |
} |
} |
@@ -1269,7 +1259,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
} else { |
code = '#.get(#)'; |
} |
- return js.call(code, [target.accept(this), node.index.accept(this)]); |
+ return js.call(code, [_visit(target), _visit(node.index)]); |
} |
/// Gets the target of a [PropertyAccess] or [IndexExpression]. |
@@ -1283,15 +1273,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
visitConditionalExpression(ConditionalExpression node) { |
return js.call('# ? # : #', [ |
- node.condition.accept(this), |
- node.thenExpression.accept(this), |
- node.elseExpression.accept(this) |
+ _visit(node.condition), |
+ _visit(node.thenExpression), |
+ _visit(node.elseExpression) |
]); |
} |
@override |
visitThrowExpression(ThrowExpression node) { |
- var expr = node.expression.accept(this); |
+ var expr = _visit(node.expression); |
if (node.parent is ExpressionStatement) { |
return js.statement('throw #;', expr); |
} else { |
@@ -1301,7 +1291,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.If visitIfStatement(IfStatement node) { |
- return new JS.If(node.condition.accept(this), _visit(node.thenStatement), |
+ return new JS.If(_visit(node.condition), _visit(node.thenStatement), |
_visitOrEmpty(node.elseStatement)); |
} |
@@ -1315,12 +1305,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.While visitWhileStatement(WhileStatement node) { |
- return new JS.While(node.condition.accept(this), node.body.accept(this)); |
+ return new JS.While(_visit(node.condition), _visit(node.body)); |
} |
@override |
JS.Do visitDoStatement(DoStatement node) { |
- return new JS.Do(node.body.accept(this), node.condition.accept(this)); |
+ return new JS.Do(_visit(node.body), _visit(node.condition)); |
} |
@override |
@@ -1329,8 +1319,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (init == null) { |
init = js.call('let #', node.loopVariable.identifier.name); |
} |
- return new JS.ForOf( |
- init, node.iterable.accept(this), node.body.accept(this)); |
+ return new JS.ForOf(init, _visit(node.iterable), _visit(node.body)); |
} |
@override |
@@ -1384,16 +1373,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
if (node.catchKeyword != null) { |
var name = node.exceptionParameter; |
if (name != null && name.name != varName) { |
- body.add(js.statement('let # = #;', [name.accept(this), varName])); |
+ body.add(js.statement('let # = #;', [_visit(name), varName])); |
} |
if (node.stackTraceParameter != null) { |
var stackVar = node.stackTraceParameter.name; |
body.add(js.statement( |
- 'let # = dart.stackTrace(#);', [stackVar, name.accept(this)])); |
+ 'let # = dart.stackTrace(#);', [stackVar, _visit(name)])); |
} |
} |
- body.add(node.body.accept(this)); |
+ body.add(_visit(node.body)); |
if (node.exceptionType != null) { |
return js.statement('if (dart.is(#, #)) #;', [ |
@@ -1407,7 +1396,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.Case visitSwitchCase(SwitchCase node) { |
- var expr = node.expression.accept(this); |
+ var expr = _visit(node.expression); |
var body = _visitList(node.statements); |
if (node.labels.isNotEmpty) { |
body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); |
@@ -1428,7 +1417,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
JS.Switch visitSwitchStatement(SwitchStatement node) => |
- new JS.Switch(node.expression.accept(this), _visitList(node.members)); |
+ new JS.Switch(_visit(node.expression), _visitList(node.members)); |
@override |
JS.Statement visitLabeledStatement(LabeledStatement node) { |
@@ -1471,15 +1460,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
var props = []; |
for (var e in entries) { |
var key = (e.key as SimpleStringLiteral).value; |
- var value = e.value.accept(this); |
+ var value = _visit(e.value); |
props.add(new JS.Property(js.escapedString(key), value)); |
} |
mapArguments = new JS.ObjectInitializer(props); |
} else { |
var values = []; |
for (var e in entries) { |
- values.add(e.key.accept(this)); |
- values.add(e.value.accept(this)); |
+ values.add(_visit(e.key)); |
+ values.add(_visit(e.value)); |
} |
mapArguments = new JS.ArrayInitializer(values); |
} |
@@ -1511,7 +1500,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
@override |
visitInterpolationExpression(InterpolationExpression node) => |
- node.expression.accept(this); |
+ _visit(node.expression); |
@override |
visitBooleanLiteral(BooleanLiteral node) => js.boolean(node.value); |
@@ -1570,17 +1559,22 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
return element is TopLevelVariableElement && !element.isConst; |
} |
- _visit(AstNode node) => node != null ? node.accept(this) : null; |
+ _visit(AstNode node) { |
+ if (node == null) return null; |
+ var result = node.accept(this); |
+ if (result is JS.Node) result.sourceInformation = node; |
+ return result; |
+ } |
- JS.Statement _visitOrEmpty(AstNode node) { |
+ JS.Statement _visitOrEmpty(Statement node) { |
if (node == null) return new JS.EmptyStatement(); |
- return node.accept(this); |
+ return _visit(node); |
} |
List _visitList(Iterable<AstNode> nodes) { |
if (nodes == null) return null; |
var result = []; |
- for (var node in nodes) result.add(node.accept(this)); |
+ for (var node in nodes) result.add(_visit(node)); |
return result; |
} |
@@ -1590,7 +1584,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
JS.Expression result = null; |
for (var node in nodes) { |
- var jsExpr = node.accept(this); |
+ var jsExpr = _visit(node); |
if (result == null) { |
result = jsExpr; |
} else { |
@@ -1712,7 +1706,9 @@ class _BindThisVisitor extends RecursiveAstVisitor { |
} |
class JSGenerator extends CodeGenerator { |
- JSGenerator(String outDir, Uri root, TypeRules rules) |
+ final JSCodeOptions options; |
+ |
+ JSGenerator(String outDir, Uri root, TypeRules rules, this.options) |
: super(outDir, root, rules); |
void generateLibrary(Iterable<CompilationUnit> units, LibraryInfo info, |
@@ -1723,15 +1719,40 @@ class JSGenerator extends CodeGenerator { |
var outputPath = path.join(outDir, jsOutputPath(info)); |
new Directory(path.dirname(outputPath)).createSync(recursive: true); |
- var context = new JS.SimpleJavaScriptPrintingContext(); |
+ if (options.emitSourceMaps) { |
+ var outFilename = path.basename(outputPath); |
+ var printer = new srcmaps.Printer(outFilename); |
+ var context = |
+ new SourceMapPrintingContext(printer, path.dirname(outputPath)); |
+ _writeLibrary(context, jsTree); |
+ printer.add('//# sourceMappingURL=$outFilename.map'); |
+ // Write output file and source map |
+ new File(outputPath).writeAsStringSync(printer.text); |
+ new File('$outputPath.map').writeAsStringSync(printer.map); |
+ } else { |
+ var context = new JS.SimpleJavaScriptPrintingContext(); |
+ _writeLibrary(context, jsTree); |
+ // Write output file and source map |
+ new File(outputPath).writeAsStringSync(context.getText()); |
+ } |
+ } |
+ |
+ void _writeLibrary(JS.JavaScriptPrintingContext context, JS.Block jsTree) { |
var opts = |
new JS.JavaScriptPrintingOptions(avoidKeywordsInIdentifiers: true); |
- var printer = new JS.Printer(opts, context); |
- printer.blockOutWithoutBraces(jsTree); |
- new File(outputPath).writeAsStringSync(context.getText()); |
+ new JS.Printer(opts, context).blockOutWithoutBraces(jsTree); |
} |
} |
+/// This is a debugging helper to print a JS node. |
+String debugJsNodeToString(JS.Node node) { |
+ var context = new JS.SimpleJavaScriptPrintingContext(); |
+ var opts = new JS.JavaScriptPrintingOptions(avoidKeywordsInIdentifiers: true); |
+ new JS.Printer(opts, context).visit(node); |
+ // Write output file and source map |
+ return context.getText(); |
+} |
+ |
/// Choose a canonical name from the library element. |
/// This never uses the library's name (the identifier in the `library` |
/// declaration) as it doesn't have any meaningful rules enforced. |
@@ -1742,3 +1763,68 @@ String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
// root. For example, "package:dev_compiler/src/codegen/js_codegen.dart" would be: |
// "ddc/src/codegen/js_codegen.js" under the output directory. |
String jsOutputPath(LibraryInfo info) => '${info.name}/${info.name}.js'; |
+ |
+class SourceMapPrintingContext extends JS.JavaScriptPrintingContext { |
+ final srcmaps.Printer printer; |
+ final String outputDir; |
+ |
+ CompilationUnit unit; |
+ Uri uri; |
+ |
+ SourceMapPrintingContext(this.printer, this.outputDir); |
+ |
+ void emit(String string) { |
+ printer.add(string); |
+ } |
+ |
+ void enterNode(JS.Node jsNode) { |
+ AstNode node = jsNode.sourceInformation; |
+ if (node is CompilationUnit) { |
+ unit = node; |
+ uri = _makeRelativeUri(unit.element.source.uri); |
+ return; |
+ } |
+ if (unit == null || node == null || node.offset == -1) return; |
+ |
+ var loc = _location(node.offset); |
+ var name = _getIdentifier(node); |
+ if (name != null) { |
+ // TODO(jmesserly): mark only uses the beginning of the span, but |
+ // we're required to pass this as a valid span. |
+ var end = _location(node.end); |
+ printer.mark(new SourceMapSpan(loc, end, name, isIdentifier: true)); |
Siggi Cherem (dart-lang)
2015/02/27 23:26:23
btw - I just realized this, in reporter.dart we do
Jennifer Messerly
2015/02/27 23:30:45
yes probably :)
opened https://github.com/dart-lan
|
+ } else { |
+ printer.mark(loc); |
+ } |
+ } |
+ |
+ SourceLocation _location(int offset) { |
+ var lineInfo = unit.lineInfo.getLocation(offset); |
+ return new SourceLocation(offset, |
+ sourceUrl: uri, |
+ line: lineInfo.lineNumber - 1, |
+ column: lineInfo.columnNumber - 1); |
+ } |
+ |
+ Uri _makeRelativeUri(Uri src) { |
+ return new Uri(path: path.relative(src.path, from: outputDir)); |
+ } |
+ |
+ void exitNode(JS.Node jsNode) { |
+ AstNode node = jsNode.sourceInformation; |
+ if (node is CompilationUnit) { |
+ unit = null; |
+ uri = null; |
+ return; |
+ } |
+ if (unit == null || node == null || node.offset == -1) return; |
+ |
+ // TODO(jmesserly): in many cases marking the end will be unncessary. |
+ printer.mark(_location(node.end)); |
+ } |
+ |
+ String _getIdentifier(AstNode node) { |
+ if (node is SimpleIdentifier) return node.name; |
+ return null; |
+ } |
+} |