My working unpac repository
1(**************************************************************************)
2(* *)
3(* OCaml *)
4(* *)
5(* Jerome Vouillon, projet Cristal, INRIA Rocquencourt *)
6(* *)
7(* Copyright 1996 Institut National de Recherche en Informatique et *)
8(* en Automatique. *)
9(* *)
10(* All rights reserved. This file is distributed under the terms of *)
11(* the GNU Lesser General Public License version 2.1, with the *)
12(* special exception on linking described in the file LICENSE. *)
13(* *)
14(**************************************************************************)
15
16(*
17 # Translation of class and object expressions
18
19 ## Objects
20
21 ### Memory layout
22
23 Objects are represented in memory using two layers:
24 - The outer layer is a block with tag [Obj.object_tag].
25 It has a first field pointing to the inner layer (the methods),
26 a second field acting as a unique identifier to allow
27 polymorphic comparison, and the rest of the block contains
28 the values of the instance variables, class parameters, and
29 other values that can vary between two objects of the same class.
30
31 - The inner layer is a regular block (with tag zero). It contains
32 all values that are shared between all objects of the same class,
33 which means mostly methods. The first field corresponds to the number of
34 public methods, the second field is a mask used for optimising method
35 access, the following fields are alternating between the method closures
36 and the hash of their name (sorted in increasing hash order).
37 Additional fields are used for private methods.
38
39 +-------+------+-------+-------+-----+-------+-------+-----------+
40 | n_pub | mask | met_1 | tag_1 | ... | met_n | tag_n | other ... |
41 +-------+------+-------+-------+-----+-------+-------+-----------+
42
43 ### Primitives
44
45 Method access is compiled in one of three possible ways:
46 - Generic method access (outside a class, or to an object that is not
47 self or an ancestor) uses dynamic lookup. A dichotomic search in
48 the part of the method array that stores public methods finds
49 the expected closure and calls it on the current object.
50 In most cases, a fast path also exists: each method access in the
51 source code has an associated cache location that stores the offset
52 of the last method called at this point in its method array.
53 Before the dichotomic search, the last stored offset (clamped
54 to the actual size of the method array using the mask) is checked,
55 and if the tag matches the associated closure is called directly.
56 - Method access through the self object inside a class:
57 the (runtime) index of the method inside the method array
58 has been computed at class creation time, so the method is fetched
59 from the block through a dynamic block load (like an array load).
60 - Accessing the method of an ancestor inside a class (ancestors are
61 variables bound by [inherit ... as ancestor] constructions):
62 at class creation time, the closure of the ancestor method is bound
63 to a variable, and the method call just calls this function without
64 any (further) dynamic lookup.
65
66 Instance variable access (getting and setting) also computes offsets
67 at class initialisation time, with those offsets used to index directly
68 in the outer layer of the object.
69
70 Functional object copy [ {< ... >} ] copies the outer layer, resets the
71 unique ID, and performs the required instance variable updates.
72
73 There are no other object primitives (objects cannot be allocated
74 in the IR directly, they are allocated in [CamlinternalOO])
75
76 ## Classes
77
78 Classes are stored as module fields. The runtime value that represents
79 classes is used in two contexts:
80
81 - When using the [new] construction, to generate an object from a class.
82 - When referencing a class inside another class (either through
83 inheritance or other class expressions).
84
85 This is done by storing classes as blocks where the first field
86 is used to generate objects, and the second field is used to derive
87 classes (in a general sense, not only for inheritance).
88 In practice classes also contain one other field, which is used to
89 implement some optimisations in the main compiler (to ensure that each
90 class only runs its initialisation code once in the whole program, even
91 if its definition is in a context that is expected to be run several
92 times like a functor).
93 So the block layout is the following:
94 - A field named [obj_init] that is used for creating objects
95 - A field named [class_init] that is used for deriving classes
96 - A field named [env] containing values for all the variables
97 captured by [Translobj.oo_wrap] calls.
98
99 The module [CamlinternalOO] also defines a type [table] that represents
100 class layouts. Such values are not stored in the class block directly,
101 but the [obj_init] field captures the table for the class and [class_init]
102 manipulates such tables.
103
104 ### The [obj_init] field
105
106 As described earlier, each object contains an inner layer that is computed
107 only once at class initialisation time; it seems natural to store this
108 block in the runtime value of the class (this block is one of the fields of
109 the [CamlinternalOO.table] type). However, given that creating an
110 object also involves setting up the instance variables and running the
111 initialisers, in practice the class only exports a function that creates
112 objects, and the table is captured in this function's closure along with
113 any value necessary to properly initialise the object.
114 Classes can have parameters, so in practice this object creation function
115 takes a first unit parameter (to ensure that it is always a function)
116 and returns a regular OCaml value that is either an object (if the class
117 doesn't have parameters) or a function which, given values
118 for the class parameters, will return an object.
119
120 Here is the type of the [obj_init] function for a class which type is
121 [p1 -> ... -> pn -> object method m1 : t1 ... method mn : tn end]:
122 [unit -> p1 -> ... -> pn -> < m1 : t1; ... mn : tn >]
123 (If the class has instance variables or initialisers, they are not
124 reflected in the type of [obj_init]).
125
126 ### The [class_init] field
127
128 This field is used in two cases:
129 - When a class is defined in terms of another class, for instance as an
130 alias, a partial application, or some other kind of wrapper.
131 - When a class structure (i.e. the [object ... end] syntactic construction)
132 contains inheritance fields (e.g. [inherit cl as super]).
133
134 In both cases, we only have access to the other class' public type at
135 compile time, but we must still make sure all of the private fields
136 are setup correctly, in a way that is compatible with the current
137 class.
138
139 This is where tables come into play: the [class_init] field is a function
140 taking a table as parameter, updates it in-place, and returns a function
141 that is very similar to the [obj_init] function, except that instead of
142 taking [unit] as its first parameter and returning an object, it takes
143 a partially initialised object, and updates the parts of it that are
144 relevant for the corresponding class. It also takes the [env] field as
145 a parameter, so that different instances of the class can share the
146 same [class_init] function.
147
148 Thus, the type of [class_init] is:
149 [table -> env -> Obj.t -> p1 -> ... -> pn -> unit]
150
151 ### The [env] field
152
153 The [env] field is a structure similar to a function's closure, storing
154 the value of free variables of the class expression. The actual
155 representation is a bit complex and not very important.
156
157 ### Compilation scheme
158
159 The algorithm implemented below aims at sharing code as much as possible
160 between the various similar parts of the class.
161
162 - The code of the [obj_init] function is very similar to the code of
163 the function returned by [class_init]. The main difference is that
164 [obj_init] starts from scratch, allocating then initialising the object,
165 while inside [class_init] we want to run initialisation code on an already
166 allocated object (that we don't need to return).
167 So in practice we will build a single function that, depending on the value
168 of its first parameter, will either do the allocation and return the object
169 (if the parameter is the integer constant 0), or assume the parameter is
170 an already allocated and update it.
171 The body of this function is returned by [build_object_init].
172 - The table for the current class (that [obj_init] will read from) is
173 computed by allocating a basic table, then passing it to [class_init],
174 and finally calling [CamlinternalOO.init_class] on it.
175 This means that all the code for setting up the class (computing instance
176 variable indices, calling inherited class initialisers, and so on) is only
177 generated once, in the [class_init] function.
178 After building [obj_init], [build_class_init] wraps it with the class
179 initialization code to build the [class_init] function.
180
181 That's all for the high-level algorithm; the rest will be detailed close to
182 the corresponding code.
183
184*)
185
186open Asttypes
187open Types
188open Typedtree
189open Lambda
190open Translobj
191open Translcore
192open Debuginfo.Scoped_location
193
194(* XXX Rajouter des evenements... | Add more events... *)
195
196type error = Tags of label * label
197
198exception Error of Location.t * error
199
200let lfunction params body =
201 if params = [] then body else
202 match body with
203 | Lfunction {kind = Curried; params = params'; body = body'; attr; loc}
204 when attr.may_fuse_arity &&
205 List.length params + List.length params' <= Lambda.max_arity() ->
206 lfunction ~kind:Curried ~params:(params @ params')
207 ~return:Pgenval
208 ~body:body'
209 ~attr
210 ~loc
211 | _ ->
212 lfunction ~kind:Curried ~params ~return:Pgenval
213 ~body
214 ~attr:default_function_attribute
215 ~loc:Loc_unknown
216
217let lapply ap =
218 match ap.ap_func with
219 Lapply ap' ->
220 Lapply {ap with ap_func = ap'.ap_func; ap_args = ap'.ap_args @ ap.ap_args}
221 | _ ->
222 Lapply ap
223
224let mkappl (func, args) =
225 Lapply {
226 ap_loc=Loc_unknown;
227 ap_func=func;
228 ap_args=args;
229 ap_tailcall=Default_tailcall;
230 ap_inlined=Default_inline;
231 ap_specialised=Default_specialise;
232 }
233
234let lsequence l1 l2 =
235 if l2 = lambda_unit then l1 else Lsequence(l1, l2)
236
237let lfield v i = Lprim(Pfield (i, Pointer, Mutable),
238 [Lvar v], Loc_unknown)
239
240let transl_label l = share (Const_immstring l)
241
242let transl_meth_list lst =
243 if lst = [] then Lconst (const_int 0) else
244 share (Const_block
245 (0, List.map (fun lab -> Const_immstring lab) lst))
246
247let set_inst_var ~scopes obj id expr =
248 Lprim(Psetfield_computed (Typeopt.maybe_pointer expr, Assignment),
249 [Lvar obj; Lvar id; transl_exp ~scopes expr], Loc_unknown)
250
251let transl_val tbl create name =
252 mkappl (oo_prim (if create then "new_variable" else "get_variable"),
253 [Lvar tbl; transl_label name])
254
255let transl_vals tbl create strict vals rem =
256 List.fold_right
257 (fun (name, id) rem ->
258 Llet(strict, Pgenval, id, transl_val tbl create name, rem))
259 vals rem
260
261let meths_super tbl meths inh_meths =
262 List.fold_right
263 (fun (nm, id) rem ->
264 try
265 (nm, id,
266 mkappl(oo_prim "get_method", [Lvar tbl; Lvar (Meths.find nm meths)]))
267 :: rem
268 with Not_found -> rem)
269 inh_meths []
270
271(*
272[build_class_init] has two parameters ([cstr] and [super]) that are set when
273translating the expression of a class that will be inherited in an outer class.
274
275They could be replaced with the following type:
276
277```
278type inheritance_status =
279 | Normal (** Not under an [inherit] construct *)
280 | Inheriting of {
281 must_narrow : bool;
282 (** [false] if we already went through a call to [narrow] *)
283 method_getters : (Ident.t * lambda) list;
284 (** Ancestor methods are accessed through identifiers.
285 These identifiers are bound at class initialisation time,
286 by fetching the actual closures from the table just
287 after setting up the inherited class. *)
288 instance_vars : (string * Ident.t) list;
289 (** Inherited instance variables need to have their index bound
290 in the scope of the child class *)
291 }
292```
293
294[cstr] is the negation of [must_narrow], and [super] is the pair
295[(instance_vars, method_getters)].
296*)
297
298let bind_super tbl (vals, meths) cl_init =
299 transl_vals tbl false StrictOpt vals
300 (List.fold_right (fun (_nm, id, def) rem ->
301 Llet(StrictOpt, Pgenval, id, def, rem))
302 meths cl_init)
303
304let create_object cl obj init =
305 let obj' = Ident.create_local "self" in
306 let (inh_init, obj_init, has_init) = init obj' in
307 if obj_init = lambda_unit then
308 (inh_init,
309 mkappl (oo_prim (if has_init then "create_object_and_run_initializers"
310 else "create_object_opt"),
311 [obj; Lvar cl]))
312 else begin
313 (inh_init,
314 Llet(Strict, Pgenval, obj',
315 mkappl (oo_prim "create_object_opt", [obj; Lvar cl]),
316 Lsequence(obj_init,
317 if not has_init then Lvar obj' else
318 mkappl (oo_prim "run_initializers_opt",
319 [obj; Lvar obj'; Lvar cl]))))
320 end
321
322let name_pattern default p =
323 match p.pat_desc with
324 | Tpat_var (id, _, _) -> id
325 | Tpat_alias(_, id, _, _, _) -> id
326 | _ -> Ident.create_local default
327
328(*
329 [build_object_init] returns an expression that creates and initialises new
330 objects. If the class takes parameters, it is a function that, given values
331 for the parameters, performs the initialisations and (if needed) object
332 creation.
333 The [obj] expression will be bound to either the integer 0, in which case
334 [obj_init] must allocate the object and return it, or to an already allocated
335 object, in which case [obj_init] will initialize the relevant parts of it
336 through side-effects. In the case of an immediate object it is always 0.
337 Parameters:
338 - [scopes] corresponds to the location scopes (as in the rest of the
339 translation code)
340 - [cl_table] is the variable to which the table for the current class is
341 bound
342 - [obj] is the parameter of the [obj_init] function we want to create.
343 As explained above at runtime it might point to either an already allocated
344 object, when inheriting, or a dummy zero value, when calling [new].
345 - [params] stores the anonymous instance variables associated with all
346 variables that occur inside the class definition but outside the
347 [object ... end] structure: class parameters and class let bindings.
348 The definition is always the identifier corresponding to the original
349 variable.
350 - [inh_init] accumulates data about the class identifiers encountered, and is
351 returned at the end to be reused in [build_class_init].
352 - [cl] is the class we're compiling *)
353
354let rec build_object_init ~scopes cl_table obj params inh_init obj_init cl =
355 match cl.cl_desc with
356 Tcl_ident (path, _, _) ->
357 (* The object initialiser for the class in [path], specialised
358 to the class being defined *)
359 let obj_init = Ident.create_local "obj_init" in
360 let envs, inh_init = inh_init in
361 let env =
362 match envs with None -> []
363 | Some envs ->
364 [Lprim(Pfield (List.length inh_init + 1, Pointer, Mutable),
365 [Lvar envs],
366 Loc_unknown)]
367 in
368 let loc = of_location ~scopes cl.cl_loc in
369 let path_lam = transl_class_path loc cl.cl_env path in
370 (* Note: we don't need to bind [params] here, as they are
371 only used in structures. Outside structures (in class lets or
372 applications) we use the regular identifiers. *)
373 ((envs, (path, path_lam, obj_init) :: inh_init),
374 mkappl(Lvar obj_init, env @ [obj]))
375 | Tcl_structure str ->
376 (* Initialising a concrete class structure *)
377 create_object cl_table obj (fun obj ->
378 (* [obj] will be bound to the allocated object,
379 unlike the original [obj] which might be zero if called directly
380 from an object creation expression. *)
381 let (inh_init, obj_init, has_init) =
382 List.fold_right
383 (fun field (inh_init, obj_init, has_init) ->
384 match field.cf_desc with
385 Tcf_inherit (_, cl, _, _, _) ->
386 let (inh_init, obj_init') =
387 (* Reset [params]. The current ones will be bound
388 outside the structure. *)
389 build_object_init ~scopes cl_table (Lvar obj) [] inh_init
390 (fun _ -> lambda_unit) cl
391 in
392 (* Since [obj] is bound to a concrete object,
393 only the side-effects of [obj_init'] are relevant. *)
394 (inh_init, lsequence obj_init' obj_init, true)
395 | Tcf_val (_, _, id, Tcfk_concrete (_, exp), _) ->
396 (inh_init,
397 lsequence (set_inst_var ~scopes obj id exp) obj_init,
398 has_init)
399 | Tcf_method _ | Tcf_val _ | Tcf_constraint _ | Tcf_attribute _->
400 (inh_init, obj_init, has_init)
401 | Tcf_initializer _ ->
402 (inh_init, obj_init, true)
403 )
404 str.cstr_fields
405 (inh_init, obj_init obj, false)
406 in
407 (* Set the instance variables associated to the class parameters and
408 let bindings to their expected value. *)
409 (inh_init,
410 List.fold_right
411 (fun (id, expr) rem ->
412 lsequence (Lifused (id, set_inst_var ~scopes obj id expr)) rem)
413 params obj_init,
414 has_init))
415 | Tcl_fun (_, pat, vals, cl, partial) ->
416 let (inh_init, obj_init) =
417 (* [vals] maps all pattern variables to idents for use inside methods *)
418 build_object_init ~scopes cl_table obj (vals @ params)
419 inh_init obj_init cl
420 in
421 (inh_init,
422 let build params rem =
423 let param = name_pattern "param" pat in
424 Lambda.lfunction
425 ~kind:Curried ~params:((param, Pgenval)::params)
426 ~return:Pgenval
427 ~attr:default_function_attribute
428 ~loc:(of_location ~scopes pat.pat_loc)
429 ~body:(Matching.for_function ~scopes pat.pat_loc
430 None (Lvar param) [pat, rem] partial)
431 in
432 begin match obj_init with
433 Lfunction {kind = Curried; params; body = rem} -> build params rem
434 | rem -> build [] rem
435 end)
436 | Tcl_apply (cl, oexprs) ->
437 let (inh_init, obj_init) =
438 build_object_init ~scopes cl_table obj params inh_init obj_init cl
439 in
440 (inh_init, transl_apply ~scopes obj_init oexprs Loc_unknown)
441 | Tcl_let (rec_flag, defs, vals, cl) ->
442 (* See comment on the [Tcl_fun] case for the meaning of [vals] *)
443 let (inh_init, obj_init) =
444 build_object_init ~scopes cl_table obj (vals @ params)
445 inh_init obj_init cl
446 in
447 (inh_init, Translcore.transl_let ~scopes rec_flag defs obj_init)
448 | Tcl_open (_, cl)
449 (* Class local opens are restricted to paths only, so no code is generated
450 *)
451 | Tcl_constraint (cl, _, _, _, _) ->
452 build_object_init ~scopes cl_table obj params inh_init obj_init cl
453
454(* The manual specifies that toplevel lets *must* be evaluated outside of the
455 class. This piece of code makes sure we skip them. *)
456let rec build_object_init_0
457 ~scopes cl_table params cl copy_env subst_env top ids =
458 match cl.cl_desc with
459 Tcl_let (_rec_flag, _defs, vals, cl) ->
460 build_object_init_0
461 ~scopes cl_table (vals@params) cl copy_env subst_env top ids
462 | Tcl_open (_descr, cl) ->
463 build_object_init_0
464 ~scopes cl_table params cl copy_env subst_env top ids
465 | _ ->
466 let self = Ident.create_local "self" in
467 let env = Ident.create_local "env" in
468 let obj = if ids = [] then lambda_unit else Lvar self in
469 let envs = if top then None else Some env in
470 let ((_,inh_init), obj_init) =
471 build_object_init ~scopes cl_table obj params (envs,[]) copy_env cl in
472 let obj_init =
473 if ids = [] then obj_init else lfunction [self, Pgenval] obj_init in
474 (inh_init, lfunction [env, Pgenval] (subst_env env inh_init obj_init))
475
476
477let bind_method tbl lab id cl_init =
478 Llet(Strict, Pgenval, id, mkappl (oo_prim "get_method_label",
479 [Lvar tbl; transl_label lab]),
480 cl_init)
481
482let bind_methods tbl meths vals cl_init =
483 let methl = Meths.fold (fun lab id tl -> (lab,id) :: tl) meths [] in
484 let len = List.length methl and nvals = List.length vals in
485 if len < 2 && nvals = 0 then Meths.fold (bind_method tbl) meths cl_init else
486 if len = 0 && nvals < 2 then transl_vals tbl true Strict vals cl_init else
487 let ids = Ident.create_local "ids" in
488 let i = ref (len + nvals) in
489 let getter, names =
490 if nvals = 0 then "get_method_labels", [] else
491 "new_methods_variables", [transl_meth_list (List.map fst vals)]
492 in
493 Llet(Strict, Pgenval, ids,
494 mkappl (oo_prim getter,
495 [Lvar tbl; transl_meth_list (List.map fst methl)] @ names),
496 List.fold_right
497 (fun (_lab,id) lam -> decr i; Llet(StrictOpt, Pgenval, id,
498 lfield ids !i, lam))
499 (methl @ vals) cl_init)
500
501let output_methods tbl methods lam =
502 match methods with
503 [] -> lam
504 | [lab; code] ->
505 lsequence (mkappl(oo_prim "set_method", [Lvar tbl; lab; code])) lam
506 | _ ->
507 lsequence (mkappl(oo_prim "set_methods",
508 [Lvar tbl; Lprim(Pmakeblock(0,Immutable,None),
509 methods, Loc_unknown)]))
510 lam
511
512let rec ignore_cstrs cl =
513 match cl.cl_desc with
514 Tcl_constraint (cl, _, _, _, _) -> ignore_cstrs cl
515 | Tcl_apply (cl, _) -> ignore_cstrs cl
516 | _ -> cl
517
518let rec index a = function
519 [] -> raise Not_found
520 | b :: l ->
521 if b = a then 0 else 1 + index a l
522
523let bind_id_as_val (id, _) = ("", id)
524
525(** Build the class initialisation code.
526 Parameters:
527 - [scopes] corresponds to the location scopes (as in the rest of the
528 translation code)
529 - [cla] is the variable to which the table for the current class is bound
530 - [cstr] is [true] when called from outside, but [false] when called
531 from an [inherit] field. Narrowing is necessary during inheritance to
532 prevent clashes between methods/variables in the child class and private
533 methods/variables in the parent.
534 - [super] stores, if we're building an inherited class, the variables and
535 methods exposed to the child. The variables need to have their associated
536 index exposed, and methods have to be bound in case the child refers to
537 them through the ancestor variables.
538 - [inh_init] is the sequence of inheritance paths computed during
539 [build_object_init].
540 - [cl_init] is the expression we're building.
541 - [msubst] replaces methods with builtin methods when possible.
542 - [top] is [false] if the current class is under [Translobj.oo_wrap].
543 - [cl] is the class we're compiling *)
544let rec build_class_init ~scopes cla cstr super inh_init cl_init msubst top cl =
545 match cl.cl_desc with
546 | Tcl_ident _ ->
547 begin match inh_init with
548 | (_, path_lam, obj_init)::inh_init ->
549 (inh_init,
550 Llet (Strict, Pgenval, obj_init,
551 (* Load the [class_init] field of the class,
552 and apply it to our current table and the class' environment.
553 This gets us the object initialiser. *)
554 mkappl(Lprim(Pfield (1, Pointer, Mutable),
555 [path_lam], Loc_unknown), Lvar cla ::
556 if top then [Lprim(Pfield (2, Pointer, Mutable),
557 [path_lam], Loc_unknown)]
558 else []),
559 (* The methods and variables for this class are fully registered
560 in the table. If we are in an inheritance context, we can now
561 bind everything. *)
562 bind_super cla super cl_init))
563 | _ ->
564 assert false
565 end
566 | Tcl_structure str ->
567 let cl_init = bind_super cla super cl_init in
568 let (inh_init, cl_init, methods, values) =
569 List.fold_right
570 (fun field (inh_init, cl_init, methods, values) ->
571 match field.cf_desc with
572 Tcf_inherit (_, cl, _, vals, meths) ->
573 let cl_init = output_methods cla methods cl_init in
574 let inh_init, cl_init =
575 (* Build the initialisation code for the inherited class,
576 plus its wrappers.
577 Make sure the wrappers bind the inherited methods
578 and variables. *)
579 build_class_init ~scopes cla false
580 (vals, meths_super cla str.cstr_meths meths)
581 inh_init cl_init msubst top cl in
582 (inh_init, cl_init, [], values)
583 | Tcf_val (name, _, id, _, over) ->
584 (* If this is an override, the variable is the same as
585 the one from the earlier definition, and must not be
586 bound again. *)
587 let values =
588 if over then values else (name.txt, id) :: values
589 in
590 (inh_init, cl_init, methods, values)
591 | Tcf_method (_, _, Tcfk_virtual _)
592 | Tcf_constraint _
593 ->
594 (inh_init, cl_init, methods, values)
595 | Tcf_method (name, _, Tcfk_concrete (_, exp)) ->
596 let scopes = enter_method_definition ~scopes name.txt in
597 let met_code =
598 msubst true (transl_scoped_exp ~scopes exp) in
599 let met_code =
600 if !Clflags.native_code && List.length met_code = 1 then
601 (* Force correct naming of method for profiles *)
602 let met = Ident.create_local ("method_" ^ name.txt) in
603 [Llet(Strict, Pgenval, met, List.hd met_code, Lvar met)]
604 else met_code
605 in
606 (inh_init, cl_init,
607 Lvar(Meths.find name.txt str.cstr_meths) :: met_code @ methods,
608 values)
609 | Tcf_initializer exp ->
610 (inh_init,
611 Lsequence(mkappl (oo_prim "add_initializer",
612 Lvar cla :: msubst false
613 (transl_exp ~scopes exp)),
614 cl_init),
615 methods, values)
616 | Tcf_attribute _ ->
617 (inh_init, cl_init, methods, values))
618 str.cstr_fields
619 (inh_init, cl_init, [], [])
620 in
621 (* In order of execution at runtime:
622 - Bind the method and variable indices for the current class
623 ([bind_methods])
624 - Run the code for setting up the individual fields ([cl_init], plus
625 [output_methods] for the remaining unset methods)
626 - If we are in an inheritance context, bind the inherited variables
627 and methods for use in the child ([bind_super] at the top of this
628 branch) *)
629 let cl_init = output_methods cla methods cl_init in
630 (inh_init, bind_methods cla str.cstr_meths values cl_init)
631 | Tcl_fun (_, _pat, vals, cl, _) ->
632 let (inh_init, cl_init) =
633 build_class_init ~scopes cla cstr super inh_init cl_init msubst top cl
634 in
635 (* Create anonymous instance variables and define them in the table *)
636 let vals = List.map bind_id_as_val vals in
637 (inh_init, transl_vals cla true StrictOpt vals cl_init)
638 | Tcl_apply (cl, _exprs) ->
639 build_class_init ~scopes cla cstr super inh_init cl_init msubst top cl
640 | Tcl_let (_rec_flag, _defs, vals, cl) ->
641 let (inh_init, cl_init) =
642 build_class_init ~scopes cla cstr super inh_init cl_init msubst top cl
643 in
644 (* Create anonymous instance variables and define them in the table *)
645 let vals = List.map bind_id_as_val vals in
646 (inh_init, transl_vals cla true StrictOpt vals cl_init)
647 | Tcl_constraint (cl, _, vals, meths, concr_meths) ->
648 let virt_meths =
649 List.filter (fun lab -> not (MethSet.mem lab concr_meths)) meths in
650 let concr_meths = MethSet.elements concr_meths in
651 let narrow_args =
652 [Lvar cla;
653 transl_meth_list vals;
654 transl_meth_list virt_meths;
655 transl_meth_list concr_meths] in
656 let cl = ignore_cstrs cl in
657 begin match cl.cl_desc, inh_init with
658 | Tcl_ident (path, _, _), (path', path_lam, obj_init)::inh_init ->
659 assert (Path.same path path');
660 let inh = Ident.create_local "inh"
661 and ofs = List.length vals + 1
662 and valids, methids = super in
663 let cl_init =
664 List.fold_left
665 (fun init (nm, id, _) ->
666 Llet(StrictOpt, Pgenval, id,
667 lfield inh (index nm concr_meths + ofs),
668 init))
669 cl_init methids in
670 let cl_init =
671 List.fold_left
672 (fun init (nm, id) ->
673 Llet(StrictOpt, Pgenval, id,
674 lfield inh (index nm vals + 1), init))
675 cl_init valids in
676 (inh_init,
677 Llet (Strict, Pgenval, inh,
678 mkappl(oo_prim "inherits", narrow_args @
679 [path_lam;
680 Lconst(const_int (if top then 1 else 0))]),
681 Llet(StrictOpt, Pgenval, obj_init, lfield inh 0, cl_init)))
682 | _ ->
683 let core cl_init =
684 build_class_init
685 ~scopes cla true super inh_init cl_init msubst top cl
686 in
687 (* Skip narrowing if we're not directly under [inherit] *)
688 if cstr then core cl_init else
689 let (inh_init, cl_init) =
690 core (Lsequence (mkappl (oo_prim "widen", [Lvar cla]), cl_init))
691 in
692 (inh_init,
693 Lsequence(mkappl (oo_prim "narrow", narrow_args),
694 cl_init))
695 end
696 | Tcl_open (_, cl) ->
697 build_class_init ~scopes cla cstr super inh_init cl_init msubst top cl
698
699let rec build_class_lets ~scopes cl =
700 match cl.cl_desc with
701 Tcl_let (rec_flag, defs, _vals, cl') ->
702 let env, wrap = build_class_lets ~scopes cl' in
703 (env, fun lam_and_kind ->
704 let lam, rkind = wrap lam_and_kind in
705 Translcore.transl_let ~scopes rec_flag defs lam, rkind)
706 | Tcl_open (open_descr, cl) ->
707 (* Failsafe to ensure we get a compilation error if arbitrary
708 module expressions become allowed *)
709 let _ : Path.t * Longident.t loc = open_descr.open_expr in
710 build_class_lets ~scopes cl
711 | _ ->
712 (cl.cl_env, fun lam_and_kind -> lam_and_kind)
713
714let rec get_class_meths cl =
715 match cl.cl_desc with
716 Tcl_structure cl ->
717 Meths.fold (fun _ -> Ident.Set.add) cl.cstr_meths Ident.Set.empty
718 | Tcl_ident _ -> Ident.Set.empty
719 | Tcl_fun (_, _, _, cl, _)
720 | Tcl_let (_, _, _, cl)
721 | Tcl_apply (cl, _)
722 | Tcl_open (_, cl)
723 | Tcl_constraint (cl, _, _, _, _) -> get_class_meths cl
724
725(*
726 XXX Il devrait etre peu couteux d'ecrire des classes :
727 | Writing classes should be cheap
728 class c x y = d e f
729*)
730let rec transl_class_rebind ~scopes obj_init cl vf =
731 match cl.cl_desc with
732 Tcl_ident (path, _, _) ->
733 if vf = Concrete then begin
734 try if (Env.find_class path cl.cl_env).cty_new = None then raise Exit
735 with Not_found -> raise Exit
736 end;
737 let cl_loc = of_location ~scopes cl.cl_loc in
738 let path_lam = transl_class_path cl_loc cl.cl_env path in
739 (path, path_lam, obj_init)
740 | Tcl_fun (_, pat, _, cl, partial) ->
741 let path, path_lam, obj_init =
742 transl_class_rebind ~scopes obj_init cl vf in
743 let build params rem =
744 let param = name_pattern "param" pat in
745 Lambda.lfunction
746 ~kind:Curried ~params:((param, Pgenval)::params)
747 ~return:Pgenval
748 ~attr:default_function_attribute
749 ~loc:(of_location ~scopes pat.pat_loc)
750 ~body:(Matching.for_function ~scopes pat.pat_loc
751 None (Lvar param) [pat, rem] partial)
752 in
753 (path, path_lam,
754 match obj_init with
755 Lfunction {kind = Curried; params; body} -> build params body
756 | rem -> build [] rem)
757 | Tcl_apply (cl, oexprs) ->
758 let path, path_lam, obj_init =
759 transl_class_rebind ~scopes obj_init cl vf in
760 (path, path_lam, transl_apply ~scopes obj_init oexprs Loc_unknown)
761 | Tcl_let (rec_flag, defs, _vals, cl) ->
762 let path, path_lam, obj_init =
763 transl_class_rebind ~scopes obj_init cl vf in
764 (path, path_lam, Translcore.transl_let ~scopes rec_flag defs obj_init)
765 | Tcl_structure _ -> raise Exit
766 | Tcl_constraint (cl', _, _, _, _) ->
767 let path, path_lam, obj_init =
768 transl_class_rebind ~scopes obj_init cl' vf in
769 let rec check_constraint = function
770 Cty_constr(path', _, _) when Path.same path path' -> ()
771 | Cty_arrow (_, _, cty) -> check_constraint cty
772 | _ -> raise Exit
773 in
774 check_constraint cl.cl_type;
775 (path, path_lam, obj_init)
776 | Tcl_open (_, cl) ->
777 transl_class_rebind ~scopes obj_init cl vf
778
779let rec transl_class_rebind_0 ~scopes (self:Ident.t) obj_init cl vf =
780 match cl.cl_desc with
781 Tcl_let (rec_flag, defs, _vals, cl) ->
782 let path, path_lam, obj_init =
783 transl_class_rebind_0 ~scopes self obj_init cl vf
784 in
785 (path, path_lam, Translcore.transl_let ~scopes rec_flag defs obj_init)
786 | _ ->
787 let path, path_lam, obj_init =
788 transl_class_rebind ~scopes obj_init cl vf in
789 (path, path_lam, lfunction [self, Pgenval] obj_init)
790
791let transl_class_rebind ~scopes cl vf =
792 try
793 let obj_init = Ident.create_local "obj_init"
794 and self = Ident.create_local "self" in
795 let obj_init0 =
796 lapply {
797 ap_loc=Loc_unknown;
798 ap_func=Lvar obj_init;
799 ap_args=[Lvar self];
800 ap_tailcall=Default_tailcall;
801 ap_inlined=Default_inline;
802 ap_specialised=Default_specialise;
803 }
804 in
805 let _, path_lam, obj_init' =
806 transl_class_rebind_0 ~scopes self obj_init0 cl vf in
807 let id = (obj_init' = lfunction [self, Pgenval] obj_init0) in
808 if id then path_lam else
809
810 let cla = Ident.create_local "class"
811 and new_init = Ident.create_local "new_init"
812 and env_init = Ident.create_local "env_init"
813 and table = Ident.create_local "table"
814 and envs = Ident.create_local "envs" in
815 Llet(
816 Strict, Pgenval, new_init, lfunction [obj_init, Pgenval] obj_init',
817 Llet(
818 Alias, Pgenval, cla, path_lam,
819 Lprim(Pmakeblock(0, Immutable, None),
820 [mkappl(Lvar new_init, [lfield cla 0]);
821 lfunction [table, Pgenval]
822 (Llet(Strict, Pgenval, env_init,
823 mkappl(lfield cla 1, [Lvar table]),
824 lfunction [envs, Pgenval]
825 (mkappl(Lvar new_init,
826 [mkappl(Lvar env_init, [Lvar envs])]))));
827 lfield cla 2],
828 Loc_unknown)))
829 with Exit ->
830 lambda_unit
831
832(* Rewrite a closure using builtins. Improves native code size. *)
833
834let const_path local = function
835 Lvar id -> not (List.mem id local)
836 | Lconst _ -> true
837 | Lfunction {kind = Curried; body} ->
838 let fv = free_variables body in
839 List.for_all (fun x -> not (Ident.Set.mem x fv)) local
840 | _ -> false
841
842let rec builtin_meths self env env2 body =
843 let const_path = const_path (env::self) in
844 let conv = function
845 (* Lvar s when List.mem s self -> "_self", [] *)
846 | p when const_path p -> "const", [p]
847 | Lprim(Parrayrefu _, [Lvar s; Lvar n], _) when List.mem s self ->
848 "var", [Lvar n]
849 | Lprim(Pfield(n, _, _), [Lvar e], _) when Ident.same e env ->
850 "env", [Lvar env2; Lconst(const_int n)]
851 | Lsend(Self, met, Lvar s, [], _) when List.mem s self ->
852 "meth", [met]
853 | _ -> raise Not_found
854 in
855 match body with
856 | Llet(_str, _k, s', Lvar s, body) when List.mem s self ->
857 builtin_meths (s'::self) env env2 body
858 | Lapply{ap_func = f; ap_args = [arg]} when const_path f ->
859 let s, args = conv arg in ("app_"^s, f :: args)
860 | Lapply{ap_func = f; ap_args = [arg; p]} when const_path f && const_path p ->
861 let s, args = conv arg in
862 ("app_"^s^"_const", f :: args @ [p])
863 | Lapply{ap_func = f; ap_args = [p; arg]} when const_path f && const_path p ->
864 let s, args = conv arg in
865 ("app_const_"^s, f :: p :: args)
866 | Lsend(Self, Lvar n, Lvar s, [arg], _) when List.mem s self ->
867 let s, args = conv arg in
868 ("meth_app_"^s, Lvar n :: args)
869 | Lsend(Self, met, Lvar s, [], _) when List.mem s self ->
870 ("get_meth", [met])
871 | Lsend(Public, met, arg, [], _) ->
872 let s, args = conv arg in
873 ("send_"^s, met :: args)
874 | Lsend(Cached, met, arg, [_;_], _) ->
875 let s, args = conv arg in
876 ("send_"^s, met :: args)
877 | Lfunction {kind = Curried; params = [x, _]; body} ->
878 let rec enter self = function
879 | Lprim(Parraysetu _, [Lvar s; Lvar n; Lvar x'], _)
880 when Ident.same x x' && List.mem s self ->
881 ("set_var", [Lvar n])
882 | Llet(_str, _k, s', Lvar s, body) when List.mem s self ->
883 enter (s'::self) body
884 | _ -> raise Not_found
885 in enter self body
886 | Lfunction _ -> raise Not_found
887 | _ ->
888 let s, args = conv body in ("get_"^s, args)
889
890module M = struct
891 open CamlinternalOO
892 let builtin_meths self env env2 body =
893 let builtin, args = builtin_meths self env env2 body in
894 (* if not arr then [mkappl(oo_prim builtin, args)] else *)
895 let tag = match builtin with
896 "get_const" -> GetConst
897 | "get_var" -> GetVar
898 | "get_env" -> GetEnv
899 | "get_meth" -> GetMeth
900 | "set_var" -> SetVar
901 | "app_const" -> AppConst
902 | "app_var" -> AppVar
903 | "app_env" -> AppEnv
904 | "app_meth" -> AppMeth
905 | "app_const_const" -> AppConstConst
906 | "app_const_var" -> AppConstVar
907 | "app_const_env" -> AppConstEnv
908 | "app_const_meth" -> AppConstMeth
909 | "app_var_const" -> AppVarConst
910 | "app_env_const" -> AppEnvConst
911 | "app_meth_const" -> AppMethConst
912 | "meth_app_const" -> MethAppConst
913 | "meth_app_var" -> MethAppVar
914 | "meth_app_env" -> MethAppEnv
915 | "meth_app_meth" -> MethAppMeth
916 | "send_const" -> SendConst
917 | "send_var" -> SendVar
918 | "send_env" -> SendEnv
919 | "send_meth" -> SendMeth
920 | _ -> assert false
921 in Lconst(const_int (Obj.magic tag)) :: args
922end
923open M
924
925
926(*
927 Class translation.
928 Three subcases:
929 * reapplication of a known class -> transl_class_rebind
930 * class without local dependencies -> direct translation
931 * with local dependencies -> generate a stubs tree,
932 with a node for every local classes inherited
933 A class is a 3-tuple:
934 (obj_init, class_init, env)
935 obj_init: creation function (unit -> params -> obj)
936 class_init: inheritance function (table -> env -> obj_init)
937 (one by source code)
938 env: local environment
939
940 The local environment is used for cached classes. When a
941 class definition occurs under a call to Translobj.oo_wrap
942 (typically inside a functor), the class creation code is
943 split between a static part (depending only on toplevel names)
944 and a dynamic part, the environment. The static part is cached
945 in a toplevel structure, so that only the first class creation
946 computes it and the subsequent classes can reuse it.
947 Because of that, the (static) [class_init] function takes both
948 the class table to be filled and the environment as parameters,
949 and when called is given the [env] field of the class.
950 For the [obj_init] part, an [env_init] function (of type [env -> obj_init])
951 is stored in the cache, and called on the environment to generate
952 the [obj_init] at class creation time.
953*)
954
955(*
956let prerr_ids msg ids =
957 let names = List.map Ident.unique_toplevel_name ids in
958 prerr_endline (String.concat " " (msg :: names))
959*)
960
961let free_methods l =
962 let fv = ref Ident.Set.empty in
963 let rec free l =
964 Lambda.iter_head_constructor free l;
965 match l with
966 | Lsend(Self, Lvar meth, _, _, _) ->
967 fv := Ident.Set.add meth !fv
968 | Lsend _ -> ()
969 | Lfunction{params} ->
970 List.iter (fun (param, _) -> fv := Ident.Set.remove param !fv) params
971 | Llet(_, _k, id, _arg, _body)
972 | Lmutlet(_k, id, _arg, _body) ->
973 fv := Ident.Set.remove id !fv
974 | Lletrec(decl, _body) ->
975 List.iter (fun { id } -> fv := Ident.Set.remove id !fv) decl
976 | Lstaticcatch(_e1, (_,vars), _e2) ->
977 List.iter (fun (id, _) -> fv := Ident.Set.remove id !fv) vars
978 | Ltrywith(_e1, exn, _e2) ->
979 fv := Ident.Set.remove exn !fv
980 | Lfor(v, _e1, _e2, _dir, _e3) ->
981 fv := Ident.Set.remove v !fv
982 | Lassign _
983 | Lvar _ | Lmutvar _ | Lconst _ | Lapply _
984 | Lprim _ | Lswitch _ | Lstringswitch _ | Lstaticraise _
985 | Lifthenelse _ | Lsequence _ | Lwhile _
986 | Levent _ | Lifused _ -> ()
987 in free l; !fv
988
989let transl_class ~scopes ids cl_id pub_meths cl vflag =
990 let open Value_rec_types in
991 (* First check if it is not only a rebind *)
992 let rebind = transl_class_rebind ~scopes cl vflag in
993 if rebind <> lambda_unit then rebind, Dynamic else
994
995 (* Prepare for heavy environment handling *)
996 let scopes = enter_class_definition ~scopes cl_id in
997 let tables = Ident.create_local (Ident.name cl_id ^ "_tables") in
998 let (top_env, req) = oo_add_class tables in
999 let top = not req in
1000 (* The manual specifies that toplevel lets *must* be evaluated outside of the
1001 class *)
1002 let cl_env, llets = build_class_lets ~scopes cl in
1003 let new_ids = if top then [] else Env.diff top_env cl_env in
1004 let env2 = Ident.create_local "env" in
1005 let meth_ids = get_class_meths cl in
1006 let subst env lam i0 new_ids' =
1007 let fv = free_variables lam in
1008 (* prerr_ids "cl_id =" [cl_id]; prerr_ids "fv =" (Ident.Set.elements fv); *)
1009 let fv = List.fold_right Ident.Set.remove !new_ids' fv in
1010 (* We need to handle method ids specially, as they do not appear
1011 in the typing environment (PR#3576, PR#4560) *)
1012 (* very hacky: we add and remove free method ids on the fly,
1013 depending on the visit order... *)
1014 method_ids :=
1015 Ident.Set.diff (Ident.Set.union (free_methods lam) !method_ids) meth_ids;
1016 (* prerr_ids "meth_ids =" (Ident.Set.elements meth_ids);
1017 prerr_ids "method_ids =" (Ident.Set.elements !method_ids); *)
1018 let new_ids = List.fold_right Ident.Set.add new_ids !method_ids in
1019 let fv = Ident.Set.inter fv new_ids in
1020 new_ids' := !new_ids' @ Ident.Set.elements fv;
1021 (* prerr_ids "new_ids' =" !new_ids'; *)
1022 let i = ref (i0-1) in
1023 List.fold_left
1024 (fun subst id ->
1025 incr i; Ident.Map.add id (lfield env !i) subst)
1026 Ident.Map.empty !new_ids'
1027 in
1028 let new_ids_meths = ref [] in
1029 let no_env_update _ _ env = env in
1030 let msubst arr = function
1031 Lfunction {kind = Curried; params = (self, Pgenval) :: args; body} ->
1032 let env = Ident.create_local "env" in
1033 let body' =
1034 if new_ids = [] then body else
1035 Lambda.subst no_env_update (subst env body 0 new_ids_meths) body in
1036 begin try
1037 (* Doesn't seem to improve size for bytecode *)
1038 (* if not !Clflags.native_code then raise Not_found; *)
1039 if not arr || !Clflags.debug then raise Not_found;
1040 builtin_meths [self] env env2 (lfunction args body')
1041 with Not_found ->
1042 [lfunction ((self, Pgenval) :: args)
1043 (if not (Ident.Set.mem env (free_variables body')) then body' else
1044 Llet(Alias, Pgenval, env,
1045 Lprim(Pfield_computed,
1046 [Lvar self; Lvar env2],
1047 Loc_unknown),
1048 body'))]
1049 end
1050 | _ -> assert false
1051 in
1052 let new_ids_init = ref [] in
1053 let env1 = Ident.create_local "env" and env1' = Ident.create_local "env'" in
1054 let copy_env self =
1055 if top then lambda_unit else
1056 Lifused(env2, Lprim(Psetfield_computed (Pointer, Assignment),
1057 [Lvar self; Lvar env2; Lvar env1'],
1058 Loc_unknown))
1059 and subst_env envs l lam =
1060 if top then lam else
1061 (* must be called only once! *)
1062 let lam = Lambda.subst no_env_update (subst env1 lam 1 new_ids_init) lam in
1063 Llet(Alias, Pgenval, env1, (if l = [] then Lvar envs else lfield envs 0),
1064 Llet(Alias, Pgenval, env1',
1065 (if !new_ids_init = [] then Lvar env1 else lfield env1 0),
1066 lam))
1067 in
1068
1069 (* Now we start compiling the class *)
1070 let cla = Ident.create_local "class" in
1071 let (inh_init, obj_init) =
1072 build_object_init_0 ~scopes cla [] cl copy_env subst_env top ids in
1073 let inh_init' = List.rev inh_init in
1074 let (inh_init', cl_init) =
1075 build_class_init ~scopes cla true ([],[]) inh_init' obj_init msubst top cl
1076 in
1077 assert (inh_init' = []);
1078 let table = Ident.create_local "table"
1079 and class_init = Ident.create_local (Ident.name cl_id ^ "_init")
1080 and env_init = Ident.create_local "env_init"
1081 and obj_init = Ident.create_local "obj_init" in
1082 (* Sort methods by hash *)
1083 let pub_meths =
1084 List.sort
1085 (fun s s' -> compare (Btype.hash_variant s) (Btype.hash_variant s'))
1086 pub_meths in
1087 (* Check for hash conflicts *)
1088 let tags = List.map Btype.hash_variant pub_meths in
1089 let rev_map = List.combine tags pub_meths in
1090 List.iter2
1091 (fun tag name ->
1092 let name' = List.assoc tag rev_map in
1093 if name' <> name then raise(Error(cl.cl_loc, Tags(name, name'))))
1094 tags pub_meths;
1095 let ltable table lam =
1096 Llet(Strict, Pgenval, table,
1097 mkappl (oo_prim "create_table", [transl_meth_list pub_meths]), lam)
1098 and ldirect obj_init =
1099 Llet(Strict, Pgenval, obj_init, cl_init,
1100 Lsequence(mkappl (oo_prim "init_class", [Lvar cla]),
1101 mkappl (Lvar obj_init, [lambda_unit])))
1102 in
1103 (* Simplest case: an object defined at toplevel (ids=[]) *)
1104 if top && ids = [] then llets (ltable cla (ldirect obj_init), Dynamic) else
1105
1106 let concrete = (vflag = Concrete)
1107 and lclass mk_lam_and_kind =
1108 let cl_init, _ =
1109 llets (Lambda.lfunction
1110 ~kind:Curried
1111 ~attr:default_function_attribute
1112 ~loc:Loc_unknown
1113 ~return:Pgenval
1114 ~params:[cla, Pgenval]
1115 ~body:cl_init,
1116 Dynamic (* Placeholder, real kind is computed in [lbody] below *))
1117 in
1118 let lam, rkind = mk_lam_and_kind (free_variables cl_init) in
1119 Llet(Strict, Pgenval, class_init, cl_init, lam), rkind
1120 and lbody fv =
1121 if List.for_all (fun id -> not (Ident.Set.mem id fv)) ids then
1122 (* Not recursive: can use make_class directly *)
1123 mkappl (oo_prim "make_class",[transl_meth_list pub_meths;
1124 Lvar class_init]),
1125 Dynamic
1126 else
1127 (* Recursive: need to have an actual allocation for let rec compilation
1128 to work, so hardcode make_class *)
1129 ltable table (
1130 Llet(
1131 Strict, Pgenval, env_init, mkappl (Lvar class_init, [Lvar table]),
1132 Lsequence(
1133 mkappl (oo_prim "init_class", [Lvar table]),
1134 Lprim(Pmakeblock(0, Immutable, None),
1135 [mkappl (Lvar env_init, [lambda_unit]);
1136 Lvar class_init; lambda_unit],
1137 Loc_unknown)))),
1138 Static
1139 and lbody_virt lenvs =
1140 (* Virtual classes only need to provide the [class_init] and [env]
1141 fields. [obj_init] is filled with a dummy [lambda_unit] value. *)
1142 Lprim(Pmakeblock(0, Immutable, None),
1143 [lambda_unit; Lambda.lfunction
1144 ~kind:Curried
1145 ~attr:default_function_attribute
1146 ~loc:Loc_unknown
1147 ~return:Pgenval
1148 ~params:[cla, Pgenval] ~body:cl_init;
1149 lenvs],
1150 Loc_unknown),
1151 Static
1152 in
1153 (* Still easy: a class defined at toplevel *)
1154 if top && concrete then lclass lbody else
1155 if top then llets (lbody_virt lambda_unit) else
1156
1157 (* Now for the hard stuff: prepare for table caching *)
1158 let envs = Ident.create_local "envs"
1159 and cached = Ident.create_local "cached" in
1160 let lenvs =
1161 if !new_ids_meths = [] && !new_ids_init = [] && inh_init = []
1162 then lambda_unit
1163 else Lvar envs in
1164 let lenv =
1165 let menv =
1166 if !new_ids_meths = [] then lambda_unit else
1167 Lprim(Pmakeblock(0, Immutable, None),
1168 List.map (fun id -> Lvar id) !new_ids_meths,
1169 Loc_unknown) in
1170 if !new_ids_init = [] then menv else
1171 Lprim(Pmakeblock(0, Immutable, None),
1172 menv :: List.map (fun id -> Lvar id) !new_ids_init,
1173 Loc_unknown)
1174 and linh_envs =
1175 List.map
1176 (fun (_, path_lam, _) ->
1177 Lprim(Pfield (2, Pointer, Mutable), [path_lam], Loc_unknown))
1178 (List.rev inh_init)
1179 in
1180 let make_envs (lam, rkind) =
1181 Llet(StrictOpt, Pgenval, envs,
1182 (if linh_envs = [] then lenv else
1183 Lprim(Pmakeblock(0, Immutable, None),
1184 lenv :: linh_envs, Loc_unknown)),
1185 lam),
1186 rkind
1187 and def_ids cla lam =
1188 Llet(StrictOpt, Pgenval, env2,
1189 mkappl (oo_prim "new_variable", [Lvar cla; transl_label ""]),
1190 lam)
1191 in
1192 let inh_paths =
1193 List.filter
1194 (fun (path, _, _) -> List.mem (Path.head path) new_ids) inh_init
1195 in
1196 let inh_keys =
1197 List.map
1198 (fun (_, path_lam, _) ->
1199 Lprim(Pfield (1, Pointer, Mutable), [path_lam], Loc_unknown))
1200 inh_paths
1201 in
1202 let lclass lam =
1203 Llet(Strict, Pgenval, class_init,
1204 Lambda.lfunction
1205 ~kind:Curried ~params:[cla, Pgenval]
1206 ~return:Pgenval
1207 ~attr:default_function_attribute
1208 ~loc:Loc_unknown
1209 ~body:(def_ids cla cl_init), lam)
1210 and lset cached i lam =
1211 Lprim(Psetfield(i, Pointer, Assignment),
1212 [Lvar cached; lam], Loc_unknown)
1213 in
1214 let ldirect () =
1215 ltable cla
1216 (Llet(Strict, Pgenval, env_init, def_ids cla cl_init,
1217 Lsequence(mkappl (oo_prim "init_class", [Lvar cla]),
1218 lset cached 0 (Lvar env_init))))
1219 and lclass_virt () =
1220 lset cached 0
1221 (Lambda.lfunction
1222 ~kind:Curried
1223 ~attr:default_function_attribute
1224 ~loc:Loc_unknown
1225 ~return:Pgenval
1226 ~params:[cla, Pgenval]
1227 ~body:(def_ids cla cl_init))
1228 in
1229 let lupdate_cache =
1230 if ids = [] then ldirect () else
1231 if not concrete then lclass_virt () else
1232 lclass (
1233 mkappl (oo_prim "make_class_store",
1234 [transl_meth_list pub_meths;
1235 Lvar class_init; Lvar cached])) in
1236 let lcheck_cache =
1237 if !Clflags.native_code && !Clflags.afl_instrument then
1238 (* When afl-fuzz instrumentation is enabled, ignore the cache
1239 so that the program's behaviour does not change between runs *)
1240 lupdate_cache
1241 else
1242 Lifthenelse(lfield cached 0, lambda_unit, lupdate_cache) in
1243 let lcache (lam, rkind) =
1244 let lam = Lsequence (lcheck_cache, lam) in
1245 let lam =
1246 if inh_keys = []
1247 then Llet(Alias, Pgenval, cached, Lvar tables, lam)
1248 else
1249 Llet(Strict, Pgenval, cached,
1250 mkappl (oo_prim "lookup_tables",
1251 [Lvar tables; Lprim(Pmakeblock(0, Immutable, None),
1252 inh_keys, Loc_unknown)]),
1253 lam)
1254 in
1255 lam, rkind
1256 in
1257 llets (
1258 lcache (
1259 make_envs (
1260 if ids = []
1261 then mkappl (lfield cached 0, [lenvs]), Dynamic
1262 else
1263 Lprim(Pmakeblock(0, Immutable, None),
1264 (if concrete then
1265 [mkappl (lfield cached 0, [lenvs]);
1266 lfield cached 1;
1267 lenvs]
1268 else [lambda_unit; lfield cached 0; lenvs]),
1269 Loc_unknown
1270 ),
1271 Static)))
1272
1273(* Wrapper for class compilation *)
1274(*
1275 let cl_id = ci.ci_id_class in
1276(* TODO: cl_id is used somewhere else as typesharp ? *)
1277 let _arity = List.length ci.ci_params in
1278 let pub_meths = m in
1279 let cl = ci.ci_expr in
1280 let vflag = vf in
1281*)
1282
1283let transl_class ~scopes ids id pub_meths cl vf =
1284 oo_wrap_gen cl.cl_env false (transl_class ~scopes ids id pub_meths cl) vf
1285
1286let () =
1287 transl_object := (fun ~scopes id meths cl ->
1288 let lam, _rkind = transl_class ~scopes [] id meths cl Concrete in
1289 lam)
1290
1291(* Error report *)
1292
1293open Format_doc
1294module Style = Misc.Style
1295
1296let report_error_doc ppf = function
1297 | Tags (lab1, lab2) ->
1298 fprintf ppf "Method labels %a and %a are incompatible.@ %s"
1299 Style.inline_code lab1
1300 Style.inline_code lab2
1301 "Change one of them."
1302
1303let () =
1304 Location.register_error_of_exn
1305 (function
1306 | Error (loc, err) ->
1307 Some (Location.error_of_printer ~loc report_error_doc err)
1308 | _ ->
1309 None
1310 )
1311
1312let report_error = Format_doc.compat report_error_doc