Index: lib/base.ts |
diff --git a/lib/base.ts b/lib/base.ts |
index 709b28a71c87512052b34ed04d3e120a32cec133..55799bbfbf8fd6e700a1a0ebb0cfe5f12cb4e6c0 100644 |
--- a/lib/base.ts |
+++ b/lib/base.ts |
@@ -1,7 +1,14 @@ |
import * as ts from 'typescript'; |
-import {Transpiler} from './main'; |
+ |
+import {OutputContext, Transpiler} from './main'; |
export type ClassLike = ts.ClassDeclaration | ts.InterfaceDeclaration; |
+export type NamedDeclaration = ClassLike | ts.PropertyDeclaration | ts.VariableDeclaration | |
+ ts.MethodDeclaration | ts.ModuleDeclaration | ts.FunctionDeclaration; |
+ |
+export type Set = { |
+ [s: string]: boolean |
+}; |
export function ident(n: ts.Node): string { |
if (n.kind === ts.SyntaxKind.Identifier) return (<ts.Identifier>n).text; |
@@ -13,13 +20,165 @@ export function ident(n: ts.Node): string { |
return null; |
} |
+export function isFunctionTypedefLikeInterface(ifDecl: ts.InterfaceDeclaration): boolean { |
+ return ifDecl.members && ifDecl.members.length === 1 && |
+ ifDecl.members[0].kind === ts.SyntaxKind.CallSignature; |
+} |
+ |
+export function getDeclaration(type: ts.Type): ts.Declaration { |
+ let symbol = type.getSymbol(); |
+ if (!symbol) return null; |
+ if (symbol.valueDeclaration) return symbol.valueDeclaration; |
+ return symbol.declarations && symbol.declarations.length > 0 ? symbol.declarations[0] : null; |
+} |
+ |
+export function isExtendsClause(heritageClause: ts.HeritageClause) { |
+ return heritageClause.token === ts.SyntaxKind.ExtendsKeyword && |
+ heritageClause.parent.kind !== ts.SyntaxKind.InterfaceDeclaration; |
+} |
+export function isConstructor(n: ts.Node): boolean { |
+ return n.kind === ts.SyntaxKind.Constructor || n.kind === ts.SyntaxKind.ConstructSignature; |
+} |
+ |
+export function isStatic(n: ts.Node): boolean { |
+ let hasStatic = false; |
+ ts.forEachChild(n, (child) => { |
+ if (child.kind === ts.SyntaxKind.StaticKeyword) { |
+ hasStatic = true; |
+ } |
+ }); |
+ return hasStatic; |
+} |
+ |
+export function isCallableType(type: ts.TypeNode, tc: ts.TypeChecker): boolean { |
+ if (isFunctionType(type, tc)) return true; |
+ if (type.kind === ts.SyntaxKind.TypeReference) { |
+ if (tc.getSignaturesOfType(tc.getTypeAtLocation(type), ts.SignatureKind.Call).length > 0) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+export function isFunctionType(type: ts.TypeNode, tc: ts.TypeChecker): boolean { |
+ let kind = type.kind; |
+ if (kind === ts.SyntaxKind.FunctionType) return true; |
+ if (kind === ts.SyntaxKind.TypeReference) { |
+ let t = tc.getTypeAtLocation(type); |
+ if (t.symbol && t.symbol.flags & ts.SymbolFlags.Function) return true; |
+ } |
+ |
+ if (kind === ts.SyntaxKind.UnionType) { |
+ let types = (<ts.UnionTypeNode>type).types; |
+ for (let i = 0; i < types.length; ++i) { |
+ if (!isFunctionType(types[i], tc)) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ // Warning: if the kind is a reference type and the reference is to an |
+ // interface that only has a call member we will not return that it is a |
+ // function type. |
+ if (kind === ts.SyntaxKind.TypeLiteral) { |
+ let members = (<ts.TypeLiteralNode>type).members; |
+ for (let i = 0; i < members.length; ++i) { |
+ if (members[i].kind !== ts.SyntaxKind.CallSignature) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+export function isTypeNode(node: ts.Node): boolean { |
+ switch (node.kind) { |
+ case ts.SyntaxKind.UnionType: |
+ case ts.SyntaxKind.TypeReference: |
+ case ts.SyntaxKind.TypeLiteral: |
+ case ts.SyntaxKind.LastTypeNode: |
+ case ts.SyntaxKind.ArrayType: |
+ case ts.SyntaxKind.TypePredicate: |
+ case ts.SyntaxKind.TypeQuery: |
+ case ts.SyntaxKind.TupleType: |
+ case ts.SyntaxKind.NumberKeyword: |
+ case ts.SyntaxKind.StringKeyword: |
+ case ts.SyntaxKind.VoidKeyword: |
+ case ts.SyntaxKind.BooleanKeyword: |
+ case ts.SyntaxKind.AnyKeyword: |
+ case ts.SyntaxKind.FunctionType: |
+ return true; |
+ default: |
+ return false; |
+ } |
+} |
+ |
+export function isCallable(decl: ClassLike): boolean { |
+ let members = decl.members as Array<ts.ClassElement>; |
+ return members.some((member) => { return member.kind === ts.SyntaxKind.CallSignature; }); |
+} |
+ |
+export function copyLocation(src: ts.TextRange, dest: ts.TextRange) { |
+ dest.pos = src.pos; |
+ dest.end = src.end; |
+} |
+ |
+// Polyfill for ES6 Array.find. |
+export function arrayFindPolyfill<T>( |
+ nodeArray: ts.NodeArray<T>, predicate: (node: T) => boolean): T { |
+ for (let i = 0; i < nodeArray.length; ++i) { |
+ if (predicate(nodeArray[i])) return nodeArray[i]; |
+ } |
+ return null; |
+} |
+ |
+export function getAncestor(n: ts.Node, kind: ts.SyntaxKind): ts.Node { |
+ for (let parent = n; parent; parent = parent.parent) { |
+ if (parent.kind === kind) return parent; |
+ } |
+ return null; |
+} |
+ |
+export function getEnclosingClass(n: ts.Node): ClassLike { |
+ for (let parent = n.parent; parent; parent = parent.parent) { |
+ if (parent.kind === ts.SyntaxKind.ClassDeclaration || |
+ parent.kind === ts.SyntaxKind.InterfaceDeclaration) { |
+ return <ClassLike>parent; |
+ } |
+ } |
+ return null; |
+} |
+ |
+export function isConstCall(node: ts.CallExpression): boolean { |
+ return node && ident(node.expression) === 'CONST_EXPR'; |
+} |
+ |
+export function isInsideConstExpr(node: ts.Node): boolean { |
+ return isConstCall(<ts.CallExpression>getAncestor(node, ts.SyntaxKind.CallExpression)); |
+} |
+ |
+ |
export class TranspilerBase { |
private idCounter: number = 0; |
- constructor(private transpiler: Transpiler) {} |
+ constructor(protected transpiler: Transpiler) {} |
visit(n: ts.Node) { this.transpiler.visit(n); } |
+ pushContext(context: OutputContext) { this.transpiler.pushContext(context); } |
+ popContext() { this.transpiler.popContext(); } |
emit(s: string) { this.transpiler.emit(s); } |
emitNoSpace(s: string) { this.transpiler.emitNoSpace(s); } |
+ enterCodeComment() { return this.transpiler.enterCodeComment(); } |
+ exitCodeComment() { return this.transpiler.exitCodeComment(); } |
+ |
+ emitImport(toEmit: string) { |
+ if (!this.transpiler.importsEmitted[toEmit]) { |
+ this.pushContext(OutputContext.Import); |
+ this.emit(`import "${toEmit}";`); |
+ this.transpiler.importsEmitted[toEmit] = true; |
+ this.popContext(); |
+ } |
+ } |
+ |
reportError(n: ts.Node, message: string) { this.transpiler.reportError(n, message); } |
visitNode(n: ts.Node): boolean { throw new Error('not implemented'); } |
@@ -39,7 +198,7 @@ export class TranspilerBase { |
uniqueId(name: string): string { |
const id = this.idCounter++; |
- return `_${name}\$\$ts2dart\$${id}`; |
+ return `_${name}\$\$js_facade_gen\$${id}`; |
} |
assert(c: ts.Node, condition: boolean, reason: string): void { |
@@ -56,7 +215,7 @@ export class TranspilerBase { |
return null; |
} |
- hasAncestor(n: ts.Node, kind: ts.SyntaxKind): boolean { return !!this.getAncestor(n, kind); } |
+ hasAncestor(n: ts.Node, kind: ts.SyntaxKind): boolean { return !!getAncestor(n, kind); } |
hasAnnotation(decorators: ts.NodeArray<ts.Decorator>, name: string): boolean { |
if (!decorators) return false; |
@@ -74,27 +233,6 @@ export class TranspilerBase { |
return n && (n.flags & flag) !== 0 || false; |
} |
- isConst(decl: ClassLike) { |
- return this.hasAnnotation(decl.decorators, 'CONST') || |
- (<ts.NodeArray<ts.Declaration>>decl.members).some((m) => { |
- if (m.kind !== ts.SyntaxKind.Constructor) return false; |
- return this.hasAnnotation(m.decorators, 'CONST'); |
- }); |
- } |
- |
- maybeDestructureIndexType(node: ts.TypeLiteralNode): [ts.TypeNode, ts.TypeNode] { |
- let members = node.members; |
- if (members.length !== 1 || members[0].kind !== ts.SyntaxKind.IndexSignature) { |
- return null; |
- } |
- let indexSig = <ts.IndexSignatureDeclaration>(members[0]); |
- if (indexSig.parameters.length > 1) { |
- this.reportError(indexSig, 'Expected an index signature to have a single parameter'); |
- } |
- return [indexSig.parameters[0].type, indexSig.type]; |
- } |
- |
- |
getRelativeFileName(fileName: string): string { |
return this.transpiler.getRelativeFileName(fileName); |
} |
@@ -112,4 +250,33 @@ export class TranspilerBase { |
this.emit('>'); |
} |
} |
+ |
+ visitParameters(parameters: ts.ParameterDeclaration[]) { |
+ this.emit('('); |
+ let firstInitParamIdx = 0; |
+ for (; firstInitParamIdx < parameters.length; firstInitParamIdx++) { |
+ // ObjectBindingPatterns are handled within the parameter visit. |
+ let isOpt = parameters[firstInitParamIdx].initializer || |
+ parameters[firstInitParamIdx].questionToken || |
+ parameters[firstInitParamIdx].dotDotDotToken; |
+ if (isOpt && parameters[firstInitParamIdx].name.kind !== ts.SyntaxKind.ObjectBindingPattern) { |
+ break; |
+ } |
+ } |
+ |
+ if (firstInitParamIdx !== 0) { |
+ let requiredParams = parameters.slice(0, firstInitParamIdx); |
+ this.visitList(requiredParams); |
+ } |
+ |
+ if (firstInitParamIdx !== parameters.length) { |
+ if (firstInitParamIdx !== 0) this.emit(','); |
+ let positionalOptional = parameters.slice(firstInitParamIdx, parameters.length); |
+ this.emit('['); |
+ this.visitList(positionalOptional); |
+ this.emit(']'); |
+ } |
+ |
+ this.emit(')'); |
+ } |
} |