| 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 backend_ast_emitter; | 5 library backend_ast_emitter; |
| 6 | 6 |
| 7 import '../tree_ir/tree_ir_nodes.dart' as tree; | |
| 8 import 'backend_ast_nodes.dart'; | 7 import 'backend_ast_nodes.dart'; |
| 9 import '../constants/expressions.dart'; | |
| 10 import '../constants/values.dart'; | |
| 11 import '../dart_types.dart'; | 8 import '../dart_types.dart'; |
| 12 import '../elements/elements.dart'; | 9 import '../elements/elements.dart'; |
| 13 import '../elements/modelx.dart' as modelx; | |
| 14 import '../universe/universe.dart'; | |
| 15 import '../tree/tree.dart' as tree show Modifiers; | |
| 16 | |
| 17 /// Translates the dart_tree IR to Dart backend AST. | |
| 18 RootNode emit(tree.RootNode root) { | |
| 19 return new ASTEmitter().emit(root); | |
| 20 } | |
| 21 | |
| 22 // TODO(johnniwinther): Split into function/block state. | |
| 23 class BuilderContext<T> { | |
| 24 /// Builder context for the enclosing function, or null if the current | |
| 25 /// function is not a local function. | |
| 26 BuilderContext<T> _parent; | |
| 27 | |
| 28 /// Variables to be hoisted at the top of the current function. | |
| 29 final List<VariableDeclaration> variables = <VariableDeclaration>[]; | |
| 30 | |
| 31 /// Maps variables to their name. | |
| 32 final Map<tree.Variable, String> variableNames; | |
| 33 | |
| 34 /// Maps local constants to their name. | |
| 35 final Map<VariableElement, String> constantNames = | |
| 36 <VariableElement, String>{}; | |
| 37 | |
| 38 /// Variables that have had their declaration created. | |
| 39 final Set<tree.Variable> declaredVariables = new Set<tree.Variable>(); | |
| 40 | |
| 41 /// Variables that are used as catch handler parameters. | |
| 42 final Set<tree.Variable> handlerVariables = new Set<tree.Variable>(); | |
| 43 | |
| 44 /// Variable names that have already been used. Used to avoid name clashes. | |
| 45 final Set<String> usedVariableNames; | |
| 46 | |
| 47 /// Statements emitted by the most recent call to [visitStatement]. | |
| 48 List<T> _statementBuffer = <T>[]; | |
| 49 | |
| 50 /// The element currently being emitted. | |
| 51 ExecutableElement currentElement; | |
| 52 | |
| 53 /// Bookkeeping object needed to synthesize a variable declaration. | |
| 54 final modelx.VariableList variableList | |
| 55 = new modelx.VariableList(tree.Modifiers.EMPTY); | |
| 56 | |
| 57 /// Input to [visitStatement]. Denotes the statement that will execute next | |
| 58 /// if the statements produced by [visitStatement] complete normally. | |
| 59 /// Set to null if control will fall over the end of the method. | |
| 60 tree.Statement fallthrough = null; | |
| 61 | |
| 62 /// Labels that could not be eliminated using fallthrough. | |
| 63 final Set<tree.Label> _usedLabels = new Set<tree.Label>(); | |
| 64 | |
| 65 final bool inInitializer; | |
| 66 | |
| 67 /// The first dart_tree statement that is not converted to a variable | |
| 68 /// initializer. | |
| 69 tree.Statement firstStatement; | |
| 70 | |
| 71 BuilderContext() : usedVariableNames = new Set<String>(), | |
| 72 inInitializer = false, | |
| 73 variableNames = <tree.Variable, String>{}; | |
| 74 | |
| 75 BuilderContext.inner(BuilderContext<T> parent) | |
| 76 : this._parent = parent, | |
| 77 usedVariableNames = parent.usedVariableNames, | |
| 78 inInitializer = false, | |
| 79 variableNames = <tree.Variable, String>{}; | |
| 80 | |
| 81 BuilderContext.initializer(BuilderContext<T> parent) | |
| 82 : this._parent = parent, | |
| 83 usedVariableNames = parent.usedVariableNames, | |
| 84 inInitializer = true, | |
| 85 variableNames = | |
| 86 new Map<tree.Variable, String>.from(parent.variableNames); | |
| 87 | |
| 88 // TODO(johnniwinther): Fully encapsulate handling of parameter, variable | |
| 89 // and local function declarations. | |
| 90 void addDeclaration(tree.Variable variable, [Expression initializer]) { | |
| 91 assert(!declaredVariables.contains(variable)); | |
| 92 String name = getVariableName(variable); | |
| 93 VariableDeclaration decl = new VariableDeclaration(name, initializer); | |
| 94 decl.element = variable.element; | |
| 95 declaredVariables.add(variable); | |
| 96 variables.add(decl); | |
| 97 } | |
| 98 | |
| 99 /// Creates an [Identifier] referring to the given variable. | |
| 100 Expression makeVariableAccess(tree.Variable variable) { | |
| 101 return new Identifier(getVariableName(variable)) | |
| 102 ..element = variable.element; | |
| 103 } | |
| 104 | |
| 105 /// Generates a name for the given variable and synthesizes an element for it, | |
| 106 /// if necessary. | |
| 107 String getVariableName(tree.Variable variable) { | |
| 108 // If the variable belongs to an enclosing function, ask the parent emitter | |
| 109 // for the variable name. | |
| 110 if (!inInitializer && variable.host != currentElement) { | |
| 111 return _parent.getVariableName(variable); | |
| 112 } | |
| 113 | |
| 114 // Get the name if we already have one. | |
| 115 String name = variableNames[variable]; | |
| 116 if (name != null) { | |
| 117 return name; | |
| 118 } | |
| 119 | |
| 120 // Synthesize a variable name that isn't used elsewhere. | |
| 121 // The [usedVariableNames] set is shared between nested emitters, | |
| 122 // so this also prevents clash with variables in an enclosing/inner scope. | |
| 123 // The renaming phase after codegen will further prefix local variables | |
| 124 // so they cannot clash with top-level variables or fields. | |
| 125 String prefix = variable.element == null ? 'v' : variable.element.name; | |
| 126 int counter = 0; | |
| 127 name = variable.element == null ? '$prefix$counter' : variable.element.name; | |
| 128 while (!usedVariableNames.add(name)) { | |
| 129 ++counter; | |
| 130 name = '$prefix$counter'; | |
| 131 } | |
| 132 variableNames[variable] = name; | |
| 133 | |
| 134 // Synthesize an element for the variable | |
| 135 if (variable.element == null || name != variable.element.name) { | |
| 136 // TODO(johnniwinther): Replace by synthetic [Entity]. | |
| 137 variable.element = new _SyntheticLocalVariableElement( | |
| 138 name, | |
| 139 currentElement, | |
| 140 variableList); | |
| 141 } | |
| 142 return name; | |
| 143 } | |
| 144 | |
| 145 /// Adds declarations for all variables that are still undeclared. | |
| 146 void declareRemainingVariables() { | |
| 147 // These variables can be referenced from other variable initializers if | |
| 148 // they are set by an assignment expression, so we declare variables before | |
| 149 // those with initializers. | |
| 150 List<VariableDeclaration> declarations = <VariableDeclaration>[]; | |
| 151 for (tree.Variable variable in variableNames.keys) { | |
| 152 if (!declaredVariables.contains(variable)) { | |
| 153 String name = getVariableName(variable); | |
| 154 VariableDeclaration decl = new VariableDeclaration(name); | |
| 155 decl.element = variable.element; | |
| 156 declarations.add(decl); | |
| 157 declaredVariables.add(variable); | |
| 158 } | |
| 159 } | |
| 160 // Prepend all variables at once to avoid quadratic blowup. | |
| 161 variables.insertAll(0, declarations); | |
| 162 } | |
| 163 | |
| 164 String getConstantName(VariableElement element) { | |
| 165 assert(element.kind == ElementKind.VARIABLE); | |
| 166 if (element.enclosingElement != currentElement) { | |
| 167 return _parent.getConstantName(element); | |
| 168 } | |
| 169 String name = constantNames[element]; | |
| 170 if (name != null) { | |
| 171 return name; | |
| 172 } | |
| 173 String prefix = element.name; | |
| 174 int counter = 0; | |
| 175 name = element.name; | |
| 176 while (!usedVariableNames.add(name)) { | |
| 177 ++counter; | |
| 178 name = '$prefix$counter'; | |
| 179 } | |
| 180 constantNames[element] = name; | |
| 181 return name; | |
| 182 } | |
| 183 | |
| 184 List<T> inSubcontext(f(BuilderContext<T> subcontext), | |
| 185 {tree.Statement fallthrough}) { | |
| 186 List<T> savedBuffer = this._statementBuffer; | |
| 187 tree.Statement savedFallthrough = this.fallthrough; | |
| 188 List<T> buffer = this._statementBuffer = <T>[]; | |
| 189 if (fallthrough != null) { | |
| 190 this.fallthrough = fallthrough; | |
| 191 } | |
| 192 f(this); | |
| 193 this.fallthrough = savedFallthrough; | |
| 194 this._statementBuffer = savedBuffer; | |
| 195 return buffer; | |
| 196 } | |
| 197 | |
| 198 /// Removes a trailing "return null" from the current block. | |
| 199 void removeTrailingReturn(bool isReturnNull(T statement)) { | |
| 200 if (_statementBuffer.isEmpty) return; | |
| 201 if (isReturnNull(_statementBuffer.last)) { | |
| 202 _statementBuffer.removeLast(); | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 /// Register [label] as used. | |
| 207 void useLabel(tree.Label label) { | |
| 208 _usedLabels.add(label); | |
| 209 } | |
| 210 | |
| 211 /// Remove [label] and return `true` if it was used. | |
| 212 bool removeUsedLabel(tree.Label label) { | |
| 213 return _usedLabels.remove(label); | |
| 214 } | |
| 215 | |
| 216 /// Add [statement] to the current block. | |
| 217 void addStatement(T statement) { | |
| 218 _statementBuffer.add(statement); | |
| 219 } | |
| 220 | |
| 221 /// The statements in the current block. | |
| 222 Iterable<T> get statements => _statementBuffer; | |
| 223 } | |
| 224 | |
| 225 /// Translates the dart_tree IR to Dart backend AST. | |
| 226 /// An instance of this class should only be used once; a fresh emitter | |
| 227 /// must be created for each function to be emitted. | |
| 228 class ASTEmitter | |
| 229 extends tree.StatementVisitor1<dynamic, BuilderContext<Statement>> | |
| 230 with tree.ExpressionVisitor1<Expression, BuilderContext<Statement>>, | |
| 231 tree.RootVisitor1<RootNode, BuilderContext<Statement>>, | |
| 232 tree.InitializerVisitor1<Initializer, BuilderContext<Statement>> { | |
| 233 | |
| 234 RootNode emit(tree.RootNode node) { | |
| 235 return visitRootNode(node, new BuilderContext<Statement>()); | |
| 236 } | |
| 237 | |
| 238 @override | |
| 239 FieldDefinition visitFieldDefinition(tree.FieldDefinition definition, | |
| 240 BuilderContext<Statement> context) { | |
| 241 context.currentElement = definition.element; | |
| 242 Expression initializer; | |
| 243 if (!definition.isEmpty) { | |
| 244 visitStatement(definition.body, context); | |
| 245 List<Statement> bodyParts; | |
| 246 for (tree.Variable variable in context.variableNames.keys) { | |
| 247 if (!context.declaredVariables.contains(variable)) { | |
| 248 context.addDeclaration(variable); | |
| 249 } | |
| 250 } | |
| 251 if (context.variables.length > 0) { | |
| 252 bodyParts = new List<Statement>(); | |
| 253 bodyParts.add(new VariableDeclarations(context.variables)); | |
| 254 bodyParts.addAll(context.statements); | |
| 255 } else { | |
| 256 bodyParts = context.statements; | |
| 257 } | |
| 258 initializer = ensureExpression(bodyParts); | |
| 259 } | |
| 260 | |
| 261 return new FieldDefinition(definition.element, initializer); | |
| 262 } | |
| 263 | |
| 264 /// Returns an expression that will evaluate all of [bodyParts]. | |
| 265 /// If [bodyParts] is a single [Return] return its value. | |
| 266 /// Otherwise wrap the body-parts in an immediately invoked closure. | |
| 267 Expression ensureExpression(List<Statement> bodyParts) { | |
| 268 if (bodyParts.length == 1) { | |
| 269 Statement onlyStatement = bodyParts.single; | |
| 270 if (onlyStatement is Return) { | |
| 271 return onlyStatement.expression; | |
| 272 } | |
| 273 } | |
| 274 Statement body = new Block(bodyParts); | |
| 275 FunctionExpression function = | |
| 276 new FunctionExpression(new Parameters([]), body); | |
| 277 function.element = null; | |
| 278 return new CallFunction(function, []); | |
| 279 } | |
| 280 | |
| 281 bool _recognizeTrailingReturn(Statement statement) { | |
| 282 if (statement is Return) { | |
| 283 Expression expr = statement.expression; | |
| 284 if (expr == null || expr is Literal && expr.value.isNull) { | |
| 285 return true; | |
| 286 } | |
| 287 } | |
| 288 return false; | |
| 289 } | |
| 290 | |
| 291 @override | |
| 292 FunctionExpression visitConstructorDefinition( | |
| 293 tree.ConstructorDefinition definition, | |
| 294 BuilderContext<Statement> context) { | |
| 295 context.currentElement = definition.element; | |
| 296 | |
| 297 Parameters parameters = emitRootParameters( | |
| 298 definition, definition.defaultParameterValues, context); | |
| 299 | |
| 300 // Declare parameters. | |
| 301 for (tree.Variable param in definition.parameters) { | |
| 302 context.variableNames[param] = param.element.name; | |
| 303 context.usedVariableNames.add(param.element.name); | |
| 304 context.declaredVariables.add(param); | |
| 305 } | |
| 306 | |
| 307 List<Initializer> initializers; | |
| 308 Statement body; | |
| 309 | |
| 310 if (!definition.isEmpty) { | |
| 311 initializers = | |
| 312 definition.initializers.map((tree.Initializer initializer) { | |
| 313 return visitInitializer(initializer, context); | |
| 314 }).toList(); | |
| 315 | |
| 316 context.firstStatement = definition.body; | |
| 317 visitStatement(definition.body, context); | |
| 318 context.removeTrailingReturn(_recognizeTrailingReturn); | |
| 319 | |
| 320 // Some of the variable declarations have already been added | |
| 321 // if their first assignment could be pulled into the initializer. | |
| 322 // Add the remaining variable declarations now. | |
| 323 context.declareRemainingVariables(); | |
| 324 | |
| 325 // Add constant declarations. | |
| 326 List<VariableDeclaration> constants = <VariableDeclaration>[]; | |
| 327 for (ConstDeclaration constDecl in definition.localConstants) { | |
| 328 if (!context.constantNames.containsKey(constDecl.element)) { | |
| 329 continue; // Discard unused constants declarations. | |
| 330 } | |
| 331 String name = context.getConstantName(constDecl.element); | |
| 332 Expression value = | |
| 333 ConstantEmitter.createExpression(constDecl.expression, context); | |
| 334 VariableDeclaration decl = new VariableDeclaration(name, value); | |
| 335 decl.element = constDecl.element; | |
| 336 constants.add(decl); | |
| 337 } | |
| 338 | |
| 339 List<Statement> bodyParts = []; | |
| 340 if (constants.length > 0) { | |
| 341 bodyParts.add(new VariableDeclarations(constants, isConst: true)); | |
| 342 } | |
| 343 if (context.variables.length > 0) { | |
| 344 bodyParts.add(new VariableDeclarations(context.variables)); | |
| 345 } | |
| 346 bodyParts.addAll(context.statements); | |
| 347 body = new Block(bodyParts); | |
| 348 } | |
| 349 return new ConstructorDefinition( | |
| 350 parameters, | |
| 351 body, | |
| 352 initializers, | |
| 353 context.currentElement.name, | |
| 354 definition.element.isConst)..element = context.currentElement; | |
| 355 } | |
| 356 | |
| 357 @override | |
| 358 FunctionExpression visitFunctionDefinition( | |
| 359 tree.FunctionDefinition definition, | |
| 360 BuilderContext<Statement> context) { | |
| 361 context.currentElement = definition.element; | |
| 362 | |
| 363 Parameters parameters = emitRootParameters( | |
| 364 definition, definition.defaultParameterValues, context); | |
| 365 | |
| 366 // Declare parameters. | |
| 367 for (tree.Variable param in definition.parameters) { | |
| 368 context.variableNames[param] = param.element.name; | |
| 369 context.usedVariableNames.add(param.element.name); | |
| 370 context.declaredVariables.add(param); | |
| 371 } | |
| 372 | |
| 373 Statement body; | |
| 374 if (definition.isEmpty) { | |
| 375 body = new EmptyStatement(); | |
| 376 } else { | |
| 377 context.firstStatement = definition.body; | |
| 378 visitStatement(definition.body, context); | |
| 379 context.removeTrailingReturn(_recognizeTrailingReturn); | |
| 380 | |
| 381 // Some of the variable declarations have already been added | |
| 382 // if their first assignment could be pulled into the initializer. | |
| 383 // Add the remaining variable declarations now. | |
| 384 context.declareRemainingVariables(); | |
| 385 | |
| 386 // Add constant declarations. | |
| 387 List<VariableDeclaration> constants = <VariableDeclaration>[]; | |
| 388 for (ConstDeclaration constDecl in definition.localConstants) { | |
| 389 if (!context.constantNames.containsKey(constDecl.element)) { | |
| 390 continue; // Discard unused constants declarations. | |
| 391 } | |
| 392 String name = context.getConstantName(constDecl.element); | |
| 393 Expression value = | |
| 394 ConstantEmitter.createExpression(constDecl.expression, context); | |
| 395 VariableDeclaration decl = new VariableDeclaration(name, value); | |
| 396 decl.element = constDecl.element; | |
| 397 constants.add(decl); | |
| 398 } | |
| 399 | |
| 400 List<Statement> bodyParts = []; | |
| 401 if (constants.length > 0) { | |
| 402 bodyParts.add(new VariableDeclarations(constants, isConst: true)); | |
| 403 } | |
| 404 if (context.variables.length > 0) { | |
| 405 bodyParts.add(new VariableDeclarations(context.variables)); | |
| 406 } | |
| 407 bodyParts.addAll(context.statements); | |
| 408 | |
| 409 body = new Block(bodyParts); | |
| 410 } | |
| 411 FunctionType functionType = context.currentElement.type; | |
| 412 | |
| 413 return new FunctionExpression( | |
| 414 parameters, | |
| 415 body, | |
| 416 name: context.currentElement.name, | |
| 417 returnType: TypeGenerator.createOptionalType(functionType.returnType), | |
| 418 isGetter: context.currentElement.isGetter, | |
| 419 isSetter: context.currentElement.isSetter) | |
| 420 ..element = context.currentElement; | |
| 421 } | |
| 422 | |
| 423 /// Emits parameters that are not nested inside other parameters. | |
| 424 /// Root parameters can have default values, while inner parameters cannot. | |
| 425 Parameters emitRootParameters(tree.RootNode function, | |
| 426 List<ConstantExpression> defaults, | |
| 427 BuilderContext<Statement> context) { | |
| 428 FunctionType functionType = function.element.type; | |
| 429 List<Parameter> required = TypeGenerator.createParameters( | |
| 430 functionType.parameterTypes, | |
| 431 context: context, | |
| 432 elements: function.parameters.map((p) => p.element)); | |
| 433 bool optionalParametersAreNamed = !functionType.namedParameters.isEmpty; | |
| 434 List<Parameter> optional = TypeGenerator.createParameters( | |
| 435 optionalParametersAreNamed | |
| 436 ? functionType.namedParameterTypes | |
| 437 : functionType.optionalParameterTypes, | |
| 438 context: context, | |
| 439 defaultValues: defaults, | |
| 440 elements: function.parameters.skip(required.length) | |
| 441 .map((p) => p.element)); | |
| 442 return new Parameters(required, optional, optionalParametersAreNamed); | |
| 443 } | |
| 444 | |
| 445 /// True if the two expressions are a reference to the same variable. | |
| 446 bool isSameVariable(Receiver e1, Receiver e2) { | |
| 447 return e1 is Identifier && | |
| 448 e2 is Identifier && | |
| 449 e1.element is VariableElement && | |
| 450 e1.element == e2.element; | |
| 451 } | |
| 452 | |
| 453 Expression makeAssignment(Expression target, Expression value) { | |
| 454 // Try to print as compound assignment or increment | |
| 455 if (value is BinaryOperator && isCompoundableOperator(value.operator)) { | |
| 456 Receiver leftOperand = value.left; | |
| 457 Expression rightOperand = value.right; | |
| 458 bool valid = false; | |
| 459 if (isSameVariable(target, leftOperand)) { | |
| 460 valid = true; | |
| 461 } else if (target is FieldExpression && | |
| 462 leftOperand is FieldExpression && | |
| 463 isSameVariable(target.object, leftOperand.object) && | |
| 464 target.fieldName == leftOperand.fieldName) { | |
| 465 valid = true; | |
| 466 } else if (target is IndexExpression && | |
| 467 leftOperand is IndexExpression && | |
| 468 isSameVariable(target.object, leftOperand.object) && | |
| 469 isSameVariable(target.index, leftOperand.index)) { | |
| 470 valid = true; | |
| 471 } | |
| 472 if (valid) { | |
| 473 if (rightOperand is Literal && rightOperand.value.isOne && | |
| 474 (value.operator == '+' || value.operator == '-')) { | |
| 475 return new Increment.prefix(target, value.operator + value.operator); | |
| 476 } else { | |
| 477 return new Assignment(target, value.operator + '=', rightOperand); | |
| 478 } | |
| 479 } | |
| 480 } | |
| 481 // Fall back to regular assignment | |
| 482 return new Assignment(target, '=', value); | |
| 483 } | |
| 484 | |
| 485 Block visitInSubContext(tree.Statement statement, | |
| 486 BuilderContext<Statement> context, | |
| 487 {tree.Statement fallthrough}) { | |
| 488 return new Block(context.inSubcontext( | |
| 489 (BuilderContext<Statement> subcontext) { | |
| 490 visitStatement(statement, subcontext); | |
| 491 }, fallthrough: fallthrough)); | |
| 492 } | |
| 493 | |
| 494 void addLabeledStatement(tree.Label label, | |
| 495 Statement statement, | |
| 496 BuilderContext<Statement> context) { | |
| 497 if (context.removeUsedLabel(label)) { | |
| 498 context.addStatement(new LabeledStatement(label.name, statement)); | |
| 499 } else { | |
| 500 context.addStatement(statement); | |
| 501 } | |
| 502 } | |
| 503 | |
| 504 @override | |
| 505 void visitExpressionStatement(tree.ExpressionStatement stmt, | |
| 506 BuilderContext<Statement> context) { | |
| 507 if (stmt.expression is tree.Assign) { | |
| 508 emitAssignStatement(stmt.expression, stmt, context); | |
| 509 return; | |
| 510 } | |
| 511 Expression e = visitExpression(stmt.expression, context); | |
| 512 context.addStatement(new ExpressionStatement(e)); | |
| 513 visitStatement(stmt.next, context); | |
| 514 } | |
| 515 | |
| 516 @override | |
| 517 void visitVariableDeclaration(tree.VariableDeclaration node, | |
| 518 BuilderContext<Statement> context) { | |
| 519 Expression value = visitExpression(node.value, context); | |
| 520 String name = context.getVariableName(node.variable); | |
| 521 VariableDeclaration decl = new VariableDeclaration(name, value) | |
| 522 ..element = node.variable.element; | |
| 523 context.declaredVariables.add(node.variable); | |
| 524 context.addStatement(new VariableDeclarations([decl])); | |
| 525 visitStatement(node.next, context); | |
| 526 } | |
| 527 | |
| 528 @override | |
| 529 void visitLabeledStatement(tree.LabeledStatement stmt, | |
| 530 BuilderContext<Statement> context) { | |
| 531 Block block = visitInSubContext(stmt.body, context, fallthrough: stmt.next); | |
| 532 addLabeledStatement(stmt.label, block, context); | |
| 533 | |
| 534 visitStatement(stmt.next, context); | |
| 535 } | |
| 536 | |
| 537 bool isNullLiteral(Expression exp) => exp is Literal && exp.value.isNull; | |
| 538 | |
| 539 void emitAssignStatement(tree.Assign assign, | |
| 540 tree.Statement statement, | |
| 541 BuilderContext<Statement> context) { | |
| 542 // Try to emit a local function declaration. This is useful for functions | |
| 543 // that may occur in expression context, but could not be inlined anywhere. | |
| 544 if (assign.variable.element is FunctionElement && | |
| 545 assign.value is tree.FunctionExpression && | |
| 546 !context.declaredVariables.contains(assign.variable) && | |
| 547 assign.variable.writeCount == 1) { | |
| 548 tree.FunctionExpression functionExp = assign.value; | |
| 549 FunctionExpression function = | |
| 550 makeSubFunction(functionExp.definition, context); | |
| 551 FunctionDeclaration decl = new FunctionDeclaration(function); | |
| 552 context.addStatement(decl); | |
| 553 context.declaredVariables.add(assign.variable); | |
| 554 | |
| 555 visitStatement(statement.next, context); | |
| 556 return; | |
| 557 } | |
| 558 | |
| 559 Expression definition = visitExpression(assign.value, context); | |
| 560 bool isFirstOccurrence = (context.variableNames[assign.variable] == null); | |
| 561 bool isDeclaredHere = assign.variable.host == context.currentElement; | |
| 562 bool isFirstStatement = context.firstStatement == statement; | |
| 563 | |
| 564 // Try to pull into initializer. | |
| 565 if (isFirstStatement && isFirstOccurrence && isDeclaredHere) { | |
| 566 if (isNullLiteral(definition)) definition = null; | |
| 567 context.addDeclaration(assign.variable, definition); | |
| 568 context.firstStatement = statement.next; | |
| 569 visitStatement(statement.next, context); | |
| 570 return; | |
| 571 } | |
| 572 | |
| 573 context.addStatement(new ExpressionStatement(makeAssignment( | |
| 574 context.makeVariableAccess(assign.variable), | |
| 575 definition))); | |
| 576 visitStatement(statement.next, context); | |
| 577 } | |
| 578 | |
| 579 @override | |
| 580 void visitReturn(tree.Return stmt, BuilderContext<Statement> context) { | |
| 581 if (context.currentElement.isGenerativeConstructor && | |
| 582 !context.inInitializer) { | |
| 583 assert(() { | |
| 584 tree.Expression value = stmt.value; | |
| 585 return value is tree.Constant && value.value.isNull; | |
| 586 }); | |
| 587 context.addStatement(new Return(null)); | |
| 588 } else { | |
| 589 Expression inner = visitExpression(stmt.value, context); | |
| 590 context.addStatement(new Return(inner)); | |
| 591 } | |
| 592 } | |
| 593 | |
| 594 @override | |
| 595 void visitThrow(tree.Throw stmt, BuilderContext<Statement> context) { | |
| 596 Expression value = visitExpression(stmt.value, context); | |
| 597 context.addStatement(new ExpressionStatement(new Throw(value))); | |
| 598 } | |
| 599 | |
| 600 @override | |
| 601 void visitRethrow(tree.Rethrow stmt, BuilderContext<Statement> context) { | |
| 602 context.addStatement(new Rethrow()); | |
| 603 } | |
| 604 | |
| 605 @override | |
| 606 void visitBreak(tree.Break stmt, BuilderContext<Statement> context) { | |
| 607 tree.Statement fall = context.fallthrough; | |
| 608 if (stmt.target.binding.next == fall) { | |
| 609 // Fall through to break target | |
| 610 } else if (fall is tree.Break && fall.target == stmt.target) { | |
| 611 // Fall through to equivalent break | |
| 612 } else { | |
| 613 context.useLabel(stmt.target); | |
| 614 context.addStatement(new Break(stmt.target.name)); | |
| 615 } | |
| 616 } | |
| 617 | |
| 618 @override | |
| 619 void visitContinue(tree.Continue stmt, | |
| 620 BuilderContext<Statement> context) { | |
| 621 tree.Statement fall = context.fallthrough; | |
| 622 if (stmt.target.binding == fall) { | |
| 623 // Fall through to continue target | |
| 624 } else if (fall is tree.Continue && fall.target == stmt.target) { | |
| 625 // Fall through to equivalent continue | |
| 626 } else { | |
| 627 context.useLabel(stmt.target); | |
| 628 context.addStatement(new Continue(stmt.target.name)); | |
| 629 } | |
| 630 } | |
| 631 | |
| 632 @override | |
| 633 void visitIf(tree.If stmt, | |
| 634 BuilderContext<Statement> context) { | |
| 635 Expression condition = visitExpression(stmt.condition, context); | |
| 636 Block thenBlock = visitInSubContext(stmt.thenStatement, context); | |
| 637 Block elseBlock= visitInSubContext(stmt.elseStatement, context); | |
| 638 context.addStatement(new If(condition, thenBlock, elseBlock)); | |
| 639 } | |
| 640 | |
| 641 @override | |
| 642 void visitWhileTrue(tree.WhileTrue stmt, | |
| 643 BuilderContext<Statement> context) { | |
| 644 Block body = visitInSubContext(stmt.body, context, fallthrough: stmt); | |
| 645 Statement statement = | |
| 646 new While(new Literal(new TrueConstantValue()), body); | |
| 647 addLabeledStatement(stmt.label, statement, context); | |
| 648 } | |
| 649 | |
| 650 @override | |
| 651 void visitWhileCondition(tree.WhileCondition stmt, | |
| 652 BuilderContext<Statement> context) { | |
| 653 Expression condition = visitExpression(stmt.condition, context); | |
| 654 Block body = visitInSubContext(stmt.body, context, fallthrough: stmt); | |
| 655 Statement statement = new While(condition, body); | |
| 656 addLabeledStatement(stmt.label, statement, context); | |
| 657 | |
| 658 visitStatement(stmt.next, context); | |
| 659 } | |
| 660 | |
| 661 @override | |
| 662 void visitTry(tree.Try stmt, | |
| 663 BuilderContext<Statement> context) { | |
| 664 Block tryBody = visitInSubContext(stmt.tryBody, context); | |
| 665 Block catchBody = visitInSubContext(stmt.catchBody, context); | |
| 666 CatchBlock catchBlock; | |
| 667 tree.Variable exceptionVariable = stmt.catchParameters[0]; | |
| 668 context.handlerVariables.add(exceptionVariable); | |
| 669 VariableDeclaration exceptionParameter = | |
| 670 new VariableDeclaration(context.getVariableName(exceptionVariable)); | |
| 671 exceptionParameter.element = exceptionVariable.element; | |
| 672 stmt.catchParameters.forEach(context.declaredVariables.add); | |
| 673 if (stmt.catchParameters.length == 2) { | |
| 674 tree.Variable stackTraceVariable = stmt.catchParameters[1]; | |
| 675 context.handlerVariables.add(stackTraceVariable); | |
| 676 VariableDeclaration stackTraceParameter = | |
| 677 new VariableDeclaration(context.getVariableName(stackTraceVariable)); | |
| 678 stackTraceParameter.element = stackTraceVariable.element; | |
| 679 catchBlock = new CatchBlock(catchBody, | |
| 680 exceptionVar: exceptionParameter, | |
| 681 stackVar: stackTraceParameter); | |
| 682 } else { | |
| 683 assert(stmt.catchParameters.length == 1); | |
| 684 catchBlock = new CatchBlock(catchBody, | |
| 685 exceptionVar: exceptionParameter); | |
| 686 } | |
| 687 context.addStatement(new Try(tryBody, <CatchBlock>[catchBlock], null)); | |
| 688 } | |
| 689 | |
| 690 @override | |
| 691 Expression visitConstant(tree.Constant exp, | |
| 692 BuilderContext<Statement> context) { | |
| 693 return ConstantEmitter.createExpression(exp.expression, context); | |
| 694 } | |
| 695 | |
| 696 @override | |
| 697 Expression visitThis(tree.This exp, | |
| 698 BuilderContext<Statement> context) { | |
| 699 return new This(); | |
| 700 } | |
| 701 | |
| 702 @override | |
| 703 Expression visitReifyTypeVar(tree.ReifyTypeVar exp, | |
| 704 BuilderContext<Statement> context) { | |
| 705 return new ReifyTypeVar(exp.typeVariable.name) | |
| 706 ..element = exp.typeVariable; | |
| 707 } | |
| 708 | |
| 709 List<Expression> visitExpressions(List<tree.Expression> expressions, | |
| 710 BuilderContext<Statement> context) { | |
| 711 return expressions.map((expression) => visitExpression(expression, context)) | |
| 712 .toList(growable: false); | |
| 713 } | |
| 714 | |
| 715 @override | |
| 716 Expression visitLiteralList(tree.LiteralList exp, | |
| 717 BuilderContext<Statement> context) { | |
| 718 return new LiteralList(visitExpressions(exp.values, context), | |
| 719 typeArgument: | |
| 720 TypeGenerator.createOptionalType(exp.type.typeArguments.single)); | |
| 721 } | |
| 722 | |
| 723 @override | |
| 724 Expression visitLiteralMap(tree.LiteralMap exp, | |
| 725 BuilderContext<Statement> context) { | |
| 726 List<LiteralMapEntry> entries = new List<LiteralMapEntry>.generate( | |
| 727 exp.entries.length, | |
| 728 (i) => new LiteralMapEntry( | |
| 729 visitExpression(exp.entries[i].key, context), | |
| 730 visitExpression(exp.entries[i].value, context))); | |
| 731 List<TypeAnnotation> typeArguments = exp.type.treatAsRaw | |
| 732 ? null | |
| 733 : exp.type.typeArguments.map(TypeGenerator.createType) | |
| 734 .toList(growable: false); | |
| 735 return new LiteralMap(entries, typeArguments: typeArguments); | |
| 736 } | |
| 737 | |
| 738 @override | |
| 739 Expression visitTypeOperator(tree.TypeOperator exp, | |
| 740 BuilderContext<Statement> context) { | |
| 741 return new TypeOperator(visitExpression(exp.value, context), | |
| 742 exp.operator, | |
| 743 TypeGenerator.createType(exp.type)); | |
| 744 } | |
| 745 | |
| 746 List<Argument> emitArguments(List<Expression> arguments, | |
| 747 Selector selector) { | |
| 748 int positionalArgumentCount = selector.positionalArgumentCount; | |
| 749 List<Argument> result = new List<Argument>.generate(positionalArgumentCount, | |
| 750 (i) => arguments[i]); | |
| 751 for (int i = 0; i < selector.namedArgumentCount; ++i) { | |
| 752 result.add(new NamedArgument(selector.namedArguments[i], | |
| 753 arguments[positionalArgumentCount + i])); | |
| 754 } | |
| 755 return result; | |
| 756 } | |
| 757 | |
| 758 List<Expression> visitArgumentList(List<tree.Expression> arguments, | |
| 759 BuilderContext context) { | |
| 760 return arguments | |
| 761 .map((tree.Expression argument) => visitExpression(argument, context)) | |
| 762 .toList(); | |
| 763 } | |
| 764 | |
| 765 @override | |
| 766 Expression visitInvokeStatic(tree.InvokeStatic exp, | |
| 767 BuilderContext<Statement> context) { | |
| 768 switch (exp.selector.kind) { | |
| 769 case SelectorKind.GETTER: | |
| 770 return new Identifier(exp.target.name)..element = exp.target; | |
| 771 | |
| 772 case SelectorKind.SETTER: | |
| 773 return new Assignment( | |
| 774 new Identifier(exp.target.name)..element = exp.target, | |
| 775 '=', | |
| 776 visitExpression(exp.arguments[0], context)); | |
| 777 | |
| 778 case SelectorKind.CALL: | |
| 779 return new CallStatic( | |
| 780 null, exp.target.name, | |
| 781 emitArguments(visitArgumentList(exp.arguments, context), | |
| 782 exp.selector)) | |
| 783 ..element = exp.target; | |
| 784 | |
| 785 default: | |
| 786 throw "Unexpected selector kind: ${exp.selector.kind}"; | |
| 787 } | |
| 788 } | |
| 789 | |
| 790 Expression emitMethodCall(tree.Invoke exp, Receiver receiver, | |
| 791 BuilderContext<Statement> context) { | |
| 792 List<Argument> args = | |
| 793 emitArguments(visitArgumentList(exp.arguments, context), exp.selector); | |
| 794 switch (exp.selector.kind) { | |
| 795 case SelectorKind.CALL: | |
| 796 if (exp.selector.name == "call") { | |
| 797 return new CallFunction(receiver, args); | |
| 798 } | |
| 799 return new CallMethod(receiver, exp.selector.name, args); | |
| 800 | |
| 801 case SelectorKind.OPERATOR: | |
| 802 if (args.length == 0) { | |
| 803 String name = exp.selector.name; | |
| 804 if (name == 'unary-') { | |
| 805 name = '-'; | |
| 806 } | |
| 807 return new UnaryOperator(name, receiver); | |
| 808 } | |
| 809 return new BinaryOperator(receiver, exp.selector.name, args[0]); | |
| 810 | |
| 811 case SelectorKind.GETTER: | |
| 812 return new FieldExpression(receiver, exp.selector.name); | |
| 813 | |
| 814 case SelectorKind.SETTER: | |
| 815 return makeAssignment( | |
| 816 new FieldExpression(receiver, exp.selector.name), | |
| 817 args[0]); | |
| 818 | |
| 819 case SelectorKind.INDEX: | |
| 820 Expression e = new IndexExpression(receiver, args[0]); | |
| 821 if (args.length == 2) { | |
| 822 e = makeAssignment(e, args[1]); | |
| 823 } | |
| 824 return e; | |
| 825 | |
| 826 default: | |
| 827 throw "Unexpected selector in InvokeMethod: ${exp.selector.kind}"; | |
| 828 } | |
| 829 } | |
| 830 | |
| 831 @override | |
| 832 Expression visitInvokeMethod(tree.InvokeMethod exp, | |
| 833 BuilderContext<Statement> context) { | |
| 834 Expression receiver = visitExpression(exp.receiver, context); | |
| 835 return emitMethodCall(exp, receiver, context); | |
| 836 } | |
| 837 | |
| 838 @override | |
| 839 Expression visitInvokeMethodDirectly(tree.InvokeMethodDirectly exp, | |
| 840 BuilderContext<Statement> context) { | |
| 841 // When targeting Dart, InvokeMethodDirectly is only used for super calls. | |
| 842 // The receiver is known to be `this`, and the target method is a method | |
| 843 // on the super class. So we just translate it as a method call with the | |
| 844 // super receiver. | |
| 845 return emitMethodCall(exp, new SuperReceiver(), context); | |
| 846 } | |
| 847 | |
| 848 @override | |
| 849 Expression visitInvokeConstructor(tree.InvokeConstructor exp, | |
| 850 BuilderContext<Statement> context) { | |
| 851 List<Argument> args = | |
| 852 emitArguments(visitArgumentList(exp.arguments, context), exp.selector); | |
| 853 FunctionElement constructor = exp.target; | |
| 854 String name = constructor.name.isEmpty ? null : constructor.name; | |
| 855 return new CallNew(TypeGenerator.createType(exp.type), | |
| 856 args, | |
| 857 constructorName: name, | |
| 858 isConst: exp.constant != null) | |
| 859 ..constructor = constructor | |
| 860 ..dartType = exp.type; | |
| 861 } | |
| 862 | |
| 863 @override | |
| 864 Expression visitConcatenateStrings(tree.ConcatenateStrings exp, | |
| 865 BuilderContext<Statement> context) { | |
| 866 return new StringConcat(visitExpressions(exp.arguments, context)); | |
| 867 } | |
| 868 | |
| 869 @override | |
| 870 Expression visitConditional(tree.Conditional exp, | |
| 871 BuilderContext<Statement> context) { | |
| 872 return new Conditional( | |
| 873 visitExpression(exp.condition, context), | |
| 874 visitExpression(exp.thenExpression, context), | |
| 875 visitExpression(exp.elseExpression, context)); | |
| 876 } | |
| 877 | |
| 878 @override | |
| 879 Expression visitLogicalOperator(tree.LogicalOperator exp, | |
| 880 BuilderContext<Statement> context) { | |
| 881 return new BinaryOperator(visitExpression(exp.left, context), | |
| 882 exp.operator, | |
| 883 visitExpression(exp.right, context)); | |
| 884 } | |
| 885 | |
| 886 @override | |
| 887 Expression visitNot(tree.Not exp, | |
| 888 BuilderContext<Statement> context) { | |
| 889 return new UnaryOperator('!', visitExpression(exp.operand, context)); | |
| 890 } | |
| 891 | |
| 892 @override | |
| 893 Expression visitVariableUse(tree.VariableUse exp, | |
| 894 BuilderContext<Statement> context) { | |
| 895 return context.makeVariableAccess(exp.variable); | |
| 896 } | |
| 897 | |
| 898 @override | |
| 899 Expression visitAssign(tree.Assign node, BuilderContext<Statement> context) { | |
| 900 // This is called only when an assignment occurs in expression context. | |
| 901 return makeAssignment( | |
| 902 context.makeVariableAccess(node.variable), | |
| 903 visitExpression(node.value, context)); | |
| 904 } | |
| 905 | |
| 906 FunctionExpression makeSubFunction(tree.FunctionDefinition function, | |
| 907 BuilderContext<Statement> context) { | |
| 908 return visitFunctionDefinition(function, | |
| 909 new BuilderContext<Statement>.inner(context)); | |
| 910 } | |
| 911 | |
| 912 @override | |
| 913 Expression visitFunctionExpression(tree.FunctionExpression exp, | |
| 914 BuilderContext<Statement> context) { | |
| 915 return makeSubFunction(exp.definition, context)..name = null; | |
| 916 } | |
| 917 | |
| 918 @override | |
| 919 void visitFunctionDeclaration(tree.FunctionDeclaration node, | |
| 920 BuilderContext<Statement> context) { | |
| 921 assert(context.variableNames[node.variable] == null); | |
| 922 String name = context.getVariableName(node.variable); | |
| 923 FunctionExpression inner = makeSubFunction(node.definition, context); | |
| 924 inner.name = name; | |
| 925 FunctionDeclaration decl = new FunctionDeclaration(inner); | |
| 926 context.declaredVariables.add(node.variable); | |
| 927 context.addStatement(decl); | |
| 928 visitStatement(node.next, context); | |
| 929 } | |
| 930 | |
| 931 List<Statement> buildInInitializerContext(tree.Statement root, | |
| 932 BuilderContext context) { | |
| 933 BuilderContext inner = new BuilderContext<Statement>.initializer(context); | |
| 934 inner.currentElement = context.currentElement; | |
| 935 inner.firstStatement = root; | |
| 936 visitStatement(root, inner); | |
| 937 List<Statement> bodyParts; | |
| 938 for (tree.Variable variable in inner.variableNames.keys) { | |
| 939 if (!context.declaredVariables.contains(variable) && | |
| 940 !inner.declaredVariables.contains(variable)) { | |
| 941 inner.addDeclaration(variable); | |
| 942 } | |
| 943 } | |
| 944 if (inner.variables.length > 0) { | |
| 945 bodyParts = new List<Statement>(); | |
| 946 bodyParts.add(new VariableDeclarations(inner.variables)); | |
| 947 bodyParts.addAll(inner.statements); | |
| 948 } else { | |
| 949 bodyParts = inner.statements; | |
| 950 } | |
| 951 return bodyParts; | |
| 952 } | |
| 953 | |
| 954 @override | |
| 955 Initializer visitFieldInitializer(tree.FieldInitializer node, | |
| 956 BuilderContext<Statement> context) { | |
| 957 return new FieldInitializer(node.element, | |
| 958 ensureExpression(buildInInitializerContext(node.body, context))); | |
| 959 } | |
| 960 | |
| 961 @override | |
| 962 Initializer visitSuperInitializer(tree.SuperInitializer node, | |
| 963 BuilderContext<Statement> context) { | |
| 964 List<Argument> arguments = node.arguments.map((tree.Statement argument) { | |
| 965 return ensureExpression(buildInInitializerContext(argument, context)); | |
| 966 }).toList(); | |
| 967 return new SuperInitializer(node.target, | |
| 968 emitArguments(arguments, node.selector)); | |
| 969 } | |
| 970 | |
| 971 @override | |
| 972 Expression visitGetStatic(tree.GetStatic node, | |
| 973 BuilderContext<Statement> context) { | |
| 974 return new Identifier(node.element.name)..element = node.element; | |
| 975 } | |
| 976 | |
| 977 @override | |
| 978 Expression visitSetStatic(tree.SetStatic node, | |
| 979 BuilderContext<Statement> context) { | |
| 980 Expression target = | |
| 981 new Identifier(node.element.name)..element = node.element; | |
| 982 Expression value = visitExpression(node.value, context); | |
| 983 return makeAssignment(target, value); | |
| 984 } | |
| 985 | |
| 986 @override | |
| 987 Expression visitTypeExpression(tree.TypeExpression node, arg) { | |
| 988 throw '$node not supported by dart backend'; | |
| 989 } | |
| 990 | |
| 991 @override | |
| 992 visitGetField(tree.GetField node, arg) => errorUnsupportedNode(node); | |
| 993 | |
| 994 @override | |
| 995 visitSetField(tree.SetField node, arg) => errorUnsupportedNode(node); | |
| 996 | |
| 997 @override | |
| 998 visitCreateBox(tree.CreateBox node, arg) => errorUnsupportedNode(node); | |
| 999 | |
| 1000 @override | |
| 1001 visitCreateInstance(tree.CreateInstance node, arg) { | |
| 1002 return errorUnsupportedNode(node); | |
| 1003 } | |
| 1004 | |
| 1005 @override | |
| 1006 visitCreateInvocationMirror(tree.CreateInvocationMirror node, arg) { | |
| 1007 return errorUnsupportedNode(node); | |
| 1008 } | |
| 1009 | |
| 1010 @override | |
| 1011 Expression visitReadTypeVariable(tree.ReadTypeVariable node, arg) { | |
| 1012 return errorUnsupportedNode(node); | |
| 1013 } | |
| 1014 | |
| 1015 @override | |
| 1016 Expression visitReifyRuntimeType(tree.ReifyRuntimeType node, arg) { | |
| 1017 return errorUnsupportedNode(node); | |
| 1018 } | |
| 1019 | |
| 1020 errorUnsupportedNode(tree.JsSpecificNode node) { | |
| 1021 throw '$node not supported by dart backend'; | |
| 1022 } | |
| 1023 | |
| 1024 } | |
| 1025 | 10 |
| 1026 class TypeGenerator { | 11 class TypeGenerator { |
| 1027 | 12 |
| 1028 /// TODO(johnniwinther): Remove this when issue 21283 has been resolved. | 13 /// TODO(johnniwinther): Remove this when issue 21283 has been resolved. |
| 1029 static int pseudoNameCounter = 0; | 14 static int pseudoNameCounter = 0; |
| 1030 | 15 |
| 1031 static Parameter emitParameter(DartType type, | 16 static Parameter emitParameter(DartType type, |
| 1032 BuilderContext<Statement> context, | |
| 1033 {String name, | 17 {String name, |
| 1034 Element element, | 18 Element element}) { |
| 1035 ConstantExpression defaultValue}) { | |
| 1036 if (name == null && element != null) { | 19 if (name == null && element != null) { |
| 1037 name = element.name; | 20 name = element.name; |
| 1038 } | 21 } |
| 1039 if (name == null) { | 22 if (name == null) { |
| 1040 name = '_${pseudoNameCounter++}'; | 23 name = '_${pseudoNameCounter++}'; |
| 1041 } | 24 } |
| 1042 Parameter parameter; | 25 Parameter parameter; |
| 1043 if (type.isFunctionType) { | 26 if (type.isFunctionType) { |
| 1044 FunctionType functionType = type; | 27 FunctionType functionType = type; |
| 1045 TypeAnnotation returnType = createOptionalType(functionType.returnType); | 28 TypeAnnotation returnType = createOptionalType(functionType.returnType); |
| 1046 Parameters innerParameters = | 29 Parameters innerParameters = |
| 1047 createParametersFromType(functionType); | 30 createParametersFromType(functionType); |
| 1048 parameter = new Parameter.function(name, returnType, innerParameters); | 31 parameter = new Parameter.function(name, returnType, innerParameters); |
| 1049 } else { | 32 } else { |
| 1050 TypeAnnotation typeAnnotation = createOptionalType(type); | 33 TypeAnnotation typeAnnotation = createOptionalType(type); |
| 1051 parameter = new Parameter(name, type: typeAnnotation); | 34 parameter = new Parameter(name, type: typeAnnotation); |
| 1052 } | 35 } |
| 1053 parameter.element = element; | 36 parameter.element = element; |
| 1054 if (defaultValue != null && !defaultValue.value.isNull) { | |
| 1055 parameter.defaultValue = | |
| 1056 ConstantEmitter.createExpression(defaultValue, context); | |
| 1057 } | |
| 1058 return parameter; | 37 return parameter; |
| 1059 } | 38 } |
| 1060 | 39 |
| 1061 static Parameters createParametersFromType(FunctionType functionType) { | 40 static Parameters createParametersFromType(FunctionType functionType) { |
| 1062 pseudoNameCounter = 0; | 41 pseudoNameCounter = 0; |
| 1063 if (functionType.namedParameters.isEmpty) { | 42 if (functionType.namedParameters.isEmpty) { |
| 1064 return new Parameters( | 43 return new Parameters( |
| 1065 createParameters(functionType.parameterTypes), | 44 createParameters(functionType.parameterTypes), |
| 1066 createParameters(functionType.optionalParameterTypes), | 45 createParameters(functionType.optionalParameterTypes), |
| 1067 false); | 46 false); |
| 1068 } else { | 47 } else { |
| 1069 return new Parameters( | 48 return new Parameters( |
| 1070 createParameters(functionType.parameterTypes), | 49 createParameters(functionType.parameterTypes), |
| 1071 createParameters(functionType.namedParameterTypes, | 50 createParameters(functionType.namedParameterTypes, |
| 1072 names: functionType.namedParameters), | 51 names: functionType.namedParameters), |
| 1073 true); | 52 true); |
| 1074 } | 53 } |
| 1075 } | 54 } |
| 1076 | 55 |
| 1077 static List<Parameter> createParameters( | 56 static List<Parameter> createParameters( |
| 1078 Iterable<DartType> parameterTypes, | 57 Iterable<DartType> parameterTypes, |
| 1079 {BuilderContext<Statement> context, | 58 {Iterable<String> names: const <String>[], |
| 1080 Iterable<String> names: const <String>[], | |
| 1081 Iterable<ConstantExpression> defaultValues: const <ConstantExpression>[], | |
| 1082 Iterable<Element> elements: const <Element>[]}) { | 59 Iterable<Element> elements: const <Element>[]}) { |
| 1083 Iterator<String> name = names.iterator; | 60 Iterator<String> name = names.iterator; |
| 1084 Iterator<ConstantExpression> defaultValue = defaultValues.iterator; | |
| 1085 Iterator<Element> element = elements.iterator; | 61 Iterator<Element> element = elements.iterator; |
| 1086 return parameterTypes.map((DartType type) { | 62 return parameterTypes.map((DartType type) { |
| 1087 name.moveNext(); | 63 name.moveNext(); |
| 1088 defaultValue.moveNext(); | |
| 1089 element.moveNext(); | 64 element.moveNext(); |
| 1090 return emitParameter(type, context, | 65 return emitParameter(type, |
| 1091 name: name.current, | 66 name: name.current, |
| 1092 defaultValue: defaultValue.current, | |
| 1093 element: element.current); | 67 element: element.current); |
| 1094 }).toList(); | 68 }).toList(); |
| 1095 } | 69 } |
| 1096 | 70 |
| 1097 /// Like [createTypeAnnotation] except the dynamic type is converted to null. | 71 /// Like [createTypeAnnotation] except the dynamic type is converted to null. |
| 1098 static TypeAnnotation createOptionalType(DartType type) { | 72 static TypeAnnotation createOptionalType(DartType type) { |
| 1099 if (type.treatAsDynamic) { | 73 if (type.treatAsDynamic) { |
| 1100 return null; | 74 return null; |
| 1101 } else { | 75 } else { |
| 1102 return createType(type); | 76 return createType(type); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1122 } else if (type is DynamicType) { | 96 } else if (type is DynamicType) { |
| 1123 return new TypeAnnotation("dynamic") | 97 return new TypeAnnotation("dynamic") |
| 1124 ..dartType = type; | 98 ..dartType = type; |
| 1125 } else if (type is MalformedType) { | 99 } else if (type is MalformedType) { |
| 1126 return new TypeAnnotation(type.name) | 100 return new TypeAnnotation(type.name) |
| 1127 ..dartType = type; | 101 ..dartType = type; |
| 1128 } else { | 102 } else { |
| 1129 throw "Unsupported type annotation: $type"; | 103 throw "Unsupported type annotation: $type"; |
| 1130 } | 104 } |
| 1131 } | 105 } |
| 1132 | |
| 1133 } | 106 } |
| 1134 | |
| 1135 | |
| 1136 class ConstantEmitter | |
| 1137 extends ConstantExpressionVisitor<Expression, BuilderContext<Statement>> { | |
| 1138 const ConstantEmitter(); | |
| 1139 | |
| 1140 /// Creates the [Expression] for the constant [exp]. | |
| 1141 static Expression createExpression(ConstantExpression exp, | |
| 1142 BuilderContext<Statement> context) { | |
| 1143 return const ConstantEmitter().visit(exp, context); | |
| 1144 } | |
| 1145 | |
| 1146 Expression handlePrimitiveConstant(PrimitiveConstantValue value) { | |
| 1147 // Num constants may be negative, while literals must be non-negative: | |
| 1148 // Literals are non-negative in the specification, and a negated literal | |
| 1149 // parses as a call to unary `-`. The AST unparser assumes literals are | |
| 1150 // non-negative and relies on this to avoid incorrectly generating `--`, | |
| 1151 // the predecrement operator. | |
| 1152 // Translate such constants into their positive value wrapped by | |
| 1153 // the unary minus operator. | |
| 1154 if (value.isNum) { | |
| 1155 NumConstantValue numConstant = value; | |
| 1156 if (numConstant.primitiveValue.isNegative) { | |
| 1157 return negatedLiteral(numConstant); | |
| 1158 } | |
| 1159 } | |
| 1160 return new Literal(value); | |
| 1161 } | |
| 1162 | |
| 1163 List<Expression> visitExpressions(List<ConstantExpression> expressions, | |
| 1164 BuilderContext<Statement> context) { | |
| 1165 return expressions.map((expression) => visit(expression, context)) | |
| 1166 .toList(growable: false); | |
| 1167 } | |
| 1168 | |
| 1169 @override | |
| 1170 Expression visitBool(BoolConstantExpression exp, | |
| 1171 BuilderContext<Statement> context) { | |
| 1172 return handlePrimitiveConstant(exp.value); | |
| 1173 } | |
| 1174 | |
| 1175 @override | |
| 1176 Expression visitInt(IntConstantExpression exp, | |
| 1177 BuilderContext<Statement> context) { | |
| 1178 return handlePrimitiveConstant(exp.value); | |
| 1179 } | |
| 1180 | |
| 1181 @override | |
| 1182 Expression visitDouble(DoubleConstantExpression exp, | |
| 1183 BuilderContext<Statement> context) { | |
| 1184 return handlePrimitiveConstant(exp.value); | |
| 1185 } | |
| 1186 | |
| 1187 @override | |
| 1188 Expression visitString(StringConstantExpression exp, | |
| 1189 BuilderContext<Statement> context) { | |
| 1190 return handlePrimitiveConstant(exp.value); | |
| 1191 } | |
| 1192 | |
| 1193 @override | |
| 1194 Expression visitNull(NullConstantExpression exp, | |
| 1195 BuilderContext<Statement> context) { | |
| 1196 return handlePrimitiveConstant(exp.value); | |
| 1197 } | |
| 1198 | |
| 1199 /// Given a negative num constant, returns the corresponding positive | |
| 1200 /// literal wrapped by a unary minus operator. | |
| 1201 Expression negatedLiteral(NumConstantValue constant) { | |
| 1202 assert(constant.primitiveValue.isNegative); | |
| 1203 NumConstantValue positiveConstant; | |
| 1204 if (constant.isInt) { | |
| 1205 positiveConstant = new IntConstantValue(-constant.primitiveValue); | |
| 1206 } else if (constant.isDouble) { | |
| 1207 positiveConstant = new DoubleConstantValue(-constant.primitiveValue); | |
| 1208 } else { | |
| 1209 throw "Unexpected type of NumConstant: $constant"; | |
| 1210 } | |
| 1211 return new UnaryOperator('-', new Literal(positiveConstant)); | |
| 1212 } | |
| 1213 | |
| 1214 @override | |
| 1215 Expression visitList(ListConstantExpression exp, | |
| 1216 BuilderContext<Statement> context) { | |
| 1217 return new LiteralList( | |
| 1218 visitExpressions(exp.values, context), | |
| 1219 isConst: true, | |
| 1220 typeArgument: | |
| 1221 TypeGenerator.createOptionalType(exp.type.typeArguments.single)); | |
| 1222 } | |
| 1223 | |
| 1224 @override | |
| 1225 Expression visitMap(MapConstantExpression exp, | |
| 1226 BuilderContext<Statement> context) { | |
| 1227 List<LiteralMapEntry> entries = new List<LiteralMapEntry>.generate( | |
| 1228 exp.values.length, | |
| 1229 (i) => new LiteralMapEntry(visit(exp.keys[i], context), | |
| 1230 visit(exp.values[i], context))); | |
| 1231 List<TypeAnnotation> typeArguments = exp.type.treatAsRaw | |
| 1232 ? null | |
| 1233 : exp.type.typeArguments.map(TypeGenerator.createType).toList(); | |
| 1234 return new LiteralMap(entries, isConst: true, typeArguments: typeArguments); | |
| 1235 } | |
| 1236 | |
| 1237 @override | |
| 1238 Expression visitConstructed(ConstructedConstantExpression exp, | |
| 1239 BuilderContext<Statement> context) { | |
| 1240 int positionalArgumentCount = exp.callStructure.positionalArgumentCount; | |
| 1241 List<Argument> args = new List<Argument>.generate( | |
| 1242 positionalArgumentCount, | |
| 1243 (i) => visit(exp.arguments[i], context)); | |
| 1244 for (int i = 0; i < exp.callStructure.namedArgumentCount; ++i) { | |
| 1245 args.add(new NamedArgument(exp.callStructure.namedArguments[i], | |
| 1246 visit(exp.arguments[positionalArgumentCount + i], context))); | |
| 1247 } | |
| 1248 | |
| 1249 FunctionElement constructor = exp.target; | |
| 1250 String name = constructor.name.isEmpty ? null : constructor.name; | |
| 1251 return new CallNew(TypeGenerator.createType(exp.type), | |
| 1252 args, | |
| 1253 constructorName: name, | |
| 1254 isConst: true) | |
| 1255 ..constructor = constructor | |
| 1256 ..dartType = exp.type; | |
| 1257 } | |
| 1258 | |
| 1259 @override | |
| 1260 Expression visitConcatenate(ConcatenateConstantExpression exp, | |
| 1261 BuilderContext<Statement> context) { | |
| 1262 | |
| 1263 return new StringConcat(visitExpressions(exp.expressions, context)); | |
| 1264 } | |
| 1265 | |
| 1266 @override | |
| 1267 Expression visitSymbol(SymbolConstantExpression exp, | |
| 1268 BuilderContext<Statement> context) { | |
| 1269 return new LiteralSymbol(exp.name); | |
| 1270 } | |
| 1271 | |
| 1272 @override | |
| 1273 Expression visitType(TypeConstantExpression exp, | |
| 1274 BuilderContext<Statement> context) { | |
| 1275 DartType type = exp.type; | |
| 1276 return new LiteralType(type.name) | |
| 1277 ..type = type; | |
| 1278 } | |
| 1279 | |
| 1280 @override | |
| 1281 Expression visitVariable(VariableConstantExpression exp, | |
| 1282 BuilderContext<Statement> context) { | |
| 1283 Element element = exp.element; | |
| 1284 if (element.kind != ElementKind.VARIABLE) { | |
| 1285 return new Identifier(element.name)..element = element; | |
| 1286 } | |
| 1287 String name = context.getConstantName(element); | |
| 1288 return new Identifier(name) | |
| 1289 ..element = element; | |
| 1290 } | |
| 1291 | |
| 1292 @override | |
| 1293 Expression visitFunction(FunctionConstantExpression exp, | |
| 1294 BuilderContext<Statement> context) { | |
| 1295 return new Identifier(exp.element.name) | |
| 1296 ..element = exp.element; | |
| 1297 } | |
| 1298 | |
| 1299 @override | |
| 1300 Expression visitBinary(BinaryConstantExpression exp, | |
| 1301 BuilderContext<Statement> context) { | |
| 1302 return handlePrimitiveConstant(exp.value); | |
| 1303 } | |
| 1304 | |
| 1305 @override | |
| 1306 Expression visitIdentical(IdenticalConstantExpression exp, | |
| 1307 BuilderContext<Statement> context) { | |
| 1308 return handlePrimitiveConstant(exp.value); | |
| 1309 } | |
| 1310 | |
| 1311 @override | |
| 1312 Expression visitConditional(ConditionalConstantExpression exp, | |
| 1313 BuilderContext<Statement> context) { | |
| 1314 if (exp.condition.value.isTrue) { | |
| 1315 return exp.trueExp.accept(this); | |
| 1316 } else { | |
| 1317 return exp.falseExp.accept(this); | |
| 1318 } | |
| 1319 } | |
| 1320 | |
| 1321 @override | |
| 1322 Expression visitUnary(UnaryConstantExpression exp, | |
| 1323 BuilderContext<Statement> context) { | |
| 1324 return handlePrimitiveConstant(exp.value); | |
| 1325 } | |
| 1326 | |
| 1327 @override | |
| 1328 Expression visitNamed(NamedArgumentReference exp, | |
| 1329 BuilderContext<Statement> context) { | |
| 1330 throw new UnsupportedError("ConstantEmitter.visitNamed"); | |
| 1331 } | |
| 1332 | |
| 1333 @override | |
| 1334 Expression visitPositional(PositionalArgumentReference exp, | |
| 1335 BuilderContext<Statement> context) { | |
| 1336 throw new UnsupportedError("ConstantEmitter.visitPositional"); | |
| 1337 } | |
| 1338 | |
| 1339 @override | |
| 1340 Expression visitBoolFromEnvironment( | |
| 1341 BoolFromEnvironmentConstantExpression exp, | |
| 1342 BuilderContext<Statement> context) { | |
| 1343 return handlePrimitiveConstant(exp.value); | |
| 1344 } | |
| 1345 | |
| 1346 @override | |
| 1347 Expression visitIntFromEnvironment( | |
| 1348 IntFromEnvironmentConstantExpression exp, | |
| 1349 BuilderContext<Statement> context) { | |
| 1350 return handlePrimitiveConstant(exp.value); | |
| 1351 } | |
| 1352 | |
| 1353 @override | |
| 1354 Expression visitStringFromEnvironment( | |
| 1355 StringFromEnvironmentConstantExpression exp, | |
| 1356 BuilderContext<Statement> context) { | |
| 1357 return handlePrimitiveConstant(exp.value); | |
| 1358 } | |
| 1359 | |
| 1360 @override | |
| 1361 Expression visitDeferred(DeferredConstantExpression exp, | |
| 1362 BuilderContext<Statement> context) { | |
| 1363 return exp.expression.accept(this); | |
| 1364 } | |
| 1365 } | |
| 1366 | |
| 1367 /// Moves function parameters into a separate variable if one of its uses is | |
| 1368 /// shadowed by an inner function parameter. | |
| 1369 /// This artifact is necessary because function parameters cannot be renamed. | |
| 1370 class UnshadowParameters extends tree.RecursiveVisitor { | |
| 1371 | |
| 1372 /// Maps parameter names to their bindings. | |
| 1373 Map<String, tree.Variable> environment = <String, tree.Variable>{}; | |
| 1374 | |
| 1375 /// Parameters that are currently shadowed by another parameter. | |
| 1376 Set<tree.Variable> shadowedParameters = new Set<tree.Variable>(); | |
| 1377 | |
| 1378 /// Parameters that are used in a context where it is shadowed. | |
| 1379 Set<tree.Variable> hasShadowedUse = new Set<tree.Variable>(); | |
| 1380 | |
| 1381 void unshadow(tree.RootNode definition) { | |
| 1382 if (definition.isEmpty) return; | |
| 1383 unshadowFunction(definition); | |
| 1384 } | |
| 1385 | |
| 1386 void unshadowFunction(tree.RootNode definition) { | |
| 1387 var oldShadow = shadowedParameters; | |
| 1388 var oldEnvironment = environment; | |
| 1389 environment = new Map<String, tree.Variable>.from(environment); | |
| 1390 shadowedParameters = new Set<tree.Variable>.from(shadowedParameters); | |
| 1391 for (tree.Variable param in definition.parameters) { | |
| 1392 tree.Variable oldVariable = environment[param.element.name]; | |
| 1393 if (oldVariable != null) { | |
| 1394 shadowedParameters.add(oldVariable); | |
| 1395 } | |
| 1396 environment[param.element.name] = param; | |
| 1397 } | |
| 1398 definition.forEachBody(visitStatement); | |
| 1399 environment = oldEnvironment; | |
| 1400 shadowedParameters = oldShadow; | |
| 1401 | |
| 1402 for (int i=0; i<definition.parameters.length; i++) { | |
| 1403 tree.Variable param = definition.parameters[i]; | |
| 1404 if (hasShadowedUse.remove(param)) { | |
| 1405 tree.Variable newParam = new tree.Variable(definition.element, | |
| 1406 param.element); | |
| 1407 definition.parameters[i] = newParam; | |
| 1408 definition.replaceEachBody((tree.Statement body) { | |
| 1409 return tree.Assign.makeStatement( | |
| 1410 param, | |
| 1411 new tree.VariableUse(newParam), | |
| 1412 body); | |
| 1413 }); | |
| 1414 newParam.writeCount = 1; // Being a parameter counts as a write. | |
| 1415 param.writeCount--; // Not a parameter anymore. | |
| 1416 } | |
| 1417 } | |
| 1418 } | |
| 1419 | |
| 1420 @override | |
| 1421 void visitInnerFunction(tree.FunctionDefinition definition) { | |
| 1422 unshadowFunction(definition); | |
| 1423 } | |
| 1424 | |
| 1425 @override | |
| 1426 visitVariable(tree.Variable variable) { | |
| 1427 if (shadowedParameters.contains(variable)) { | |
| 1428 hasShadowedUse.add(variable); | |
| 1429 } | |
| 1430 } | |
| 1431 | |
| 1432 } | |
| 1433 | |
| 1434 // TODO(johnniwinther): Remove this when the dart `backend_ast` does not need | |
| 1435 // [Element] for entities. | |
| 1436 class _SyntheticLocalVariableElement extends modelx.VariableElementX | |
| 1437 implements LocalVariableElement { | |
| 1438 | |
| 1439 _SyntheticLocalVariableElement(String name, | |
| 1440 ExecutableElement enclosingElement, | |
| 1441 modelx.VariableList variables) | |
| 1442 : super(name, ElementKind.VARIABLE, enclosingElement, variables, null); | |
| 1443 | |
| 1444 ExecutableElement get executableContext => enclosingElement; | |
| 1445 | |
| 1446 ExecutableElement get memberContext => executableContext.memberContext; | |
| 1447 | |
| 1448 bool get isLocal => true; | |
| 1449 | |
| 1450 LibraryElement get implementationLibrary => enclosingElement.library; | |
| 1451 } | |
| OLD | NEW |