Haxe for javascript without global namespace pollution? - javascript

This question only applies to Haxe version < 2.10
I've known about haxe for a while, but never really played with it until yesterday. Being curious, I decided to port showdown.js, a javascript port of markdown.pl, to haxe. This was pretty straightforward, and the javascript it generates seems to run fine (edit: If you want to see it in action, check it out here).
However, I noticed that the generated code dumps a ton of stuff in the global namespace... and what's worse, it does it by assigning values to undeclared identifiers without using the var keyword, so they're global even if you wrap the whole thing with a closure.
For example...
if(typeof js=='undefined') js = {}
...
Hash = function(p) { if( p === $_ ) return; {
...
EReg = function(r,opt) { if( r === $_ ) return; {
...
I managed to clean most of that up with sed, but I'm also bothered by stuff like this:
{
String.prototype.__class__ = String;
String.__name__ = ["String"];
Array.prototype.__class__ = Array;
Array.__name__ = ["Array"];
Int = { __name__ : ["Int"]}
Dynamic = { __name__ : ["Dynamic"]}
Float = Number;
Float.__name__ = ["Float"];
Bool = { __ename__ : ["Bool"]}
Class = { __name__ : ["Class"]}
Enum = { }
Void = { __ename__ : ["Void"]}
}
{
Math.__name__ = ["Math"];
Math.NaN = Number["NaN"];
Math.NEGATIVE_INFINITY = Number["NEGATIVE_INFINITY"];
Math.POSITIVE_INFINITY = Number["POSITIVE_INFINITY"];
Math.isFinite = function(i) {
return isFinite(i);
}
Math.isNaN = function(i) {
return isNaN(i);
}
}
This is some pretty unsavory javascript.
Questions
Is there a fork or clone of haxe somewhere that doesn't pollute globals? Is it worth it to modify the haxe source to get what I want, or has someone already solved this? Googling hasn't turned up much. I'm open to any suggestions. Meanwhile, I'm dying to see what kind of PHP code this thing's going to produce... :D
Answers?
Here are some of the ideas I've tried:
postprocessing
Here's my humble build script; it does a pretty good job of stripping stuff out, but it doesn't catch everything. I'm hesitant to remove the modifications to the built-in constructor prototypes; I'm sure that would break things. Fixing everything might be a bit of a task, and I don't want to start on it if someone's already done the work...
haxe -cp ~/Projects/wmd-new -main Markdown -js markdown.js
echo "this.Markdown=(function(){ var \$closure, Float;" > markdown.clean.js;
sed "s/^if(typeof js=='undefined') js = {}$/if(typeof js=='undefined') var js = {};/g ;
s/^\([ \x09]*\)\([\$_a-zA-Z0-9]* = \({\|function\)\)/\1var \2/g ;
/^[ \x09]*\(else \)\?null;$/d ;
" markdown.js >> markdown.clean.js
echo "return Markdown}());" >> markdown.clean.js;
java -jar closure/compiler.jar --js markdown.clean.js \
--compilation_level SIMPLE_OPTIMIZATIONS \
> markdown.cc.js
--js-namespace switch saves the day
Thanks to Dean Burge for pointing out the namespace switch. This pretty much solved my problem, with a minor bit of help. Here's my current build script. I think this catches all the global variables...
NS=N\$
haxe -cp ~/Projects/wmd-new -main Markdown --js-namespace $NS -js markdown.js
# export our function and declare some vars
echo "this.markdown=(function(){var \$_,\$Main,\$closure,\$estr,js,"$NS"" > markdown.clean.js;
# strip silly lines containing "null;" or "else null;"
sed "/^[ \x09]*\(else \)\?null;$/d ;" markdown.js >> markdown.clean.js
# finish the closure
echo "return "$NS".Markdown.makeHtml}());" >> markdown.clean.js;

I use the namespace switch on the compiler to clean those global root types up.

Haxe is not meant to be used for writing an isolated reusable component in a javascript web application. This is evidenced by the fact that the compiler emits standard library for every goddamn compilation. Most optimal use of javascript target is to write an application entirely in haxe and call external stuff using untyped blocks hoping it won't break anything. You should treat haxe output like a flash clip, oblivious to the environment it runs in, assumes it is the only thing running.
Or you might try wrapping the code with a with() block.

there's a namespaced (experimental) haxe compiler here http://github.com/webr3/haxe

The JSTM JavaScript generator macro optimizes haxe output in a number of ways:
the javascript output is split into seperate files per type
these files are optimized
a loader script loads the required types asynchronously
only one global variable is used: jstm
only code that is actually required to run your app is downloaded
new types can be loaded at runtime which makes possible highly scalable apps
check out http://code.google.com/p/jstm/ for more info.

Related

Can V8 perform build-time precompilation of JS code?

We're trying to optimize for start-up time of JS code on mobile and looking for the opportunities. I've found Facebook Hermes JS engine created for a similar purpose but we heavily depend on V8 at the moment.
Can build-time precompilation be done with V8 meaning parsing and code optimization will be done in compile-time and saving some time in runtime? Generating LLVM bitcode from the source code and executing bitcode in runtime seems to be pretty close to what i imagine. WASM seems to be not an option (at least for mobile).
If it's possible, can one provide a simple example of trivial JS code optimized with V8?
PS. Probably it would also help with memory consumption which can be the secondary goal.
V8 supports heap snapshots for this very purpose. It's used by the Atom editor, for instance, to improve startup time. It's not so much about precompiling as it is about prebuilding your global environment and instantiating your functions (which may not be compiled [yet], just converted to bytecode for Ignition, which is sufficient). If you're using Electron, the mksnapshot npm package may be useful. (And if not, looking at how it works may still be useful.)
I haven't done any V8 hacking, but the example they link from the blog post above is as follows:
TEST(PerIsolateSnapshotBlobs) {
DisableTurbofan();
const char* source1 = "function f() { return 42; }";
const char* source2 =
"function f() { return g() * 2; }"
"function g() { return 43; }"
"/./.test('a')";
v8::StartupData data1 = v8::V8::CreateSnapshotDataBlob(source1);
v8::StartupData data2 = v8::V8::CreateSnapshotDataBlob(source2);
v8::Isolate::CreateParams params1;
params1.snapshot_blob = &data1;
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate1 = v8::Isolate::New(params1);
{
v8::Isolate::Scope i_scope(isolate1);
v8::HandleScope h_scope(isolate1);
v8::Local<v8::Context> context = v8::Context::New(isolate1);
delete[] data1.data; // We can dispose of the snapshot blob now.
v8::Context::Scope c_scope(context);
CHECK_EQ(42, CompileRun("f()")->ToInt32(isolate1)->Int32Value());
CHECK(CompileRun("this.g")->IsUndefined());
}
isolate1->Dispose();
v8::Isolate::CreateParams params2;
params2.snapshot_blob = &data2;
params2.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate2 = v8::Isolate::New(params2);
{
v8::Isolate::Scope i_scope(isolate2);
v8::HandleScope h_scope(isolate2);
v8::Local<v8::Context> context = v8::Context::New(isolate2);
delete[] data2.data; // We can dispose of the snapshot blob now.
v8::Context::Scope c_scope(context);
CHECK_EQ(86, CompileRun("f()")->ToInt32(isolate2)->Int32Value());
CHECK_EQ(43, CompileRun("g()")->ToInt32(isolate2)->Int32Value());
}
isolate2->Dispose();
}
That blog post (and the associated example?) is from 2015 so things have probably moved on since.

Jasmine shared specs scoping issues with coffeescript

I'm attempting to DRY up some jasmine tests by extracting out shared examples.
#sharedExamplesForThing = (thing) ->
beforeEach ->
#thingy = new thing
it "is neat", ->
expect(#thingy.neat).toBeTruthy()
describe "widget with shared behavior", ->
sharedExamplesForThing(-> new Widget)
This works nicely when everything is defined in one file. The problems I'm encountering occur when I try to move the sharedExamples to a separate file. I get Can't find variable: sharedExamplesForThing ...
So in the interest of debugging, I tried the following:
describe "widget with shared behavior", ->
it "is acting like a meany", ->
console.log sharedExamplesForThing
expect(false).toBeTruthy()
sharedExamplesForThing(-> new Widget)
In the is acting like a meany block, the log shows sharedExamplesForThing as [Function] but I still get the Can't find variable outside the it. I feel like this might have something to do with a scoping issue outside of my current experience, but I could be completely wrong about that. What am I missing here?
(using rails, jasminerice, guard-jasmine)
I found the piece on shared examples from thoughtbot really useful.
I implemented it in coffeescript as follows:
1) In some helper file that is loaded before all spec files:
window.FooSpec =
sharedExamples: {}
window.itShouldBehaveLike = (->
exampleName = _.first(arguments)
exampleArguments =
_.select(_.rest(arguments), ((arg) => return !_.isFunction(arg)))
innerBlock = _.detect(arguments, ((arg) => return _.isFunction(arg)))
exampleGroup = FooSpec.sharedExamples[exampleName]
if(exampleGroup)
return describe(exampleName, (->
exampleGroup.apply(this, exampleArguments)
if(innerBlock) then innerBlock()
)
)
else
return it("cannot find shared behavior: '" + exampleName + "'", (->
expect(false).toEqual(true)
)
)
)
2) For my specs:
(a) I can define a shared behaviour:
FooSpec.sharedExamples["had a good day"] = (->
it "finds good code examples", ->
expect(this.thoughtbot_spy).toHaveBeenCalled()
)
(b) And use it anywhere in some spec as:
itShouldBehaveLike("had a good day")
(Note: I am assuming the spec has defined this.thoughtbot_spy accordingly before the above line)
When you assign a top-level member variable in CoffeeScript it is assigned as a property of the global object (window in a Browser). So it generates the following JavaScript:
window.sharedExamplesForThing = ...;
That means that you can refer to it outside the file as window.sharedExamplesForThing or just sharedExamplesForThing. So what you are doing should work assuming the shared example file has been loaded before the spec file. I think the problem you have having is that the spec file is loaded first (because describe functions are run as the file is loaded whereas it functions are run after all the files have loaded). So you might need to adjust the load order, you could try putting your shared examples files in a support directory and then requiring this first.
Rather than assigning variables directly to the window object it may be better to set up a namespace to export your shared variables into (so that you don't clutter the global object):
window.MyNamespace = {}
MyNamespace.sharedExamplesForThing = ...
Then in your spec file you can refer to it as MyNamespace.sharedExamplesForThing.
I find it helpful to look at the generated JavaScript to try to understand how CoffeeScript exports variables between files.
Here's the blog post I wrote about how best to do shared examples. Hope this helps.
http://pivotallabs.com/drying-up-jasmine-specs-with-shared-behavior/

node.js and browser code reuse: importing constants into modules

I have some constants in JavaScript that I'd like to reuse in several files while saving typing, reducing bugs from mistyping, keeping runtime performance high, and being useful on either the node.js server scripts or on the client web browser scripts.
example:
const cAPPLE = 17;
const cPEAR = 23;
const cGRAPE = 38;
...(some later js file)...
for...if (deliciousness[i][cAPPLE] > 45) ...
Here are some things I could do:
copy/paste const list to top of each file where used. Oh, Yuck. I'd rather not. This is compatible with keeping the constant names short and simple. It violates DRY and invites all sorts of awful bugs if anything in the list changes.
constant list ---> const.js
on browser, this is FINE ... script gets fed in by the html file and works fine.
but on node.js, the require mechanism changes the constant names, interfering with code reuse and requiring more typing, because of how require works....
AFAIK This doesn't work, by design, in node.js, for any const.js without using globals:
require('./const.js');
for...if...deliciousness[i][cAPPLE] > 45 ...;
This is the node.js way:
(... const.js ....)
exports.APPLE = 17;
(... dependency.js ... )
var C = require('./const.js');
for...if...deliciousness[i][C.APPLE] > 45.....
so I would either have to have two files of constants, one for the node.js requires and one for the browser, or I have to go with something further down the list...
3 make the constants properties of an object to be imported ... still needs two files... since the node.js way of importing doesn't match the browser. Also makes the names longer and probably takes a little more time to do the lookups which as I've hinted may occur in loops.
4 External constant list, internal adapter.... read the external constants, however stored, into internal structure in each file instead of trying to use the external list directly
const.js
exports.cAPPLE = 17
browser.js
const cAPPLE = exports.cAPPLE;
...code requiring cAPPLE...
node.js
CONST = require(./const.js)
const cAPPLE = CONST.cAPPLE;
...code requiring cAPPLE...
This requires a one-time-hit per file to write the code to extract the constants back out, and so would duplicate a bunch of code over and over in a slightly more evolved cut and paste.
It does allows the code requiring cAPPLE to continue to work based on use of short named constants
Are there any other solutions, perhaps a more experienced JavaScripter might know, that I might be overlooking?
module.exports = Object.create({},{
"foo": { value:"bar", writable:false, enumerable:true }
});
Properties are not writable. Works in strict mode unlike "const".
I would just make them global keys:
...(module consts.js)...
global.APPLE = 17;
global.PEAR = 23;
global.GRAPE = 38;
...(some later js file)...
var C = require('./const.js');
for (var i = 0; i < something.length; i++) {
if (deliciousness[i][global.APPLE] > 45) { blah(); }
}
They wouldn't be enforced constants, but if you stick to the ALL_CAPS naming convention for constants it should be apparent that they shouldn't be altered. And you should be able to reuse the same file for the browser if you include it and use it like so:
var global = {};
<script src="const.js"></script>
<script>
if (someVar > global.GRAPE) { doStuff(); }
</script>
You can make an object unwritable using Object.freeze .
var configs={
ENVIRONMENT:"development",
BUILDPATH:"./buildFiles/",
}
Object.freeze(configs);
module.exports=configs;
Than you can use it as constant
var config=require('config');
// config.BUILDPATH will act as constant and will be not writable.

Suggestions for dealing with `exports` in node.js

Theory:
One of the things that appeals to me about node.js is using it as a command line tool.
In theory, I can write libraries in Javascript and place them in my ~/.node_libraries directory, and then then I can reuse those libraries.
So for instance, I have a text.js in ~/.node_libraries, and it has a bunch of text-related functions I use over and over (depunctuate(), tokenize_text(), things like that).
The beauty of this is that I could use the same text.js file with my command-line scripts and server side. Right now I'm doing all that text processing stuff with Python, but I'd like to just stick to one language.
Practice:
AFAICT, in order to create a node.js module, I have to attach everything that I want to be available to exports or this. I.e., in text.js, I have to do:
exports.depunctuate = depunctuate
or
this.depunctuate = depunctuate
If I use exports, I have problems with using the library server-side à la:
<script src=text.js></script>
because then I get exports is not defined errors.
If I use this, I avoid the error, but everything I export ends up getting attached to the window object.
Is there some way I can set up these libraries that avoid both of these problems? For instance, is there some way I can wrap the exporting of exports so that the var will be apparent to node, but not when it's used in a plain Javascript file on a server?
How about testing for the existence of the exports object before sticking stuff in it?
This has worked well for me so far, but maybe there are better ideas out there:
if(typeof(exports) !== 'undefined' && exports !== null) {
exports.foo = foo;
exports.bar = bar;
}
In CoffeeScript, this can be done a little more tersely:
[exports.foo, exports.bar] = [foo, bar] if exports?
so this comes into a namespacing issue. Unless a function is called with the new operator you will get a this context === to window (global). A way to dodge this is:
(function( exports ) {
/* put your depuncuate definition here to keep it from leaking to the global */
exports.depunctuate = depunctuate;
})( (typeof exports === 'undefined') ? myAppNamespace : exports );

Using code in both Actionscript3 and Javascript

Here's an interesting architectural query. I have a piece of code that needs to run on the server (under Node.js) and on the client (in a Flash 10 app written with Actionscript 3). The code is mostly fairly intricate object manipulation, it doesn't make any API calls, and works fine in both contexts.
So far the project is just a demo, so I've been happy to copy and paste the code into both places. But it might be quite interesting to move forward with this.
So how would you do it?
I assume there is no easy way to get the Flash SDK (has to build without an IDE) to read and do something useful with a .js file.
My only thought is that I could write a code-generator that takes the .js file and places it in an ActionScript wrapper.
Are there any obvious approaches that I've missed?
Just to pre-empt an obvious answer, I know about cross-platform languages like Haxe.
A possible way is using include in your wrapper Actionscript code. Just a quick and very simple test:
package {
import flash.display.Sprite;
import flash.text.TextField;
public class Main extends Sprite {
private var _alertTxt:TextField;
include "some.js"
public function Main() {
_alertTxt = new TextField();
_alertTxt.multiline = true;
_alertTxt.height = 400;
_alertTxt.width = 400;
addChild(_alertTxt);
run();
}
public function alert(msg) {
_alertTxt.text += msg + "\n";
}
}
}
some.js
function run() {
alert("run");
var obj = {
a : 'hello',
b : 4.5,
c : false
};
loop(obj);
}
function loop(obj) {
for (var field in obj) {
alert(obj[field]);
}
}
To compile from command-line (you might want to add other options):
mxmlc -strict=false Main.as
If you don't set strict to false, it won't compile because of the lack of type declarations.

Categories