google / jsinterop-generator

Generates Java annotated with JsInterop from JavaScript extern sources
Apache License 2.0
78 stars 24 forks source link

Add a mechanism for testing whether feature is present #30

Open realityforge opened 5 years ago

realityforge commented 5 years ago

Sometimes a particular property/operator is only available on some browsers. The generator will generate the field/methods regardless and the downstream consumer is left to try and feature detect in some other way. The simplest approach may be to just generate a @JsOverlay boolean supportsMyFeature() method that will allow caller to detect whether a feature is supported.

It would be too costly to generate supports*() methods for every feature so maybe the generator could load the canisue database and only generate supports*() methods if they are not supported compared to a particular base line (IE11?).

See also #29 because these features could be combined and driven by the same data.

jDramaix commented 5 years ago

We already think to add this kind of support and I need to think more about that. The canisue json file seems very helpful for that. But this kind of feature will not be implemented directly in jsinterop-generator as it's only make sense for Elemental2. So I'm thinking maybe adding a kind of plugin system for jsinterop-generator where the plugin would be able to transform the java model before generating the code.

realityforge commented 5 years ago

Ooer - that does sound like an interesting idea. One thing I have been contemplating lately is somehow injecting other sources of metadata into the generation pipeline. In particular I was thinking about ways of how we could read WebIDL and use that to enhance or replace the externs used as input to Elemental2.

The advantage of that is that we could read WebIDL from either specs, Chrome and/of Firefox and generate a relatively maintenance free, accurate API for interacting with the browsers. There would still be some work due to differences between browsers (i.e. Chromes ApplicationCache == Firefoxes OfflineResourceList) and some work on partitioning different parts of the API.

This would mean that we could throw away integer_entities.txt and name_mappings.txt and that a lot of current edge cases would just be fixed. We could also inline constants into java code rather than using externs to access them. We could also more accurately model browser APIs as there is several constructs in specs that are not representable in closure type system. (i.e. overloaded operations that return different types). It would also mean that some edge cases would disappear due to names of types in Closure not aligning with names used by browsers. To get this working we would also need to generate closure externs from WebIDL but that is not infeasible.

Anyhoo - something to consider when thinking about this. I don't know if the above is feasible with current architecture and I am not yet at work to eyeball the code. But I guess what I am asking that if you were to add plugins that could run at various stages. I would love to be able to write a plugin that ran on the MetaData model AST as well as plugins that ran on the Java model ;)

gkdn commented 5 years ago

jsinterop generator could generate the support calls while externally it could be provided by elemental2 for which APIs to generate it for.

Being said that, in jsinterop-base we can probably just add the nestedHas utility (in analogy to nestedGet) so you can do

Js.global().nestedHas("my.new.feature")

and elemental2 will put a nicer looking helper on Global so user code will look like:

DomGlobal.hasFeature("my.new.feature")

I think this will be good enough.

jDramaix commented 5 years ago

The advantage of that is that we could read WebIDL from either specs, Chrome and/of Firefox and generate a relatively maintenance free, accurate API for interacting with the browsers

The initial version of the jsinterop-generator was using WebIDL as input instead of extern files and we gave up. IIRC, it was mainly because:

This would mean that we could throw away integer_entities.txt and name_mappings.txt

The initial version of integer_entities.txt has been generated from WebIdl. We could do the same with name_mappings.txt

We could also inline constants into java code rather than using externs to access them.

But then you are starting to generate code for them. With J2CL + closure-compiler, after compilation you don't have specific code for accessing the constant (other than the referring directly to the name of the constant).

realityforge commented 5 years ago

...snip...

DomGlobal.hasFeature("my.new.feature")

That is certainly a better place than where we are now and would probably work in most cases.

realityforge commented 5 years ago

The initial version of the jsinterop-generator was using WebIDL as input instead of extern files and we gave up.

Interesting.

  • the type defintion in WebIdl does not contains enough information for generating good java code (absence of Parametrized type for example).

Interesting. I had not though about this for browser APIs. Handling elemental-core would be doable ... but the browser APIs would need a custom pass. Hmmm I guess it is much like that tool that generates the typescript definitions of the browser from WebIDL. Although this does add a lot more infrastructure requirements to the project

  • With J2CL you need to provide extern files otherwise the closure compiler will complain. If you use WebIdl as sources your generator needs now to generate those extern files and you start to get in trouble because the generated extern file are different that the one used by the closure compiler.

Yes - you would need to use a custom env and bring in the right externs but this I don't see as too much of an issue ... at least external to google. Part of the reason I was contemplating WebIDL was because it is taking a lot of effort to get closures externs to match the spec. Not so much because writing the code is hard but because I suspect a lot of google internal code relies on the existing definitions ;)

The initial version of integer_entities.txt has been generated from WebIdl. We could do the same with name_mappings.txt

Yep. Or just use a process like read from closure externs first, then WebIDL second and thrdly use overrides if no other case covered.

We could also inline constants into java code rather than using externs to access them.

But then you are starting to generate code for them. With J2CL + closure-compiler, after compilation you don't have specific code for accessing the constant (other than the referring directly to the name of the constant).

I am not sure I understand. What I want to do (and what we have patched the tool to do locally) is to represent WebSocket.OPEN as public static final int OPEN = 1 rather than using it via looking up the browser. When done across a whole range of constants this can reduce the output code size a bit ... at least for smaller projects.