Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 code_generator; | 5 library code_generator; |
| 6 | 6 |
| 7 import 'glue.dart'; | 7 import 'glue.dart'; |
| 8 | 8 |
| 9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; | 9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; |
| 10 import '../../js/js.dart' as js; | 10 import '../../js/js.dart' as js; |
| 11 import '../../elements/elements.dart'; | 11 import '../../elements/elements.dart'; |
| 12 import '../../util/maplet.dart'; | 12 import '../../util/maplet.dart'; |
| 13 import '../../constants/values.dart'; | 13 import '../../constants/values.dart'; |
| 14 import '../../dart2jslib.dart'; | 14 import '../../dart2jslib.dart'; |
| 15 | 15 |
| 16 class CodeGenerator extends tree_ir.Visitor<dynamic, js.Expression> { | 16 class CodeGenerator extends tree_ir.Visitor<dynamic, js.Expression> { |
| 17 static const String UNIMPLEMENTED = "Javascript generation aborted"; | 17 static const String UNIMPLEMENTED = "Javascript generation aborted"; |
| 18 | 18 |
| 19 final CodegenRegistry registry; | 19 final CodegenRegistry registry; |
| 20 | 20 |
| 21 final Glue glue; | 21 final Glue glue; |
| 22 | 22 |
| 23 ExecutableElement currentFunction; | |
| 24 | |
| 23 /// Variables to be hoisted at the top of the current function. | 25 /// Variables to be hoisted at the top of the current function. |
| 24 List<js.VariableDeclaration> variables = <js.VariableDeclaration>[]; | 26 List<js.VariableInitialization> variables = <js.VariableInitialization>[]; |
| 25 | 27 |
| 26 /// Maps variables to their name. | 28 /// Maps variables to their name. |
| 27 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; | 29 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; |
| 28 | 30 |
| 29 /// Maps local constants to their name. | 31 /// Maps local constants to their name. |
| 30 Maplet<VariableElement, String> constantNames = | 32 Maplet<VariableElement, String> constantNames = |
| 31 new Maplet<VariableElement, String>(); | 33 new Maplet<VariableElement, String>(); |
| 32 | 34 |
| 33 /// Variables that have had their declaration created. | 35 /// Variables that have had their declaration created. |
|
floitsch
2014/11/13 15:59:32
I'm not sure I understand that comment.
sigurdm
2014/11/18 15:17:46
I tried to improve it.
| |
| 34 Set<tree_ir.Variable> declaredVariables = new Set<tree_ir.Variable>(); | 36 Set<tree_ir.Variable> declaredVariables = new Set<tree_ir.Variable>(); |
| 35 | 37 |
| 36 /// Variable names that have already been used. Used to avoid name clashes. | 38 /// Variable names that have already been used. Used to avoid name clashes. |
| 37 Set<String> usedVariableNames; | 39 Set<String> usedVariableNames = new Set<String>(); |
| 38 | 40 |
| 39 List<js.Parameter> parameters = new List<js.Parameter>(); | 41 List<js.Parameter> parameters = new List<js.Parameter>(); |
| 40 List<js.Statement> accumulator = new List<js.Statement>(); | 42 List<js.Statement> accumulator = new List<js.Statement>(); |
| 41 | 43 |
| 44 tree_ir.Statement firstStatement; | |
| 45 | |
| 42 js.Block body; | 46 js.Block body; |
| 43 | 47 |
| 44 /// Generates JavaScript code for the body of [function]. | 48 /// Generates JavaScript code for the body of [function]. |
| 45 /// The code will be in [body] and the parameters will be in [parameters]. | 49 /// The code will be in [body] and the parameters will be in [parameters]. |
| 46 CodeGenerator(this.glue, this.registry); | 50 CodeGenerator(this.glue, this.registry); |
| 47 | 51 |
| 48 void buildFunction(tree_ir.FunctionDefinition function) { | 52 void buildFunction(tree_ir.FunctionDefinition function) { |
| 53 currentFunction = function.element; | |
| 54 firstStatement = function.body; | |
| 49 visitStatement(function.body); | 55 visitStatement(function.body); |
| 50 for (tree_ir.Variable parameter in function.parameters) { | 56 for (tree_ir.Variable parameter in function.parameters) { |
| 51 parameters.add(new js.Parameter(variableNames[parameter])); | 57 parameters.add(new js.Parameter(variableNames[parameter])); |
| 58 variableNames[parameter] = parameter.element.name; | |
|
floitsch
2014/11/13 15:59:32
We will need to filter names here.
There are some
sigurdm
2014/11/18 15:17:46
Yes I think that is the right thing to do. I think
| |
| 59 usedVariableNames.add(parameter.element.name); | |
| 60 declaredVariables.add(parameter); | |
| 61 } | |
| 62 | |
| 63 | |
| 64 // Some of the variable declarations have already been added | |
| 65 // if their first assignment could be pulled into the initializer. | |
|
floitsch
2014/11/13 15:59:32
Not completely clear what this means.
| |
| 66 // Add the remaining variable declarations now. | |
| 67 for (tree_ir.Variable variable in variableNames.keys) { | |
| 68 if (!declaredVariables.contains(variable)) { | |
| 69 addDeclaration(variable); | |
| 70 } | |
| 71 } | |
| 72 if (variables.length > 0) { | |
| 73 // Would be nice to avoid inserting at the beginning of list. | |
| 74 accumulator.insert(0, new js.ExpressionStatement( | |
| 75 new js.VariableDeclarationList(variables))); | |
| 52 } | 76 } |
| 53 body = new js.Block(accumulator); | 77 body = new js.Block(accumulator); |
| 54 } | 78 } |
| 55 | 79 |
| 80 void addDeclaration(tree_ir.Variable variable, [js.Expression initializer]) { | |
| 81 assert(!declaredVariables.contains(variable)); | |
| 82 String name = getVariableName(variable); | |
| 83 js.VariableInitialization decl = new js.VariableInitialization( | |
| 84 new js.VariableDeclaration(name), | |
| 85 initializer); | |
| 86 declaredVariables.add(variable); | |
| 87 variables.add(decl); | |
| 88 } | |
| 89 | |
| 90 /// Generates a name for the given variable. First trying with the name of | |
| 91 /// the [Variable.element] if it is non-null. | |
| 92 String getVariableName(tree_ir.Variable variable) { | |
| 93 // TODO(sigurdm): Handle case where the variable belongs to an enclosing | |
| 94 // function. | |
| 95 if (variable.host.element != currentFunction) throw UNIMPLEMENTED; | |
| 96 | |
| 97 // Get the name if we already have one. | |
| 98 String name = variableNames[variable]; | |
| 99 if (name != null) { | |
| 100 return name; | |
| 101 } | |
| 102 | |
| 103 // Synthesize a variable name that isn't used elsewhere. | |
| 104 // The [usedVariableNames] set is shared between nested emitters, | |
| 105 // so this also prevents clash with variables in an enclosing/inner scope. | |
| 106 // The renaming phase after codegen will further prefix local variables | |
| 107 // so they cannot clash with top-level variables or fields. | |
| 108 String prefix = variable.element == null ? 'v' : variable.element.name; | |
| 109 int counter = 0; | |
| 110 name = variable.element == null ? '$prefix$counter' : variable.element.name; | |
| 111 while (!usedVariableNames.add(name)) { | |
| 112 ++counter; | |
| 113 name = '$prefix$counter'; | |
| 114 } | |
| 115 variableNames[variable] = name; | |
| 116 | |
| 117 return name; | |
| 118 } | |
| 119 | |
| 56 List<js.Expression> visitArguments(List<tree_ir.Expression> arguments) { | 120 List<js.Expression> visitArguments(List<tree_ir.Expression> arguments) { |
| 57 return arguments.map(visitExpression).toList(); | 121 return arguments.map(visitExpression).toList(); |
| 58 } | 122 } |
| 59 | 123 |
| 60 giveup(tree_ir.Node node) { | 124 giveup(tree_ir.Node node) { |
| 61 throw UNIMPLEMENTED; | 125 throw UNIMPLEMENTED; |
| 62 } | 126 } |
| 63 | 127 |
| 64 @override | 128 @override |
| 65 js.Expression visitConcatenateStrings(tree_ir.ConcatenateStrings node) { | 129 js.Expression visitConcatenateStrings(tree_ir.ConcatenateStrings node) { |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 } | 231 } |
| 168 | 232 |
| 169 @override | 233 @override |
| 170 js.Expression visitTypeOperator(tree_ir.TypeOperator node) { | 234 js.Expression visitTypeOperator(tree_ir.TypeOperator node) { |
| 171 return giveup(node); | 235 return giveup(node); |
| 172 // TODO: implement visitTypeOperator | 236 // TODO: implement visitTypeOperator |
| 173 } | 237 } |
| 174 | 238 |
| 175 @override | 239 @override |
| 176 js.Expression visitVariable(tree_ir.Variable node) { | 240 js.Expression visitVariable(tree_ir.Variable node) { |
| 177 return giveup(node); | 241 return new js.VariableUse(getVariableName(node)); |
| 178 // TODO: implement visitVariable | 242 // TODO: implement visitVariable |
| 179 } | 243 } |
| 180 | 244 |
| 181 @override | 245 @override |
| 182 void visitContinue(tree_ir.Continue node) { | 246 void visitContinue(tree_ir.Continue node) { |
| 183 return giveup(node); | 247 return giveup(node); |
| 184 // TODO: implement visitContinue | 248 // TODO: implement visitContinue |
| 185 } | 249 } |
| 186 | 250 |
| 187 @override | 251 @override |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 204 } | 268 } |
| 205 | 269 |
| 206 @override | 270 @override |
| 207 void visitLabeledStatement(tree_ir.LabeledStatement node) { | 271 void visitLabeledStatement(tree_ir.LabeledStatement node) { |
| 208 giveup(node); | 272 giveup(node); |
| 209 // TODO: implement visitLabeledStatement | 273 // TODO: implement visitLabeledStatement |
| 210 } | 274 } |
| 211 | 275 |
| 212 @override | 276 @override |
| 213 void visitAssign(tree_ir.Assign node) { | 277 void visitAssign(tree_ir.Assign node) { |
| 214 giveup(node); | 278 // Try to emit a local function declaration. This is useful for functions |
| 215 // TODO: implement visitAssign | 279 // that may occur in expression context, but could not be inlined anywhere. |
| 280 if (node.variable.element is FunctionElement && | |
| 281 node.definition is tree_ir.FunctionExpression && | |
| 282 !declaredVariables.contains(node.variable)) { | |
| 283 // TODO(sigurdm): implement function expressions. | |
| 284 giveup(node.definition); | |
| 285 } | |
| 286 | |
| 287 | |
| 288 bool isFirstOccurrence = (variableNames[node.variable] == null); | |
| 289 bool isDeclaredHere = node.variable.host.element == currentFunction; | |
| 290 String name = getVariableName(node.variable); | |
| 291 tree_ir.Expression value = node.definition; | |
| 292 js.Expression definition = visitExpression(value); | |
| 293 | |
| 294 // Try to pull into initializer. | |
|
floitsch
2014/11/13 15:59:32
I don't think we want to do this.
We found that:
v
sigurdm
2014/11/18 15:17:45
Ok, removed for now.
| |
| 295 if (firstStatement == node && isFirstOccurrence && isDeclaredHere) { | |
| 296 // if (isNullLiteral(definition)) definition = null; | |
|
floitsch
2014/11/13 15:59:32
commented code.
sigurdm
2014/11/18 15:17:46
Removed
| |
| 297 addDeclaration(node.variable, definition); | |
| 298 firstStatement = node.next; | |
| 299 visitStatement(node.next); | |
| 300 return; | |
| 301 } | |
| 302 | |
| 303 // Emit a variable declaration if we are required to do so. | |
| 304 // This is to ensure that a fresh closure variable is created. | |
| 305 if (node.isDeclaration) { | |
| 306 assert(isFirstOccurrence); | |
| 307 assert(isDeclaredHere); | |
| 308 // if (isNullLiteral(definition)) definition = null; | |
|
floitsch
2014/11/13 15:59:32
commented code.
sigurdm
2014/11/18 15:17:46
Done.
| |
| 309 js.VariableInitialization decl = | |
| 310 new js.VariableInitialization(new js.VariableDeclaration(name), | |
| 311 definition); | |
| 312 declaredVariables.add(node.variable); | |
| 313 accumulator.add(new js.ExpressionStatement( | |
| 314 new js.VariableDeclarationList([decl]))); | |
| 315 visitStatement(node.next); | |
|
floitsch
2014/11/13 15:59:32
I'm surprised that the visit-functions have to tri
sigurdm
2014/11/18 15:17:46
Yes - this is a side effect of how the tree IR is
| |
| 316 return; | |
| 317 } | |
| 318 | |
| 319 accumulator.add(new js.ExpressionStatement(new js.Assignment( | |
| 320 visitVariable(node.variable), | |
| 321 definition))); | |
| 322 visitStatement(node.next); | |
| 323 | |
| 216 } | 324 } |
| 217 | 325 |
| 218 @override | 326 @override |
| 219 void visitBreak(tree_ir.Break node) { | 327 void visitBreak(tree_ir.Break node) { |
| 220 giveup(node); | 328 giveup(node); |
| 221 // TODO: implement visitBreak | 329 // TODO: implement visitBreak |
| 222 } | 330 } |
| 223 | 331 |
| 224 @override | 332 @override |
| 225 void visitWhileCondition(tree_ir.WhileCondition node) { | 333 void visitWhileCondition(tree_ir.WhileCondition node) { |
| 226 giveup(node); | 334 giveup(node); |
| 227 // TODO: implement visitWhileCondition | 335 // TODO: implement visitWhileCondition |
| 228 } | 336 } |
| 229 | 337 |
| 230 @override | 338 @override |
| 231 void visitWhileTrue(tree_ir.WhileTrue node) { | 339 void visitWhileTrue(tree_ir.WhileTrue node) { |
| 232 giveup(node); | 340 giveup(node); |
| 233 // TODO: implement visitWhileTrue | 341 // TODO: implement visitWhileTrue |
| 234 } | 342 } |
| 235 | 343 |
| 236 @override | 344 @override |
| 237 void visitReturn(tree_ir.Return node) { | 345 void visitReturn(tree_ir.Return node) { |
| 238 if (node.value != null) { | 346 if (node.value != null) { |
| 239 accumulator.add(new js.Return(visitExpression(node.value))); | 347 accumulator.add(new js.Return(visitExpression(node.value))); |
| 240 } | 348 } |
| 241 } | 349 } |
| 242 } | 350 } |
| OLD | NEW |