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

Unified Diff: lib/merge.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/main.ts ('k') | lib/module.ts » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/merge.ts
diff --git a/lib/merge.ts b/lib/merge.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0b9c8d746496b1608dd7eb08d7b35a25735520ed
--- /dev/null
+++ b/lib/merge.ts
@@ -0,0 +1,322 @@
+import ts = require('typescript');
+import base = require('./base');
+import {FacadeConverter} from './facade_converter';
+
+/**
+ * To support arbitrary d.ts files in Dart we often have to merge two TypeScript
+ * types into a single Dart type because Dart lacks features such as method
+ * overloads, type aliases, and union types.
+ */
+export class MergedType {
+ constructor(private fc: FacadeConverter) {}
+
+ merge(t?: ts.TypeNode) {
+ if (t) {
+ // TODO(jacobr): get a better unique name for a type.
+ switch (t.kind) {
+ case ts.SyntaxKind.UnionType:
+ let union = <ts.UnionTypeNode>t;
+ union.types.forEach(this.merge.bind(this));
+ return;
+ case ts.SyntaxKind.LastTypeNode:
+ this.merge((t as ts.ParenthesizedTypeNode).type);
+ return;
+ case ts.SyntaxKind.TypePredicate:
+ this.merge((t as ts.TypePredicateNode).type);
+ return;
+ case ts.SyntaxKind.TypeReference:
+ // We need to follow Alais types as Dart does not support them for non
+ // function types. TODO(jacobr): handle them for Function types?
+ let typeRef = <ts.TypeReferenceNode>t;
+ let decl = this.fc.getDeclaration(typeRef.typeName);
+ if (decl !== null && decl.kind === ts.SyntaxKind.TypeAliasDeclaration) {
+ let alias = <ts.TypeAliasDeclaration>decl;
+
+ if (typeRef.typeArguments) {
+ throw 'TypeReference with arguements not supported yet:' + t.getText();
+ }
+
+ this.merge(alias.type);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ this.types[this.fc.generateDartTypeName(t, true)] = t;
+ }
+ }
+
+ toTypeNode(): ts.TypeNode {
+ let names = Object.getOwnPropertyNames(this.types);
+ if (names.length === 0) {
+ return null;
+ }
+ if (names.length === 1) {
+ return this.types[names[0]];
+ }
+ let union = <ts.UnionTypeNode>ts.createNode(ts.SyntaxKind.UnionType);
+ base.copyLocation(this.types[names[0]], union);
+
+ union.types = <ts.NodeArray<ts.TypeNode>>[];
+ for (let i = 0; i < names.length; ++i) {
+ union.types.push(this.types[names[i]]);
+ }
+ return union;
+ }
+
+ private types: {[name: string]: ts.TypeNode} = {};
+}
+
+/**
+ * Handle a parameter that is the result of merging parameter declarations from
+ * multiple method overloads.
+ */
+export class MergedParameter {
+ constructor(param: ts.ParameterDeclaration, fc: FacadeConverter) {
+ this.type = new MergedType(fc);
+ this.textRange = param;
+ this.merge(param);
+ }
+
+ merge(param: ts.ParameterDeclaration) {
+ this.name[base.ident(param.name)] = true;
+ if (!this.optional) {
+ this.optional = !!param.questionToken;
+ }
+ this.type.merge(param.type);
+ }
+
+ toParameterDeclaration(): ts.ParameterDeclaration {
+ let ret = <ts.ParameterDeclaration>ts.createNode(ts.SyntaxKind.Parameter);
+ let nameIdentifier = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier);
+ nameIdentifier.text = Object.getOwnPropertyNames(this.name).join('_');
+ ret.name = nameIdentifier;
+ if (this.optional) {
+ ret.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
+ }
+ base.copyLocation(this.textRange, ret);
+ ret.type = this.type.toTypeNode();
+ return ret;
+ }
+
+ setOptional() { this.optional = true; }
+
+ private name: {[s: string]: boolean} = {};
+ private type: MergedType;
+ private optional: boolean = false;
+ private textRange: ts.TextRange;
+}
+
+/**
+ * Normalize a SourceFile
+ */
+export function normalizeSourceFile(f: ts.SourceFile) {
+ let modules: {[name: string]: ts.ModuleDeclaration} = {};
+
+ // Merge top level modules.
+ for (let i = 0; i < f.statements.length; ++i) {
+ let statement = f.statements[i];
+ if (statement.kind !== ts.SyntaxKind.ModuleDeclaration) continue;
+ let moduleDecl = <ts.ModuleDeclaration>statement;
+ let name = moduleDecl.name.text;
+ if (modules.hasOwnProperty(name)) {
+ let srcBody = modules[name].body;
+ let srcBodyBlock: ts.ModuleBlock;
+
+ if (srcBody.kind !== ts.SyntaxKind.ModuleBlock) {
+ throw 'Module body must be a module block.';
+ }
+ srcBodyBlock = <ts.ModuleBlock>srcBody;
+
+ let body = moduleDecl.body;
+ if (body.kind === ts.SyntaxKind.ModuleBlock) {
+ let bodyBlock = <ts.ModuleBlock>body;
+ Array.prototype.push.apply(srcBodyBlock.statements, bodyBlock.statements);
+ } else {
+ // moduleDecl.body is a ModuleDeclaration.
+ srcBodyBlock.statements.push(moduleDecl.body);
+ }
+
+ f.statements.splice(i, 1);
+ i--;
+ } else {
+ modules[name] = moduleDecl;
+ }
+ }
+
+ function addModifier(n: ts.Node, modifier: ts.Node) {
+ if (!n.modifiers) {
+ n.modifiers = <ts.ModifiersArray>[];
+ n.modifiers.flags = 0;
+ }
+ modifier.parent = n;
+ n.modifiers.push(modifier);
+ }
+
+ function mergeVariablesIntoClasses(n: ts.Node, classes: {[name: string]: base.ClassLike}) {
+ switch (n.kind) {
+ case ts.SyntaxKind.VariableStatement:
+ let statement = <ts.VariableStatement>n;
+ statement.declarationList.declarations.forEach(function(
+ declaration: ts.VariableDeclaration) {
+ if (declaration.name.kind === ts.SyntaxKind.Identifier) {
+ let name: string = (<ts.Identifier>(declaration.name)).text;
+ if (classes.hasOwnProperty(name)) {
+ let existing = classes[name];
+ let members = existing.members as Array<ts.ClassElement>;
+ if (declaration.type) {
+ let type: ts.TypeNode = declaration.type;
+ if (type.kind === ts.SyntaxKind.TypeLiteral) {
+ removeNode(n);
+ let literal = <ts.TypeLiteralNode>type;
+ literal.members.forEach((member: ts.Node) => {
+ switch (member.kind) {
+ case ts.SyntaxKind.ConstructSignature:
+ let signature: any = member;
+ let constructor =
+ <ts.ConstructorDeclaration>ts.createNode(ts.SyntaxKind.Constructor);
+ constructor.parameters = signature.parameters;
+ constructor.type = signature.type;
+ base.copyLocation(signature, constructor);
+ constructor.typeParameters = signature.typeParameters;
+ constructor.parent = existing;
+ members.push(<ts.ClassElement>constructor);
+ break;
+ case ts.SyntaxKind.Constructor:
+ member.parent = existing.parent;
+ members.push(<ts.ClassElement>member);
+ break;
+ case ts.SyntaxKind.MethodSignature:
+ member.parent = existing.parent;
+ members.push(<ts.ClassElement>member);
+ break;
+ case ts.SyntaxKind.PropertySignature:
+ addModifier(member, ts.createNode(ts.SyntaxKind.StaticKeyword));
+ member.parent = existing;
+ members.push(<ts.ClassElement>member);
+ break;
+ case ts.SyntaxKind.IndexSignature:
+ member.parent = existing.parent;
+ members.push(<ts.ClassElement>member);
+ break;
+ case ts.SyntaxKind.CallSignature:
+ member.parent = existing.parent;
+ members.push(<ts.ClassElement>member);
+ break;
+ default:
+ throw 'Unhandled TypeLiteral member type:' + member.kind;
+ }
+ });
+ }
+ }
+ }
+ } else {
+ throw 'Unexpected VariableStatement identifier kind';
+ }
+ });
+ break;
+ case ts.SyntaxKind.ModuleBlock:
+ ts.forEachChild(n, (child) => mergeVariablesIntoClasses(child, classes));
+ break;
+ default:
+ break;
+ }
+ }
+
+ function removeFromArray(nodes: ts.NodeArray<ts.Node>, v: ts.Node) {
+ for (let i = 0, len = nodes.length; i < len; ++i) {
+ if (nodes[i] === v) {
+ nodes.splice(i, 1);
+ break;
+ }
+ }
+ }
+
+ function removeNode(n: ts.Node) {
+ let parent = n.parent;
+ switch (parent.kind) {
+ case ts.SyntaxKind.ModuleBlock:
+ let block = <ts.ModuleBlock>parent;
+ removeFromArray(block.statements, n);
+ break;
+ case ts.SyntaxKind.SourceFile:
+ let sourceFile = <ts.SourceFile>parent;
+ removeFromArray(sourceFile.statements, n);
+ break;
+ default:
+ throw 'removeNode not implemented for kind:' + parent.kind;
+ }
+ }
+
+ function makeCallableClassesImplementFunction(decl: base.ClassLike) {
+ if (base.isCallable(decl)) {
+ // Modify the AST to explicitly state that the class implements Function
+ if (!decl.heritageClauses) {
+ decl.heritageClauses = <ts.NodeArray<ts.HeritageClause>>[];
+ base.copyLocation(decl, decl.heritageClauses);
+ }
+ let clauses = decl.heritageClauses;
+ let clause = base.arrayFindPolyfill(
+ clauses, (c) => c.token !== ts.SyntaxKind.ExtendsKeyword ||
+ decl.kind === ts.SyntaxKind.InterfaceDeclaration);
+ if (clause == null) {
+ clause = <ts.HeritageClause>ts.createNode(ts.SyntaxKind.HeritageClause);
+ clause.token = decl.kind === ts.SyntaxKind.InterfaceDeclaration ?
+ ts.SyntaxKind.ExtendsKeyword :
+ ts.SyntaxKind.ImplementsKeyword;
+ clause.types = <ts.NodeArray<ts.ExpressionWithTypeArguments>>[];
+ clause.parent = decl;
+ base.copyLocation(decl, clause);
+ clauses.push(clause);
+ }
+ let functionType =
+ <ts.ExpressionWithTypeArguments>ts.createNode(ts.SyntaxKind.ExpressionWithTypeArguments);
+ functionType.parent = clause;
+ base.copyLocation(clause, functionType);
+ let fn = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier);
+ fn.text = 'Function';
+ fn.parent = functionType;
+ base.copyLocation(functionType, fn);
+ functionType.expression = fn;
+ clause.types.push(functionType);
+ }
+ }
+
+ function gatherClasses(n: ts.Node, classes: {[name: string]: base.ClassLike}) {
+ switch (n.kind) {
+ case ts.SyntaxKind.ClassDeclaration:
+ case ts.SyntaxKind.InterfaceDeclaration:
+ let classDecl = <base.ClassLike>n;
+ let name = classDecl.name.text;
+ // TODO(jacobr): validate that the classes have consistent
+ // modifiers, etc.
+ if (classes.hasOwnProperty(name)) {
+ let existing = classes[name];
+ (classDecl.members as Array<ts.ClassElement>).forEach((e: ts.ClassElement) => {
+ (existing.members as Array<ts.ClassElement>).push(e);
+ e.parent = existing;
+ });
+ removeNode(classDecl);
+ } else {
+ classes[name] = classDecl;
+ // Perform other class level post processing here.
+ makeCallableClassesImplementFunction(classDecl);
+ }
+ break;
+ case ts.SyntaxKind.ModuleDeclaration:
+ case ts.SyntaxKind.SourceFile:
+ let moduleClasses: {[name: string]: base.ClassLike} = {};
+ ts.forEachChild(n, (child) => gatherClasses(child, moduleClasses));
+ ts.forEachChild(n, (child) => mergeVariablesIntoClasses(child, moduleClasses));
+
+ break;
+ case ts.SyntaxKind.ModuleBlock:
+ ts.forEachChild(n, (child) => gatherClasses(child, classes));
+ break;
+ default:
+ break;
+ }
+ }
+ gatherClasses(f, {});
+}
« no previous file with comments | « lib/main.ts ('k') | lib/module.ts » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698