#1109 Wrapping a javascript library in a (native classes only) pod, to be referenced by other pods

dfreire Fri 4 Jun 2010

Hello,

I'm trying to wrap a JavaScript library in Fantom native code.

My pod looks like this:

my_pod/
  fan/
    JsObjectWrapper.fan (native class)
  js/
    JsObjectWrapperPeer.js
  res/
    JsObject.js
  build.fan

So, the idea is simple, I want to have a JsObjectWrapper.fan class for each JsObject.js object, so I can use the JavaScript library in other Fantom code.

Now, the first thing to notice is that if I build my pod with this structure, I will get a successful build, but there's no js code included inside the built pod. Maybe this is intentional behavior, since JsObjectWrapper.fan is not referenced by any other code.

So I have created a fan/JsObjectWrapperDemo.fan (@Js class) that references fan/JsObjectWrapper.fan. This forces the build to generate the javascript file.

The generated javascript code works fine (I have tried to copy it manually from inside the pod, and created an html file file that glues it together with JsObject.js)

It seams that creating a test/JsObjectWrapperTest.fan (@Js class) that references fan/JsObjectWrapper.fan will reach the same result, i.e. the javascript code gets generated. But, the generated javascript code also includes the test code (as expected) and that seems awkward in production environments.

And then I have tried a second scenario where I have two pods:

my_pod/
  fan/
    JsObjectWrapper.fan (native class)
  js/
    JsObjectWrapperPeer.js
  res/
    JsObject.js
  build.fan

my_pod_demo/
  fan/
    JsObjectWrapperDemo.fan (@Js class)
  build.fan

As before JsObjectWrapperDemo.fan references JsObjectWrapper.fan.

Now building my_pod does not generate any javascript, but building my_pod_demo does. The problem is that the my_pod_demo generated javascript misses some relevant code (namely the declarations for the fan.my_demo var)

So, my overall purpose is to have my native classes (and their peers) in my_pod. And create other pods with Fantom classes that use the native code of my_pod. I will appreciate any help.

Thanks in advance :-)

brian Sat 5 Jun 2010

Couple issues there...

The behavior of whether to compile JS or not was triggered if any classes had the @Js facet - see source code. But I think that maybe one case missing was that there was a jsDir of JavaScript code (even if no @Js types).

I pushed a fix for that - changeset. With that change if you have a pod with JS source code, it will always generate the compressed JS file in the pod zip. So you shouldn't need to use res at all - just stick all your JS code in your js/ directory (all the files get coalesced into one big JS file per pod during compilation).

But, the generated javascript code also includes the test code (as expected) and that seems awkward in production environments.

Regarding stripping test code, we have discussed that and eventually need to do something. But since it is just an optimization we haven't gotten around to it yet.

dfreire Sat 5 Jun 2010

Hi Brian,

I have tested your fix, and I confirm that now the JavaScript code is being generated. However, I'm still having some problems, maybe I have tried to do too much of my first attempt - as you say "Couple issues there..." :-)

So, I have decided to start a step-by-step approach increasing the complexity slowly. I am putting everything together under http://github.com/dfreire/hello-fantom so the code snippets can be useful for others (and for myself as I forget how have I done things in the past).

Starting with step 1, the simplest JavaScript wrapper I can think of, Firebug complains with the error:

fan.helloJsStep1.JsLibWrapper is undefined

caused by:

// helloJsStep1.js (line 11)
var lib = fan.helloJsStep1.JsLibWrapper.make();

You can try it for yourself opening the helloJsStep1.html in the sandbox directory. (You can also see the generated js file there.)

Can you tell me what am I doing wrong?

Thanks!

andy Mon 7 Jun 2010

Quick survey of your code and looks like your missing the @Js facet on your JsLibWrapper class. Should work after that - let me know if you run into additional issues.

dfreire Tue 8 Jun 2010

Hi Andy, thanks for taking a look at this :-)

Simply adding @Js to JsLibWrapper causes the build to fail

compile [helloJsStep1]
  Compile [helloJsStep1]
    FindSourceFiles [2 files]
    CompileJs
JsLibWrapper.fan: Missing native impl for helloJsStep1::JsLibWrapper

and of course, the native impl exists, it's JsLibWrapperPeer.js

Btw, I'm using the latest Fantom version from mercurial, not the official 1.0.53.

JohnDG Tue 8 Jun 2010

Any plans to implement FFI for JavaScript? Something like:

using [javascript] console as Console:Obj
using [javascript] XMLHttpRequestObj as HttpRequest:Class

...
Console->info('hello world')

h := HttpRequest()
h->onreadystatechanged = ...

andy Tue 8 Jun 2010

Ah yeah, pure native classes work a bit different than partial natives. They more or less fully delegate to the JavaScript implementation - so they look more like the sys classes - they don't take self and don't use the "Peer" designation (so use same file and class name as Fantom type). These changes should get you up and running:

  • Mark Fantom class with @Js facet
  • Rename Peer to simply JsLibWrapper
  • Remove self func arguments
  • Provide static make constructor
// JsLibWrapper.fan
@Js native class JsLibWrapper
{
  Void say(Str message)
}

// JsLibWrapper.js
fan.helloJsStep1.JsLibWrapper = fan.sys.Obj.$extend(fan.sys.Obj);
fan.helloJsStep1.JsLibWrapper.prototype.$ctor = function() {}
fan.helloJsStep1.JsLibWrapper.make = function() 
{
  return new fan.helloJsStep1.JsLibWrapper(); 
}
fan.helloJsStep1.JsLibWrapper.prototype.say = function(message) { LIB.say(message); }

andy Tue 8 Jun 2010

@JohnDG - yeah we need to get that working. The function delegation is pretty straightforward using dynamic invoke. But haven't come up with a good way to create the object instance to begin with.

dfreire Tue 8 Jun 2010

Got it, now it works properly. Off I go to the next steps...

Login or Signup to reply.