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 CodegenBailout { | 16 class CodegenBailout { |
| 17 final tree_ir.Node node; | 17 final tree_ir.Node node; |
| 18 final String reason; | 18 final String reason; |
| 19 CodegenBailout(this.node, this.reason); | 19 CodegenBailout(this.node, this.reason); |
| 20 String get message { | 20 String get message { |
| 21 return 'bailout${node != null ? " on $node" : ""}: $reason'; | 21 return 'bailout${node != null ? " on $node" : ""}: $reason'; |
| 22 } | 22 } |
| 23 } | 23 } |
| 24 | 24 |
| 25 class CodeGenerator extends tree_ir.Visitor<dynamic, js.Expression> { | 25 class CodeGenerator extends tree_ir.Visitor<dynamic, js.Expression> { |
| 26 final CodegenRegistry registry; | 26 final CodegenRegistry registry; |
| 27 | 27 |
| 28 final Glue glue; | 28 final Glue glue; |
| 29 | 29 |
| 30 ExecutableElement currentFunction; | |
| 31 | |
| 30 /// Variables to be hoisted at the top of the current function. | 32 /// Variables to be hoisted at the top of the current function. |
| 31 List<js.VariableDeclaration> variables = <js.VariableDeclaration>[]; | 33 List<js.VariableInitialization> variables = <js.VariableInitialization>[]; |
| 32 | 34 |
| 33 /// Maps variables to their name. | 35 /// Maps variables to their name. |
| 34 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; | 36 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; |
| 35 | 37 |
| 36 /// Maps local constants to their name. | 38 /// Maps local constants to their name. |
| 37 Maplet<VariableElement, String> constantNames = | 39 Maplet<VariableElement, String> constantNames = |
| 38 new Maplet<VariableElement, String>(); | 40 new Maplet<VariableElement, String>(); |
| 39 | 41 |
| 40 /// Variables that have had their declaration created. | 42 /// Variables that already have had their declaration created. |
| 43 /// We keep this set, so that we can create declarations for the rest after | |
| 44 /// visiting a function body. | |
| 41 Set<tree_ir.Variable> declaredVariables = new Set<tree_ir.Variable>(); | 45 Set<tree_ir.Variable> declaredVariables = new Set<tree_ir.Variable>(); |
| 42 | 46 |
| 43 /// Variable names that have already been used. Used to avoid name clashes. | 47 /// Variable names that have already been used. Used to avoid name clashes. |
| 44 Set<String> usedVariableNames; | 48 Set<String> usedVariableNames = new Set<String>(); |
| 45 | 49 |
| 46 List<js.Parameter> parameters = new List<js.Parameter>(); | 50 List<js.Parameter> parameters = new List<js.Parameter>(); |
| 47 List<js.Statement> accumulator = new List<js.Statement>(); | 51 List<js.Statement> accumulator = new List<js.Statement>(); |
| 48 | 52 |
| 49 js.Block body; | 53 js.Block body; |
| 50 | 54 |
| 51 /// Generates JavaScript code for the body of [function]. | 55 /// Generates JavaScript code for the body of [function]. |
| 52 /// The code will be in [body] and the parameters will be in [parameters]. | 56 /// The code will be in [body] and the parameters will be in [parameters]. |
| 53 CodeGenerator(this.glue, this.registry); | 57 CodeGenerator(this.glue, this.registry); |
| 54 | 58 |
| 55 void buildFunction(tree_ir.FunctionDefinition function) { | 59 void buildFunction(tree_ir.FunctionDefinition function) { |
| 60 currentFunction = function.element; | |
| 56 visitStatement(function.body); | 61 visitStatement(function.body); |
| 57 for (tree_ir.Variable parameter in function.parameters) { | 62 for (tree_ir.Variable parameter in function.parameters) { |
| 58 parameters.add(new js.Parameter(variableNames[parameter])); | 63 String name = getVariableName(parameter); |
| 64 parameters.add(new js.Parameter(name)); | |
| 65 declaredVariables.add(parameter); | |
| 66 } | |
| 67 | |
| 68 // Some of the variable declarations have already been added | |
| 69 // if their first assignment could be pulled into the initializer. | |
| 70 // Add the remaining variable declarations now. | |
| 71 for (tree_ir.Variable variable in variableNames.keys) { | |
| 72 if (!declaredVariables.contains(variable)) { | |
| 73 addDeclaration(variable); | |
| 74 } | |
| 75 } | |
| 76 if (variables.length > 0) { | |
| 77 // Would be nice to avoid inserting at the beginning of list. | |
| 78 accumulator.insert(0, new js.ExpressionStatement( | |
| 79 new js.VariableDeclarationList(variables))); | |
| 59 } | 80 } |
| 60 body = new js.Block(accumulator); | 81 body = new js.Block(accumulator); |
| 61 } | 82 } |
| 62 | 83 |
| 84 void addDeclaration(tree_ir.Variable variable, [js.Expression initializer]) { | |
| 85 assert(!declaredVariables.contains(variable)); | |
| 86 String name = getVariableName(variable); | |
| 87 js.VariableInitialization decl = new js.VariableInitialization( | |
| 88 new js.VariableDeclaration(name), | |
| 89 initializer); | |
| 90 declaredVariables.add(variable); | |
| 91 variables.add(decl); | |
| 92 } | |
| 93 | |
| 94 /// Generates a name for the given variable. First trying with the name of | |
| 95 /// the [Variable.element] if it is non-null. | |
| 96 String getVariableName(tree_ir.Variable variable) { | |
| 97 // TODO(sigurdm): Handle case where the variable belongs to an enclosing | |
| 98 // function. | |
| 99 if (variable.host.element != currentFunction) giveup(variable); | |
| 100 | |
| 101 // Get the name if we already have one. | |
| 102 String name = variableNames[variable]; | |
| 103 if (name != null) { | |
| 104 return name; | |
| 105 } | |
| 106 | |
| 107 // Synthesize a variable name that isn't used elsewhere. | |
| 108 // The [usedVariableNames] set is shared between nested emitters, | |
| 109 // so this also prevents clash with variables in an enclosing/inner scope. | |
| 110 // The renaming phase after codegen will further prefix local variables | |
| 111 // so they cannot clash with top-level variables or fields. | |
| 112 String prefix = variable.element == null ? 'v' : variable.element.name; | |
| 113 int counter = 0; | |
| 114 name = glue.safeVariableName(variable.element == null | |
| 115 ? '$prefix$counter' | |
| 116 : variable.element.name); | |
| 117 while (!usedVariableNames.add(name)) { | |
| 118 ++counter; | |
| 119 name = '$prefix$counter'; | |
| 120 } | |
| 121 variableNames[variable] = name; | |
| 122 | |
| 123 return name; | |
| 124 } | |
| 125 | |
| 63 List<js.Expression> visitArguments(List<tree_ir.Expression> arguments) { | 126 List<js.Expression> visitArguments(List<tree_ir.Expression> arguments) { |
| 64 return arguments.map(visitExpression).toList(); | 127 return arguments.map(visitExpression).toList(); |
| 65 } | 128 } |
| 66 | 129 |
| 67 giveup(tree_ir.Node node, | 130 giveup(tree_ir.Node node, |
| 68 [String reason = 'unimplemented in CodeGenerator']) { | 131 [String reason = 'unimplemented in CodeGenerator']) { |
| 69 throw new CodegenBailout(node, reason); | 132 throw new CodegenBailout(node, reason); |
| 70 } | 133 } |
| 71 | 134 |
| 72 @override | 135 @override |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 } | 222 } |
| 160 | 223 |
| 161 @override | 224 @override |
| 162 js.Expression visitTypeOperator(tree_ir.TypeOperator node) { | 225 js.Expression visitTypeOperator(tree_ir.TypeOperator node) { |
| 163 return giveup(node); | 226 return giveup(node); |
| 164 // TODO: implement visitTypeOperator | 227 // TODO: implement visitTypeOperator |
| 165 } | 228 } |
| 166 | 229 |
| 167 @override | 230 @override |
| 168 js.Expression visitVariable(tree_ir.Variable node) { | 231 js.Expression visitVariable(tree_ir.Variable node) { |
| 169 return giveup(node); | 232 return new js.VariableUse(getVariableName(node)); |
| 170 // TODO: implement visitVariable | 233 // TODO: implement visitVariable |
| 171 } | 234 } |
| 172 | 235 |
| 173 @override | 236 @override |
| 174 void visitContinue(tree_ir.Continue node) { | 237 void visitContinue(tree_ir.Continue node) { |
| 175 return giveup(node); | 238 return giveup(node); |
| 176 // TODO: implement visitContinue | 239 // TODO: implement visitContinue |
| 177 } | 240 } |
| 178 | 241 |
| 179 @override | 242 @override |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 196 } | 259 } |
| 197 | 260 |
| 198 @override | 261 @override |
| 199 void visitLabeledStatement(tree_ir.LabeledStatement node) { | 262 void visitLabeledStatement(tree_ir.LabeledStatement node) { |
| 200 giveup(node); | 263 giveup(node); |
| 201 // TODO: implement visitLabeledStatement | 264 // TODO: implement visitLabeledStatement |
| 202 } | 265 } |
| 203 | 266 |
| 204 @override | 267 @override |
| 205 void visitAssign(tree_ir.Assign node) { | 268 void visitAssign(tree_ir.Assign node) { |
| 206 giveup(node); | 269 // Try to emit a local function declaration. This is useful for functions |
| 207 // TODO: implement visitAssign | 270 // that may occur in expression context, but could not be inlined anywhere. |
| 271 if (node.variable.element is FunctionElement && | |
|
floitsch
2014/11/18 19:00:43
I don't think we need to special case function-exp
| |
| 272 node.definition is tree_ir.FunctionExpression && | |
| 273 !declaredVariables.contains(node.variable)) { | |
| 274 // TODO(sigurdm): implement function expressions. | |
| 275 giveup(node.definition); | |
| 276 } | |
| 277 | |
| 278 bool isFirstOccurrence = (variableNames[node.variable] == null); | |
| 279 bool isDeclaredHere = node.variable.host.element == currentFunction; | |
| 280 String name = getVariableName(node.variable); | |
| 281 tree_ir.Expression value = node.definition; | |
| 282 js.Expression definition = visitExpression(value); | |
| 283 | |
| 284 // Emit a variable declaration if we are required to do so. | |
| 285 // This is to ensure that a fresh closure variable is created. | |
|
floitsch
2014/11/18 19:00:44
That doesn't make sense in the dart2js backend.
I
| |
| 286 if (node.isDeclaration) { | |
| 287 if (isNullLiteral(definition)) { | |
| 288 definition = null; | |
| 289 } | |
| 290 js.VariableInitialization initialization = | |
| 291 new js.VariableInitialization(new js.VariableDeclaration(name), | |
| 292 definition); | |
| 293 declaredVariables.add(node.variable); | |
| 294 accumulator.add(new js.ExpressionStatement( | |
| 295 new js.VariableDeclarationList([initialization]))); | |
| 296 visitStatement(node.next); | |
| 297 return; | |
| 298 } | |
| 299 | |
| 300 accumulator.add(new js.ExpressionStatement(new js.Assignment( | |
| 301 visitVariable(node.variable), | |
| 302 definition))); | |
| 303 visitStatement(node.next); | |
| 304 | |
| 208 } | 305 } |
| 209 | 306 |
| 210 @override | 307 @override |
| 211 void visitBreak(tree_ir.Break node) { | 308 void visitBreak(tree_ir.Break node) { |
| 212 giveup(node); | 309 giveup(node); |
| 213 // TODO: implement visitBreak | 310 // TODO: implement visitBreak |
| 214 } | 311 } |
| 215 | 312 |
| 216 @override | 313 @override |
| 217 void visitWhileCondition(tree_ir.WhileCondition node) { | 314 void visitWhileCondition(tree_ir.WhileCondition node) { |
| 218 giveup(node); | 315 giveup(node); |
| 219 // TODO: implement visitWhileCondition | 316 // TODO: implement visitWhileCondition |
| 220 } | 317 } |
| 221 | 318 |
| 222 @override | 319 @override |
| 223 void visitWhileTrue(tree_ir.WhileTrue node) { | 320 void visitWhileTrue(tree_ir.WhileTrue node) { |
| 224 giveup(node); | 321 giveup(node); |
| 225 // TODO: implement visitWhileTrue | 322 // TODO: implement visitWhileTrue |
| 226 } | 323 } |
| 227 | 324 |
| 228 @override | 325 @override |
| 229 void visitReturn(tree_ir.Return node) { | 326 void visitReturn(tree_ir.Return node) { |
| 230 if (node.value != null) { | 327 if (node.value != null) { |
| 231 accumulator.add(new js.Return(visitExpression(node.value))); | 328 accumulator.add(new js.Return(visitExpression(node.value))); |
| 232 } | 329 } |
| 233 } | 330 } |
| 331 | |
| 332 bool isNullLiteral(js.Expression exp) => exp is js.LiteralNull; | |
| 333 | |
| 234 } | 334 } |
| OLD | NEW |