Skip to content

Commit

Permalink
Fixed the JS annotations on the private classes emitted for extensions (
Browse files Browse the repository at this point in the history
dart-archive#62)

Fixed the JS annotations on the private classes emitted for extensions
Changed declaration_test to test promise members of both classes and interfaces
  • Loading branch information
derekxu16 authored Nov 8, 2019
1 parent 16d9b7c commit 1db6628
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 44 deletions.
20 changes: 10 additions & 10 deletions lib/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
return !interfaceDecl.classLikeVariableDeclaration;
}

maybeEmitJsAnnotation(node: ts.Node) {
maybeEmitJsAnnotation(node: ts.Node, {suppressUnneededPaths}: {suppressUnneededPaths: boolean}) {
// No need to emit the annotations as an entity outside the code comment
// will already have the same annotation.
if (this.insideCodeComment) return;
Expand All @@ -117,7 +117,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
this.emit('@JS()');
return;
}
let name: String = this.getJsPath(node, true);
const name = this.getJsPath(node, suppressUnneededPaths);
this.emit('@JS(');
if (name.length > 0) {
this.emit(`"${name}"`);
Expand Down Expand Up @@ -467,7 +467,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
// 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.maybeEmitJsAnnotation(decl, {suppressUnneededPaths: true});
this.emit('class');
this.emit(decl.name.text);
this.emit('{');
Expand Down Expand Up @@ -648,7 +648,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
this.emit('\n/* Skipping class ' + base.ident(classDecl.name) + '*/\n');
break;
}
this.maybeEmitJsAnnotation(node);
this.maybeEmitJsAnnotation(node, {suppressUnneededPaths: true});

if (isInterface || this.hasModifierFlag(classDecl, ts.ModifierFlags.Abstract)) {
this.visitClassLike('abstract class', classDecl);
Expand Down Expand Up @@ -905,7 +905,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
const visitNameOfExtensions = () => {
this.visitClassLikeName(name, typeParameters, ts.createNodeArray(), true);
};
this.emitMembersAsExtensions(name, visitName, visitNameOfExtensions, this.promiseMembers);
this.emitMembersAsExtensions(decl, visitName, visitNameOfExtensions, this.promiseMembers);
this.promiseMembers = [];
}
this.emit('\n');
Expand Down Expand Up @@ -948,7 +948,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
case ts.SyntaxKind.PropertySignature:
case ts.SyntaxKind.FunctionDeclaration:
if (!base.getEnclosingClass(decl)) {
this.maybeEmitJsAnnotation(decl);
this.maybeEmitJsAnnotation(decl, {suppressUnneededPaths: true});
}
this.emit('external');
break;
Expand Down Expand Up @@ -985,7 +985,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
const {name, type} = declaration;

if (emitJsAnnotation) {
this.maybeEmitJsAnnotation(declaration);
this.maybeEmitJsAnnotation(declaration, {suppressUnneededPaths: true});
}
if (isExternal) {
this.emit('external');
Expand Down Expand Up @@ -1021,12 +1021,12 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
}

private emitMembersAsExtensions(
className: ts.Identifier, visitClassName: () => void, visitNameOfExtensions: () => void,
methods: ts.SignatureDeclaration[]) {
classDecl: ts.ObjectTypeDeclaration, visitClassName: () => void,
visitNameOfExtensions: () => void, methods: ts.SignatureDeclaration[]) {
this.visitPromises = true;
this.fc.emitPromisesAsFutures = false;
// Emit private class containing external methods
this.emit(`@JS('${base.ident(className)}')`);
this.maybeEmitJsAnnotation(classDecl, {suppressUnneededPaths: false});
this.emit(`abstract class _`);
visitClassName();
this.emit('{');
Expand Down
150 changes: 116 additions & 34 deletions test/declaration_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,15 +258,17 @@ class X {
}`);
});
it('should emit extension methods to return Futures from methods that return Promises', () => {
expectTranslate(`declare interface MyMath {
expectTranslate(`declare class MyMath {
randomInRange(start: number, end: number): Promise<number>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class MyMath {}
class MyMath {
// @Ignore
MyMath.fakeConstructor$();
}
@JS('MyMath')
@JS("MyMath")
abstract class _MyMath {
external Promise<num> randomInRange(num start, num end);
}
Expand All @@ -285,15 +287,17 @@ abstract class Promise<T> {
void executor(void resolve(T result), Function reject));
external Promise then(void onFulfilled(T result), [Function onRejected]);
}`);
expectTranslate(`declare interface X<T> {
expectTranslate(`declare class X<T> {
f(a: T): Promise<T>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class X<T> {}
class X<T> {
// @Ignore
X.fakeConstructor$();
}
@JS('X')
@JS("X")
abstract class _X<T> {
external Promise<T> f(T a);
}
Expand All @@ -312,27 +316,29 @@ abstract class Promise<T> {
void executor(void resolve(T result), Function reject));
external Promise then(void onFulfilled(T result), [Function onRejected]);
}`);
expectTranslate(`declare interface Y {
expectTranslate(`declare class Y {
a: number;
}
declare interface Z extends Y {
declare class Z extends Y {
f(): Promise<string>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class Y {
class Y {
// @Ignore
Y.fakeConstructor$();
external num get a;
external set a(num v);
external factory Y({num a});
}
@anonymous
@JS()
abstract class Z implements Y {}
class Z extends Y {
// @Ignore
Z.fakeConstructor$() : super.fakeConstructor$();
}
@JS('Z')
@JS("Z")
abstract class _Z {
external Promise<String> f();
}
Expand All @@ -351,16 +357,18 @@ abstract class Promise<T> {
void executor(void resolve(T result), Function reject));
external Promise then(void onFulfilled(T result), [Function onRejected]);
}`);
expectTranslate(`declare interface X {
expectTranslate(`declare class X {
f(a: string): Promise<number>;
f(a: string, b: number): Promise<number>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class X {}
class X {
// @Ignore
X.fakeConstructor$();
}
@JS('X')
@JS("X")
abstract class _X {
/*external Promise<num> f(String a);*/
/*external Promise<num> f(String a, num b);*/
Expand All @@ -384,17 +392,19 @@ abstract class Promise<T> {
void executor(void resolve(T result), Function reject));
external Promise then(void onFulfilled(T result), [Function onRejected]);
}`);
expectTranslate(`declare interface X {
expectTranslate(`declare class X {
f(a: string): Promise<number>;
f(a: number, b: number): Promise<number>;
f(c: number[]): Promise<number>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class X {}
class X {
// @Ignore
X.fakeConstructor$();
}
@JS('X')
@JS("X")
abstract class _X {
/*external Promise<num> f(String a);*/
/*external Promise<num> f(num a, num b);*/
Expand Down Expand Up @@ -523,18 +533,18 @@ class X {
}`);
});
it('should emit extension getters/setters that expose Futures in place of Promises', () => {
expectTranslate(`interface MyMath {
expectTranslate(`declare class MyMath {
readonly two: Promise<num>;
three: Promise<num>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class MyMath {
external factory MyMath({Promise<num> two, Promise<num> three});
class MyMath {
// @Ignore
MyMath.fakeConstructor$();
}
@JS('MyMath')
@JS("MyMath")
abstract class _MyMath {
external Promise<num> get two;
external Promise<num> get three;
Expand Down Expand Up @@ -569,17 +579,17 @@ abstract class Promise<T> {
void executor(void resolve(T result), Function reject));
external Promise then(void onFulfilled(T result), [Function onRejected]);
}`);
expectTranslate(`interface X<T> {
expectTranslate(`declare class X<T> {
aPromise: Promise<T>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class X<T> {
external factory X({Promise<T> aPromise});
class X<T> {
// @Ignore
X.fakeConstructor$();
}
@JS('X')
@JS("X")
abstract class _X<T> {
external Promise<T> get aPromise;
external set aPromise(Promise<T> v);
Expand Down Expand Up @@ -640,7 +650,77 @@ abstract class X {
external factory X({String x, y});
}`);
});
it('should emit extension methods to return Futures from methods that return Promises',
() => {
expectTranslate(`declare interface X<T> {
f(a: T): Promise<T>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class X<T> {}
@anonymous
@JS()
abstract class _X<T> {
external Promise<T> f(T a);
}
extension XExtensions<T> on X<T> {
Future<T> f(T a) {
final Object t = this;
final _X<T> tt = t;
return promiseToFuture(tt.f(a));
}
}
@JS()
abstract class Promise<T> {
external factory Promise(
void executor(void resolve(T result), Function reject));
external Promise then(void onFulfilled(T result), [Function onRejected]);
}`);
expectTranslate(`declare interface Y {
a: number;
}
declare interface Z extends Y {
f(): Promise<string>;
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
@anonymous
@JS()
abstract class Y {
external num get a;
external set a(num v);
external factory Y({num a});
}
@anonymous
@JS()
abstract class Z implements Y {}
@anonymous
@JS()
abstract class _Z {
external Promise<String> f();
}
extension ZExtensions on Z {
Future<String> f() {
final Object t = this;
final _Z tt = t;
return promiseToFuture(tt.f());
}
}
@JS()
abstract class Promise<T> {
external factory Promise(
void executor(void resolve(T result), Function reject));
external Promise then(void onFulfilled(T result), [Function onRejected]);
}`);
});
it('handles interface properties with names that are invalid in Dart ', () => {
expectTranslate(`interface X { '!@#$%^&*': string; }`).to.equal(`@anonymous
@JS()
Expand Down Expand Up @@ -770,7 +850,9 @@ abstract class Y {
external static set d(String v);
}`);
});
});

describe('flags', () => {
// If the lib.dom.d.ts file is compiled, it creates conflicting interface definitions which causes
// a problem for variable declarations that we have merged into classes. The main problem was that
// when the declaration transpiler performed the notSimpleBagOfProperties check, it was checking
Expand Down

0 comments on commit 1db6628

Please sign in to comment.