On Sat, Nov 1, 2014 at 3:33 AM, Manuel Carrasco Moñino <[email protected]>
wrote:

> After reading, and playing with some stub code based on these ideas, I
> come to the conclusion than 'native' is almost perfect from the point of
> view of a gwt developer.
>
> We want pure java syntax and simplicity, and any other conventions like
> DSLs or magic interface names, etc, could obscure the understanding
> existing code,  or trying to figure out how to implement something.
>
> For java developers 'native' means that the platform knows how to deal
> with this method, and in the case of gwt this is true and intuitive,
> meaning that 'this is native because it's implemented natively in JS'.
>
> The compiler ability to automatically resolve native methods, could be
> very useful not only for JsInterop but for many other cases where we use
> them.
>
> Hence any getter or setter could be automatically written by the compiler
> if it does not have the jsni fragment. For instance in
> c.g.g.d.c.StyleElement we could get rid of all of the jsni fragments and
> just write methods declaration
>
>  public final native void setCssText(String cssText);
> If the compiler does this, we could save thousands of line of code in jsni
> fragments in the gwt core, also in elemental, and in several other 3 party
> libraries.
>
> Even, we could have an annotation to deal with simple JSNI code instead of
> using c++ comment blocks to change the default implementation:
>
>  @Jsni("return $doc.head || $doc.getElementsByTagName('head')[0]")
>  public final native void getHead();
>
>
I don't have any intention to go beyond what java supports in terms of
native methods. The JSNI annotation just add another obscure way of doing
what could do with js(....) statements.



>
> Also it would be nice if the compiler is able to deal directly with type
> conversion and it is able to box returned values: java arrays to jsArrays,
> js numbers to java types, etc. This will make gwt developer experience
> better.
>
>
This is in the plans and we are planning to support something like
JsConvert (see the doc).


