@JsExport/@JsType slicing is not intuitive for a lot of people esp. with gwt-exporter background. People are confused about when to use what.
There is no reason to why @JsType doesn’t have any effect on the static methods. That is only because of the original use cases that the design was tackling only cared about well formed prototypal structures. Diving deeper into Elemental and different javascript output styles, ability to define the full class structure without exporting proves to be useful.
@JsExport uses @JsType to define the prototype structure. However this imposes unnecessary restriction if/when there will be no javascript implementers of the @JsType contract. @JsType that extends non-JsType is normally ok if it is not implemented in js.
You always need to fully qualify the name of the export even if you just want to change the simple name.
There will be single annotation called @Js. Applying @Js to a member is making that member available in javascript without any obfuscation. However it is not safe from pruning if there are no references in java code, so one needs to put enable exporting for the type if no pruning wanted. Applying @Js at class level should considered as a shortcut to apply @Js to all members. See following chart for the attributes and their corresponding behavior:
@JsType | @Js(exports = INSTANCE_MEMBERS) |
@JsFunction | @Js(mode = FUNCTION) |
@JsLiteral | @Js(mode = LITERAL) |
@JsMethod | @Js(name = "myName") |
@JsProperty | @Js(property = true) |
@JsNamespace | @Js(namespace = "mynamespace") |
@JsExport | @Js(exports = STATIC_MEMBERS) @Js(name = “A”, namespace=”a.b.c.”, exports = ALL) // When applied to a member @Js(export = true) @Js(name = “myName”, export = true) |
@JsNoExport | @Js(ignore=true) |
@JsOpaque | @Js(opaque=true) |
See Appendix below for a complete comparison to existing annotations.
Implementation:
Apply all Js names as bridge methods (or the reverse if Js extends Java object case needs to be supported).
Optimize away everything with regular optimization rules if the member is not exported.
Generate export statements for all pinned methods/classes.
Usage:
Hybrid / Inbox use case needs to use @Js with exports. This will make the whole object exported and not pruned.
Regular library importing should use @Js with interfaces (no exports), if it is a callback the @Js interface should be marked as exported so the methods are not pruned when the object is not pruned.
Elemental needs to use not exported Js types with prototype set and native methods.
mode and exports is only used in types.
export and ignore is only used in members.
property is only used in methods.
name is only used in members and types.
namespace is only used in exported static members, types and packages.
mode=FUNCTION cannot have any attribute set.
We could follow the above design but keep using old annotations for class level annotations:
@Js(mode=OBJECT) --> @JsType
@Js(mode=FUNCTION) --> @JsFunction
@Js(mode=LITERAL) --> @JsLiteral
@Js(namespace=”...”) --> @JsNamespace
@JsMember for the rest.
Pros:
Reads well . (e.g. @JsType interface Element instead of @Js interface Element { .. } ).
These modes are substantially different so different annotations makes that explicit and helps to document.
Need to add additional checks as attributes are mostly disjoint. e.g exports doesn't apply to members, name/namespace isn't applicable for Js(mode=LITERAL) etc.
Cons:
Multiple annotations to learn.
Using JsType/JsFunction/JsLiteral in the same type is forbidden but having single annotations automatically enforces that.
We can introduce two different concepts JsImport and JsExport. Both annotation will imply old JsType behavior if applied at class level. It can be applied at method level to provide method level customizations.
The main advantage is that the name implies what the developer is trying to achieve. If importing a library or generating Elemental, @JsImport is used. For exporting code @JsExport is used.
However, it actually make things more confusing when it comes to callbacks. In that case, an imported callback is actually an export so @JsExport should be applied instead.
The main issue is, unlike the above designs it doesn’t let you configure your JS output without introducing exports.
@JsType | @JsImport |
@JsFunction | @JsImport(mode = FUNCTION) // Another gotcha, here we are actually logically not importing always. If it is to call some javascript code, it is for importing but when you implement it in java, it is for exporting. So one can argue we should just keep it @JsFunction. |
@JsLiteral | @JsExport(mode = LITERAL) |
@JsMethod | {@JsImport/@JsExport}(name = "myName") // Another gotcha, need to choose one depending on context so we should probably stick to @JsExport/@JsName approach in the old design |
@JsProperty | @JsImport/JsExport(isProperty=true) |
@JsNamespace | @JsNamespace |
@JsExport | @JsExport // also implies old @JsType semantics @JsExport(name = “A”, namespace=”a.b.c.”, pinned = true) |
@JsNoExport | @JsIgnore |
@JsOpaque | @JsOpaque |
@JsExport @JsType class MyClass {} | @Js(exports=ALL) class MyClass {} |
@JsExport class MyClass {} | @Js(exports=STATIC_MEMBERS) class MyClass {} |
@JsExport(“Name”) class MyClass {} @JsExport(“a.b.c.someName”) class MyClass {} | @Js(name=”Name”, namespace=GLOBAL, exports=STATIC_MEMBERS) class MyClass {} @Js(name=”Name”, namespace=”a.b.c”, exports=STATIC_MEMBERS) class MyClass {} Also see (*) |
@JsType class MyClass {} | @Js(exports=INSTANCE_MEMBERS) class MyClass {} |
@JsType interface MyInterface{} | @Js(exports=INSTANCE_MEMBERS) interface MyInterface{} |
@JsType interface ImportedLibrary{} | @Js interface ImportedLibrary{} |
@JsType interface ImportedLibraryCallback{} | @Js(exports=INSTANCE_MEMBERS) interface ImportedLibraryCallback{} |
@JsExport static void someStaticMethod{} | @Js(export=true) static void someStaticMethod{} |
@JsExport(“someName”) static void someStaticMethod{} @JsExport(“a.b.c.someName”) static void someStaticMethod{} | @Js(name=”someName”, namespace=GLOBAL, export=true) static void someStaticMethod{} @Js(name=”someName”, namespace=”a.b.c”, export=true) static void someStaticMethod{} Also see (*) |
@JsNoExport static void someStaticMethod{} | @Js(ignore=true) or @Js(export=false) static void someStaticMethod{} |
@JsNoExport void someMethod{} | @Js(ignore=true) void someMethod{} |
@JsProperty void someMethod{} | @Js(property=true) void someMethod{} |
(*) In most of the cases, export name is provided for changing the simple name (not the fully qualified name). In those cases, namespace attribute doesn’t need to be set.
@JsExport @JsType class MyClass {} | @JsType(exports=ALL) class MyClass {} |
@JsExport class MyClass {} | @JsType(exports=STATIC_MEMBERS) class MyClass {} |
@JsExport(“Name”) class MyClass {} @JsExport(“a.b.c.someName”) class MyClass {} | @JsNamespace(GLOBAL) @JsType(name=”Name”, exports=STATIC_MEMBERS) class MyClass {} @JsNamespace(“a.b.c”) @JsType(name=”Name”, exports=STATIC_MEMBERS) class MyClass {} Also see (*) |
@JsType class MyClass {} | @JsType(exports=INSTANCE_MEMBERS) class MyClass {} |
@JsType interface MyInterface{} | @JsType(exports=INSTANCE_MEMBERS) interface MyInterface{} |
@JsType interface ImportedLibrary{} | @JsType interface ImportedLibrary{} |
@JsType interface ImportedLibraryCallback{} | @JsType(exports=INSTANCE_MEMBERS) interface ImportedLibraryCallback{} |
@JsExport static void someStaticMethod{} | @JsMember(export=true) static void someStaticMethod{} |
@JsExport(“someName”) static void someStaticMethod{} @JsExport(“a.b.c.someName”) static void someStaticMethod{} | @JsNamespace(GLOBAL) @JsMember(name=”someName”, export=true) static void someStaticMethod{} @JsNamespace(“a.b.c.”) @JsMember(name=”someName”, export=true) static void someStaticMethod{} Also see (*) |
@JsNoExport static void someStaticMethod{} | @JsMember(ignore=true) or @JsMember(export=false) static void someStaticMethod{} |
@JsNoExport void someMethod{} | @JsMember(ignore=true) void someMethod{} |
@JsProperty void someMethod{} | @JsMember(property=true) void someMethod{} |
(*) In most of the cases, export name is provided for changing the simple name (not the fully qualified name). In those cases, JsNamespace doesn’t need to be set.
--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/778b8f0b-8cc1-4a90-84d5-39c1b9c02afb%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAPVRV7ec1A4w4sb4ZN9wpruLg7K%2BrdnQq3nC0LXtSCK2N5%3DBeQ%40mail.gmail.com.
Damn it, Safari crashed. So now a shorter version of my answer:First I agree that the current design has become quite confusing. Personally I really dislike the single @Js annotation approach because:- can be applied everywhere so you throw away some compile time checks provided by Java's @Target annotation
- exports vs. export is a bit misleading. One must be used with interfaces/classes the other with methods. That issue only exists because @Js alone has no real meaning.
- You have to type more because it is always annotation name + property + value if you can't stick with the default "@Js".
- Can't see a good use case for splitting exports in ALL, INSTANCE_MEMBERS and STATIC_MEMBERS. When I want to export a class I want to export its public API.
Personally what describes JsInterop best is the alternative using the import/export concepts. So I would stick with:Import/Export types:@JsImport: can only be applied on interfaces.@JsExport: always exports all public API in a given scope (package, class, method). So no ALL, INSTANCE_MEMBER, STATIC_MEMBER distinction. @JsIgnore can be used to opt-out selectively.Configure import/export:@JsNamespace: import/export from/to namespace@JsName: workaround reserved keywords@JsProperty: mark method as JS property@JsIgnore: opt-out of export. Might even be useful for import, e.g. do not generate trampoline for annotated default method.Special constructs:@JsLiteral
I guess that is the cleanest you can get. Not sure what @JsOpaque is good for?!
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAN%3DyUA3JFP%2BruK-ysKOCD6AMEDvvV4neVsHk%3DcMGP6AbNNLFXA%40mail.gmail.com.
- exports vs. export is a bit misleading. One must be used with interfaces/classes the other with methods. That issue only exists because @Js alone has no real meaning.Mostly agreed though @Js alone has meaning. As there are no exports, the methods can be pruned. This difference will be more significant when we change the output style to be more idiomatic javascript and have a closer integration with Closure compiler in the future.
- You have to type more because it is always annotation name + property + value if you can't stick with the default "@Js".I'm assuming you are comparing Option 1 and Option 2 (see my recent email). Based on that I'm not sure which part you are referring. Can you give an example?
- Can't see a good use case for splitting exports in ALL, INSTANCE_MEMBERS and STATIC_MEMBERS. When I want to export a class I want to export its public API.Yes, that was my assumption as well and that's how I started. But looking at real life code inside Google, especially in places where the code is shared by different platforms, I see people choosing to only export instance members or static members. I will need to re-evaluate this after we migrate all Google code to new annotations.
Personally what describes JsInterop best is the alternative using the import/export concepts. So I would stick with:Import/Export types:@JsImport: can only be applied on interfaces.@JsExport: always exports all public API in a given scope (package, class, method). So no ALL, INSTANCE_MEMBER, STATIC_MEMBER distinction. @JsIgnore can be used to opt-out selectively.Configure import/export:@JsNamespace: import/export from/to namespace@JsName: workaround reserved keywords@JsProperty: mark method as JS property@JsIgnore: opt-out of export. Might even be useful for import, e.g. do not generate trampoline for annotated default method.Special constructs:@JsLiteralThis is the one I listed as Alternative 2 in the doc (Option 3 as I re-listed in the recent email). It doesn't hold water well; works well for simple but gets confusing quickly as there are many gotchas.
- exports vs. export is a bit misleading. One must be used with interfaces/classes the other with methods. That issue only exists because @Js alone has no real meaning.Mostly agreed though @Js alone has meaning. As there are no exports, the methods can be pruned. This difference will be more significant when we change the output style to be more idiomatic javascript and have a closer integration with Closure compiler in the future.Yeah ok probably bad wording from my side. @Js is equivalent to @JsImport.What I wanted to express is that the export flag is kind of bad because its a "marker" and the act of marking something for export should already be covered by applying the corresponding annotation. So if you would have @JsExport then applying @JsExport automatically means that it is marked for export. You don't need that boolean flag then. You would end up with @JsExport(STATIC_MEMBERS) for types and @JsExport for members which seems more straight forward (yes I know @JsExport(STATIC_MEMBERS) applied to a member seems strange but you would have the same issue with @Js).Also see next inline answer.
- You have to type more because it is always annotation name + property + value if you can't stick with the default "@Js".I'm assuming you are comparing Option 1 and Option 2 (see my recent email). Based on that I'm not sure which part you are referring. Can you give an example?I think what bugs me most are the boolean properties that are used as "markers", e.g.@Js(property = true)@JsProperty@Js(export = true, property = true)@JsExport @JsProperty@Js(export = false)@Js(ignore = true)@JsIgnore
It simply reads better without these flags. It is not so much an "issue" for more complex examples, e.g.@Js(exports = ALL, namespace = GLOBAL, name = "Foo")@JsExport(ALL) @JsNamespace(GLOBAL) @JsName("Foo")
- Can't see a good use case for splitting exports in ALL, INSTANCE_MEMBERS and STATIC_MEMBERS. When I want to export a class I want to export its public API.Yes, that was my assumption as well and that's how I started. But looking at real life code inside Google, especially in places where the code is shared by different platforms, I see people choosing to only export instance members or static members. I will need to re-evaluate this after we migrate all Google code to new annotations.Interesting. Wondering why that is the case. Given that it is just a shortcut to avoid lots of @Js(ignore=true) or @Js(export = true) annotations it is not so much of an issue. However I am wondering if the shared Java code would end up better if we enforce the "all public API will be exported unless you opt out using @JsIgnore" route. For example a developer would then have created a static factory class and then choose to export that factory or not.But I guess we'll just trust the guys with the real world code and if you think its a good idea to have these shortcuts then fine.Personally what describes JsInterop best is the alternative using the import/export concepts. So I would stick with:Import/Export types:@JsImport: can only be applied on interfaces.@JsExport: always exports all public API in a given scope (package, class, method). So no ALL, INSTANCE_MEMBER, STATIC_MEMBER distinction. @JsIgnore can be used to opt-out selectively.Configure import/export:@JsNamespace: import/export from/to namespace@JsName: workaround reserved keywords@JsProperty: mark method as JS property@JsIgnore: opt-out of export. Might even be useful for import, e.g. do not generate trampoline for annotated default method.Special constructs:@JsLiteralThis is the one I listed as Alternative 2 in the doc (Option 3 as I re-listed in the recent email). It doesn't hold water well; works well for simple but gets confusing quickly as there are many gotchas.Honestly I don't get the gotchas probably because I am not so deep into the matter. Basically you say for Javascript callbacks it is unclear if import or export should be used. But for that case we would have @JsFunction which simply does the right thing and you do not have to think about it. Also the @JsMethod annotation does not exists in the spec so not sure about that one.Can you give me a confusing example?
---- J.
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/0e267848-d4ec-4121-899e-a9a09b47c9e7%40googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/778b8f0b-8cc1-4a90-84d5-39c1b9c02afb%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAPVRV7ec1A4w4sb4ZN9wpruLg7K%2BrdnQq3nC0LXtSCK2N5%3DBeQ%40mail.gmail.com.
--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.
-- J.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.
...
--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/50de995a-3b18-4432-9b79-76ae51940729%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAPVRV7cii4dCEdLWqK_-gL%2B4nSB57fCZp2mFzJSL1BUS_7-5aQ%40mail.gmail.com.
@JsExport @JsType class MyClass {} | @JsType(exports=ALL) class MyClass {} |
@JsExport class MyClass {} | @JsType(exports=STATIC_MEMBERS) class MyClass {} |
@JsExport(“Name”) class MyClass {} |
@JsType(name=”Name”, exports=STATIC_MEMBERS) class MyClass {} | |
@JsExport(“Name”) @JsType class MyClass {} | @JsType(name=”Name”, exports=ALL) class MyClass {} |
@JsType class MyClass {} | @JsType(exports=INSTANCE_MEMBERS) class MyClass {} |
@JsType interface MyInterface{} | @JsType(exports=INSTANCE_MEMBERS) interface MyInterface{} |
@JsType interface ImportedLibrary{} | @JsType interface ImportedLibrary{} |
@JsType interface ImportedLibraryCallback{} | @JsType(exports=INSTANCE_MEMBERS) interface ImportedLibraryCallback{} |
@JsExport SomeConstructor{} static void someStaticMethod{} static Object someStaticField; | @JsConstructor(export=true) SomeConstructor{} static void someStaticMethod{} static Object someStaticField; |
@JsExport(“someName”) static void someStaticMethod{} static Object someStaticField; | @JsMethod(name=”someName”, export=true) static void someStaticMethod{} static Object someStaticField; |
@JsNoExport static void someStaticMethod{} | @JsMethod(export=false) or @JsIgnore static void someStaticMethod{} |
@JsNoExport void someMethod{} | @JsIgnore void someMethod{} |
@JsProperty void someMethod{} | @JsProperty void someMethod{} |
@JsProperty(“someName”) int someField; void someMethod{} | @JsProperty(name = “someName”) int someField; void someMethod{} |
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAN%3DyUA1WnH3WsQ-f0oHgbDTnTWW5wjS1mHREtD%3DxvDNxR_DbNg%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CA%2BkiFsdnB-E2%3DALHtHbx6ypS-Ce7waCr1aNpjCUNJkEeE9DYqA%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CADcXZMwib-AW3COfPQ16VJAFHypTyPcGqD8cFUp2qcvPMihxzA%40mail.gmail.com.
...
Marcin
--...
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/a0979a8f-b56e-4e29-a54c-8972bb908f34%40googlegroups.com.
--
You received this message because you are subscribed to a topic in the Google Groups "GWT Contributors" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/google-web-toolkit-contributors/Dwdk-aEHs8Y/unsubscribe.
To unsubscribe from this group and all its topics, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAN%3DyUA0S5%3DS0mvjS-%2BCx_2%2BG5wvLs0FhJ9cDhh80t6HCdRfoZw%40mail.gmail.com.
Seems to look good. Seems pretty similar to the single @Js annotation version but reads better because of separate @JsMethod/@JsProperty/@JsConstrutor annotations and gives the possibility to add specific attributes to just one of them if ever needed.Two short questions:- JsConstrutor does not have a name attribute so maybe its worth documenting that you can not use JavaScript reserved words as (yes, ugly lower case) class names when @JsConstructor(export = true) is used?
- Currently name and export attributes are repeated through @JsType/Constructor/Method/Property. Is there any reason why @JsNamespace stands alone? For consistency I would probably convert @JsNamespace into an attribute as well, especially if @JsNamespace can be used with all four annotations.
-- J.
--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/bf832bd5-ae11-45eb-9421-bc73d5aff1a2%40googlegroups.com.