#2501 ClosureFuncSpecs for non JS classes

SlimerDude Sat 26 Dec 2015

I would expect the following code to run just fine in Javascript because the class JavaOnly is not referenced in any way:

@Js
class Example {
    Void main() {
        if (Env.cur.runtime == "java")
            obj := JavaOnly { it.msg = "wotever" }
    }
}

class JavaOnly {
    Str msg
    new make(|This| in) { in(this) }
}

But instead it causes this error:

Uncaught sys::UnknownTypeErr: afAlifeGui::JavaOnly
  fan.sys.Pod.type         @ sys.js:6915
  fanx_TypeParser.find     @ sys.js:12303
  fanx_TypeParser.load     @ sys.js:12284
  fan.sys.Type.find        @ sys.js:8854
  fan.sys.Param.$ctor      @ sys.js:6849
  f                        @ sys.js:10
  fan.sys.ClosureFuncSpec$ @ sys.js:3521
  (anonymous function)     @ acme.js:755

caused by this auto-generated code:

fan.acme.$clos$_u0 = new fan.sys.ClosureFuncSpec$(fan.sys.Void.$type,["it","acme::JavaOnly","false"]);

The code is generated by compilerJs::JsClosure.write() - and is generated because of the it-block. I looked at not writing ClosureFuncSpec vals for non JS classes, but I couldn't work out how to get hold of the type facets.

Re-writing the code not to make use of an it-block ctor fixes the issue:

@Js
class Example {
    Void main() {
        if (Env.cur.runtime == "java") {
            obj := JavaOnly()
            obj.msg = "wotever"
        }
    }
}

class JavaOnly {
    Str? msg
}

Note I believe this issue was introduced in Fantom 1.0.68.

andy Mon 28 Dec 2015

That was probably one of those cases that shouldn't have worked :) We did add some optimization to cache closure typing - which is why you see it now. Removing the with block is the right workaround for now.

SlimerDude Sun 7 Feb 2016

Hi Andy,

Yeah, I see the optimisation - it looks good.

Though I still firmly believe the above code should work - it is perfectly valid Fantom code. The optimisation shouldn't even attempt to touch the JavaOnly class because it is not annotated with @Js.

It's been mentioned in other topics that cross platform libraries are a great boon to Fantom. And being able to toggle snippets of code on and off at runtime is a great way to get over the idiosyncrasies of running on different platforms.

andy Mon 8 Feb 2016

I don't know - still seems like bad practice to mix code like that - and there are clearer ways to handle that. You have a real use case for that pattern?

SlimerDude Mon 8 Feb 2016

You have a real use case for that pattern?

You mean checking the runtime? Yeah, I do it loads in most of my cross-platform libraries. Funcs, files, images, FWT components, and more, all require special handling in JS. Here are just a couple of examples from many:

// afReflux
// CTab is Java only
tab = Env.cur.runtime == "js" ? Tab() : CTab()

...

// afReflux
// in JS, empty panes don't collapse so set their weight to zero 
if (Env.cur.runtime == "js") { ... }

...

// afIoc
// we forego immutable checks in JS 
if (Env.cur.runtime == "js") return

...

// afIoc
if (obj is Func && Env.cur.runtime == "js")
  throw Err("Immutable funcs are not available in Javascript: ${key.qname}\nSee http://fantom.org/forum/topic/114 for details.")

brian Mon 8 Feb 2016

I would have to give it some thought, but I don't think code that can't be run should be allowed inside a @Js class - to me that is very much like allowing some weird sort of if/block dependency. However, I do think it should be noted at compile time with an extra check.

SlimerDude Wed 10 Feb 2016

I think it's just peachy as it is right now in Fantom 1.0.67.

The above code generates the following compiler warning:

Type 'acme::JavaOnly' not available in Js

So you're alerted to the fact that it may not work - but as it is fenced by the runtime check you know it won't get executed.

The only problem I have is with Fantom 1.0.68. The generated Javascript errors because the optimisation code runs for all types, whereas it should just run for @Js types.

matthew Wed 10 Feb 2016

Ticket promoted to #2501 and assigned to matthew

Think I have a fix for this. Will post soon.

matthew Wed 10 Feb 2016

Ticket resolved in 1.0.68

When writing the closure, we check its signature and if any of param types or the return type is non-Js, then we won't write the ClosureFuncSpec, but instead we write a dummy closure that indicates this condition. If the code never executes, then we won't get any type errors at runtime.

LightDye Thu 11 Feb 2016

Ticket resolved in 1.0.68

That is awesome. Has the release date of v1.0.68 been decided yet, please?

Login or Signup to reply.