| OLD | NEW |
| (Empty) |
| 1 import ts = require('typescript'); | |
| 2 import base = require('./base'); | |
| 3 import ts2dart = require('./main'); | |
| 4 import {FacadeConverter} from './facade_converter'; | |
| 5 | |
| 6 export default class CallTranspiler extends base.TranspilerBase { | |
| 7 constructor(tr: ts2dart.Transpiler, private fc: FacadeConverter) { super(tr);
} | |
| 8 | |
| 9 visitNode(node: ts.Node): boolean { | |
| 10 switch (node.kind) { | |
| 11 case ts.SyntaxKind.Block: | |
| 12 // This is a bit ugly: to separate Declarations from Calls, this code ha
s to special case | |
| 13 // blocks that are actually constructor bodies. | |
| 14 if (node.parent && node.parent.kind === ts.SyntaxKind.Constructor) { | |
| 15 return this.visitConstructorBody(<ts.ConstructorDeclaration>node.paren
t); | |
| 16 } | |
| 17 return false; | |
| 18 case ts.SyntaxKind.NewExpression: | |
| 19 let newExpr = <ts.NewExpression>node; | |
| 20 if (this.hasAncestor(node, ts.SyntaxKind.Decorator)) { | |
| 21 // Constructor calls in annotations must be const constructor calls. | |
| 22 this.emit('const'); | |
| 23 } else if (this.fc.isInsideConstExpr(node)) { | |
| 24 this.emit('const'); | |
| 25 } else { | |
| 26 // Some implementations can replace the `new` keyword. | |
| 27 if (this.fc.shouldEmitNew(newExpr)) { | |
| 28 this.emit('new'); | |
| 29 } | |
| 30 } | |
| 31 if (this.fc.maybeHandleCall(newExpr)) break; | |
| 32 this.visitCall(newExpr); | |
| 33 break; | |
| 34 case ts.SyntaxKind.CallExpression: | |
| 35 let callExpr = <ts.CallExpression>node; | |
| 36 if (this.fc.maybeHandleCall(callExpr)) break; | |
| 37 if (this.maybeHandleSuperCall(callExpr)) break; | |
| 38 this.visitCall(callExpr); | |
| 39 break; | |
| 40 case ts.SyntaxKind.SuperKeyword: | |
| 41 this.emit('super'); | |
| 42 break; | |
| 43 default: | |
| 44 return false; | |
| 45 } | |
| 46 return true; | |
| 47 } | |
| 48 | |
| 49 private visitCall(c: ts.CallExpression) { | |
| 50 if (c.expression.kind === ts.SyntaxKind.Identifier) { | |
| 51 this.fc.visitTypeName(<ts.Identifier>c.expression); | |
| 52 } else { | |
| 53 this.visit(c.expression); | |
| 54 } | |
| 55 if (c.typeArguments) { | |
| 56 // For DDC, emit generic method arguments in /* block comments */ | |
| 57 // NB: Surprisingly, whitespace within the comment is significant here :-( | |
| 58 // TODO(martinprobst): Remove once Dart natively supports generic methods. | |
| 59 if (c.kind !== ts.SyntaxKind.NewExpression) this.emit('/*'); | |
| 60 this.maybeVisitTypeArguments(c); | |
| 61 if (c.kind !== ts.SyntaxKind.NewExpression) this.emitNoSpace('*/'); | |
| 62 } | |
| 63 this.emit('('); | |
| 64 if (c.arguments && !this.handleNamedParamsCall(c)) { | |
| 65 this.visitList(c.arguments); | |
| 66 } | |
| 67 this.emit(')'); | |
| 68 } | |
| 69 | |
| 70 private handleNamedParamsCall(c: ts.CallExpression): boolean { | |
| 71 // Preamble: This is all committed in the name of backwards compat with the
traceur transpiler. | |
| 72 | |
| 73 // Terrible hack: transform foo(a, b, {c: d}) into foo(a, b, c: d), which is
Dart's calling | |
| 74 // syntax for named/optional parameters. An alternative would be to transfor
m the method | |
| 75 // declaration to take a plain object literal and destructure in the method,
but then client | |
| 76 // code written against Dart wouldn't get nice named parameters. | |
| 77 if (c.arguments.length === 0) return false; | |
| 78 let last = c.arguments[c.arguments.length - 1]; | |
| 79 if (last.kind !== ts.SyntaxKind.ObjectLiteralExpression) return false; | |
| 80 let objLit = <ts.ObjectLiteralExpression>last; | |
| 81 if (objLit.properties.length === 0) return false; | |
| 82 // Even worse: foo(a, b, {'c': d}) is considered to *not* be a named paramet
ers call. | |
| 83 let hasNonPropAssignments = objLit.properties.some( | |
| 84 (p) => | |
| 85 (p.kind !== ts.SyntaxKind.PropertyAssignment || | |
| 86 (<ts.PropertyAssignment>p).name.kind !== ts.SyntaxKind.Identifier))
; | |
| 87 if (hasNonPropAssignments) return false; | |
| 88 | |
| 89 let len = c.arguments.length - 1; | |
| 90 this.visitList(c.arguments.slice(0, len)); | |
| 91 if (len) this.emit(','); | |
| 92 let props = objLit.properties; | |
| 93 for (let i = 0; i < props.length; i++) { | |
| 94 let prop = <ts.PropertyAssignment>props[i]; | |
| 95 this.emit(base.ident(prop.name)); | |
| 96 this.emit(':'); | |
| 97 this.visit(prop.initializer); | |
| 98 if (i < objLit.properties.length - 1) this.emit(','); | |
| 99 } | |
| 100 return true; | |
| 101 } | |
| 102 | |
| 103 /** | |
| 104 * Handles constructor initializer lists and bodies. | |
| 105 * | |
| 106 * <p>Dart's super() ctor calls have to be moved to the constructors initializ
er list, and `const` | |
| 107 * constructors must be completely empty, only assigning into fields through t
he initializer list. | |
| 108 * The code below finds super() calls and handles const constructors, marked w
ith the special | |
| 109 * `@CONST` annotation on the class. | |
| 110 * | |
| 111 * <p>Not emitting super() calls when traversing the ctor body is handled by m
aybeHandleSuperCall | |
| 112 * below. | |
| 113 */ | |
| 114 private visitConstructorBody(ctor: ts.ConstructorDeclaration): boolean { | |
| 115 let body = ctor.body; | |
| 116 if (!body) return false; | |
| 117 | |
| 118 let errorAssignmentsSuper = 'const constructors can only contain assignments
and super calls'; | |
| 119 let errorThisAssignment = 'assignments in const constructors must assign int
o this.'; | |
| 120 | |
| 121 let parent = <base.ClassLike>ctor.parent; | |
| 122 let parentIsConst = this.isConst(parent); | |
| 123 let superCall: ts.CallExpression; | |
| 124 let expressions: ts.Expression[] = []; | |
| 125 // Find super() calls and (if in a const ctor) collect assignment expression
s (not statements!) | |
| 126 body.statements.forEach((stmt) => { | |
| 127 if (stmt.kind !== ts.SyntaxKind.ExpressionStatement) { | |
| 128 if (parentIsConst) this.reportError(stmt, errorAssignmentsSuper); | |
| 129 return; | |
| 130 } | |
| 131 let nestedExpr = (<ts.ExpressionStatement>stmt).expression; | |
| 132 | |
| 133 // super() call? | |
| 134 if (nestedExpr.kind === ts.SyntaxKind.CallExpression) { | |
| 135 let callExpr = <ts.CallExpression>nestedExpr; | |
| 136 if (callExpr.expression.kind !== ts.SyntaxKind.SuperKeyword) { | |
| 137 if (parentIsConst) this.reportError(stmt, errorAssignmentsSuper); | |
| 138 return; | |
| 139 } | |
| 140 superCall = callExpr; | |
| 141 return; | |
| 142 } | |
| 143 | |
| 144 // this.x assignment? | |
| 145 if (parentIsConst) { | |
| 146 // Check for assignment. | |
| 147 if (nestedExpr.kind !== ts.SyntaxKind.BinaryExpression) { | |
| 148 this.reportError(nestedExpr, errorAssignmentsSuper); | |
| 149 return; | |
| 150 } | |
| 151 let binExpr = <ts.BinaryExpression>nestedExpr; | |
| 152 if (binExpr.operatorToken.kind !== ts.SyntaxKind.EqualsToken) { | |
| 153 this.reportError(binExpr, errorAssignmentsSuper); | |
| 154 return; | |
| 155 } | |
| 156 // Check for 'this.' | |
| 157 if (binExpr.left.kind !== ts.SyntaxKind.PropertyAccessExpression) { | |
| 158 this.reportError(binExpr, errorThisAssignment); | |
| 159 return; | |
| 160 } | |
| 161 let lhs = <ts.PropertyAccessExpression>binExpr.left; | |
| 162 if (lhs.expression.kind !== ts.SyntaxKind.ThisKeyword) { | |
| 163 this.reportError(binExpr, errorThisAssignment); | |
| 164 return; | |
| 165 } | |
| 166 let ident = lhs.name; | |
| 167 binExpr.left = ident; | |
| 168 expressions.push(nestedExpr); | |
| 169 } | |
| 170 }); | |
| 171 | |
| 172 let hasInitializerExpr = expressions.length > 0; | |
| 173 if (hasInitializerExpr) { | |
| 174 // Write out the assignments. | |
| 175 this.emit(':'); | |
| 176 this.visitList(expressions); | |
| 177 } | |
| 178 if (superCall) { | |
| 179 this.emit(hasInitializerExpr ? ',' : ':'); | |
| 180 this.emit('super ('); | |
| 181 if (!this.handleNamedParamsCall(superCall)) { | |
| 182 this.visitList(superCall.arguments); | |
| 183 } | |
| 184 this.emit(')'); | |
| 185 } | |
| 186 if (parentIsConst) { | |
| 187 // Const ctors don't have bodies. | |
| 188 this.emit(';'); | |
| 189 return true; // completely handled. | |
| 190 } else { | |
| 191 return false; | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 /** | |
| 196 * Checks whether `callExpr` is a super() call that should be ignored because
it was already | |
| 197 * handled by `maybeEmitSuperInitializer` above. | |
| 198 */ | |
| 199 private maybeHandleSuperCall(callExpr: ts.CallExpression): boolean { | |
| 200 if (callExpr.expression.kind !== ts.SyntaxKind.SuperKeyword) return false; | |
| 201 // Sanity check that there was indeed a ctor directly above this call. | |
| 202 let exprStmt = callExpr.parent; | |
| 203 let ctorBody = exprStmt.parent; | |
| 204 let ctor = ctorBody.parent; | |
| 205 if (ctor.kind !== ts.SyntaxKind.Constructor) { | |
| 206 this.reportError(callExpr, 'super calls must be immediate children of thei
r constructors'); | |
| 207 return false; | |
| 208 } | |
| 209 this.emit('/* super call moved to initializer */'); | |
| 210 return true; | |
| 211 } | |
| 212 } | |
| OLD | NEW |