Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(614)

Unified Diff: lib/declaration.ts

Issue 2225953002: Strip more unused features. (Closed) Base URL: git@github.com:dart-lang/js_facade_gen.git@master
Patch Set: Fix types Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/dart_libraries_for_browser_types.ts ('k') | lib/expression.ts » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/declaration.ts
diff --git a/lib/declaration.ts b/lib/declaration.ts
index 2301b102a60d8e86bfb4fa91732663205e3f2cb6..68bef4461317208b5688f5a1e4bddff8c07e549a 100644
--- a/lib/declaration.ts
+++ b/lib/declaration.ts
@@ -1,110 +1,596 @@
import * as ts from 'typescript';
+
import * as base from './base';
-import {Transpiler} from './main';
import {FacadeConverter} from './facade_converter';
+import {Transpiler} from './main';
+import {MergedParameter, MergedType} from './merge';
+
+export function isFunctionLikeProperty(
+ decl: ts.PropertyDeclaration|ts.ParameterDeclaration, tc: ts.TypeChecker): boolean {
+ if (!decl.type) return false;
+ let name = base.ident(decl.name);
+ if (name.match(/^on[A-Z]/)) return false;
+ return base.isFunctionType(decl.type, tc);
+}
export default class DeclarationTranspiler extends base.TranspilerBase {
+ private tc: ts.TypeChecker;
+
+ private moduleStack: string[] = [];
+ private extendsClass: boolean = false;
+
+ static NUM_FAKE_REST_PARAMETERS = 5;
+
+ setTypeChecker(tc: ts.TypeChecker) { this.tc = tc; }
+ setFacadeConverter(fc: FacadeConverter) { this.fc = fc; }
+
+ getJsPath(node: ts.Node): string {
+ let path = [].concat(this.moduleStack);
+ let classDecl = base.getEnclosingClass(node);
+ if (classDecl) {
+ path.push(classDecl.name.text);
+ }
+
+ switch (node.kind) {
+ case ts.SyntaxKind.ModuleDeclaration:
+ break;
+ case ts.SyntaxKind.ClassDeclaration:
+ case ts.SyntaxKind.InterfaceDeclaration:
+ path.push((<base.ClassLike>node).name.text);
+ break;
+ case ts.SyntaxKind.EnumDeclaration:
+ path.push((<ts.EnumDeclaration>node).name.text);
+ break;
+ case ts.SyntaxKind.PropertyDeclaration:
+ case ts.SyntaxKind.VariableDeclaration:
+ case ts.SyntaxKind.MethodDeclaration:
+ case ts.SyntaxKind.FunctionDeclaration:
+ case ts.SyntaxKind.GetAccessor:
+ case ts.SyntaxKind.SetAccessor:
+ case ts.SyntaxKind.PropertySignature:
+ let memberName = base.ident((<base.NamedDeclaration>node).name);
+ if (!base.isStatic(node) && classDecl != null) return memberName;
+ path.push(memberName);
+ break;
+ default:
+ throw 'Internal error. Unexpected node kind:' + node.kind;
+ }
+ if (path.length === 1) {
+ // No need to specify the path if is simply the node name.
+ return '';
+ }
+ return path.join('.');
+ }
+
+ private isAnonymousInterface(node: ts.Node): boolean {
+ if (node.kind !== ts.SyntaxKind.InterfaceDeclaration) return false;
+ // This is a bit of a hack but for the purposes of Dart codegen,
+ // interfaces with static members or constructors have a known class name
+ // at least for the purposes of resolving static members.
+ // Example case that triggers this case:
+ // interface Foo {
+ // bar();
+ // }
+ // declare let Foo: {
+ // new(): Foo,
+ // SOME_STATIC : number;
+ // }
+ return (<ts.InterfaceDeclaration>node).members.every((m: ts.Declaration) => {
+ return m.kind !== ts.SyntaxKind.Constructor && !base.isStatic(m);
+ });
+ }
+
+ maybeEmitJsAnnotation(node: ts.Node) {
+ // No need to emit the annotations as an entity outside the code comment
+ // will already have the same annotation.
+ if (this.insideCodeComment) return;
+
+ if (this.isAnonymousInterface(node)) {
+ this.emit('@anonymous');
+ this.emit('@JS()');
+ return;
+ }
+ let name: String = this.getJsPath(node);
+ this.emit('@JS(');
+ if (name.length > 0) {
+ this.emit('"' + name + '"');
+ }
+ this.emit(')');
+ }
+
+ /**
+ * Emit fake constructors to placate the Dart Analyzer for JS Interop classes.
+ */
+ maybeEmitFakeConstructors(decl: base.ClassLike) {
+ if (decl.kind === ts.SyntaxKind.ClassDeclaration) {
+ // Required to avoid spurious dart errors involving base classes without
+ // default constructors.
+ this.emit('// @Ignore\n');
+ this.fc.visitTypeName(decl.name);
+ this.emit('.fakeConstructor$()');
+ if (this.extendsClass) {
+ // Required to keep the Dart Analyzer happy when a class has subclasses.
+ this.emit(': super.fakeConstructor$()');
+ }
+ this.emit(';\n');
+ }
+ }
+
+ private visitName(name: ts.Node) {
+ if (base.getEnclosingClass(name) != null) {
+ this.visit(name);
+ return;
+ }
+ // Have to rewrite names in this case as we could have conflicts
+ // due to needing to support multiple JS modules in a single JS module
+ if (name.kind !== ts.SyntaxKind.Identifier) {
+ throw 'Internal error: unexpected function name kind:' + name.kind;
+ }
+ let entry = this.fc.lookupCustomDartTypeName(<ts.Identifier>name, this.insideCodeComment);
+ if (entry) {
+ this.emit(entry.name);
+ return;
+ }
+
+ this.visit(name);
+ }
+
+ private notSimpleBagOfProperties(type: ts.Type): boolean {
+ if (this.tc.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0) return true;
+ if (this.tc.getSignaturesOfType(type, ts.SignatureKind.Construct).length > 0) return true;
+ if (type.symbol) {
+ let declaration = <ts.InterfaceDeclaration>type.symbol.declarations[0];
+ // We have to check the actual declaration as
+ if (declaration && declaration.members) {
+ let members = declaration.members;
+ for (let i = 0; i < members.length; ++i) {
+ let node = members[i];
+ if (base.isStatic(node)) return true;
+ switch (node.kind) {
+ case ts.SyntaxKind.PropertyDeclaration:
+ case ts.SyntaxKind.PropertySignature:
+ case ts.SyntaxKind.VariableDeclaration:
+ break;
+ default:
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether all members of the class and all base classes
+ */
+ hasOnlyProperties(decl: ts.InterfaceDeclaration, outProperties: ts.PropertyDeclaration[]):
+ boolean {
+ let type = <ts.InterfaceType>this.tc.getTypeAtLocation(decl);
+
+ let properties = this.tc.getPropertiesOfType(type);
+ let baseTypes = this.tc.getBaseTypes(type);
+ if (this.notSimpleBagOfProperties(type)) return false;
+ for (let i = 0; i < baseTypes.length; ++i) {
+ let baseType = baseTypes[i];
+ if (this.notSimpleBagOfProperties(baseType)) return false;
+ }
+
+ for (let i = 0; i < properties.length; ++i) {
+ let symbol = properties[i];
+ let node = symbol.valueDeclaration;
+ switch (node.kind) {
+ case ts.SyntaxKind.PropertyDeclaration:
+ case ts.SyntaxKind.PropertySignature:
+ case ts.SyntaxKind.VariableDeclaration:
+ let prop = <ts.PropertyDeclaration>node;
+ if (this.promoteFunctionLikeMembers && isFunctionLikeProperty(prop, this.tc)) {
+ return false;
+ }
+ outProperties.push(prop);
+ break;
+ default:
+ return false;
+ }
+ }
+ return outProperties.length > 0;
+ }
+
+ visitClassBody(decl: base.ClassLike) {
+ let properties: ts.PropertyDeclaration[] = [];
+ let isPropertyBag = decl.kind === ts.SyntaxKind.InterfaceDeclaration &&
+ this.hasOnlyProperties(<ts.InterfaceDeclaration>decl, properties);
+ this.visitMergingOverloads(decl.members);
+
+ if (isPropertyBag) {
+ this.emit('external factory');
+ this.fc.visitTypeName(decl.name);
+ this.emitNoSpace('({');
+ for (let i = 0; i < properties.length; i++) {
+ if (i > 0) this.emitNoSpace(',');
+ let p = properties[i];
+ this.visit(p.type);
+ this.visit(p.name);
+ }
+ this.emitNoSpace('});');
+ }
+ }
+
+ visitMergingOverloads(members: Array<ts.Node>) {
+ // TODO(jacobr): merge method overloads.
+ let groups: {[name: string]: Array<ts.Node>} = {};
+ let orderedGroups: Array<Array<ts.Node>> = [];
+ members.forEach((node) => {
+ let name = '';
+ switch (node.kind) {
+ case ts.SyntaxKind.Block:
+ // For JS interop we always skip the contents of a block.
+ break;
+ case ts.SyntaxKind.PropertyDeclaration:
+ case ts.SyntaxKind.PropertySignature:
+ case ts.SyntaxKind.VariableDeclaration: {
+ let propertyDecl = <ts.PropertyDeclaration|ts.VariableDeclaration>node;
+ // We need to emit these as properties not fields.
+ if (!this.promoteFunctionLikeMembers || !isFunctionLikeProperty(propertyDecl, this.tc)) {
+ orderedGroups.push([node]);
+ return;
+ }
+ // Convert to a Method.
+ let type = propertyDecl.type;
+ let funcDecl = <ts.FunctionLikeDeclaration>ts.createNode(ts.SyntaxKind.MethodDeclaration);
+ funcDecl.parent = node.parent;
+ funcDecl.name = propertyDecl.name as ts.Identifier;
+ switch (type.kind) {
+ case ts.SyntaxKind.FunctionType:
+ let callSignature = <ts.SignatureDeclaration>(<ts.Node>type);
+ funcDecl.parameters = <ts.NodeArray<ts.ParameterDeclaration>>callSignature.parameters;
+ funcDecl.type = callSignature.type;
+ // Fall through to the function case using this node
+ node = funcDecl;
+ break;
+ case ts.SyntaxKind.UnionType:
+ case ts.SyntaxKind.TypeLiteral:
+ throw 'Not supported yet';
+ default:
+ throw 'Unexpected case';
+ }
+ name = base.ident((<ts.FunctionLikeDeclaration>node).name);
+ } break;
+ case ts.SyntaxKind.FunctionDeclaration:
+ case ts.SyntaxKind.MethodDeclaration:
+ case ts.SyntaxKind.MethodSignature:
+ case ts.SyntaxKind.FunctionExpression:
+ name = base.ident((<ts.FunctionLikeDeclaration>node).name);
+ break;
+ case ts.SyntaxKind.CallSignature:
+ name = 'call';
+ break;
+ case ts.SyntaxKind.Constructor:
+ break;
+ case ts.SyntaxKind.ConstructSignature:
+ break;
+ case ts.SyntaxKind.IndexSignature:
+ name = '[]';
+ break;
+ case ts.SyntaxKind.ClassDeclaration:
+ case ts.SyntaxKind.InterfaceDeclaration:
+ case ts.SyntaxKind.VariableStatement:
+ orderedGroups.push([node]);
+ return;
+ case ts.SyntaxKind.GetAccessor:
+ case ts.SyntaxKind.SetAccessor:
+ case ts.SyntaxKind.SemicolonClassElement:
+ case ts.SyntaxKind.ModuleDeclaration:
+ case ts.SyntaxKind.TypeAliasDeclaration:
+ case ts.SyntaxKind.ExportAssignment:
+ orderedGroups.push([node]);
+ return;
+ default:
+ console.log('Warning: unexpected type... overloads: ' + node.kind + ' ' + node.getText());
+ orderedGroups.push([node]);
+ return;
+ }
+ let group: Array<ts.Node>;
+ if (Object.prototype.hasOwnProperty.call(groups, name)) {
+ group = groups[name];
+ } else {
+ group = [];
+ groups[name] = group;
+ orderedGroups.push(group);
+ }
+ group.push(node);
+ });
+
+ orderedGroups.forEach((group: Array<ts.Node>) => {
+ if (group.length === 1) {
+ this.visit(group[0]);
+ return;
+ }
+ group.forEach((fn: ts.Node) => {
+ // Emit overrides in a comment that the Dart analyzer can at some point
+ // use to improve autocomplete.
+ this.maybeLineBreak();
+ this.enterCodeComment();
+ this.visit(fn);
+ this.exitCodeComment();
+ this.maybeLineBreak();
+ });
+ // TODO: actually merge.
+ let first = <ts.SignatureDeclaration>group[0];
+ let kind = first.kind;
+ let merged = <ts.SignatureDeclaration>ts.createNode(kind);
+ merged.parent = first.parent;
+ base.copyLocation(first, merged);
+ switch (kind) {
+ case ts.SyntaxKind.FunctionDeclaration:
+ case ts.SyntaxKind.MethodDeclaration:
+ case ts.SyntaxKind.MethodSignature:
+ case ts.SyntaxKind.FunctionExpression:
+ let fn = <ts.FunctionLikeDeclaration>first;
+ merged.name = fn.name;
+ break;
+ case ts.SyntaxKind.CallSignature:
+ break;
+ case ts.SyntaxKind.Constructor:
+ break;
+ case ts.SyntaxKind.ConstructSignature:
+ break;
+ case ts.SyntaxKind.IndexSignature:
+ break;
+ default:
+ throw 'Unexpected kind:' + kind;
+ }
+ let mergedParams = first.parameters.map(
+ (param: ts.ParameterDeclaration) => new MergedParameter(param, this.fc));
+ let mergedType = new MergedType(this.fc);
+ mergedType.merge(first.type);
+
+ for (let i = 1; i < group.length; ++i) {
+ let signature = <ts.SignatureDeclaration>group[i];
+ mergedType.merge(signature.type);
+ let overlap = Math.min(signature.parameters.length, mergedParams.length);
+ for (let j = 0; j < overlap; ++j) {
+ mergedParams[j].merge(signature.parameters[j]);
+ }
+ for (let j = overlap; j < mergedParams.length; ++j) {
+ mergedParams[j].setOptional();
+ }
+ for (let j = mergedParams.length; j < signature.parameters.length; ++j) {
+ let param = new MergedParameter(signature.parameters[j], this.fc);
+ param.setOptional();
+ mergedParams.push(param);
+ }
+ }
+ merged.parameters = <ts.NodeArray<ts.ParameterDeclaration>>mergedParams.map(
+ (p) => p.toParameterDeclaration());
+ merged.type = mergedType.toTypeNode();
+
+ this.fc.visit(merged);
+ });
+ }
+
+
constructor(
- tr: Transpiler, private fc: FacadeConverter, private enforceUnderscoreConventions: boolean) {
+ tr: Transpiler, private fc: FacadeConverter, private enforceUnderscoreConventions: boolean,
+ private promoteFunctionLikeMembers: boolean) {
super(tr);
}
visitNode(node: ts.Node): boolean {
switch (node.kind) {
- case ts.SyntaxKind.VariableDeclarationList:
- // Note: VariableDeclarationList can only occur as part of a for loop.
- let varDeclList = <ts.VariableDeclarationList>node;
- this.visitList(varDeclList.declarations);
+ case ts.SyntaxKind.ModuleDeclaration:
+ let moduleDecl = <ts.ModuleDeclaration>node;
+ this.emit('\n// Module ' + moduleDecl.name.text + '\n');
+ this.moduleStack.push(moduleDecl.name.text);
+
+ this.visit(moduleDecl.body);
+ this.emit('\n// End module ' + moduleDecl.name.text + '\n');
+ this.moduleStack.pop();
break;
- case ts.SyntaxKind.VariableDeclaration:
+ case ts.SyntaxKind.ExportKeyword:
+ // TODO(jacobr): perhaps add a specific Dart annotation to indicate
+ // exported members or provide a flag to only generate code for exported
+ // members.
+ break;
+ case ts.SyntaxKind.EnumDeclaration: {
+ let decl = <ts.EnumDeclaration>node;
+ // The only legal modifier for an enum decl is const.
+ let isConst = decl.modifiers && (decl.modifiers.flags & ts.NodeFlags.Const);
+ if (isConst) {
+ this.reportError(node, 'const enums are not supported');
+ }
+ // In JS interop mode we have to treat enums as JavaScript classes
+ // with static members for each enum constant instead of as first
+ // class enums.
+ this.maybeEmitJsAnnotation(decl);
+ this.emit('class');
+ this.emit(decl.name.text);
+ this.emit('{');
+ let nodes = decl.members;
+ for (let i = 0; i < nodes.length; i++) {
+ this.emit('external static num get');
+ this.visit(nodes[i]);
+ this.emitNoSpace(';');
+ }
+ this.emit('}');
+ } break;
+ case ts.SyntaxKind.Parameter: {
+ let paramDecl = <ts.ParameterDeclaration>node;
+ if (paramDecl.type && paramDecl.type.kind === ts.SyntaxKind.FunctionType) {
+ // Dart uses "returnType paramName ( parameters )" syntax.
+ let fnType = <ts.FunctionOrConstructorTypeNode>paramDecl.type;
+ let hasRestParameter = fnType.parameters.some(p => !!p.dotDotDotToken);
+ if (!hasRestParameter) {
+ // Dart does not support rest parameters/varargs, degenerate to just "Function".
+ // TODO(jacobr): also consider faking 0 - NUM_FAKE_REST_PARAMETERS
+ // instead.
+ this.visit(fnType.type);
+ this.visit(paramDecl.name);
+ this.visitParameters(fnType.parameters);
+ break;
+ }
+ }
+
+ if (paramDecl.dotDotDotToken) {
+ // Weak support of varargs that works ok if you have 5 of fewer args.
+ let paramType: ts.TypeNode;
+ let type = paramDecl.type;
+ if (type) {
+ if (type.kind === ts.SyntaxKind.ArrayType) {
+ let arrayType = <ts.ArrayTypeNode>type;
+ paramType = arrayType.elementType;
+ } else if (type.kind !== ts.SyntaxKind.AnyKeyword) {
+ throw 'Unexpected type for varargs: ' + type.kind;
+ }
+ }
+
+ for (let i = 1; i <= DeclarationTranspiler.NUM_FAKE_REST_PARAMETERS; ++i) {
+ if (i > 1) {
+ this.emitNoSpace(',');
+ }
+ this.visit(paramType);
+ this.emit(base.ident(paramDecl.name) + i);
+ }
+ break;
+ }
+ // TODO(jacobr): should we support
+ if (paramDecl.name.kind === ts.SyntaxKind.ObjectBindingPattern) {
+ this.emit('Object');
+ let pattern = paramDecl.name as ts.BindingPattern;
+ let elements = pattern.elements;
+ let name = elements.map((e) => base.ident(e.name)).join('_');
+ // Warning: this name is unlikely to but could possible overlap with
+ // other parameter names.
+ this.emit(name);
+ this.enterCodeComment();
+ this.emit(pattern.getText());
+ this.exitCodeComment();
+ break;
+ }
+
+ if (paramDecl.name.kind !== ts.SyntaxKind.Identifier) {
+ throw 'Unsupported parameter name kind: ' + paramDecl.name.kind;
+ }
+ this.visit(paramDecl.type);
+ this.visit(paramDecl.name);
+ } break;
+ case ts.SyntaxKind.EnumMember: {
+ let member = <ts.EnumMember>node;
+ this.visit(member.name);
+ } break;
+ case ts.SyntaxKind.ModuleBlock: {
+ let block = <ts.ModuleBlock>node;
+ this.visitMergingOverloads(block.statements);
+ } break;
+ case ts.SyntaxKind.VariableDeclarationList: {
+ // We have to handle variable declaration lists differently in the case
+ // of JS interop because Dart does not support external variables.
+ let varDeclList = <ts.VariableDeclarationList>node;
+ this.visitList(varDeclList.declarations, ';');
+ } break;
+ case ts.SyntaxKind.VariableDeclaration: {
+ // We have to handle variable declarations differently in the case of JS
+ // interop because Dart does not support external variables.
let varDecl = <ts.VariableDeclaration>node;
- this.visitVariableDeclarationType(varDecl);
- this.visit(varDecl.name);
- if (varDecl.initializer) {
- this.emit('=');
- this.visit(varDecl.initializer);
+ this.maybeEmitJsAnnotation(varDecl);
+ this.emit('external');
+ this.visit(varDecl.type);
+ this.emit('get');
+ this.visitName(varDecl.name);
+ if (!this.hasFlag(varDecl.parent, ts.NodeFlags.Const)) {
+ this.emitNoSpace(';');
+ this.maybeEmitJsAnnotation(varDecl);
+ this.emit('external');
+ this.emit('set');
+ this.visitName(varDecl.name);
+ this.emitNoSpace('(');
+ this.visit(varDecl.type);
+ this.emit('v)');
}
+ } break;
+ case ts.SyntaxKind.StringLiteral: {
+ this.emit('String');
+ this.enterCodeComment();
+ let sLit = <ts.LiteralExpression>node;
+ let text = JSON.stringify(sLit.text);
+ this.emit(text);
+ this.exitCodeComment();
+ } break;
+ case ts.SyntaxKind.CallSignature: {
+ let fn = <ts.SignatureDeclaration>node;
+ this.emit('external');
+ this.visit(fn.type);
+ this.emit('call');
+ this.visitParameters(fn.parameters);
+ this.emitNoSpace(';');
+ } break;
+ case ts.SyntaxKind.IndexSignature:
+ this.emit('/* Index signature is not yet supported by JavaScript interop. */\n');
+ break;
+ case ts.SyntaxKind.ExportAssignment:
+ // let exportAssignment = <ts.ExportAssignment>node;
+ this.emit('/* WARNING: export assignment not yet supported. */\n');
+ break;
+ case ts.SyntaxKind.TypeAliasDeclaration:
+ // Dart does not provide special syntax for definning type alais
+ // declarations so we do not emit anything here and resolve alaises
+ // to their original types at each usage site.
break;
-
case ts.SyntaxKind.ClassDeclaration:
- let classDecl = <ts.ClassDeclaration>node;
- if (classDecl.modifiers && (classDecl.modifiers.flags & ts.NodeFlags.Abstract)) {
+ case ts.SyntaxKind.InterfaceDeclaration: {
+ this.extendsClass = false;
+ let classDecl = <ts.ClassDeclaration|ts.InterfaceDeclaration>node;
+ let isInterface = node.kind === ts.SyntaxKind.InterfaceDeclaration;
+ if (isInterface &&
+ base.isFunctionTypedefLikeInterface(classDecl as ts.InterfaceDeclaration)) {
+ let member = <ts.CallSignatureDeclaration>classDecl.members[0];
+ this.visitFunctionTypedefInterface(classDecl.name.text, member, classDecl.typeParameters);
+ break;
+ }
+
+ let customName = this.fc.lookupCustomDartTypeName(classDecl.name, this.insideCodeComment);
+ if (customName && !customName.keep) {
+ this.emit('\n/* Skipping class ' + base.ident(classDecl.name) + '*/\n');
+ break;
+ }
+ this.maybeEmitJsAnnotation(node);
+
+ if (isInterface ||
+ (classDecl.modifiers && (classDecl.modifiers.flags & ts.NodeFlags.Abstract))) {
this.visitClassLike('abstract class', classDecl);
} else {
this.visitClassLike('class', classDecl);
}
- break;
- case ts.SyntaxKind.InterfaceDeclaration:
- let ifDecl = <ts.InterfaceDeclaration>node;
- // Function type interface in an interface with a single declaration
- // of a call signature (http://goo.gl/ROC5jN).
- if (ifDecl.members.length === 1 && ifDecl.members[0].kind === ts.SyntaxKind.CallSignature) {
- let member = <ts.CallSignatureDeclaration>ifDecl.members[0];
- this.visitFunctionTypedefInterface(ifDecl.name.text, member, ifDecl.typeParameters);
- } else {
- this.visitClassLike('abstract class', ifDecl);
- }
- break;
- case ts.SyntaxKind.HeritageClause:
+ } break;
+ case ts.SyntaxKind.HeritageClause: {
let heritageClause = <ts.HeritageClause>node;
- if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword &&
- heritageClause.parent.kind !== ts.SyntaxKind.InterfaceDeclaration) {
+ if (base.isExtendsClause(<ts.HeritageClause>heritageClause)) {
+ this.extendsClass = true;
+ }
+
+ if (base.isExtendsClause(heritageClause)) {
this.emit('extends');
} else {
this.emit('implements');
}
// Can only have one member for extends clauses.
this.visitList(heritageClause.types);
- break;
- case ts.SyntaxKind.ExpressionWithTypeArguments:
+ } break;
+ case ts.SyntaxKind.ExpressionWithTypeArguments: {
let exprWithTypeArgs = <ts.ExpressionWithTypeArguments>node;
this.visit(exprWithTypeArgs.expression);
this.maybeVisitTypeArguments(exprWithTypeArgs);
- break;
- case ts.SyntaxKind.EnumDeclaration:
- let decl = <ts.EnumDeclaration>node;
- // The only legal modifier for an enum decl is const.
- let isConst = decl.modifiers && (decl.modifiers.flags & ts.NodeFlags.Const);
- if (isConst) {
- this.reportError(node, 'const enums are not supported');
- }
- this.emit('enum');
- this.fc.visitTypeName(decl.name);
- this.emit('{');
- // Enums can be empty in TS ...
- if (decl.members.length === 0) {
- // ... but not in Dart.
- this.reportError(node, 'empty enums are not supported');
- }
- this.visitList(decl.members);
- this.emit('}');
- break;
- case ts.SyntaxKind.EnumMember:
- let member = <ts.EnumMember>node;
- this.visit(member.name);
- if (member.initializer) {
- this.reportError(node, 'enum initializers are not supported');
- }
- break;
+ } break;
case ts.SyntaxKind.Constructor:
+ case ts.SyntaxKind.ConstructSignature: {
let ctorDecl = <ts.ConstructorDeclaration>node;
// Find containing class name.
- let className: ts.Identifier;
- for (let parent = ctorDecl.parent; parent; parent = parent.parent) {
- if (parent.kind === ts.SyntaxKind.ClassDeclaration) {
- className = (<ts.ClassDeclaration>parent).name;
- break;
- }
- }
- if (!className) this.reportError(ctorDecl, 'cannot find outer class node');
+ let classDecl = base.getEnclosingClass(ctorDecl);
+ if (!classDecl) this.reportError(ctorDecl, 'cannot find outer class node');
this.visitDeclarationMetadata(ctorDecl);
- if (this.isConst(<base.ClassLike>ctorDecl.parent)) {
- this.emit('const');
- }
- this.visit(className);
+ this.fc.visitTypeName(classDecl.name);
this.visitParameters(ctorDecl.parameters);
- this.visit(ctorDecl.body);
- break;
+ this.emitNoSpace(';');
+ } break;
case ts.SyntaxKind.PropertyDeclaration:
this.visitProperty(<ts.PropertyDeclaration>node);
break;
@@ -125,22 +611,9 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
break;
case ts.SyntaxKind.FunctionDeclaration:
let funcDecl = <ts.FunctionDeclaration>node;
- this.visitDecorators(funcDecl.decorators);
+ this.visitDeclarationMetadata(funcDecl);
this.visitFunctionLike(funcDecl);
break;
- case ts.SyntaxKind.ArrowFunction:
- let arrowFunc = <ts.FunctionExpression>node;
- // Dart only allows expressions following the fat arrow operator.
- // If the body is a block, we have to drop the fat arrow and emit an
- // anonymous function instead.
- if (arrowFunc.body.kind === ts.SyntaxKind.Block) {
- this.visitFunctionLike(arrowFunc);
- } else {
- this.visitParameters(arrowFunc.parameters);
- this.emit('=>');
- this.visit(arrowFunc.body);
- }
- break;
case ts.SyntaxKind.FunctionExpression:
let funcExpr = <ts.FunctionExpression>node;
this.visitFunctionLike(funcExpr);
@@ -151,56 +624,11 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
break;
case ts.SyntaxKind.MethodSignature:
let methodSignatureDecl = <ts.FunctionLikeDeclaration>node;
- this.visitEachIfPresent(methodSignatureDecl.modifiers);
+ this.visitDeclarationMetadata(methodSignatureDecl);
this.visitFunctionLike(methodSignatureDecl);
break;
- case ts.SyntaxKind.Parameter:
- let paramDecl = <ts.ParameterDeclaration>node;
- // Property parameters will have an explicit property declaration, so we just
- // need the dart assignment shorthand to reference the property.
- if (this.hasFlag(paramDecl.modifiers, ts.NodeFlags.Public) ||
- this.hasFlag(paramDecl.modifiers, ts.NodeFlags.Private) ||
- this.hasFlag(paramDecl.modifiers, ts.NodeFlags.Protected)) {
- this.visitDeclarationMetadata(paramDecl);
- this.emit('this .');
- this.visit(paramDecl.name);
- if (paramDecl.initializer) {
- this.emit('=');
- this.visit(paramDecl.initializer);
- }
- break;
- }
- if (paramDecl.dotDotDotToken) this.reportError(node, 'rest parameters are unsupported');
- if (paramDecl.name.kind === ts.SyntaxKind.ObjectBindingPattern) {
- this.visitNamedParameter(paramDecl);
- break;
- }
- this.visitDecorators(paramDecl.decorators);
-
- if (paramDecl.type && paramDecl.type.kind === ts.SyntaxKind.FunctionType) {
- // Dart uses "returnType paramName ( parameters )" syntax.
- let fnType = <ts.FunctionOrConstructorTypeNode>paramDecl.type;
- let hasRestParameter = fnType.parameters.some(p => !!p.dotDotDotToken);
- if (hasRestParameter) {
- // Dart does not support rest parameters/varargs, degenerate to just "Function".
- this.emit('Function');
- this.visit(paramDecl.name);
- } else {
- this.visit(fnType.type);
- this.visit(paramDecl.name);
- this.visitParameters(fnType.parameters);
- }
- } else {
- if (paramDecl.type) this.visit(paramDecl.type);
- this.visit(paramDecl.name);
- }
- if (paramDecl.initializer) {
- this.emit('=');
- this.visit(paramDecl.initializer);
- }
- break;
case ts.SyntaxKind.StaticKeyword:
- this.emit('static');
+ // n-op, handled in `visitFunctionLike` and `visitProperty` below.
break;
case ts.SyntaxKind.AbstractKeyword:
// Abstract methods in Dart simply lack implementation,
@@ -216,77 +644,55 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
case ts.SyntaxKind.ProtectedKeyword:
// Handled in `visitDeclarationMetadata` below.
break;
-
+ case ts.SyntaxKind.VariableStatement:
+ let variableStmt = <ts.VariableStatement>node;
+ this.visit(variableStmt.declarationList);
+ this.emitNoSpace(';');
+ break;
+ case ts.SyntaxKind.SwitchStatement:
+ case ts.SyntaxKind.ArrayLiteralExpression:
+ case ts.SyntaxKind.ExpressionStatement:
+ case ts.SyntaxKind.EmptyStatement:
+ // No need to emit anything for these cases.
+ break;
default:
return false;
}
return true;
}
- private visitVariableDeclarationType(varDecl: ts.VariableDeclaration) {
- /* Note: VariableDeclarationList can only occur as part of a for loop. This helper method
- * is meant for processing for-loop variable declaration types only.
- *
- * In Dart, all variables in a variable declaration list must have the same type. Since
- * we are doing syntax directed translation, we cannot reliably determine if distinct
- * variables are declared with the same type or not. Hence we support the following cases:
- *
- * - A variable declaration list with a single variable can be explicitly typed.
- * - When more than one variable is in the list, all must be implicitly typed.
- */
- let firstDecl = varDecl.parent.declarations[0];
- let msg = 'Variables in a declaration list of more than one variable cannot by typed';
- let isFinal = this.hasFlag(varDecl.parent, ts.NodeFlags.Const);
- let isConst = false;
- if (isFinal && varDecl.initializer) {
- // "const" in TypeScript/ES6 corresponds to "final" in Dart, i.e. reference constness.
- // If a "const" variable is immediately initialized to a CONST_EXPR(), special case it to be
- // a deeply const constant, and generate "const ...".
- isConst = varDecl.initializer.kind === ts.SyntaxKind.StringLiteral ||
- varDecl.initializer.kind === ts.SyntaxKind.NumericLiteral ||
- this.fc.isConstCall(varDecl.initializer);
- }
- if (firstDecl === varDecl) {
- if (isConst) {
- this.emit('const');
- } else if (isFinal) {
- this.emit('final');
- }
- if (!varDecl.type) {
- if (!isFinal) this.emit('var');
- } else if (varDecl.parent.declarations.length > 1) {
- this.reportError(varDecl, msg);
- } else {
- this.visit(varDecl.type);
- }
- } else if (varDecl.type) {
- this.reportError(varDecl, msg);
- }
- }
-
private visitFunctionLike(fn: ts.FunctionLikeDeclaration, accessor?: string) {
this.fc.pushTypeParameterNames(fn);
+ if (base.isStatic(fn)) {
+ this.emit('static');
+ }
+
try {
- if (fn.type) {
- if (fn.kind === ts.SyntaxKind.ArrowFunction ||
- fn.kind === ts.SyntaxKind.FunctionExpression) {
- // The return type is silently dropped for function expressions (including arrow
- // functions), it is not supported in Dart.
- this.emit('/*');
- this.visit(fn.type);
- this.emit('*/');
- } else {
- this.visit(fn.type);
+ this.visit(fn.type);
+ if (accessor) this.emit(accessor);
+ let name = fn.name;
+ if (name) {
+ if (name.kind !== ts.SyntaxKind.Identifier) {
+ this.reportError(name, 'Unexpected name kind:' + name.kind);
}
+ this.fc.visitTypeName(<ts.Identifier>name);
}
- if (accessor) this.emit(accessor);
- if (fn.name) this.visit(fn.name);
+
if (fn.typeParameters) {
- this.emit('/*<');
+ let insideComment = this.insideCodeComment;
+ if (!insideComment) {
+ this.enterCodeComment();
+ }
+ this.emitNoSpace('<');
// Emit the names literally instead of visiting, otherwise they will be replaced with the
// comment hack themselves.
- this.emit(fn.typeParameters.map(p => base.ident(p.name)).join(', '));
- this.emit('>*/');
+ // TODO(jacobr): we can use the regular type parameter visiting pattern
+ // now that we properly track whether we are inside a comment.
+ this.emitNoSpace(fn.typeParameters.map(p => base.ident(p.name)).join(', '));
+ this.emitNoSpace('>');
+ if (!insideComment) {
+ this.exitCodeComment();
+ }
}
// Dart does not even allow the parens of an empty param list on getter
if (accessor !== 'get') {
@@ -296,77 +702,42 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
this.reportError(fn, 'getter should not accept parameters');
}
}
- if (fn.body) {
- this.visit(fn.body);
- } else {
- this.emit(';');
- }
+ this.emitNoSpace(';');
} finally {
this.fc.popTypeParameterNames(fn);
}
}
- private 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;
- 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(')');
- }
-
/**
* Visit a property declaration.
+ * In the special case of property parameters in a constructor, we also allow
+ * a parameter to be emitted as a property.
+ * We have to emit properties as getter setter pairs as Dart does not support
+ * external fields.
* In the special case of property parameters in a constructor, we also allow a parameter to be
* emitted as a property.
*/
private visitProperty(decl: ts.PropertyDeclaration|ts.ParameterDeclaration, isParameter = false) {
- if (!isParameter) this.visitDeclarationMetadata(decl);
- let containingClass = <base.ClassLike>(isParameter ? decl.parent.parent : decl.parent);
- let isConstField = this.hasAnnotation(decl.decorators, 'CONST');
- let hasConstCtor = this.isConst(containingClass);
- if (isConstField) {
- // const implies final
- this.emit('const');
- } else {
- if (hasConstCtor) {
- this.emit('final');
- }
- }
- if (decl.type) {
- this.visit(decl.type);
- } else if (!isConstField && !hasConstCtor) {
- this.emit('var');
- }
- this.visit(decl.name);
- if (decl.initializer && !isParameter) {
- this.emit('=');
- this.visit(decl.initializer);
- }
- this.emit(';');
+ let isStatic = base.isStatic(decl);
+ this.emit('external');
+ if (isStatic) this.emit('static');
+ this.visit(decl.type);
+ this.emit('get');
+ this.visitName(decl.name);
+ this.emitNoSpace(';');
+
+ this.emit('external');
+ if (isStatic) this.emit('static');
+ this.emit('set');
+ this.visitName(decl.name);
+ this.emitNoSpace('(');
+ this.visit(decl.type);
+ this.emit('v');
+ this.emitNoSpace(')');
+ this.emitNoSpace(';');
}
private visitClassLike(keyword: string, decl: base.ClassLike) {
- this.visitDecorators(decl.decorators);
this.emit(keyword);
this.fc.visitTypeName(decl.name);
if (decl.typeParameters) {
@@ -374,9 +745,12 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
this.visitList(decl.typeParameters);
this.emit('>');
}
+
this.visitEachIfPresent(decl.heritageClauses);
this.emit('{');
+ this.maybeEmitFakeConstructors(decl);
+
// Synthesize explicit properties for ctor with 'property parameters'
let synthesizePropertyParam = (param: ts.ParameterDeclaration) => {
if (this.hasFlag(param.modifiers, ts.NodeFlags.Public) ||
@@ -386,108 +760,40 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
this.visitProperty(param, true);
}
};
- (<ts.NodeArray<ts.Declaration>>decl.members)
- .filter((m) => m.kind === ts.SyntaxKind.Constructor)
+ (decl.members as ts.NodeArray<ts.Declaration>)
+ .filter(base.isConstructor)
.forEach(
(ctor) =>
(<ts.ConstructorDeclaration>ctor).parameters.forEach(synthesizePropertyParam));
- this.visitEachIfPresent(decl.members);
- // Generate a constructor to host the const modifier, if needed
- if (this.isConst(decl) &&
- !(<ts.NodeArray<ts.Declaration>>decl.members)
- .some((m) => m.kind === ts.SyntaxKind.Constructor)) {
- this.emit('const');
- this.fc.visitTypeName(decl.name);
- this.emit('();');
- }
+ this.visitClassBody(decl);
this.emit('}');
}
- private visitDecorators(decorators: ts.NodeArray<ts.Decorator>) {
- if (!decorators) return;
-
- decorators.forEach((d) => {
- // Special case @CONST
- let name = base.ident(d.expression);
- if (!name && d.expression.kind === ts.SyntaxKind.CallExpression) {
- // Unwrap @CONST()
- let callExpr = (<ts.CallExpression>d.expression);
- name = base.ident(callExpr.expression);
- }
- // Make sure these match IGNORED_ANNOTATIONS below.
- if (name === 'CONST') {
- // Ignore @CONST - it is handled above in visitClassLike.
- return;
- }
- this.emit('@');
- this.visit(d.expression);
- });
- }
-
private visitDeclarationMetadata(decl: ts.Declaration) {
- this.visitDecorators(decl.decorators);
this.visitEachIfPresent(decl.modifiers);
- if (this.hasFlag(decl.modifiers, ts.NodeFlags.Protected)) {
- this.reportError(decl, 'protected declarations are unsupported');
- return;
- }
- if (!this.enforceUnderscoreConventions) return;
- // Early return in case this is a decl with no name, such as a constructor
- if (!decl.name) return;
- let name = base.ident(decl.name);
- if (!name) return;
- let isPrivate = this.hasFlag(decl.modifiers, ts.NodeFlags.Private);
- let matchesPrivate = !!name.match(/^_/);
- if (isPrivate && !matchesPrivate) {
- this.reportError(decl, 'private members must be prefixed with "_"');
- }
- if (!isPrivate && matchesPrivate) {
- this.reportError(decl, 'public members must not be prefixed with "_"');
- }
- }
-
- private visitNamedParameter(paramDecl: ts.ParameterDeclaration) {
- this.visitDecorators(paramDecl.decorators);
- let bp = <ts.BindingPattern>paramDecl.name;
- let propertyTypes = this.fc.resolvePropertyTypes(paramDecl.type);
- let initMap = this.getInitializers(paramDecl);
- this.emit('{');
- for (let i = 0; i < bp.elements.length; i++) {
- let elem = bp.elements[i];
- let propDecl = propertyTypes[base.ident(elem.name)];
- if (propDecl && propDecl.type) this.visit(propDecl.type);
- this.visit(elem.name);
- if (elem.initializer && initMap[base.ident(elem.name)]) {
- this.reportError(elem, 'cannot have both an inner and outer initializer');
- }
- let init = elem.initializer || initMap[base.ident(elem.name)];
- if (init) {
- this.emit(':');
- this.visit(init);
- }
- if (i + 1 < bp.elements.length) this.emit(',');
- }
- this.emit('}');
- }
-
- private getInitializers(paramDecl: ts.ParameterDeclaration) {
- let res: ts.Map<ts.Expression> = {};
- if (!paramDecl.initializer) return res;
- if (paramDecl.initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
- this.reportError(paramDecl, 'initializers for named parameters must be object literals');
- return res;
- }
- for (let i of (<ts.ObjectLiteralExpression>paramDecl.initializer).properties) {
- if (i.kind !== ts.SyntaxKind.PropertyAssignment) {
- this.reportError(i, 'named parameter initializers must be properties, got ' + i.kind);
- continue;
- }
- let ole = <ts.PropertyAssignment>i;
- res[base.ident(ole.name)] = ole.initializer;
+ switch (decl.kind) {
+ case ts.SyntaxKind.Constructor:
+ case ts.SyntaxKind.ConstructSignature:
+ this.emit('external factory');
+ break;
+ case ts.SyntaxKind.ArrowFunction:
+ case ts.SyntaxKind.CallSignature:
+ case ts.SyntaxKind.MethodDeclaration:
+ case ts.SyntaxKind.SetAccessor:
+ case ts.SyntaxKind.GetAccessor:
+ case ts.SyntaxKind.MethodSignature:
+ case ts.SyntaxKind.PropertySignature:
+ case ts.SyntaxKind.FunctionDeclaration:
+ if (!base.getEnclosingClass(decl)) {
+ this.maybeEmitJsAnnotation(decl);
+ }
+ this.emit('external');
+ break;
+ default:
+ throw 'Unexpected declaration kind:' + decl.kind;
}
- return res;
}
/**
@@ -503,11 +809,11 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
}
this.emit(name);
if (typeParameters) {
- this.emit('<');
+ this.emitNoSpace('<');
this.visitList(typeParameters);
- this.emit('>');
+ this.emitNoSpace('>');
}
this.visitParameters(signature.parameters);
- this.emit(';');
+ this.emitNoSpace(';');
}
}
« no previous file with comments | « lib/dart_libraries_for_browser_types.ts ('k') | lib/expression.ts » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698