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 /// Variables to be hoisted at the top of the current function. | 30 ExecutableElement currentFunction; |
| 31 List<js.VariableDeclaration> variables = <js.VariableDeclaration>[]; | |
| 32 | 31 |
| 33 /// Maps variables to their name. | 32 /// Maps variables to their name. |
| 34 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; | 33 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; |
| 35 | 34 |
| 36 /// Maps local constants to their name. | 35 /// Maps local constants to their name. |
| 37 Maplet<VariableElement, String> constantNames = | 36 Maplet<VariableElement, String> constantNames = |
| 38 new Maplet<VariableElement, String>(); | 37 new Maplet<VariableElement, String>(); |
| 39 | 38 |
| 40 /// Variables that have had their declaration created. | |
| 41 Set<tree_ir.Variable> declaredVariables = new Set<tree_ir.Variable>(); | |
| 42 | |
| 43 /// Variable names that have already been used. Used to avoid name clashes. | 39 /// Variable names that have already been used. Used to avoid name clashes. |
| 44 Set<String> usedVariableNames; | 40 Set<String> usedVariableNames = new Set<String>(); |
| 45 | 41 |
| 46 List<js.Parameter> parameters = new List<js.Parameter>(); | 42 List<js.Parameter> parameters = new List<js.Parameter>(); |
| 47 List<js.Statement> accumulator = new List<js.Statement>(); | 43 List<js.Statement> accumulator = new List<js.Statement>(); |
| 48 | 44 |
| 49 js.Block body; | 45 js.Block body; |
| 50 | 46 |
| 51 /// Generates JavaScript code for the body of [function]. | 47 /// Generates JavaScript code for the body of [function]. |
| 52 /// The code will be in [body] and the parameters will be in [parameters]. | 48 /// The code will be in [body] and the parameters will be in [parameters]. |
| 53 CodeGenerator(this.glue, this.registry); | 49 CodeGenerator(this.glue, this.registry); |
| 54 | 50 |
| 55 void buildFunction(tree_ir.FunctionDefinition function) { | 51 void buildFunction(tree_ir.FunctionDefinition function) { |
| 52 currentFunction = function.element; | |
| 56 visitStatement(function.body); | 53 visitStatement(function.body); |
| 57 for (tree_ir.Variable parameter in function.parameters) { | 54 for (tree_ir.Variable parameter in function.parameters) { |
| 58 parameters.add(new js.Parameter(variableNames[parameter])); | 55 String name = getVariableName(parameter); |
| 56 parameters.add(new js.Parameter(name)); | |
| 57 } | |
| 58 | |
| 59 List<js.VariableInitialization> jsVariables = <js.VariableInitialization>[]; | |
| 60 | |
| 61 for (tree_ir.Variable variable in variableNames.keys) { | |
| 62 String name = getVariableName(variable); | |
| 63 js.VariableInitialization jsVariable = new js.VariableInitialization( | |
| 64 new js.VariableDeclaration(name), | |
| 65 null); | |
| 66 jsVariables.add(jsVariable); | |
| 67 } | |
| 68 | |
| 69 if (jsVariables.length > 0) { | |
| 70 // Would be nice to avoid inserting at the beginning of list. | |
| 71 accumulator.insert(0, new js.ExpressionStatement( | |
| 72 new js.VariableDeclarationList(jsVariables))); | |
| 59 } | 73 } |
| 60 body = new js.Block(accumulator); | 74 body = new js.Block(accumulator); |
| 61 } | 75 } |
| 62 | 76 |
| 77 /// Generates a name for the given variable. First trying with the name of | |
| 78 /// the [Variable.element] if it is non-null. | |
| 79 String getVariableName(tree_ir.Variable variable) { | |
| 80 // TODO(sigurdm): Handle case where the variable belongs to an enclosing | |
| 81 // function. | |
| 82 if (variable.host.element != currentFunction) giveup(variable); | |
| 83 | |
| 84 // Get the name if we already have one. | |
| 85 String name = variableNames[variable]; | |
| 86 if (name != null) { | |
| 87 return name; | |
| 88 } | |
| 89 | |
| 90 // Synthesize a variable name that isn't used elsewhere. | |
| 91 // The [usedVariableNames] set is shared between nested emitters, | |
| 92 // so this also prevents clash with variables in an enclosing/inner scope. | |
| 93 // The renaming phase after codegen will further prefix local variables | |
| 94 // so they cannot clash with top-level variables or fields. | |
| 95 String prefix = variable.element == null ? 'v' : variable.element.name; | |
| 96 int counter = 0; | |
| 97 name = glue.safeVariableName(variable.element == null | |
| 98 ? '$prefix$counter' | |
| 99 : variable.element.name); | |
| 100 while (!usedVariableNames.add(name)) { | |
| 101 ++counter; | |
| 102 name = '$prefix$counter'; | |
| 103 } | |
| 104 variableNames[variable] = name; | |
| 105 | |
| 106 return name; | |
| 107 } | |
| 108 | |
| 63 List<js.Expression> visitArguments(List<tree_ir.Expression> arguments) { | 109 List<js.Expression> visitArguments(List<tree_ir.Expression> arguments) { |
| 64 return arguments.map(visitExpression).toList(); | 110 return arguments.map(visitExpression).toList(); |
| 65 } | 111 } |
| 66 | 112 |
| 67 giveup(tree_ir.Node node, | 113 giveup(tree_ir.Node node, |
| 68 [String reason = 'unimplemented in CodeGenerator']) { | 114 [String reason = 'unimplemented in CodeGenerator']) { |
| 69 throw new CodegenBailout(node, reason); | 115 throw new CodegenBailout(node, reason); |
| 70 } | 116 } |
| 71 | 117 |
| 72 @override | 118 @override |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 } | 205 } |
| 160 | 206 |
| 161 @override | 207 @override |
| 162 js.Expression visitTypeOperator(tree_ir.TypeOperator node) { | 208 js.Expression visitTypeOperator(tree_ir.TypeOperator node) { |
| 163 return giveup(node); | 209 return giveup(node); |
| 164 // TODO: implement visitTypeOperator | 210 // TODO: implement visitTypeOperator |
| 165 } | 211 } |
| 166 | 212 |
| 167 @override | 213 @override |
| 168 js.Expression visitVariable(tree_ir.Variable node) { | 214 js.Expression visitVariable(tree_ir.Variable node) { |
| 169 return giveup(node); | 215 return new js.VariableUse(getVariableName(node)); |
| 170 // TODO: implement visitVariable | 216 // TODO: implement visitVariable |
| 171 } | 217 } |
| 172 | 218 |
| 173 @override | 219 @override |
| 174 void visitContinue(tree_ir.Continue node) { | 220 void visitContinue(tree_ir.Continue node) { |
| 175 return giveup(node); | 221 return giveup(node); |
| 176 // TODO: implement visitContinue | 222 // TODO: implement visitContinue |
| 177 } | 223 } |
| 178 | 224 |
| 179 @override | 225 @override |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 196 } | 242 } |
| 197 | 243 |
| 198 @override | 244 @override |
| 199 void visitLabeledStatement(tree_ir.LabeledStatement node) { | 245 void visitLabeledStatement(tree_ir.LabeledStatement node) { |
| 200 giveup(node); | 246 giveup(node); |
| 201 // TODO: implement visitLabeledStatement | 247 // TODO: implement visitLabeledStatement |
| 202 } | 248 } |
| 203 | 249 |
| 204 @override | 250 @override |
| 205 void visitAssign(tree_ir.Assign node) { | 251 void visitAssign(tree_ir.Assign node) { |
| 206 giveup(node); | 252 |
| 207 // TODO: implement visitAssign | 253 bool isFirstOccurrence = (variableNames[node.variable] == null); |
|
floitsch
2014/11/19 12:02:19
unused
sigurdm
2014/11/20 12:22:40
Fixed by https://codereview.chromium.org/737363002
| |
| 254 bool isDeclaredHere = node.variable.host.element == currentFunction; | |
|
floitsch
2014/11/19 12:02:19
unused.
sigurdm
2014/11/20 12:22:40
Done.
| |
| 255 String name = getVariableName(node.variable); | |
|
floitsch
2014/11/19 12:02:19
unused.
sigurdm
2014/11/20 12:22:40
Done.
| |
| 256 tree_ir.Expression value = node.definition; | |
| 257 js.Expression definition = visitExpression(value); | |
| 258 | |
| 259 accumulator.add(new js.ExpressionStatement(new js.Assignment( | |
| 260 visitVariable(node.variable), | |
| 261 definition))); | |
| 262 visitStatement(node.next); | |
| 208 } | 263 } |
| 209 | 264 |
| 210 @override | 265 @override |
| 211 void visitBreak(tree_ir.Break node) { | 266 void visitBreak(tree_ir.Break node) { |
| 212 giveup(node); | 267 giveup(node); |
| 213 // TODO: implement visitBreak | 268 // TODO: implement visitBreak |
| 214 } | 269 } |
| 215 | 270 |
| 216 @override | 271 @override |
| 217 void visitWhileCondition(tree_ir.WhileCondition node) { | 272 void visitWhileCondition(tree_ir.WhileCondition node) { |
| 218 giveup(node); | 273 giveup(node); |
| 219 // TODO: implement visitWhileCondition | 274 // TODO: implement visitWhileCondition |
| 220 } | 275 } |
| 221 | 276 |
| 222 @override | 277 @override |
| 223 void visitWhileTrue(tree_ir.WhileTrue node) { | 278 void visitWhileTrue(tree_ir.WhileTrue node) { |
| 224 giveup(node); | 279 giveup(node); |
| 225 // TODO: implement visitWhileTrue | 280 // TODO: implement visitWhileTrue |
| 226 } | 281 } |
| 227 | 282 |
| 228 @override | 283 @override |
| 229 void visitReturn(tree_ir.Return node) { | 284 void visitReturn(tree_ir.Return node) { |
| 230 if (node.value != null) { | 285 if (node.value != null) { |
| 231 accumulator.add(new js.Return(visitExpression(node.value))); | 286 accumulator.add(new js.Return(visitExpression(node.value))); |
| 232 } | 287 } |
| 233 } | 288 } |
| 289 | |
| 290 bool isNullLiteral(js.Expression exp) => exp is js.LiteralNull; | |
| 291 | |
| 234 } | 292 } |
| OLD | NEW |