> Related with testing, I think that because we already use the GWTTestCase
> to run JRE tests (returning module null), it should be not difficult to
> modify it and return proxies of classes implementing native methods ant
> throwing sensible messages to the user telling what methods are used, etc.
>
> Coming back to JsInterop, I agree that native methods is an easy solution,
> probably not so difficult to implement, and definitively solves many
> problems like inheritance, static methods, constructors, etc.
>
>
>
>
> On Sat, Nov 1, 2014 at 6:52 AM, 'Goktug Gokdogan' via GWT Contributors <
> [email protected]> wrote:
>
>> I finally had some time to think about JsInterop and how to abstract
>> browser APIs.
>>
>> I will try to summarize my thought process and different options that I
>> have played with so that you could get a better understanding where I'm
>> coming from and I hope it will provide good documentation for future.
>>
>> So I started by playing with the static functions, constructors and how
>> to represent that with the current JsInterop semantics.
>>
>> First I started with the original approach: use @JsType on an interface
>> and use some simple conventions to generate the rest:
>>
>> @JsType(prototype = "Object")interface JsObject extends Auto_JsObject {
>>
>>   static JsObject of(Object obj) {
>>     return obj instanceof JsObject ? (JsObject) obj : null;
>>   }
>>
>>   interface Static {
>>     String[] keys(JsObject obj);
>>     JsObject defineProperties(JsObject obj, JsObject props);
>>   }
>>
>>   interface constructors {
>>     void constructor(String param);
>>   }
>>
>>   boolean hasOwnProperty(String prop);
>>   boolean isPrototypeOf(JsObject prop);
>> }
>>
>> ​
>> then with the annotation processor following code will be generated:
>>
>> interface Auto_JsObject {
>>
>>     public static JsObject newInstance(String param) {
>>       return js("new Object($0)", param);
>>     }
>>
>>     public static String[] keys(JsObject obj) {
>>       return js("Object.keys($0)", obj);
>>     }
>>
>>     public static JsObject defineProperties(JsObject obj, JsObject props) {
>>       ...
>>     }
>>
>>     @PrototypeOfJsType(JsObject.class)
>>     public static class prototype {
>>        JsObject constructor(String param) { return null}
>>        boolean hasOwnProperty(String prop) { return false; };
>>        boolean isPrototypeOf(JsObject prop) { return false; };
>>     }
>> }
>>
>> ​
>>
>> And it looks like following when used:
>>
>>   MyObject extends JsObject.prototype {}
>>
>>   JsObject.keys( ... );
>>
>>   JsObject.newInstance( ... );
>>
>>   JsObject.of(new Object());
>>
>> ​
>> One of the advantages of this is; I can additionally generate a special
>> method named statics() that returns an instance of JsObject.Static where
>> we forward all calls to the static methods. This is pretty useful for
>> people who care about pure unit testing.
>>
>> Although this solution is not ideal due to need for extending a separate
>> class (i.e. JsObject.prototype), it looks reasonably good at first sight.
>> But there is one major gotcha: it doesn't work!
>>
>> The reason is, according to here
>> <http://docs.oracle.com/javase/tutorial/java/IandI/override.html> unlike
>> static methods in real classes, static methods in interfaces are not
>> 'inherited' by subclasses.
>>
>> So I stepped backed and started questioning what we are doing here.
>>
>> We basically creating a DSL but this DSL is trying to enhance the very
>> same class where it is used. This model isn't directly supported with
>> annotation processors (that's why we do the trick of extending generated
>> class) and we already decided not to use Lombok hacks so it looks like we
>> can't do much here.
>>
>> So I'm thinking; as we are already defining a DSL why not make it more
>> explicit in a separate class and let it generate the actual API?
>>
>> Here we go:
>>
>> @JsApi("Object") // A new annotation for the DSL, not required to be in the 
>> SDKinterface __JsObject { // Used underscore so it doesn't show up in code 
>> completion
>>
>>   String[] keys(JsObject obj);
>>
>>   JsObject defineProperties(JsObject obj, JsObject props);
>>
>>   static JsObject of(Object obj) {
>>     return obj instanceof JsObject ? (JsObject) obj : null;
>>   }
>>
>>   interface prototype {
>>     boolean hasOwnProperty(String prop);
>>     boolean isPrototypeOf(JsObject prop);
>>   }
>>
>>   interface constructors {
>>     void constructor(String param);
>>   }
>> }
>>
>> ​
>> generates following:
>>
>> @JsType(prototype = "Object")interface JsObject {
>>   static JsObject newInstance(String param) {
>>     return js("new Object($0)", param);
>>   }
>>
>>   static String[] keys(JsObject obj) {
>>     return js("Object.keys($0)", obj);
>>   };
>>
>>   static JsObject defineProperties(JsObject obj, JsObject props) {
>>      ...
>>   }
>>
>>   static JsObject of(Object obj) {
>>     return __JsObject.of(obj);
>>   }
>>
>>   boolean hasOwnProperty(String prop);
>>   boolean isPrototypeOf(JsObject prop);
>>
>>   public static JsObject defineProperties(JsObject obj, JsObject props) {
>>     ...
>>   }
>>
>>   @PrototypeOfJsType(JsObject.class)
>>   public static class prototype {
>>      JsObject constructor(String param) { return null;}
>>      boolean hasOwnProperty(String prop) { return false; };
>>      boolean isPrototypeOf(JsObject prop) { return false; };
>>   }
>>
>> }
>>
>> ​
>> So this is basically a working version of the previous one.
>>
>> One may argue that if we are committing to use some kind of DSL why not
>> use something else like IDL or xml etc. There are 3 main reasons to use
>> java + APT instead of others:
>>
>> First, our user base already familiar with java so they don't need to
>> learn anything new other than a few simple naming conventions. Also
>> developers need to able to write java code snippets for helper functions
>> (see JsObject.of) which plays well with this approach.
>>
>> Second, IDE will give free syntax highlighting, checks and auto
>> completion for whole process.
>>
>> Third, logistics for APT based code generation is already provided by
>> IDEs, build systems and very likely will be provided by superdev in the
>> future.
>>
>>
>> Long story short; this is quite feasible and viable option but there are
>> still some stuff I don't like about it:
>>
>>  - JsObject.protoype is still an annoying artifact that all API
>> consumers need to know about.
>>  - I don't like the compiler silently ignoring some method bodies (i.e.
>> JsObject.prototype::*).
>>  - There is plenty of code generation going on which causes extra
>> indirections.
>>
>>
>> So I stepped back one more time and decided give a short break to my
>> strong prejudice on native methods and created a solution around native
>> methods:
>>
>> @JsType(prototype="Object")public class JsObject {
>>
>>   @JsConstructor
>>   public static native JsObject newInstance(String param);
>>
>>   public static native String[] keys(JsObject obj);
>>
>>   public static native JsObject defineProperties(JsObject obj, JsObject 
>> props);
>>
>>   public static JsObject of(Object obj) {
>>     return obj instanceof JsObject ? (JsObject) obj : null;
>>   }
>>
>>   public native boolean hasOwnProperty(String prop);
>>   public native boolean isPrototypeOf(JsObject prop);
>> }
>>
>> ​
>> This solution is perfect from a purist point of view. No code generation
>> involved; we basically utilize the host language's extension point created
>> solely for this purpose. Here we are saying that this methods exist in the
>> runtime and implemented natively by the platform.
>>
>> Pros:
>> It is intuitive for java developers. There is no magic prototype class to
>> extend from, no conventions or DSL, no code generators. It is more explicit
>> and it is very clear where the native boundary is when you look at the
>> code. Probably it is even more pleasing to implement in the compiler.
>>
>> All the cons are around the testability aspect. For JRE testing, native
>> methods are a headache. Also we no longer generate an interface for static
>> methods. We are basically going back to square one with respect to
>> testability.
>>
>> But on the other hand it is not horrible. For people who insists on JRE
>> testing; we can provide a classloader that stubs all native methods or they
>> can use a more powerful mocking library like powermock or gwt-mockito.
>>
>> I would like to first get high level feedback here before discussing this
>> internally. I'll update the design doc if we conclude on something.
>>
>> - Goktug
>>
>> --
>> 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 [email protected].
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAN%3DyUA2DvnfqtL1F4zd_ydM4KdWx9bBXCdtY7RGs_vaVvh88%2Bg%40mail.gmail.com
>> <https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAN%3DyUA2DvnfqtL1F4zd_ydM4KdWx9bBXCdtY7RGs_vaVvh88%2Bg%40mail.gmail.com?utm_medium=email&utm_source=footer>
>> .
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>  --
> 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 [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAM28XAtxB%3DZwRGfKxGVN-qa-%3DKk1jSZAMsq-0zsKTGRWuqzFZw%40mail.gmail.com
> <https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAM28XAtxB%3DZwRGfKxGVN-qa-%3DKk1jSZAMsq-0zsKTGRWuqzFZw%40mail.gmail.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/google-web-toolkit-contributors/CAN%3DyUA2Dn2hUSBsGf%2BkHOiN6Rd2Gtzq1jz8RBmz6qikZjFiODQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to