Closure Compiler support #4

Open
wants to merge 8 commits into from

3 participants

@dsheets

Hi Jake,

I've removed the trampoline arity restriction to accomodate curried object constructors with many arguments. I've also added caml_mod_float and fixed a string literal printing bug (the native caml_is_printable returns true for 127-255).

Let me know if there are changes you find unpalatable and I can revert them. Feel free to comment inline.

Best,

David

@avsm

Is this function for debugging or closure support?

This is to define the print symbol that print_token below relies on. Closure doesn't have a problem with 'print' being undefined unless you turn warnings on VERBOSE at which point it becomes an error. o_O

I defined this function here to satisfy both c-c's normal and strict checking.

@jaked

Does this change have to do with ocamljs representing ints as js numbers? Js numbers (being doubles) can represent ints up to 2^52 exactly, so I don't understand the intention here.

Also, changes to stdlib should be changed across all OCaml versions.

My intention was to change as little as possible while still getting the numerical semantics correct. Closure-compiler is quite adamant that bit-shifting left by 62 is out of range for js numbers. Left shifting 1 by an integer never results in 0 in Webkit (perhaps the spec says something else). I simply changed the 1 to a 2, abusing Javascript's shift operator quirks so that the condition passes.

As for JS number semantics, I'm not sure that OCaml can or should try to use them. The int type in ocaml admits shifting past the modulus and has wrapping. In javascript, that behavior appears to occur at 32 bits.

The difference in the bit shift operators may also belie a translation bug between the native shift operations and the JS shift operations.

When we nail down these diffs, I will look into porting them to the other OCaml versions. For now, I only have 3.11.2 installed on my machines and restricting it to this version only makes testing faster. Is this reasonable? Do you have suggestions for a better workflow?

Owner

Got it. Not sure I understand your 2nd paragraph... you mean we should try to emulate OCaml int semantics in ocamljs? js_of_ocaml does this, at a heavy performance penalty. I am not very interested in getting the semantics to match, since nobody is going to port bit-banging code to ocamljs.

No problem working just on the 3.11.2 branch for now, but I don't want to take patches that don't cover all versions. For something small like this it would be OK to just make the change on the other versions and hope for the best. I'd rather have a version be broken but include the change than to forget a change which should be applied to all versions; that would be confusing.

I see what you mean re: js_of_ocaml. We certainly can't introduce conditionals that check for wrapping onto every integer op. Logical left shift, arithmetic left shift, and logical right shift should perform consistently with addition/subtraction, multiplication, and exponentiation, however.

Perhaps the answer is to provide correct bit-banging ops for Javascript's increased precision ints and emulate Int32 using JavaScript's shift ops plus special arithmetic ops that check bounds.

To maintain correctness, something has to change to harmonize the integer representation. Even a warning about different bit-shifting semantics might suffice so that users know their ocaml has not been translated faithfully. This is a hard problem. Thanks for talking about it.

Owner

If we translate lsl, asl, lsr and +/-/* to the equivalent Javascript operators, do we not get something that is consistent in your sense? But of course it is not an int in the usual sense.

Yes, maybe it would be best to provide correct int32s (following js_of_ocaml) but let ints be weird. (One hassle is that ocamljs compiles from the lambda code, which is not typed; however in this case I think it is only the operators that need to be special, and we know in lambda code what the int32 operators are.)

I like the approach of providing correct but slow int32 and leaving int with native semantics. After all, it's fine in UNIX/OCaml for native ints to be 31/63-bits and a little strange towards the edges as a result...

If int stays at native precision, native int bitwise ops need to operate at that precision. Right now, arithmetic ops run with 52 bits of precision but bitwise ops run with 32 bits.

(5*(1<<30)) | 0;
1073741824

(5*(1<<30));
5368709120

Int32 will only need "<<0" or "|0" appended to most operations to mimic 32-bit precision machine ints, I believe.

We need to write 52-bit-wide bitwise operations for javascript ints if we report max_int at that precision so cross-compiled ocaml code doesn't unknowingly left-truncate bits. As for performance, this only affects "standard precision" bitwise operations which the programmer should probably be doing on known-precision variables anyway (int32). Int32 will then have the opposite trade-off of super-cheap bitwise ops but 2x expensive arithmetic ops (first the real op, then an identity bitwise op to wrap if needed).

If this is copasetic, I'll start work on it in the next few days.

Owner

Argh, ok, got it. I didn't realize Javascript worked that way.

How about we just report the width as 32 bits in pervasives.ml? We are no worse off with arithmetic ops, which can overflow to floats anyway.

@jaked

The point of these defs is to support OUnit (running in a patched Spidermonkey, see ocamljs/src/ounit/README). Maybe it would be better to modify OUnit to call through some other primitives and leave these unimplemented.

I see two issues with these defs:

  1. Where is print_verbatim defined? Closure-compiler could not find it and neither can I. Perhaps as an external in a patched binary?
  2. caml_ml_output_char should at least pretend to do something like its name implies or tell you that it cannot.

I tried to, again, do the simplest possible thing and mimic the other unimplemented runtime functions.

What feature of OUnit requires a patched Spidermonkey?

Owner

Take a look at that README; print_verbatim is a new primitive added by the patch. The problem is that regular print() always adds a newline (in Spidermonkey at least) so there is no way to implement stdout. But as I said above perhaps it would be better just to hack OUnit not to use stdout at all, and leave these functions unimplemented to avoid confusion.

Is there any way to tell closure-compiler that a particular function is actually defined? The only way to output in Spidermonkey is this print() function that adds newlines, so I don't see how to avoid the patch. (Well, maybe this OUnit-specific output channel could collect output until it sees a newline then print() it at once.)

I see. It is possible to declare functions as external to c-c's working set which would get around this issue, at least temporarily.

It seems to me that the output channel semantics you want are OCaml's which in turn are POSIX's? You suggested something like line-buffering. Obviously this doesn't solve the unbuffered write problem but perhaps that is done through a callback? Then you can make an HTML div into something like a terminal with innerHTML or innerText or what-have-you. Do we need page-buffered mode? Like for the filesystem?

Owner

I don't think output channels are very useful in ocamljs, although I guess they could be for porting existing code (to a web-based console as you suggest). For OUnit I just use the command-line Spidermonkey though.

One approach would be to implement channels as Javascript objects (without any semantics provided by ocamljs, so the primitives just call methods on the objects), which the calling code could pass in somehow in order to implement a console.

Delegating to a javascript object sounds like a great solution that gives users a lot of flexibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment