forked from rescript-lang/rescript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathj.ml
302 lines (251 loc) · 8.98 KB
/
j.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
(* OCamlScript compiler
* Copyright (C) 2015-2016 Bloomberg Finance L.P.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)
(* Author: Hongbo Zhang *)
(** Javascript IR
It's a subset of Javascript AST specialized for OCaml lambda backend
Note it's not exactly the same as Javascript, the AST itself follows lexical
convention and [Block] is just a sequence of statements, which means it does
not introduce new scope
*)
type label = string
and binop = Js_op.binop
and int_op = Js_op.int_op
and kind = Js_op.kind
and property = Js_op.property
and number = Js_op.number
and mutable_flag = Js_op.mutable_flag
and ident_info = Js_op.ident_info
and exports = Js_op.exports
and required_modules = Js_op.required_modules
(** object literal, if key is ident, in this case, it might be renamed by
Google Closure optimizer,
currently we always use quote
*)
and property_name = string
and ident = Ident.t
and vident =
| Id of ident
| Qualified of ident * kind * string option
(* Since camldot is only available for toplevel module accessors,
we don't need print `A.length$2`
just print `A.length` - it's guarateed to be unique
when the third one is None, it means the whole module
*)
and exception_ident = ident
and for_ident = ident
and for_direction = Asttypes.direction_flag
and property_map =
(property_name * expression) list
and expression_desc =
| Math of string * expression list
| Array_length of expression
| String_length of expression
| Bytes_length of expression
| Function_length of expression
| Char_of_int of expression
| Char_to_int of expression
| Array_of_size of expression
(* used in [js_create_array] primitive, note having
uninitilized array is not as bad as in ocaml,
since GC does not rely on it
*)
| Array_append of expression * expression list (* For [caml_array_append]*)
| Tag_ml_obj of expression
| String_append of expression * expression
| Int_of_boolean of expression
| Is_type_number of expression (* typeof v === "number"*)
| Not of expression (* !v *)
| String_of_small_int_array of expression
(* String.fromCharCode.apply(null, args) *)
(* Convert JS boolean into OCaml boolean
like [+true], note this ast talks using js
terminnology unless explicity stated
*)
| Dump of Js_op.level * expression list
(* to support
val log1 : 'a -> unit
val log2 : 'a -> 'b -> unit
val log3 : 'a -> 'b -> 'c -> unit
*)
(* TODO: Add some primitives so that [js inliner] can do a better job *)
| Seq of expression * expression
| Cond of expression * expression * expression
| Bin of binop * expression * expression
(* | Int32_bin of int_op * expression * expression *)
| FlatCall of expression * expression
(* f.apply(null,args) -- Fully applied guaranteed
TODO: once we know args's shape --
if it's know at compile time, we can turn it into
f(args[0], args[1], ... )
*)
| Call of expression * expression list * Js_call_info.t
(* Analysze over J expression is hard since,
some primitive call is translated
into a plain call, it's better to keep them
*)
| String_access of expression * expression
| Access of expression * expression
(* Invariant:
The second argument has to be type of [int],
This can be constructed either in a static way [E.index] or a dynamic way
[E.access]
*)
| Dot of expression * string * bool
(* The third argument bool indicates whether we should
print it as
a["idd"] -- false
or
a.idd -- true
There are several kinds of properties
1. OCaml module dot (need to be escaped or not)
All exported declarations have to be OCaml identifiers
2. Javascript dot (need to be preserved/or using quote)
*)
| New of expression * expression list option (* TODO: option remove *)
| Var of vident
| Fun of ident list * block * Js_fun_env.t
| Str of bool * string
(* A string is UTF-8 encoded, the string may contain
escape sequences.
The first argument is used to mark it is non-pure, please
don't optimize it, since it does have side effec,
examples like "use asm;" and our compiler may generate "error;..."
which is better to leave it alone
*)
| Array of expression list * mutable_flag
| Number of number
| Object of property_map
and for_ident_expression = expression (* pure*)
and finish_ident_expression = expression (* pure *)
(* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
block can be nested, specified in ES3
*)
(* Delay some units like [primitive] into JS layer ,
benefit: better cross module inlining, and smaller IR size?
*)
(*
[closure] captured loop mutable values in the outer loop
check if it contains loop mutable values, happens in nested loop
when closured, it's no longer loop mutable value.
which means the outer loop mutable value can not peek into the inner loop
{[
var i = f ();
for(var finish = 32; i < finish; ++i){
}
]}
when [for_ident_expression] is [None], [var i] has to
be initialized outside, so
{[
var i = f ()
(function (xxx){
for(var finish = 32; i < finish; ++i)
}(..i))
]}
This happens rare it's okay
this is because [i] has to be initialized outside, if [j]
contains a block side effect
TODO: create such example
*)
(* Since in OCaml,
[for i = 0 to k end do done ]
k is only evaluated once , to encode this invariant in JS IR,
make sure [ident] is defined in the first b
TODO: currently we guarantee that [bound] was only
excecuted once, should encode this in AST level
*)
(* Can be simplified to keep the semantics of OCaml
For (var i, e, ...){
let j = ...
}
if [i] or [j] is captured inside closure
for (var i , e, ...){
(function (){
})(i)
}
*)
(* Single return is good for ininling..
However, when you do tail-call optmization
you loose the expression oriented semantics
Block is useful for implementing goto
{[
xx:{
break xx;
}
]}
*)
and statement_desc =
| Block of block
| Variable of variable_declaration (* Function declaration and Variable declaration *)
| Exp of expression
| If of expression * block * block option
| While of label option * expression * block
* Js_closure.t (* check if it contains loop mutable values, happens in nested loop *)
| ForRange of for_ident_expression option * finish_ident_expression *
for_ident * for_direction * block
* Js_closure.t
| Continue of label
| Break (* only used when inline a fucntion *)
| Return of return_expression (* Here we need track back a bit ?, move Return to Function ...
Then we can only have one Return, which is not good *)
| Int_switch of expression * int case_clause list * block option
| String_switch of expression * string case_clause list * block option
| Throw of expression
| Try of block * (exception_ident * block) option * block option
and return_expression = {
(* since in ocaml, it's expression oriented langauge, [return] in
general has no jumps, it only happens when we do
tailcall conversion, in that case there is a jump.
However, currently a single [break] is good to cover
our compilation strategy
Attention: we should not insert [break] arbitrarily, otherwise
it would break the semantics
A more robust signature would be
{[ goto : label option ; ]}
*)
return_value : expression
}
and expression = {
expression_desc : expression_desc;
comment : string option;
}
and statement = {
statement_desc : statement_desc;
comment : string option;
}
and variable_declaration = {
ident : ident ;
value : expression option;
property : property;
ident_info : ident_info;
}
and 'a case_clause = {
case : 'a ;
body : block * bool ; (* true means break *)
}
(* TODO: For efficency: block should not be a list, it should be able to
be concatenated in both ways
*)
and block = statement list
and program = {
name : string;
modules : required_modules ;
block : block ;
exports : exports ;
export_set : Ident_set.t ;
side_effect : string option (* None: no, Some reason *)
}