Skip to content

Commit 78ffcbd

Browse files
author
Hongbo Zhang
committed
initial export
0 parents  commit 78ffcbd

File tree

949 files changed

+259744
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

949 files changed

+259744
-0
lines changed

.gitignore

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
*~
2+
_build
3+
*.compile
4+
*.native
5+
*.byte
6+
*.cmo
7+
*.annot
8+
*.cmi
9+
*.cmx
10+
jslambda
11+
12+
exports.js
13+
*.lambda
14+
*.rawlambda
15+
node_modules
16+
*.o
17+
*.lam
18+
err.log
19+
.idea
20+
ocaml_pack
21+
*.0.js
22+
*~
23+
stdlib/c.js
24+
*.map
25+
*.zip
26+
*.gz
27+
*#
28+
qcc/
29+
.#*
30+
# jscomp/stdlib/external/*.js
31+
oxml
32+
*.cmj
33+
ocamlscript
34+
*.rawlambda
35+
*.lambda
36+
ocaml/*.js
37+
ocaml/*/*/*.js
38+
ocaml/*.js
39+
40+
jscomp/stdlib/.depend
41+
jscomp/test_cases/.depend
42+
test.sh
43+
test.json
44+
ocaml/bin
45+
ocaml/lib
46+
ocaml/man
47+
*~
48+
*.annot
49+
*.mj
50+
jscomp/bench/*.js
51+
*.bak
52+
.vscode

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "ocaml"]
2+
path = ocaml
3+
url = ./ocaml

LICENSE

+339
Large diffs are not rendered by default.

README.md

+312
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
# OCamlScript
2+
3+
## Introduction
4+
OCamlScript is a Javascript backend for [the OCaml language](https://ocaml.org/)
5+
which aims to provide a better language for the Javascript platform.
6+
7+
One OCaml module is mapped to one JS module, and no name mangling happens
8+
so that:
9+
10+
1. The stacktrace is preserved, the generated code is debuggable.
11+
2. You can call `List.length` (List is a module in OCaml standard library)
12+
in a plain Javascript file.
13+
14+
15+
### A simple example
16+
17+
``` ocaml
18+
let sum n =
19+
let v = ref 0 in
20+
for i = 0 to n do
21+
v := !v + i
22+
done;
23+
!v
24+
```
25+
26+
Generated code under is as below:
27+
28+
``` js
29+
function sum(n) {
30+
var v = 0;
31+
for(var i = 0; i<= n; ++i){
32+
v += i;
33+
}
34+
return v;
35+
}
36+
```
37+
38+
As you can see, there is no name mangling in the generated code, so suppose the
39+
module is called `M`, you can call `M.fib` in vanilla Javascript
40+
41+
You can play the online [in-browser compiler](http://zhanghongbo.me/js-demo).
42+
43+
44+
45+
# Disclaimer
46+
47+
This project is currently released to exchange ideas outside
48+
Bloomberg and collect some early feedback from OCaml and Javascript community,
49+
it is in an *very early* stage and not production ready
50+
for your own projects *yet*.
51+
52+
53+
## Build
54+
55+
Note that you have to clone this project with `--recursive` option, we can only distribute
56+
the patch of OCaml due to License restrictions.
57+
58+
59+
### Linux and Mac OS
60+
61+
62+
1. Apply the patch to OCaml compiler and build
63+
64+
Please ignore the warnings generated by git apply
65+
66+
```
67+
cd ocaml
68+
git apply ../js.diff
69+
./configure -prefix `pwd`
70+
make world.opt
71+
make install
72+
```
73+
2. Build OcamlScript Compiler
74+
75+
Assume that you have ocamlopt.opt in the PATH
76+
```
77+
cd ../jscomp
78+
ocamlopt.opt -I +compiler-libs -I bin -c bin/compiler.mli bin/compiler.ml
79+
ocamlopt.opt -g -linkall -o bin/ocamlscript -I +compiler-libs ocamlcommon.cmxa ocamlbytecomp.cmxa bin/compiler.cmx main.cmx
80+
```
81+
Now you have a binary called `ocamlscript` under `jscomp` directory,
82+
put it in your `PATH`
83+
84+
3. Build the runtime with `ocamlscript`
85+
86+
```sh
87+
cd runtime
88+
make all
89+
```
90+
91+
4. Build the standard library with `ocamlscript`
92+
93+
```sh
94+
cd ../stdlib
95+
make all
96+
```
97+
5. Test
98+
99+
We first create a file called `hello.ml`:
100+
101+
```sh
102+
mkdir tmp # create tmp directory inside the stdlib
103+
cd tmp
104+
echo 'print_endline "hello world";;' >hello.ml
105+
```
106+
Then we compiled it with `ocamlscript`
107+
```sh
108+
OCAML_RAW_JS=1 ocamlscript -I . -I ../ -c hello.ml
109+
```
110+
111+
It should generate a file called `hello.js`, then we runt the `js` file
112+
113+
```sh
114+
node hello.js
115+
```
116+
If everything goes well, you will see `hello world` on your screen.
117+
118+
119+
120+
121+
# Licensing
122+
123+
The [OCaml](./ocaml) directory is a submodule from OCaml's official repo(4.02.3), all its rights
124+
are reserved by [INRIA](http://www.inria.fr/) (see its QPL LICENSE for more details).
125+
126+
Our compiler relies on a patch [(js.diff)](./js.diff) to the OCaml compiler.
127+
128+
This project reused and adapted part of [js_of_ocaml](https://github.com/ocsigen/js_of_ocaml)'s:
129+
* Some small printing utilties in [pretty printer](./jscomp/js_dump.ml).
130+
* Part of the [Javascript runtime](./jscomp/runtime) support
131+
132+
It adapted two modules [jscomp/lam_pass_exits.ml] and
133+
[jscomp/lam_pass_lets_dce] from OCaml's [bytecomp/simplif], the main
134+
reasons are those optimizations are not optimal for Javascript
135+
backend.
136+
137+
[jscomp/js_main.ml] is adapted from [driver/main], it is not actually
138+
used, since currently we make this JS backend as a plugin instead, but
139+
it shows that it is easy to assemble a whole compler using OCaml
140+
compiler libraries and upon that we can add more compilation flags for
141+
JS backend.
142+
143+
[jscomp/stdlib] is copied from [ocaml/stdlib] to have it compiled with
144+
the new JS compiler.
145+
146+
Since our work is derivative work, we choose the GPL v2 license to
147+
make it compatible with
148+
[js_of_ocaml](http://ocsigen.org/js_of_ocaml/).
149+
150+
Note that QPL license is not compatible with GPL V2, so we distribute
151+
our changes to the compiler as a patch instead.
152+
153+
## Design goal
154+
155+
1. Readability
156+
1. No name mangling
157+
2. Support Javascript modules systems
158+
3. Integrate with existing javascript ecosystem, for example,
159+
[npm](https://www.npmjs.com/), [webpack](https://github.com/webpack).
160+
4. Straight-forward FFI, generate tds file to target [Typescript](http://www.typescriptlang.org/) for better tooling
161+
162+
2. Separate and *extremely fast* compilation.
163+
164+
3. Better performance than hand-written Javascript:
165+
thanks to a sound type system in OCaml so that we
166+
can play more optimizations and pipe it to Google Closure Compiler
167+
for production mode.
168+
169+
4. Smaller code than hand written JS code, compatible with Google Closure Compiler
170+
171+
5. Support NodeJs, Web Browser and various Javascript target platform.
172+
173+
6. Compatible with OCaml semantics modulo c-bindings and Obj, Marshal module
174+
175+
## More examples
176+
177+
### NodeJS support
178+
179+
Below it is an idea how it would integrate with the existing JS
180+
eco-system:
181+
182+
```js
183+
184+
var $$Array = require('./array'); // OCaml Array module
185+
var List = require ('./list'); // OCaml List module
186+
187+
List.iter(function(x){console.log('hi, nodejs '+x)},
188+
$$Array.to_list ($$Array.init(5,function(x){return x})))
189+
```
190+
191+
You get the output:
192+
193+
```sh
194+
hi, nodejs 0
195+
hi, nodejs 1
196+
hi, nodejs 2
197+
hi, nodejs 3
198+
hi, nodejs 4
199+
```
200+
201+
### A naive benchmark with Facebook immutable
202+
203+
204+
Below is a *contrived* example to demonstrate our motivation,
205+
it tries to insert 1000,000 keys to an immutable map and query it
206+
207+
```Ocaml
208+
module IntMap = Map.Make(struct
209+
type t = int
210+
let compare (x : int) y = compare x y
211+
end)
212+
213+
let test () =
214+
let m = ref IntMap.empty in
215+
let count = 1000000 in
216+
for i = 0 to count do
217+
m := IntMap.add i i !m
218+
done;
219+
for i = 0 to count do
220+
ignore (IntMap.find i !m )
221+
done
222+
223+
let () = test()
224+
225+
```
226+
227+
This is an equivalent JS version by using Facebook's
228+
[immutable](http://facebook.github.io/immutable-js/) library
229+
230+
231+
``` js
232+
'use strict';
233+
var Immutable = require('immutable');
234+
var Map = Immutable.Map;
235+
var m = new Map();
236+
var test = function(){
237+
var count = 1000000
238+
for(var i = 0; i < count; ++i){
239+
m = m.set(i, i );
240+
}
241+
for(var j = 0; j < count ; ++j){
242+
m.get(j)
243+
}
244+
}
245+
246+
test ()
247+
```
248+
249+
Runtime performance:
250+
- OCaml Immutable Map: 1186ms
251+
- Facebook Immutable Map: 3415ms
252+
253+
Code Size:
254+
- OCaml (Prod mode): 899 Bytes
255+
- Facebook Immutable : 55.3K Bytes
256+
257+
258+
## Status
259+
260+
261+
It covers the most of OCaml language, given that it is a quite young
262+
project (5 men-months until Jan 2016), there are still plenty of work
263+
to be done.
264+
265+
Some known issues are listed as below:
266+
267+
1. Language features:
268+
269+
Recursive modules, have not looked into it yet.
270+
271+
Better Currying support. Currently, we have an inference engine for
272+
function curring and we do cross module inference, however, there
273+
are some more challenging cases, for example, high order functions,
274+
it can be resolved by either aggressive inlining or fall back to a
275+
slow path using `Function.prototype.length`. We prepared the
276+
runtime support in [module curry](jscomp/runtime/curry.ml), will support it in the near
277+
future.
278+
279+
Int32 operations, currently, Int use Float operations, this should
280+
be fixed in the near future.
281+
282+
2. Standard libraries distributed with OCaml:
283+
284+
IO support, we have very limited support for
285+
`Pervasives.print_endline` and `Pervasives.prerr_endline`, it's
286+
non-trivial to preserve the same semantics of IO between OCaml and
287+
NodeJS, one solution is to functorize all IO operations. Functors
288+
are then inlined so there will no be performance cost or code size
289+
penalty.
290+
291+
Bigarray, Unix, Num, Int64
292+
293+
## License
294+
295+
The OCaml to JS compiler libraries are distributed under the GPL (version 2.0);
296+
see the LICENSE file at the top of the source tree for more information.
297+
298+
299+
The contents of some files in this distribution was derived from external
300+
sources with different licenses. The original copyright and license
301+
notice was preserved in the affected files.
302+
303+
## Question, Comments and Feedback
304+
305+
If you have questions, comments, suggestions for improvement or any other inquiries
306+
regarding this project, feel free to open an issue in the issue tracker.
307+
308+
309+
310+
311+
312+

0 commit comments

Comments
 (0)