OLD | NEW |
1 import * as ts from 'typescript'; | 1 import * as ts from 'typescript'; |
2 import {Transpiler} from './main'; | 2 |
| 3 import {OutputContext, Transpiler} from './main'; |
3 | 4 |
4 export type ClassLike = ts.ClassDeclaration | ts.InterfaceDeclaration; | 5 export type ClassLike = ts.ClassDeclaration | ts.InterfaceDeclaration; |
| 6 export type NamedDeclaration = ClassLike | ts.PropertyDeclaration | ts.VariableD
eclaration | |
| 7 ts.MethodDeclaration | ts.ModuleDeclaration | ts.FunctionDeclaration; |
| 8 |
| 9 export type Set = { |
| 10 [s: string]: boolean |
| 11 }; |
5 | 12 |
6 export function ident(n: ts.Node): string { | 13 export function ident(n: ts.Node): string { |
7 if (n.kind === ts.SyntaxKind.Identifier) return (<ts.Identifier>n).text; | 14 if (n.kind === ts.SyntaxKind.Identifier) return (<ts.Identifier>n).text; |
8 if (n.kind === ts.SyntaxKind.QualifiedName) { | 15 if (n.kind === ts.SyntaxKind.QualifiedName) { |
9 let qname = (<ts.QualifiedName>n); | 16 let qname = (<ts.QualifiedName>n); |
10 let leftName = ident(qname.left); | 17 let leftName = ident(qname.left); |
11 if (leftName) return leftName + '.' + ident(qname.right); | 18 if (leftName) return leftName + '.' + ident(qname.right); |
12 } | 19 } |
13 return null; | 20 return null; |
14 } | 21 } |
15 | 22 |
| 23 export function isFunctionTypedefLikeInterface(ifDecl: ts.InterfaceDeclaration):
boolean { |
| 24 return ifDecl.members && ifDecl.members.length === 1 && |
| 25 ifDecl.members[0].kind === ts.SyntaxKind.CallSignature; |
| 26 } |
| 27 |
| 28 export function getDeclaration(type: ts.Type): ts.Declaration { |
| 29 let symbol = type.getSymbol(); |
| 30 if (!symbol) return null; |
| 31 if (symbol.valueDeclaration) return symbol.valueDeclaration; |
| 32 return symbol.declarations && symbol.declarations.length > 0 ? symbol.declarat
ions[0] : null; |
| 33 } |
| 34 |
| 35 export function isExtendsClause(heritageClause: ts.HeritageClause) { |
| 36 return heritageClause.token === ts.SyntaxKind.ExtendsKeyword && |
| 37 heritageClause.parent.kind !== ts.SyntaxKind.InterfaceDeclaration; |
| 38 } |
| 39 export function isConstructor(n: ts.Node): boolean { |
| 40 return n.kind === ts.SyntaxKind.Constructor || n.kind === ts.SyntaxKind.Constr
uctSignature; |
| 41 } |
| 42 |
| 43 export function isStatic(n: ts.Node): boolean { |
| 44 let hasStatic = false; |
| 45 ts.forEachChild(n, (child) => { |
| 46 if (child.kind === ts.SyntaxKind.StaticKeyword) { |
| 47 hasStatic = true; |
| 48 } |
| 49 }); |
| 50 return hasStatic; |
| 51 } |
| 52 |
| 53 export function isCallableType(type: ts.TypeNode, tc: ts.TypeChecker): boolean { |
| 54 if (isFunctionType(type, tc)) return true; |
| 55 if (type.kind === ts.SyntaxKind.TypeReference) { |
| 56 if (tc.getSignaturesOfType(tc.getTypeAtLocation(type), ts.SignatureKind.Call
).length > 0) |
| 57 return true; |
| 58 } |
| 59 return false; |
| 60 } |
| 61 |
| 62 export function isFunctionType(type: ts.TypeNode, tc: ts.TypeChecker): boolean { |
| 63 let kind = type.kind; |
| 64 if (kind === ts.SyntaxKind.FunctionType) return true; |
| 65 if (kind === ts.SyntaxKind.TypeReference) { |
| 66 let t = tc.getTypeAtLocation(type); |
| 67 if (t.symbol && t.symbol.flags & ts.SymbolFlags.Function) return true; |
| 68 } |
| 69 |
| 70 if (kind === ts.SyntaxKind.UnionType) { |
| 71 let types = (<ts.UnionTypeNode>type).types; |
| 72 for (let i = 0; i < types.length; ++i) { |
| 73 if (!isFunctionType(types[i], tc)) { |
| 74 return false; |
| 75 } |
| 76 } |
| 77 return true; |
| 78 } |
| 79 // Warning: if the kind is a reference type and the reference is to an |
| 80 // interface that only has a call member we will not return that it is a |
| 81 // function type. |
| 82 if (kind === ts.SyntaxKind.TypeLiteral) { |
| 83 let members = (<ts.TypeLiteralNode>type).members; |
| 84 for (let i = 0; i < members.length; ++i) { |
| 85 if (members[i].kind !== ts.SyntaxKind.CallSignature) { |
| 86 return false; |
| 87 } |
| 88 } |
| 89 return true; |
| 90 } |
| 91 return false; |
| 92 } |
| 93 |
| 94 export function isTypeNode(node: ts.Node): boolean { |
| 95 switch (node.kind) { |
| 96 case ts.SyntaxKind.UnionType: |
| 97 case ts.SyntaxKind.TypeReference: |
| 98 case ts.SyntaxKind.TypeLiteral: |
| 99 case ts.SyntaxKind.LastTypeNode: |
| 100 case ts.SyntaxKind.ArrayType: |
| 101 case ts.SyntaxKind.TypePredicate: |
| 102 case ts.SyntaxKind.TypeQuery: |
| 103 case ts.SyntaxKind.TupleType: |
| 104 case ts.SyntaxKind.NumberKeyword: |
| 105 case ts.SyntaxKind.StringKeyword: |
| 106 case ts.SyntaxKind.VoidKeyword: |
| 107 case ts.SyntaxKind.BooleanKeyword: |
| 108 case ts.SyntaxKind.AnyKeyword: |
| 109 case ts.SyntaxKind.FunctionType: |
| 110 return true; |
| 111 default: |
| 112 return false; |
| 113 } |
| 114 } |
| 115 |
| 116 export function isCallable(decl: ClassLike): boolean { |
| 117 let members = decl.members as Array<ts.ClassElement>; |
| 118 return members.some((member) => { return member.kind === ts.SyntaxKind.CallSig
nature; }); |
| 119 } |
| 120 |
| 121 export function copyLocation(src: ts.TextRange, dest: ts.TextRange) { |
| 122 dest.pos = src.pos; |
| 123 dest.end = src.end; |
| 124 } |
| 125 |
| 126 // Polyfill for ES6 Array.find. |
| 127 export function arrayFindPolyfill<T>( |
| 128 nodeArray: ts.NodeArray<T>, predicate: (node: T) => boolean): T { |
| 129 for (let i = 0; i < nodeArray.length; ++i) { |
| 130 if (predicate(nodeArray[i])) return nodeArray[i]; |
| 131 } |
| 132 return null; |
| 133 } |
| 134 |
| 135 export function getAncestor(n: ts.Node, kind: ts.SyntaxKind): ts.Node { |
| 136 for (let parent = n; parent; parent = parent.parent) { |
| 137 if (parent.kind === kind) return parent; |
| 138 } |
| 139 return null; |
| 140 } |
| 141 |
| 142 export function getEnclosingClass(n: ts.Node): ClassLike { |
| 143 for (let parent = n.parent; parent; parent = parent.parent) { |
| 144 if (parent.kind === ts.SyntaxKind.ClassDeclaration || |
| 145 parent.kind === ts.SyntaxKind.InterfaceDeclaration) { |
| 146 return <ClassLike>parent; |
| 147 } |
| 148 } |
| 149 return null; |
| 150 } |
| 151 |
| 152 export function isConstCall(node: ts.CallExpression): boolean { |
| 153 return node && ident(node.expression) === 'CONST_EXPR'; |
| 154 } |
| 155 |
| 156 export function isInsideConstExpr(node: ts.Node): boolean { |
| 157 return isConstCall(<ts.CallExpression>getAncestor(node, ts.SyntaxKind.CallExpr
ession)); |
| 158 } |
| 159 |
| 160 |
16 export class TranspilerBase { | 161 export class TranspilerBase { |
17 private idCounter: number = 0; | 162 private idCounter: number = 0; |
18 constructor(private transpiler: Transpiler) {} | 163 constructor(protected transpiler: Transpiler) {} |
19 | 164 |
20 visit(n: ts.Node) { this.transpiler.visit(n); } | 165 visit(n: ts.Node) { this.transpiler.visit(n); } |
| 166 pushContext(context: OutputContext) { this.transpiler.pushContext(context); } |
| 167 popContext() { this.transpiler.popContext(); } |
21 emit(s: string) { this.transpiler.emit(s); } | 168 emit(s: string) { this.transpiler.emit(s); } |
22 emitNoSpace(s: string) { this.transpiler.emitNoSpace(s); } | 169 emitNoSpace(s: string) { this.transpiler.emitNoSpace(s); } |
| 170 enterCodeComment() { return this.transpiler.enterCodeComment(); } |
| 171 exitCodeComment() { return this.transpiler.exitCodeComment(); } |
| 172 |
| 173 emitImport(toEmit: string) { |
| 174 if (!this.transpiler.importsEmitted[toEmit]) { |
| 175 this.pushContext(OutputContext.Import); |
| 176 this.emit(`import "${toEmit}";`); |
| 177 this.transpiler.importsEmitted[toEmit] = true; |
| 178 this.popContext(); |
| 179 } |
| 180 } |
| 181 |
23 reportError(n: ts.Node, message: string) { this.transpiler.reportError(n, mess
age); } | 182 reportError(n: ts.Node, message: string) { this.transpiler.reportError(n, mess
age); } |
24 | 183 |
25 visitNode(n: ts.Node): boolean { throw new Error('not implemented'); } | 184 visitNode(n: ts.Node): boolean { throw new Error('not implemented'); } |
26 | 185 |
27 visitEach(nodes: ts.Node[]) { nodes.forEach((n) => this.visit(n)); } | 186 visitEach(nodes: ts.Node[]) { nodes.forEach((n) => this.visit(n)); } |
28 | 187 |
29 visitEachIfPresent(nodes?: ts.Node[]) { | 188 visitEachIfPresent(nodes?: ts.Node[]) { |
30 if (nodes) this.visitEach(nodes); | 189 if (nodes) this.visitEach(nodes); |
31 } | 190 } |
32 | 191 |
33 visitList(nodes: ts.Node[], separator = ',') { | 192 visitList(nodes: ts.Node[], separator = ',') { |
34 for (let i = 0; i < nodes.length; i++) { | 193 for (let i = 0; i < nodes.length; i++) { |
35 this.visit(nodes[i]); | 194 this.visit(nodes[i]); |
36 if (i < nodes.length - 1) this.emit(separator); | 195 if (i < nodes.length - 1) this.emit(separator); |
37 } | 196 } |
38 } | 197 } |
39 | 198 |
40 uniqueId(name: string): string { | 199 uniqueId(name: string): string { |
41 const id = this.idCounter++; | 200 const id = this.idCounter++; |
42 return `_${name}\$\$ts2dart\$${id}`; | 201 return `_${name}\$\$js_facade_gen\$${id}`; |
43 } | 202 } |
44 | 203 |
45 assert(c: ts.Node, condition: boolean, reason: string): void { | 204 assert(c: ts.Node, condition: boolean, reason: string): void { |
46 if (!condition) { | 205 if (!condition) { |
47 this.reportError(c, reason); | 206 this.reportError(c, reason); |
48 throw new Error(reason); | 207 throw new Error(reason); |
49 } | 208 } |
50 } | 209 } |
51 | 210 |
52 getAncestor(n: ts.Node, kind: ts.SyntaxKind): ts.Node { | 211 getAncestor(n: ts.Node, kind: ts.SyntaxKind): ts.Node { |
53 for (let parent = n; parent; parent = parent.parent) { | 212 for (let parent = n; parent; parent = parent.parent) { |
54 if (parent.kind === kind) return parent; | 213 if (parent.kind === kind) return parent; |
55 } | 214 } |
56 return null; | 215 return null; |
57 } | 216 } |
58 | 217 |
59 hasAncestor(n: ts.Node, kind: ts.SyntaxKind): boolean { return !!this.getAnces
tor(n, kind); } | 218 hasAncestor(n: ts.Node, kind: ts.SyntaxKind): boolean { return !!getAncestor(n
, kind); } |
60 | 219 |
61 hasAnnotation(decorators: ts.NodeArray<ts.Decorator>, name: string): boolean { | 220 hasAnnotation(decorators: ts.NodeArray<ts.Decorator>, name: string): boolean { |
62 if (!decorators) return false; | 221 if (!decorators) return false; |
63 return decorators.some((d) => { | 222 return decorators.some((d) => { |
64 let decName = ident(d.expression); | 223 let decName = ident(d.expression); |
65 if (decName === name) return true; | 224 if (decName === name) return true; |
66 if (d.expression.kind !== ts.SyntaxKind.CallExpression) return false; | 225 if (d.expression.kind !== ts.SyntaxKind.CallExpression) return false; |
67 let callExpr = (<ts.CallExpression>d.expression); | 226 let callExpr = (<ts.CallExpression>d.expression); |
68 decName = ident(callExpr.expression); | 227 decName = ident(callExpr.expression); |
69 return decName === name; | 228 return decName === name; |
70 }); | 229 }); |
71 } | 230 } |
72 | 231 |
73 hasFlag(n: {flags: number}, flag: ts.NodeFlags): boolean { | 232 hasFlag(n: {flags: number}, flag: ts.NodeFlags): boolean { |
74 return n && (n.flags & flag) !== 0 || false; | 233 return n && (n.flags & flag) !== 0 || false; |
75 } | 234 } |
76 | 235 |
77 isConst(decl: ClassLike) { | |
78 return this.hasAnnotation(decl.decorators, 'CONST') || | |
79 (<ts.NodeArray<ts.Declaration>>decl.members).some((m) => { | |
80 if (m.kind !== ts.SyntaxKind.Constructor) return false; | |
81 return this.hasAnnotation(m.decorators, 'CONST'); | |
82 }); | |
83 } | |
84 | |
85 maybeDestructureIndexType(node: ts.TypeLiteralNode): [ts.TypeNode, ts.TypeNode
] { | |
86 let members = node.members; | |
87 if (members.length !== 1 || members[0].kind !== ts.SyntaxKind.IndexSignature
) { | |
88 return null; | |
89 } | |
90 let indexSig = <ts.IndexSignatureDeclaration>(members[0]); | |
91 if (indexSig.parameters.length > 1) { | |
92 this.reportError(indexSig, 'Expected an index signature to have a single p
arameter'); | |
93 } | |
94 return [indexSig.parameters[0].type, indexSig.type]; | |
95 } | |
96 | |
97 | |
98 getRelativeFileName(fileName: string): string { | 236 getRelativeFileName(fileName: string): string { |
99 return this.transpiler.getRelativeFileName(fileName); | 237 return this.transpiler.getRelativeFileName(fileName); |
100 } | 238 } |
101 | 239 |
102 maybeVisitTypeArguments(n: {typeArguments?: ts.NodeArray<ts.TypeNode>}) { | 240 maybeVisitTypeArguments(n: {typeArguments?: ts.NodeArray<ts.TypeNode>}) { |
103 if (n.typeArguments) { | 241 if (n.typeArguments) { |
104 // If it's a single type argument `<void>`, ignore it and emit nothing. | 242 // If it's a single type argument `<void>`, ignore it and emit nothing. |
105 // This is particularly useful for `Promise<void>`, see | 243 // This is particularly useful for `Promise<void>`, see |
106 // https://github.com/dart-lang/sdk/issues/2231#issuecomment-108313639 | 244 // https://github.com/dart-lang/sdk/issues/2231#issuecomment-108313639 |
107 if (n.typeArguments.length === 1 && n.typeArguments[0].kind === ts.SyntaxK
ind.VoidKeyword) { | 245 if (n.typeArguments.length === 1 && n.typeArguments[0].kind === ts.SyntaxK
ind.VoidKeyword) { |
108 return; | 246 return; |
109 } | 247 } |
110 this.emitNoSpace('<'); | 248 this.emitNoSpace('<'); |
111 this.visitList(n.typeArguments); | 249 this.visitList(n.typeArguments); |
112 this.emit('>'); | 250 this.emit('>'); |
113 } | 251 } |
114 } | 252 } |
| 253 |
| 254 visitParameters(parameters: ts.ParameterDeclaration[]) { |
| 255 this.emit('('); |
| 256 let firstInitParamIdx = 0; |
| 257 for (; firstInitParamIdx < parameters.length; firstInitParamIdx++) { |
| 258 // ObjectBindingPatterns are handled within the parameter visit. |
| 259 let isOpt = parameters[firstInitParamIdx].initializer || |
| 260 parameters[firstInitParamIdx].questionToken || |
| 261 parameters[firstInitParamIdx].dotDotDotToken; |
| 262 if (isOpt && parameters[firstInitParamIdx].name.kind !== ts.SyntaxKind.Obj
ectBindingPattern) { |
| 263 break; |
| 264 } |
| 265 } |
| 266 |
| 267 if (firstInitParamIdx !== 0) { |
| 268 let requiredParams = parameters.slice(0, firstInitParamIdx); |
| 269 this.visitList(requiredParams); |
| 270 } |
| 271 |
| 272 if (firstInitParamIdx !== parameters.length) { |
| 273 if (firstInitParamIdx !== 0) this.emit(','); |
| 274 let positionalOptional = parameters.slice(firstInitParamIdx, parameters.le
ngth); |
| 275 this.emit('['); |
| 276 this.visitList(positionalOptional); |
| 277 this.emit(']'); |
| 278 } |
| 279 |
| 280 this.emit(')'); |
| 281 } |
115 } | 282 } |
OLD | NEW |