| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library backend_ast_emitter; | |
| 6 | |
| 7 import 'tree_ir_nodes.dart' as tree; | |
| 8 import 'backend_ast_nodes.dart'; | |
| 9 import '../constants/expressions.dart'; | |
| 10 import '../constants/values.dart'; | |
| 11 import '../dart_types.dart'; | |
| 12 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 Expression emit(tree.FunctionDefinition definition) { | |
| 19 return new ASTEmitter().emit(definition); | |
| 20 } | |
| 21 | |
| 22 /// Translates the dart_tree IR to Dart backend AST. | |
| 23 /// An instance of this class should only be used once; a fresh emitter | |
| 24 /// must be created for each function to be emitted. | |
| 25 class ASTEmitter extends tree.Visitor<dynamic, Expression> { | |
| 26 /// Variables to be hoisted at the top of the current function. | |
| 27 List<VariableDeclaration> variables = <VariableDeclaration>[]; | |
| 28 | |
| 29 /// Maps variables to their name. | |
| 30 Map<tree.Variable, String> variableNames = <tree.Variable, String>{}; | |
| 31 | |
| 32 /// Maps local constants to their name. | |
| 33 Map<VariableElement, String> constantNames = <VariableElement, String>{}; | |
| 34 | |
| 35 /// Variables that have had their declaration created. | |
| 36 Set<tree.Variable> declaredVariables = new Set<tree.Variable>(); | |
| 37 | |
| 38 /// Variable names that have already been used. Used to avoid name clashes. | |
| 39 Set<String> usedVariableNames; | |
| 40 | |
| 41 /// Statements emitted by the most recent call to [visitStatement]. | |
| 42 List<Statement> statementBuffer = <Statement>[]; | |
| 43 | |
| 44 /// The function currently being emitted. | |
| 45 FunctionElement functionElement; | |
| 46 | |
| 47 /// Bookkeeping object needed to synthesize a variable declaration. | |
| 48 modelx.VariableList variableList | |
| 49 = new modelx.VariableList(tree.Modifiers.EMPTY); | |
| 50 | |
| 51 /// Input to [visitStatement]. Denotes the statement that will execute next | |
| 52 /// if the statements produced by [visitStatement] complete normally. | |
| 53 /// Set to null if control will fall over the end of the method. | |
| 54 tree.Statement fallthrough = null; | |
| 55 | |
| 56 /// Labels that could not be eliminated using fallthrough. | |
| 57 Set<tree.Label> usedLabels = new Set<tree.Label>(); | |
| 58 | |
| 59 /// The first dart_tree statement that is not converted to a variable | |
| 60 /// initializer. | |
| 61 tree.Statement firstStatement; | |
| 62 | |
| 63 /// Emitter for the enclosing function, or null if the current function is | |
| 64 /// not a local function. | |
| 65 ASTEmitter parent; | |
| 66 | |
| 67 ASTEmitter() : usedVariableNames = new Set<String>(); | |
| 68 | |
| 69 ASTEmitter.inner(ASTEmitter parent) | |
| 70 : this.parent = parent, | |
| 71 usedVariableNames = parent.usedVariableNames; | |
| 72 | |
| 73 FunctionExpression emit(tree.FunctionDefinition definition) { | |
| 74 functionElement = definition.element; | |
| 75 | |
| 76 Parameters parameters = emitRootParameters(definition); | |
| 77 | |
| 78 // Declare parameters. | |
| 79 for (tree.Variable param in definition.parameters) { | |
| 80 variableNames[param] = param.element.name; | |
| 81 usedVariableNames.add(param.element.name); | |
| 82 declaredVariables.add(param); | |
| 83 } | |
| 84 | |
| 85 Statement body; | |
| 86 if (definition.isAbstract) { | |
| 87 body = new EmptyStatement(); | |
| 88 } else { | |
| 89 firstStatement = definition.body; | |
| 90 visitStatement(definition.body); | |
| 91 removeTrailingReturn(); | |
| 92 | |
| 93 // Some of the variable declarations have already been added | |
| 94 // if their first assignment could be pulled into the initializer. | |
| 95 // Add the remaining variable declarations now. | |
| 96 for (tree.Variable variable in variableNames.keys) { | |
| 97 if (!declaredVariables.contains(variable)) { | |
| 98 addDeclaration(variable); | |
| 99 } | |
| 100 } | |
| 101 | |
| 102 // Add constant declarations. | |
| 103 List<VariableDeclaration> constants = <VariableDeclaration>[]; | |
| 104 for (ConstDeclaration constDecl in definition.localConstants) { | |
| 105 if (!constantNames.containsKey(constDecl.element)) | |
| 106 continue; // Discard unused constants declarations. | |
| 107 String name = getConstantName(constDecl.element); | |
| 108 Expression value = emitConstant(constDecl.expression); | |
| 109 VariableDeclaration decl = new VariableDeclaration(name, value); | |
| 110 decl.element = constDecl.element; | |
| 111 constants.add(decl); | |
| 112 } | |
| 113 | |
| 114 List<Statement> bodyParts = []; | |
| 115 if (constants.length > 0) { | |
| 116 bodyParts.add(new VariableDeclarations(constants, isConst: true)); | |
| 117 } | |
| 118 if (variables.length > 0) { | |
| 119 bodyParts.add(new VariableDeclarations(variables)); | |
| 120 } | |
| 121 bodyParts.addAll(statementBuffer); | |
| 122 | |
| 123 body = new Block(bodyParts); | |
| 124 } | |
| 125 FunctionType functionType = functionElement.type; | |
| 126 | |
| 127 return new FunctionExpression( | |
| 128 parameters, | |
| 129 body, | |
| 130 name: functionElement.name, | |
| 131 returnType: emitOptionalType(functionType.returnType), | |
| 132 isGetter: functionElement.isGetter, | |
| 133 isSetter: functionElement.isSetter) | |
| 134 ..element = functionElement; | |
| 135 } | |
| 136 | |
| 137 void addDeclaration(tree.Variable variable, [Expression initializer]) { | |
| 138 assert(!declaredVariables.contains(variable)); | |
| 139 String name = getVariableName(variable); | |
| 140 VariableDeclaration decl = new VariableDeclaration(name, initializer); | |
| 141 decl.element = variable.element; | |
| 142 declaredVariables.add(variable); | |
| 143 variables.add(decl); | |
| 144 } | |
| 145 | |
| 146 /// Removes a trailing "return null" from [statementBuffer]. | |
| 147 void removeTrailingReturn() { | |
| 148 if (statementBuffer.isEmpty) return; | |
| 149 if (statementBuffer.last is! Return) return; | |
| 150 Return ret = statementBuffer.last; | |
| 151 Expression expr = ret.expression; | |
| 152 if (expr is Literal && expr.value.isNull) { | |
| 153 statementBuffer.removeLast(); | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 /// TODO(johnniwinther): Remove this when issue 21283 has been resolved. | |
| 158 int pseudoNameCounter = 0; | |
| 159 | |
| 160 Parameter emitParameter(DartType type, | |
| 161 {String name, | |
| 162 Element element, | |
| 163 ConstantExpression defaultValue}) { | |
| 164 if (name == null && element != null) { | |
| 165 name = element.name; | |
| 166 } | |
| 167 if (name == null) { | |
| 168 name = '_${pseudoNameCounter++}'; | |
| 169 } | |
| 170 Parameter parameter; | |
| 171 if (type.isFunctionType) { | |
| 172 FunctionType functionType = type; | |
| 173 TypeAnnotation returnType = emitOptionalType(functionType.returnType); | |
| 174 Parameters innerParameters = emitParametersFromType(functionType); | |
| 175 parameter = new Parameter.function(name, returnType, innerParameters); | |
| 176 } else { | |
| 177 TypeAnnotation typeAnnotation = emitOptionalType(type); | |
| 178 parameter = new Parameter(name, type: typeAnnotation); | |
| 179 } | |
| 180 parameter.element = element; | |
| 181 if (defaultValue != null && !defaultValue.value.isNull) { | |
| 182 parameter.defaultValue = emitConstant(defaultValue); | |
| 183 } | |
| 184 return parameter; | |
| 185 } | |
| 186 | |
| 187 Parameters emitParametersFromType(FunctionType functionType) { | |
| 188 if (functionType.namedParameters.isEmpty) { | |
| 189 return new Parameters( | |
| 190 emitParameters(functionType.parameterTypes), | |
| 191 emitParameters(functionType.optionalParameterTypes), | |
| 192 false); | |
| 193 } else { | |
| 194 return new Parameters( | |
| 195 emitParameters(functionType.parameterTypes), | |
| 196 emitParameters(functionType.namedParameterTypes, | |
| 197 names: functionType.namedParameters), | |
| 198 true); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 List<Parameter> emitParameters( | |
| 203 Iterable<DartType> parameterTypes, | |
| 204 {Iterable<String> names: const <String>[], | |
| 205 Iterable<ConstantExpression> defaultValues: const <ConstantExpression>[], | |
| 206 Iterable<Element> elements: const <Element>[]}) { | |
| 207 Iterator<String> name = names.iterator; | |
| 208 Iterator<ConstantExpression> defaultValue = defaultValues.iterator; | |
| 209 Iterator<Element> element = elements.iterator; | |
| 210 return parameterTypes.map((DartType type) { | |
| 211 name.moveNext(); | |
| 212 defaultValue.moveNext(); | |
| 213 element.moveNext(); | |
| 214 return emitParameter(type, | |
| 215 name: name.current, | |
| 216 defaultValue: defaultValue.current, | |
| 217 element: element.current); | |
| 218 }).toList(); | |
| 219 } | |
| 220 | |
| 221 /// Emits parameters that are not nested inside other parameters. | |
| 222 /// Root parameters can have default values, while inner parameters cannot. | |
| 223 Parameters emitRootParameters(tree.FunctionDefinition function) { | |
| 224 FunctionType functionType = function.element.type; | |
| 225 List<Parameter> required = emitParameters( | |
| 226 functionType.parameterTypes, | |
| 227 elements: function.parameters.map((p) => p.element)); | |
| 228 bool optionalParametersAreNamed = !functionType.namedParameters.isEmpty; | |
| 229 List<Parameter> optional = emitParameters( | |
| 230 optionalParametersAreNamed | |
| 231 ? functionType.namedParameterTypes | |
| 232 : functionType.optionalParameterTypes, | |
| 233 defaultValues: function.defaultParameterValues, | |
| 234 elements: function.parameters.skip(required.length) | |
| 235 .map((p) => p.element)); | |
| 236 return new Parameters(required, optional, optionalParametersAreNamed); | |
| 237 } | |
| 238 | |
| 239 /// True if the two expressions are a reference to the same variable. | |
| 240 bool isSameVariable(Receiver e1, Receiver e2) { | |
| 241 return e1 is Identifier && | |
| 242 e2 is Identifier && | |
| 243 e1.element is VariableElement && | |
| 244 e1.element == e2.element; | |
| 245 } | |
| 246 | |
| 247 Expression makeAssignment(Expression target, Expression value) { | |
| 248 // Try to print as compound assignment or increment | |
| 249 if (value is BinaryOperator && isCompoundableOperator(value.operator)) { | |
| 250 Expression leftOperand = value.left; | |
| 251 Expression rightOperand = value.right; | |
| 252 bool valid = false; | |
| 253 if (isSameVariable(target, leftOperand)) { | |
| 254 valid = true; | |
| 255 } else if (target is FieldExpression && | |
| 256 leftOperand is FieldExpression && | |
| 257 isSameVariable(target.object, leftOperand.object) && | |
| 258 target.fieldName == leftOperand.fieldName) { | |
| 259 valid = true; | |
| 260 } else if (target is IndexExpression && | |
| 261 leftOperand is IndexExpression && | |
| 262 isSameVariable(target.object, leftOperand.object) && | |
| 263 isSameVariable(target.index, leftOperand.index)) { | |
| 264 valid = true; | |
| 265 } | |
| 266 if (valid) { | |
| 267 if (rightOperand is Literal && rightOperand.value.isOne && | |
| 268 (value.operator == '+' || value.operator == '-')) { | |
| 269 return new Increment.prefix(target, value.operator + value.operator); | |
| 270 } else { | |
| 271 return new Assignment(target, value.operator + '=', rightOperand); | |
| 272 } | |
| 273 } | |
| 274 } | |
| 275 // Fall back to regular assignment | |
| 276 return new Assignment(target, '=', value); | |
| 277 } | |
| 278 | |
| 279 void visitExpressionStatement(tree.ExpressionStatement stmt) { | |
| 280 Expression e = visitExpression(stmt.expression); | |
| 281 statementBuffer.add(new ExpressionStatement(e)); | |
| 282 visitStatement(stmt.next); | |
| 283 } | |
| 284 | |
| 285 void visitLabeledStatement(tree.LabeledStatement stmt) { | |
| 286 List<Statement> savedBuffer = statementBuffer; | |
| 287 tree.Statement savedFallthrough = fallthrough; | |
| 288 statementBuffer = <Statement>[]; | |
| 289 fallthrough = stmt.next; | |
| 290 visitStatement(stmt.body); | |
| 291 if (usedLabels.remove(stmt.label)) { | |
| 292 savedBuffer.add(new LabeledStatement(stmt.label.name, | |
| 293 new Block(statementBuffer))); | |
| 294 } else { | |
| 295 savedBuffer.add(new Block(statementBuffer)); | |
| 296 } | |
| 297 fallthrough = savedFallthrough; | |
| 298 statementBuffer = savedBuffer; | |
| 299 visitStatement(stmt.next); | |
| 300 } | |
| 301 | |
| 302 /// Generates a name for the given variable and synthesizes an element for it, | |
| 303 /// if necessary. | |
| 304 String getVariableName(tree.Variable variable) { | |
| 305 // If the variable belongs to an enclosing function, ask the parent emitter | |
| 306 // for the variable name. | |
| 307 if (variable.host.element != functionElement) { | |
| 308 return parent.getVariableName(variable); | |
| 309 } | |
| 310 | |
| 311 // Get the name if we already have one. | |
| 312 String name = variableNames[variable]; | |
| 313 if (name != null) { | |
| 314 return name; | |
| 315 } | |
| 316 | |
| 317 // Synthesize a variable name that isn't used elsewhere. | |
| 318 // The [usedVariableNames] set is shared between nested emitters, | |
| 319 // so this also prevents clash with variables in an enclosing/inner scope. | |
| 320 // The renaming phase after codegen will further prefix local variables | |
| 321 // so they cannot clash with top-level variables or fields. | |
| 322 String prefix = variable.element == null ? 'v' : variable.element.name; | |
| 323 int counter = 0; | |
| 324 name = variable.element == null ? '$prefix$counter' : variable.element.name; | |
| 325 while (!usedVariableNames.add(name)) { | |
| 326 ++counter; | |
| 327 name = '$prefix$counter'; | |
| 328 } | |
| 329 variableNames[variable] = name; | |
| 330 | |
| 331 // Synthesize an element for the variable | |
| 332 if (variable.element == null || name != variable.element.name) { | |
| 333 // TODO(johnniwinther): Replace by synthetic [Entity]. | |
| 334 variable.element = new modelx.LocalVariableElementX.synthetic( | |
| 335 name, | |
| 336 functionElement, | |
| 337 variableList); | |
| 338 } | |
| 339 return name; | |
| 340 } | |
| 341 | |
| 342 String getConstantName(VariableElement element) { | |
| 343 assert(element.kind == ElementKind.VARIABLE); | |
| 344 if (element.enclosingElement != functionElement) { | |
| 345 return parent.getConstantName(element); | |
| 346 } | |
| 347 String name = constantNames[element]; | |
| 348 if (name != null) { | |
| 349 return name; | |
| 350 } | |
| 351 String prefix = element.name; | |
| 352 int counter = 0; | |
| 353 name = element.name; | |
| 354 while (!usedVariableNames.add(name)) { | |
| 355 ++counter; | |
| 356 name = '$prefix$counter'; | |
| 357 } | |
| 358 constantNames[element] = name; | |
| 359 return name; | |
| 360 } | |
| 361 | |
| 362 bool isNullLiteral(Expression exp) => exp is Literal && exp.value.isNull; | |
| 363 | |
| 364 void visitAssign(tree.Assign stmt) { | |
| 365 // Try to emit a local function declaration. This is useful for functions | |
| 366 // that may occur in expression context, but could not be inlined anywhere. | |
| 367 if (stmt.variable.element is FunctionElement && | |
| 368 stmt.definition is tree.FunctionExpression && | |
| 369 !declaredVariables.contains(stmt.variable)) { | |
| 370 tree.FunctionExpression functionExp = stmt.definition; | |
| 371 FunctionExpression function = makeSubFunction(functionExp.definition); | |
| 372 FunctionDeclaration decl = new FunctionDeclaration(function); | |
| 373 statementBuffer.add(decl); | |
| 374 declaredVariables.add(stmt.variable); | |
| 375 visitStatement(stmt.next); | |
| 376 return; | |
| 377 } | |
| 378 | |
| 379 bool isFirstOccurrence = (variableNames[stmt.variable] == null); | |
| 380 bool isDeclaredHere = stmt.variable.host.element == functionElement; | |
| 381 String name = getVariableName(stmt.variable); | |
| 382 Expression definition = visitExpression(stmt.definition); | |
| 383 | |
| 384 // Try to pull into initializer. | |
| 385 if (firstStatement == stmt && isFirstOccurrence && isDeclaredHere) { | |
| 386 if (isNullLiteral(definition)) definition = null; | |
| 387 addDeclaration(stmt.variable, definition); | |
| 388 firstStatement = stmt.next; | |
| 389 visitStatement(stmt.next); | |
| 390 return; | |
| 391 } | |
| 392 | |
| 393 // Emit a variable declaration if we are required to do so. | |
| 394 // This is to ensure that a fresh closure variable is created. | |
| 395 if (stmt.isDeclaration) { | |
| 396 assert(isFirstOccurrence); | |
| 397 assert(isDeclaredHere); | |
| 398 if (isNullLiteral(definition)) definition = null; | |
| 399 VariableDeclaration decl = new VariableDeclaration(name, definition) | |
| 400 ..element = stmt.variable.element; | |
| 401 declaredVariables.add(stmt.variable); | |
| 402 statementBuffer.add(new VariableDeclarations([decl])); | |
| 403 visitStatement(stmt.next); | |
| 404 return; | |
| 405 } | |
| 406 | |
| 407 statementBuffer.add(new ExpressionStatement(makeAssignment( | |
| 408 visitVariable(stmt.variable), | |
| 409 definition))); | |
| 410 visitStatement(stmt.next); | |
| 411 } | |
| 412 | |
| 413 void visitReturn(tree.Return stmt) { | |
| 414 Expression inner = visitExpression(stmt.value); | |
| 415 statementBuffer.add(new Return(inner)); | |
| 416 } | |
| 417 | |
| 418 void visitBreak(tree.Break stmt) { | |
| 419 tree.Statement fall = fallthrough; | |
| 420 if (stmt.target.binding.next == fall) { | |
| 421 // Fall through to break target | |
| 422 } else if (fall is tree.Break && fall.target == stmt.target) { | |
| 423 // Fall through to equivalent break | |
| 424 } else { | |
| 425 usedLabels.add(stmt.target); | |
| 426 statementBuffer.add(new Break(stmt.target.name)); | |
| 427 } | |
| 428 } | |
| 429 | |
| 430 void visitContinue(tree.Continue stmt) { | |
| 431 tree.Statement fall = fallthrough; | |
| 432 if (stmt.target.binding == fall) { | |
| 433 // Fall through to continue target | |
| 434 } else if (fall is tree.Continue && fall.target == stmt.target) { | |
| 435 // Fall through to equivalent continue | |
| 436 } else { | |
| 437 usedLabels.add(stmt.target); | |
| 438 statementBuffer.add(new Continue(stmt.target.name)); | |
| 439 } | |
| 440 } | |
| 441 | |
| 442 void visitIf(tree.If stmt) { | |
| 443 Expression condition = visitExpression(stmt.condition); | |
| 444 List<Statement> savedBuffer = statementBuffer; | |
| 445 List<Statement> thenBuffer = statementBuffer = <Statement>[]; | |
| 446 visitStatement(stmt.thenStatement); | |
| 447 List<Statement> elseBuffer = statementBuffer = <Statement>[]; | |
| 448 visitStatement(stmt.elseStatement); | |
| 449 savedBuffer.add( | |
| 450 new If(condition, new Block(thenBuffer), new Block(elseBuffer))); | |
| 451 statementBuffer = savedBuffer; | |
| 452 } | |
| 453 | |
| 454 void visitWhileTrue(tree.WhileTrue stmt) { | |
| 455 List<Statement> savedBuffer = statementBuffer; | |
| 456 tree.Statement savedFallthrough = fallthrough; | |
| 457 statementBuffer = <Statement>[]; | |
| 458 fallthrough = stmt; | |
| 459 | |
| 460 visitStatement(stmt.body); | |
| 461 Statement body = new Block(statementBuffer); | |
| 462 Statement statement = new While(new Literal(new TrueConstantValue()), | |
| 463 body); | |
| 464 if (usedLabels.remove(stmt.label)) { | |
| 465 statement = new LabeledStatement(stmt.label.name, statement); | |
| 466 } | |
| 467 savedBuffer.add(statement); | |
| 468 | |
| 469 statementBuffer = savedBuffer; | |
| 470 fallthrough = savedFallthrough; | |
| 471 } | |
| 472 | |
| 473 void visitWhileCondition(tree.WhileCondition stmt) { | |
| 474 Expression condition = visitExpression(stmt.condition); | |
| 475 | |
| 476 List<Statement> savedBuffer = statementBuffer; | |
| 477 tree.Statement savedFallthrough = fallthrough; | |
| 478 statementBuffer = <Statement>[]; | |
| 479 fallthrough = stmt; | |
| 480 | |
| 481 visitStatement(stmt.body); | |
| 482 Statement body = new Block(statementBuffer); | |
| 483 Statement statement; | |
| 484 statement = new While(condition, body); | |
| 485 if (usedLabels.remove(stmt.label)) { | |
| 486 statement = new LabeledStatement(stmt.label.name, statement); | |
| 487 } | |
| 488 savedBuffer.add(statement); | |
| 489 | |
| 490 statementBuffer = savedBuffer; | |
| 491 fallthrough = savedFallthrough; | |
| 492 | |
| 493 visitStatement(stmt.next); | |
| 494 } | |
| 495 | |
| 496 Expression visitConstant(tree.Constant exp) { | |
| 497 return emitConstant(exp.expression); | |
| 498 } | |
| 499 | |
| 500 Expression visitThis(tree.This exp) { | |
| 501 return new This(); | |
| 502 } | |
| 503 | |
| 504 Expression visitReifyTypeVar(tree.ReifyTypeVar exp) { | |
| 505 return new ReifyTypeVar(exp.typeVariable.name) | |
| 506 ..element = exp.typeVariable; | |
| 507 } | |
| 508 | |
| 509 Expression visitLiteralList(tree.LiteralList exp) { | |
| 510 return new LiteralList( | |
| 511 exp.values.map(visitExpression).toList(growable: false), | |
| 512 typeArgument: emitOptionalType(exp.type.typeArguments.single)); | |
| 513 } | |
| 514 | |
| 515 Expression visitLiteralMap(tree.LiteralMap exp) { | |
| 516 List<LiteralMapEntry> entries = new List<LiteralMapEntry>.generate( | |
| 517 exp.values.length, | |
| 518 (i) => new LiteralMapEntry(visitExpression(exp.keys[i]), | |
| 519 visitExpression(exp.values[i]))); | |
| 520 List<TypeAnnotation> typeArguments = exp.type.treatAsRaw | |
| 521 ? null | |
| 522 : exp.type.typeArguments.map(createTypeAnnotation).toList(growable: fals
e); | |
| 523 return new LiteralMap(entries, typeArguments: typeArguments); | |
| 524 } | |
| 525 | |
| 526 Expression visitTypeOperator(tree.TypeOperator exp) { | |
| 527 return new TypeOperator(visitExpression(exp.receiver), | |
| 528 exp.operator, | |
| 529 createTypeAnnotation(exp.type)); | |
| 530 } | |
| 531 | |
| 532 List<Argument> emitArguments(tree.Invoke exp) { | |
| 533 List<tree.Expression> args = exp.arguments; | |
| 534 int positionalArgumentCount = exp.selector.positionalArgumentCount; | |
| 535 List<Argument> result = new List<Argument>.generate(positionalArgumentCount, | |
| 536 (i) => visitExpression(exp.arguments[i])); | |
| 537 for (int i = 0; i < exp.selector.namedArgumentCount; ++i) { | |
| 538 result.add(new NamedArgument(exp.selector.namedArguments[i], | |
| 539 visitExpression(exp.arguments[positionalArgumentCount + i]))); | |
| 540 } | |
| 541 return result; | |
| 542 } | |
| 543 | |
| 544 Expression visitInvokeStatic(tree.InvokeStatic exp) { | |
| 545 switch (exp.selector.kind) { | |
| 546 case SelectorKind.GETTER: | |
| 547 return new Identifier(exp.target.name)..element = exp.target; | |
| 548 | |
| 549 case SelectorKind.SETTER: | |
| 550 return new Assignment( | |
| 551 new Identifier(exp.target.name)..element = exp.target, | |
| 552 '=', | |
| 553 visitExpression(exp.arguments[0])); | |
| 554 | |
| 555 case SelectorKind.CALL: | |
| 556 return new CallStatic(null, exp.target.name, emitArguments(exp)) | |
| 557 ..element = exp.target; | |
| 558 | |
| 559 default: | |
| 560 throw "Unexpected selector kind: ${exp.selector.kind}"; | |
| 561 } | |
| 562 } | |
| 563 | |
| 564 Expression emitMethodCall(tree.Invoke exp, Receiver receiver) { | |
| 565 List<Argument> args = emitArguments(exp); | |
| 566 switch (exp.selector.kind) { | |
| 567 case SelectorKind.CALL: | |
| 568 if (exp.selector.name == "call") { | |
| 569 return new CallFunction(receiver, args); | |
| 570 } | |
| 571 return new CallMethod(receiver, exp.selector.name, args); | |
| 572 | |
| 573 case SelectorKind.OPERATOR: | |
| 574 if (args.length == 0) { | |
| 575 String name = exp.selector.name; | |
| 576 if (name == 'unary-') { | |
| 577 name = '-'; | |
| 578 } | |
| 579 return new UnaryOperator(name, receiver); | |
| 580 } | |
| 581 return new BinaryOperator(receiver, exp.selector.name, args[0]); | |
| 582 | |
| 583 case SelectorKind.GETTER: | |
| 584 return new FieldExpression(receiver, exp.selector.name); | |
| 585 | |
| 586 case SelectorKind.SETTER: | |
| 587 return makeAssignment( | |
| 588 new FieldExpression(receiver, exp.selector.name), | |
| 589 args[0]); | |
| 590 | |
| 591 case SelectorKind.INDEX: | |
| 592 Expression e = new IndexExpression(receiver, args[0]); | |
| 593 if (args.length == 2) { | |
| 594 e = makeAssignment(e, args[1]); | |
| 595 } | |
| 596 return e; | |
| 597 | |
| 598 default: | |
| 599 throw "Unexpected selector in InvokeMethod: ${exp.selector.kind}"; | |
| 600 } | |
| 601 } | |
| 602 | |
| 603 Expression visitInvokeMethod(tree.InvokeMethod exp) { | |
| 604 Expression receiver = visitExpression(exp.receiver); | |
| 605 return emitMethodCall(exp, receiver); | |
| 606 } | |
| 607 | |
| 608 Expression visitInvokeSuperMethod(tree.InvokeSuperMethod exp) { | |
| 609 return emitMethodCall(exp, new SuperReceiver()); | |
| 610 } | |
| 611 | |
| 612 Expression visitInvokeConstructor(tree.InvokeConstructor exp) { | |
| 613 List args = emitArguments(exp); | |
| 614 FunctionElement constructor = exp.target; | |
| 615 String name = constructor.name.isEmpty ? null : constructor.name; | |
| 616 return new CallNew(createTypeAnnotation(exp.type), | |
| 617 args, | |
| 618 constructorName: name, | |
| 619 isConst: exp.constant != null) | |
| 620 ..constructor = constructor | |
| 621 ..dartType = exp.type; | |
| 622 } | |
| 623 | |
| 624 Expression visitConcatenateStrings(tree.ConcatenateStrings exp) { | |
| 625 List args = exp.arguments.map(visitExpression).toList(growable:false); | |
| 626 return new StringConcat(args); | |
| 627 } | |
| 628 | |
| 629 Expression visitConditional(tree.Conditional exp) { | |
| 630 return new Conditional( | |
| 631 visitExpression(exp.condition), | |
| 632 visitExpression(exp.thenExpression), | |
| 633 visitExpression(exp.elseExpression)); | |
| 634 } | |
| 635 | |
| 636 Expression visitLogicalOperator(tree.LogicalOperator exp) { | |
| 637 return new BinaryOperator(visitExpression(exp.left), | |
| 638 exp.operator, | |
| 639 visitExpression(exp.right)); | |
| 640 } | |
| 641 | |
| 642 Expression visitNot(tree.Not exp) { | |
| 643 return new UnaryOperator('!', visitExpression(exp.operand)); | |
| 644 } | |
| 645 | |
| 646 Expression visitVariable(tree.Variable exp) { | |
| 647 return new Identifier(getVariableName(exp)) | |
| 648 ..element = exp.element; | |
| 649 } | |
| 650 | |
| 651 FunctionExpression makeSubFunction(tree.FunctionDefinition function) { | |
| 652 return new ASTEmitter.inner(this).emit(function); | |
| 653 } | |
| 654 | |
| 655 Expression visitFunctionExpression(tree.FunctionExpression exp) { | |
| 656 return makeSubFunction(exp.definition)..name = null; | |
| 657 } | |
| 658 | |
| 659 void visitFunctionDeclaration(tree.FunctionDeclaration node) { | |
| 660 assert(variableNames[node.variable] == null); | |
| 661 String name = getVariableName(node.variable); | |
| 662 FunctionExpression inner = makeSubFunction(node.definition); | |
| 663 inner.name = name; | |
| 664 FunctionDeclaration decl = new FunctionDeclaration(inner); | |
| 665 declaredVariables.add(node.variable); | |
| 666 statementBuffer.add(decl); | |
| 667 visitStatement(node.next); | |
| 668 } | |
| 669 | |
| 670 /// Like [createTypeAnnotation] except the dynamic type is converted to null. | |
| 671 TypeAnnotation emitOptionalType(DartType type) { | |
| 672 if (type.treatAsDynamic) { | |
| 673 return null; | |
| 674 } else { | |
| 675 return createTypeAnnotation(type); | |
| 676 } | |
| 677 } | |
| 678 | |
| 679 Expression emitConstant(ConstantExpression exp) { | |
| 680 return new ConstantEmitter(this).visit(exp); | |
| 681 } | |
| 682 } | |
| 683 | |
| 684 TypeAnnotation createTypeAnnotation(DartType type) { | |
| 685 if (type is GenericType) { | |
| 686 if (type.treatAsRaw) { | |
| 687 return new TypeAnnotation(type.element.name)..dartType = type; | |
| 688 } | |
| 689 return new TypeAnnotation( | |
| 690 type.element.name, | |
| 691 type.typeArguments.map(createTypeAnnotation).toList(growable:false)) | |
| 692 ..dartType = type; | |
| 693 } else if (type is VoidType) { | |
| 694 return new TypeAnnotation('void') | |
| 695 ..dartType = type; | |
| 696 } else if (type is TypeVariableType) { | |
| 697 return new TypeAnnotation(type.name) | |
| 698 ..dartType = type; | |
| 699 } else if (type is DynamicType) { | |
| 700 return new TypeAnnotation("dynamic") | |
| 701 ..dartType = type; | |
| 702 } else if (type is MalformedType) { | |
| 703 return new TypeAnnotation(type.name) | |
| 704 ..dartType = type; | |
| 705 } else { | |
| 706 throw "Unsupported type annotation: $type"; | |
| 707 } | |
| 708 } | |
| 709 | |
| 710 class ConstantEmitter extends ConstantExpressionVisitor<Expression> { | |
| 711 ASTEmitter parent; | |
| 712 ConstantEmitter(this.parent); | |
| 713 | |
| 714 Expression handlePrimitiveConstant(PrimitiveConstantValue value) { | |
| 715 // Num constants may be negative, while literals must be non-negative: | |
| 716 // Literals are non-negative in the specification, and a negated literal | |
| 717 // parses as a call to unary `-`. The AST unparser assumes literals are | |
| 718 // non-negative and relies on this to avoid incorrectly generating `--`, | |
| 719 // the predecrement operator. | |
| 720 // Translate such constants into their positive value wrapped by | |
| 721 // the unary minus operator. | |
| 722 if (value.isNum) { | |
| 723 NumConstantValue numConstant = value; | |
| 724 if (numConstant.primitiveValue.isNegative) { | |
| 725 return negatedLiteral(numConstant); | |
| 726 } | |
| 727 } | |
| 728 return new Literal(value); | |
| 729 } | |
| 730 | |
| 731 @override | |
| 732 Expression visitPrimitive(PrimitiveConstantExpression exp) { | |
| 733 return handlePrimitiveConstant(exp.value); | |
| 734 } | |
| 735 | |
| 736 /// Given a negative num constant, returns the corresponding positive | |
| 737 /// literal wrapped by a unary minus operator. | |
| 738 Expression negatedLiteral(NumConstantValue constant) { | |
| 739 assert(constant.primitiveValue.isNegative); | |
| 740 NumConstantValue positiveConstant; | |
| 741 if (constant.isInt) { | |
| 742 positiveConstant = new IntConstantValue(-constant.primitiveValue); | |
| 743 } else if (constant.isDouble) { | |
| 744 positiveConstant = new DoubleConstantValue(-constant.primitiveValue); | |
| 745 } else { | |
| 746 throw "Unexpected type of NumConstant: $constant"; | |
| 747 } | |
| 748 return new UnaryOperator('-', new Literal(positiveConstant)); | |
| 749 } | |
| 750 | |
| 751 @override | |
| 752 Expression visitList(ListConstantExpression exp) { | |
| 753 return new LiteralList( | |
| 754 exp.values.map(visit).toList(growable: false), | |
| 755 isConst: true, | |
| 756 typeArgument: parent.emitOptionalType(exp.type.typeArguments.single)); | |
| 757 } | |
| 758 | |
| 759 @override | |
| 760 Expression visitMap(MapConstantExpression exp) { | |
| 761 List<LiteralMapEntry> entries = new List<LiteralMapEntry>.generate( | |
| 762 exp.values.length, | |
| 763 (i) => new LiteralMapEntry(visit(exp.keys[i]), | |
| 764 visit(exp.values[i]))); | |
| 765 List<TypeAnnotation> typeArguments = exp.type.treatAsRaw | |
| 766 ? null | |
| 767 : exp.type.typeArguments.map(createTypeAnnotation).toList(); | |
| 768 return new LiteralMap(entries, isConst: true, typeArguments: typeArguments); | |
| 769 } | |
| 770 | |
| 771 @override | |
| 772 Expression visitConstructor(ConstructedConstantExpresssion exp) { | |
| 773 int positionalArgumentCount = exp.selector.positionalArgumentCount; | |
| 774 List<Argument> args = new List<Argument>.generate( | |
| 775 positionalArgumentCount, | |
| 776 (i) => visit(exp.arguments[i])); | |
| 777 for (int i = 0; i < exp.selector.namedArgumentCount; ++i) { | |
| 778 args.add(new NamedArgument(exp.selector.namedArguments[i], | |
| 779 visit(exp.arguments[positionalArgumentCount + i]))); | |
| 780 } | |
| 781 | |
| 782 FunctionElement constructor = exp.target; | |
| 783 String name = constructor.name.isEmpty ? null : constructor.name; | |
| 784 return new CallNew(createTypeAnnotation(exp.type), | |
| 785 args, | |
| 786 constructorName: name, | |
| 787 isConst: true) | |
| 788 ..constructor = constructor | |
| 789 ..dartType = exp.type; | |
| 790 } | |
| 791 | |
| 792 @override | |
| 793 Expression visitConcatenate(ConcatenateConstantExpression exp) { | |
| 794 return new StringConcat(exp.arguments.map(visit).toList(growable: false)); | |
| 795 } | |
| 796 | |
| 797 @override | |
| 798 Expression visitSymbol(SymbolConstantExpression exp) { | |
| 799 return new LiteralSymbol(exp.name); | |
| 800 } | |
| 801 | |
| 802 @override | |
| 803 Expression visitType(TypeConstantExpression exp) { | |
| 804 DartType type = exp.type; | |
| 805 return new LiteralType(type.name) | |
| 806 ..type = type; | |
| 807 } | |
| 808 | |
| 809 @override | |
| 810 Expression visitVariable(VariableConstantExpression exp) { | |
| 811 Element element = exp.element; | |
| 812 if (element.kind != ElementKind.VARIABLE) { | |
| 813 return new Identifier(element.name)..element = element; | |
| 814 } | |
| 815 String name = parent.getConstantName(element); | |
| 816 return new Identifier(name) | |
| 817 ..element = element; | |
| 818 } | |
| 819 | |
| 820 @override | |
| 821 Expression visitFunction(FunctionConstantExpression exp) { | |
| 822 return new Identifier(exp.element.name) | |
| 823 ..element = exp.element; | |
| 824 } | |
| 825 | |
| 826 @override | |
| 827 Expression visitBinary(BinaryConstantExpression exp) { | |
| 828 return handlePrimitiveConstant(exp.value); | |
| 829 } | |
| 830 | |
| 831 @override | |
| 832 Expression visitConditional(ConditionalConstantExpression exp) { | |
| 833 if (exp.condition.value.isTrue) { | |
| 834 return exp.trueExp.accept(this); | |
| 835 } else { | |
| 836 return exp.falseExp.accept(this); | |
| 837 } | |
| 838 } | |
| 839 | |
| 840 @override | |
| 841 Expression visitUnary(UnaryConstantExpression exp) { | |
| 842 return handlePrimitiveConstant(exp.value); | |
| 843 } | |
| 844 } | |
| 845 | |
| 846 /// Moves function parameters into a separate variable if one of its uses is | |
| 847 /// shadowed by an inner function parameter. | |
| 848 /// This artifact is necessary because function parameters cannot be renamed. | |
| 849 class UnshadowParameters extends tree.RecursiveVisitor { | |
| 850 | |
| 851 /// Maps parameter names to their bindings. | |
| 852 Map<String, tree.Variable> environment = <String, tree.Variable>{}; | |
| 853 | |
| 854 /// Parameters that are currently shadowed by another parameter. | |
| 855 Set<tree.Variable> shadowedParameters = new Set<tree.Variable>(); | |
| 856 | |
| 857 /// Parameters that are used in a context where it is shadowed. | |
| 858 Set<tree.Variable> hasShadowedUse = new Set<tree.Variable>(); | |
| 859 | |
| 860 void unshadow(tree.FunctionDefinition definition) { | |
| 861 if (definition.isAbstract) return; | |
| 862 | |
| 863 visitFunctionDefinition(definition); | |
| 864 } | |
| 865 | |
| 866 visitFunctionDefinition(tree.FunctionDefinition definition) { | |
| 867 var oldShadow = shadowedParameters; | |
| 868 var oldEnvironment = environment; | |
| 869 environment = new Map<String, tree.Variable>.from(environment); | |
| 870 shadowedParameters = new Set<tree.Variable>.from(shadowedParameters); | |
| 871 for (tree.Variable param in definition.parameters) { | |
| 872 tree.Variable oldVariable = environment[param.element.name]; | |
| 873 if (oldVariable != null) { | |
| 874 shadowedParameters.add(oldVariable); | |
| 875 } | |
| 876 environment[param.element.name] = param; | |
| 877 } | |
| 878 visitStatement(definition.body); | |
| 879 environment = oldEnvironment; | |
| 880 shadowedParameters = oldShadow; | |
| 881 | |
| 882 for (int i=0; i<definition.parameters.length; i++) { | |
| 883 tree.Variable param = definition.parameters[i]; | |
| 884 if (hasShadowedUse.remove(param)) { | |
| 885 tree.Variable newParam = new tree.Variable(definition, param.element); | |
| 886 definition.parameters[i] = newParam; | |
| 887 definition.body = new tree.Assign(param, newParam, definition.body); | |
| 888 newParam.writeCount = 1; // Being a parameter counts as a write. | |
| 889 } | |
| 890 } | |
| 891 } | |
| 892 | |
| 893 visitVariable(tree.Variable variable) { | |
| 894 if (shadowedParameters.contains(variable)) { | |
| 895 hasShadowedUse.add(variable); | |
| 896 } | |
| 897 } | |
| 898 | |
| 899 } | |
| OLD | NEW |