12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055 |
- (*
- * Neko Compiler
- * Copyright (c)2005 Motion-Twin
- *
- * This library 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; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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 or the LICENSE file for more details.
- *)
- open Nast
- open Nbytecode
- type access =
- | XEnv of int
- | XStack of int
- | XGlobal of int
- | XField of string
- | XIndex of int
- | XArray
- | XThis
- type label = {
- lname : string;
- ltraps : int list;
- lstack : int;
- mutable lpos : int option;
- mutable lwait : (unit -> unit) list;
- }
- type globals = {
- globals : (global,int) Hashtbl.t;
- gobjects : (string list,int) Hashtbl.t;
- mutable functions : (opcode DynArray.t * (int * int) DynArray.t * int * int) list;
- mutable gtable : global DynArray.t;
- labels : (string,label) Hashtbl.t;
- hfiles : (string,int) Hashtbl.t;
- files : string DynArray.t;
- }
- type context = {
- g : globals;
- version : int;
- mutable ops : opcode DynArray.t;
- mutable locals : (string,int) PMap.t;
- mutable env : (string,int) PMap.t;
- mutable nenv : int;
- mutable stack : int;
- mutable loop_limit : int;
- mutable loop_traps : int;
- mutable limit : int;
- mutable traps : int list;
- mutable breaks : ((unit -> unit) * pos) list;
- mutable continues : ((unit -> unit) * pos) list;
- mutable pos : (int * int) DynArray.t;
- mutable curpos : (int * int);
- mutable curfile : string;
- }
- type error_msg = string
- exception Error of error_msg * pos
- let error e p =
- raise (Error(e,p))
- let error_msg s =
- s
- let stack_delta o =
- match o with
- | AccNull
- | AccTrue
- | AccFalse
- | AccThis
- | AccInt _
- | AccInt32 _
- | AccStack _
- | AccGlobal _
- | AccEnv _
- | AccField _
- | AccBuiltin _
- | AccIndex _
- | JumpIf _
- | JumpIfNot _
- | Jump _
- | JumpTable _
- | Ret _
- | SetGlobal _
- | SetStack _
- | SetEnv _
- | SetThis
- | Bool
- | IsNull
- | IsNotNull
- | Not
- | Hash
- | TypeOf
- | New
- | AccStack0
- | AccStack1
- | AccIndex0
- | AccIndex1
- | Loop
- -> 0
- | Add
- | Sub
- | Mult
- | Div
- | Mod
- | Shl
- | Shr
- | UShr
- | Or
- | And
- | Xor
- | Eq
- | Neq
- | Gt
- | Gte
- | Lt
- | Lte
- | PhysCompare
- -> -1
- | AccArray -> -1
- | SetField _ | SetIndex _ | Compare -> -1
- | SetArray -> -2
- | Push -> 1
- | Pop x -> -x
- | Apply nargs | Call nargs | TailCall (nargs,_) -> -nargs
- | ObjCall nargs -> -(nargs + 1)
- | MakeEnv size | MakeArray size -> -size
- | Trap _ -> trap_stack_delta
- | EndTrap -> -trap_stack_delta
- let check_stack ctx stack p =
- if ctx.stack <> stack then error "Stack alignment failure" p
- let pos ctx =
- DynArray.length ctx.ops
- let real_null_pos =
- { pline = 0; psource = "<null>" }
- let set_pos ctx p =
- if p.psource = ctx.curfile then begin
- if p.pline <> snd ctx.curpos then ctx.curpos <- (fst ctx.curpos, p.pline);
- end else if p = real_null_pos then
- ()
- else
- let fid = (try
- Hashtbl.find ctx.g.hfiles p.psource
- with Not_found ->
- let fid = DynArray.length ctx.g.files in
- DynArray.add ctx.g.files p.psource;
- Hashtbl.add ctx.g.hfiles p.psource fid;
- fid
- ) in
- ctx.curfile <- p.psource;
- ctx.curpos <- (fid,p.pline)
- let write ctx op =
- ctx.stack <- ctx.stack + stack_delta op;
- DynArray.add ctx.pos ctx.curpos;
- if op_param op then DynArray.add ctx.pos ctx.curpos;
- DynArray.add ctx.ops op
- let jmp ctx =
- let p = pos ctx in
- write ctx (Jump 0);
- (fun() -> DynArray.set ctx.ops p (Jump(pos ctx - p)))
- let cjmp cond ctx =
- let p = pos ctx in
- write ctx (Jump 0);
- (fun() -> DynArray.set ctx.ops p (if cond then JumpIf(pos ctx - p) else JumpIfNot(pos ctx - p)))
- let trap ctx =
- let p = pos ctx in
- write ctx (Trap 0);
- (fun() -> DynArray.set ctx.ops p (Trap(pos ctx - p)))
- let goto ctx p =
- write ctx (Jump(p - pos ctx))
- let global ctx g =
- let ginf = ctx.g in
- try
- Hashtbl.find ginf.globals g
- with Not_found ->
- let gid = DynArray.length ginf.gtable in
- Hashtbl.add ginf.globals g gid;
- DynArray.add ginf.gtable g;
- gid
- let save_breaks ctx =
- let oldc = ctx.continues in
- let oldb = ctx.breaks in
- let oldl = ctx.loop_limit in
- let oldt = ctx.loop_traps in
- ctx.loop_traps <- List.length ctx.traps;
- ctx.loop_limit <- ctx.stack;
- ctx.breaks <- [];
- ctx.continues <- [];
- (ctx , oldc, oldb , oldl, oldt)
- let process_continues (ctx,oldc,_,_,_) =
- List.iter (fun (f,_) -> f()) ctx.continues;
- ctx.continues <- oldc
- let process_breaks (ctx,_,oldb,oldl, oldt) =
- List.iter (fun (f,_) -> f()) ctx.breaks;
- ctx.loop_limit <- oldl;
- ctx.loop_traps <- oldt;
- ctx.breaks <- oldb
- let check_breaks ctx =
- List.iter (fun (_,p) -> error "Break outside a loop" p) ctx.breaks;
- List.iter (fun (_,p) -> error "Continue outside a loop" p) ctx.continues
- let make_array p el =
- (ECall ((EConst (Builtin "array"),p),el), p)
- let get_cases_ints(cases) =
- let max = ref (-1) in
- let l = List.map (fun (e,e2) ->
- match e with
- | (EConst (Int n),_) when n >= 0 ->
- if n > !max then max := n;
- (n,e2)
- | _ -> raise Exit
- ) cases in
- (* // only create jump table if small or >10% cases matched *)
- let nmatches = List.length l in
- if nmatches < 3 then raise Exit;
- if !max >= 16 && (nmatches * 100) / (!max + 1) < 10 then raise Exit;
- if !max > 512 then raise Exit;
- (l,!max + 1)
- let rec scan_labels ctx supported in_block e =
- match fst e with
- | EFunction (args,e) ->
- let nargs = List.length args in
- let traps = ctx.traps in
- ctx.traps <- [];
- ctx.stack <- ctx.stack + nargs;
- scan_labels ctx supported false e;
- ctx.stack <- ctx.stack - nargs;
- ctx.traps <- traps
- | EBlock _ ->
- let old = ctx.stack in
- Nast.iter (scan_labels ctx supported true) e;
- ctx.stack <- old
- | EVars l ->
- if not in_block then error "Variable declaration must be done inside a block" (snd e);
- List.iter (fun (_,e) ->
- (match e with
- | None -> ()
- | Some e -> scan_labels ctx supported false e);
- ctx.stack <- ctx.stack + 1
- ) l
- | ELabel l when not supported ->
- error "Label is not supported in this part of the program" (snd e);
- | ELabel l when Hashtbl.mem ctx.g.labels l ->
- error ("Duplicate label " ^ l) (snd e)
- | ELabel l ->
- let label = {
- lname = l;
- ltraps = List.rev ctx.traps;
- lstack = ctx.stack;
- lpos = None;
- lwait = [];
- } in
- Hashtbl.add ctx.g.labels l label
- | ETry (e,_,e2) ->
- ctx.stack <- ctx.stack + trap_stack_delta;
- ctx.traps <- ctx.stack :: ctx.traps;
- scan_labels ctx supported false e;
- ctx.stack <- ctx.stack - trap_stack_delta;
- ctx.traps <- (match ctx.traps with [] -> assert false | _ :: l -> l);
- ctx.stack <- ctx.stack + 1;
- scan_labels ctx supported false e2;
- ctx.stack <- ctx.stack - 1;
- | EBinop ("=",e1,e2) ->
- let rec is_extended (e,_) =
- match e with
- | EParenthesis e -> is_extended e
- | EArray _
- | EField _ ->
- true
- | _ ->
- false
- in
- let ext = is_extended e1 in
- if ext then ctx.stack <- ctx.stack + 1;
- scan_labels ctx supported false e2;
- ctx.stack <- ctx.stack + 1;
- scan_labels ctx supported false e1;
- ctx.stack <- ctx.stack - (if ext then 2 else 1);
- | ECall ((EConst (Builtin "array"),_),e :: el) ->
- if ctx.version >= 2 then begin
- scan_labels ctx supported false e;
- List.iter (fun e ->
- ctx.stack <- ctx.stack + 1;
- scan_labels ctx supported false e;
- ) el;
- ctx.stack <- ctx.stack - List.length el
- end else begin
- List.iter (fun e ->
- scan_labels ctx supported false e;
- ctx.stack <- ctx.stack + 1;
- ) el;
- scan_labels ctx supported false e;
- ctx.stack <- ctx.stack - List.length el
- end
- | ECall ((EConst (Builtin x),_),el) when x <> "apply" ->
- Nast.iter (scan_labels ctx false false) e
- | ECall ((EConst (Builtin "apply"),_),e :: el)
- | ECall(e,el) ->
- List.iter (fun e ->
- scan_labels ctx supported false e;
- ctx.stack <- ctx.stack + 1;
- ) el;
- scan_labels ctx supported false e;
- ctx.stack <- ctx.stack - List.length el
- | EObject fl ->
- ctx.stack <- ctx.stack + 2;
- List.iter (fun (s,e) ->
- scan_labels ctx supported false e
- ) fl;
- ctx.stack <- ctx.stack - 2;
- | ESwitch (ee,[(econd,exec)],eo) ->
- let p = snd e in
- scan_labels ctx supported false (EIf ((EBinop ("==",ee,econd),p),exec,eo),p)
- | ESwitch (e,cases,eo) ->
- scan_labels ctx supported false e;
- let delta = (try ignore(get_cases_ints cases); 0 with Exit -> 1) in
- ctx.stack <- ctx.stack + delta;
- List.iter (fun (e1,e2) ->
- ctx.stack <- ctx.stack + delta;
- scan_labels ctx supported false e1;
- ctx.stack <- ctx.stack - delta;
- scan_labels ctx supported false e2;
- ) cases;
- (match eo with
- | None -> ()
- | Some e -> scan_labels ctx supported false e);
- ctx.stack <- ctx.stack - delta;
- | ENext (e1,e2) ->
- scan_labels ctx supported in_block e1;
- scan_labels ctx supported in_block e2;
- | EConst _
- | EContinue
- | EBreak _
- | EReturn _
- | EIf _
- | EWhile _
- | EParenthesis _ ->
- Nast.iter (scan_labels ctx supported false) e
- | EBinop (_,_,_)
- | EArray _
- | EField _
- ->
- Nast.iter (scan_labels ctx false false) e
- | ENeko _ ->
- assert false
- let compile_constant ctx c p =
- match c with
- | True -> write ctx AccTrue
- | False -> write ctx AccFalse
- | Null -> write ctx AccNull
- | This -> write ctx AccThis
- | Int n -> write ctx (AccInt n)
- | Int32 n -> write ctx (AccInt32 n)
- | Float f -> write ctx (AccGlobal (global ctx (GlobalFloat f)))
- | String s -> write ctx (AccGlobal (global ctx (GlobalString s)))
- | Builtin s ->
- (match s with
- | "tnull" -> write ctx (AccInt 0)
- | "tint" -> write ctx (AccInt 1)
- | "tfloat" -> write ctx (AccInt 2)
- | "tbool" -> write ctx (AccInt 3)
- | "tstring" -> write ctx (AccInt 4)
- | "tobject" -> write ctx (AccInt 5)
- | "tarray" -> write ctx (AccInt 6)
- | "tfunction" -> write ctx (AccInt 7)
- | "tabstract" -> write ctx (AccInt 8)
- | s ->
- write ctx (AccBuiltin s))
- | Ident s ->
- try
- let l = PMap.find s ctx.locals in
- if l <= ctx.limit then
- let e = (try
- PMap.find s ctx.env
- with Not_found ->
- let e = ctx.nenv in
- ctx.nenv <- ctx.nenv + 1;
- ctx.env <- PMap.add s e ctx.env;
- e
- ) in
- write ctx (AccEnv e);
- else
- let p = ctx.stack - l in
- write ctx (if p = 0 then AccStack0 else if p = 1 then AccStack1 else AccStack p);
- with Not_found ->
- let g = global ctx (GlobalVar s) in
- write ctx (AccGlobal g)
- let rec compile_access ctx e =
- match fst e with
- | EConst (Ident s) ->
- (try
- let l = PMap.find s ctx.locals in
- if l <= ctx.limit then
- let e = (try
- PMap.find s ctx.env
- with Not_found ->
- let e = ctx.nenv in
- ctx.nenv <- ctx.nenv + 1;
- ctx.env <- PMap.add s e ctx.env;
- e
- ) in
- XEnv e
- else
- XStack l
- with Not_found ->
- let g = global ctx (GlobalVar s) in
- XGlobal g)
- | EField (e,f) ->
- compile ctx false e;
- write ctx Push;
- XField f
- | EArray (e1,(EConst (Int n),_)) ->
- compile ctx false e1;
- write ctx Push;
- XIndex n
- | EArray (ea,ei) ->
- compile ctx false ei;
- write ctx Push;
- compile ctx false ea;
- write ctx Push;
- XArray
- | EConst This ->
- XThis
- | _ ->
- error "Invalid access" (snd e)
- and compile_access_set ctx a =
- match a with
- | XEnv n -> write ctx (SetEnv n)
- | XStack l -> write ctx (SetStack (ctx.stack - l))
- | XGlobal g -> write ctx (SetGlobal g)
- | XField f -> write ctx (SetField f)
- | XIndex i -> write ctx (SetIndex i)
- | XThis -> write ctx SetThis
- | XArray -> write ctx SetArray
- and compile_access_get ctx a =
- match a with
- | XEnv n -> write ctx (AccEnv n)
- | XStack l -> write ctx (AccStack (ctx.stack - l))
- | XGlobal g -> write ctx (AccGlobal g)
- | XField f -> write ctx (AccField f)
- | XIndex i -> write ctx (AccIndex i)
- | XThis -> write ctx AccThis
- | XArray ->
- write ctx Push;
- write ctx (AccStack 2);
- write ctx AccArray
- and write_op ctx op p =
- match op with
- | "+" -> write ctx Add
- | "-" -> write ctx Sub
- | "/" -> write ctx Div
- | "*" -> write ctx Mult
- | "%" -> write ctx Mod
- | "<<" -> write ctx Shl
- | ">>" -> write ctx Shr
- | ">>>" -> write ctx UShr
- | "|" -> write ctx Or
- | "&" -> write ctx And
- | "^" -> write ctx Xor
- | "==" -> write ctx Eq
- | "!=" -> write ctx Neq
- | ">" -> write ctx Gt
- | ">=" -> write ctx Gte
- | "<" -> write ctx Lt
- | "<=" -> write ctx Lte
- | _ -> error "Unknown operation" p
- and compile_binop ctx tail op e1 e2 p =
- match op with
- | "=" ->
- let a = compile_access ctx e1 in
- compile ctx false e2;
- compile_access_set ctx a
- | "&&" ->
- compile ctx false e1;
- let jnext = cjmp false ctx in
- compile ctx tail e2;
- jnext()
- | "||" ->
- compile ctx false e1;
- let jnext = cjmp true ctx in
- compile ctx tail e2;
- jnext()
- | "++="
- | "--=" ->
- write ctx Push;
- let base = ctx.stack in
- let a = compile_access ctx e1 in
- compile_access_get ctx a;
- write ctx (SetStack(ctx.stack - base));
- write ctx Push;
- compile ctx false e2;
- write_op ctx (String.sub op 0 (String.length op - 2)) p;
- compile_access_set ctx a;
- write ctx (AccStack 0);
- write ctx (Pop 1);
- | "+="
- | "-="
- | "/="
- | "*="
- | "%="
- | "<<="
- | ">>="
- | ">>>="
- | "|="
- | "&="
- | "^=" ->
- let a = compile_access ctx e1 in
- compile_access_get ctx a;
- write ctx Push;
- compile ctx false e2;
- write_op ctx (String.sub op 0 (String.length op - 1)) p;
- compile_access_set ctx a
- | _ ->
- match (op , e1 , e2) with
- | ("==" , _ , (EConst Null,_)) ->
- compile ctx false e1;
- write ctx IsNull
- | ("!=" , _ , (EConst Null,_)) ->
- compile ctx false e1;
- write ctx IsNotNull
- | ("==" , (EConst Null,_) , _) ->
- compile ctx false e2;
- write ctx IsNull
- | ("!=" , (EConst Null,_) , _) ->
- compile ctx false e2;
- write ctx IsNotNull
- | ("-", (EConst (Int 0),_) , (EConst (Int i),_)) ->
- compile ctx tail (EConst (Int (-i)),p)
- | _ ->
- compile ctx false e1;
- write ctx Push;
- compile ctx false e2;
- write_op ctx op p
- and compile_function main params e =
- let ctx = {
- g = main.g;
- (* // reset *)
- ops = DynArray.create();
- pos = DynArray.create();
- breaks = [];
- continues = [];
- env = PMap.empty;
- nenv = 0;
- traps = [];
- loop_traps = 0;
- limit = main.stack;
- (* // dup *)
- version = main.version;
- stack = main.stack;
- locals = main.locals;
- loop_limit = main.loop_limit;
- curpos = main.curpos;
- curfile = main.curfile;
- } in
- List.iter (fun v ->
- ctx.stack <- ctx.stack + 1;
- ctx.locals <- PMap.add v ctx.stack ctx.locals;
- ) params;
- let s = ctx.stack in
- compile ctx true e;
- write ctx (Ret (ctx.stack - ctx.limit));
- check_stack ctx s (snd e);
- check_breaks ctx;
- (* // add let *)
- let gid = DynArray.length ctx.g.gtable in
- ctx.g.functions <- (ctx.ops,ctx.pos,gid,List.length params) :: ctx.g.functions;
- DynArray.add ctx.g.gtable (GlobalFunction(gid,-1));
- (* // environment *)
- if ctx.nenv > 0 then
- let a = Array.make ctx.nenv "" in
- PMap.iter (fun v i -> a.(i) <- v) ctx.env;
- Array.iter (fun v ->
- compile_constant main (Ident v) (snd e);
- write main Push;
- ) a;
- write main (AccGlobal gid);
- write main (MakeEnv ctx.nenv);
- else
- write main (AccGlobal gid);
- and compile_builtin ctx tail b el p =
- match (b , el) with
- | ("istrue" , [e]) ->
- compile ctx false e;
- write ctx Bool
- | ("not" , [e]) ->
- compile ctx false e;
- write ctx Not
- | ("typeof" , [e]) ->
- compile ctx false e;
- write ctx TypeOf
- | ("hash" , [e]) ->
- compile ctx false e;
- write ctx Hash
- | ("new" , [e]) ->
- compile ctx false e;
- write ctx New
- | ("compare" , [e1;e2]) ->
- compile ctx false e1;
- write ctx Push;
- compile ctx false e2;
- write ctx Compare
- | ("pcompare" , [e1;e2]) ->
- compile ctx false e1;
- write ctx Push;
- compile ctx false e2;
- write ctx PhysCompare
- | ("goto" , [(EConst (Ident l) , _)] ) ->
- let l = (try Hashtbl.find ctx.g.labels l with Not_found -> error ("Unknown label " ^ l) p) in
- let os = ctx.stack in
- let rec loop l1 l2 =
- match l1, l2 with
- | x :: l1 , y :: l2 when x == y -> loop l1 l2
- | _ -> (l1,l2)
- in
- let straps , dtraps = loop (List.rev ctx.traps) l.ltraps in
- List.iter (fun l ->
- if ctx.stack <> l then write ctx (Pop(ctx.stack - l));
- write ctx EndTrap;
- ) (List.rev straps);
- let dtraps = List.map (fun l ->
- let l = l - trap_stack_delta in
- if l < ctx.stack then write ctx (Pop(ctx.stack - l));
- while ctx.stack < l do
- write ctx Push;
- done;
- trap ctx
- ) dtraps in
- if l.lstack < ctx.stack then write ctx (Pop(ctx.stack - l.lstack));
- while l.lstack > ctx.stack do
- write ctx Push;
- done;
- ctx.stack <- os;
- (match l.lpos with
- | None -> l.lwait <- jmp ctx :: l.lwait
- | Some p -> write ctx (Jump p));
- List.iter (fun t ->
- t();
- write ctx Push;
- compile_constant ctx (Builtin "raise") p;
- write ctx (Call 1);
- (* // insert an infinite loop in order to
- // comply with bytecode checker *)
- let _ = jmp ctx in
- ()
- ) dtraps;
- | ("goto" , _) ->
- error "Invalid $goto statement" p
- | ("array",e :: el) ->
- let count = List.length el in
- (* // a single let can't have >128 stack *)
- if count > 120 - ctx.stack && count > 8 then begin
- (* // split in 8 and recurse *)
- let part = count lsr 3 in
- let rec loop el acc count =
- match el with
- | [] -> [List.rev acc]
- | e :: l ->
- if count == part then
- (List.rev acc) :: loop el [] 0
- else
- loop l (e :: acc) (count + 1)
- in
- let arr = make_array p (List.map (make_array p) (loop (e :: el) [] 0)) in
- compile_builtin ctx tail "aconcat" [arr] p;
- end else if ctx.version >= 2 then begin
- compile ctx false e;
- List.iter (fun e ->
- write ctx Push;
- compile ctx false e;
- ) el;
- write ctx (MakeArray count);
- end else begin
- List.iter (fun e ->
- compile ctx false e;
- write ctx Push;
- ) el;
- compile ctx false e;
- write ctx (MakeArray count);
- end
- | ("apply",e :: el) ->
- List.iter (fun e ->
- compile ctx false e;
- write ctx Push;
- ) el;
- compile ctx false e;
- let nargs = List.length el in
- if nargs > 0 then write ctx (Apply nargs);
- | _ ->
- List.iter (fun e ->
- compile ctx false e;
- write ctx Push;
- ) el;
- compile_constant ctx (Builtin b) p;
- if tail then
- write ctx (TailCall(List.length el,ctx.stack - ctx.limit))
- else
- write ctx (Call (List.length el))
- and compile ctx tail (e,p) =
- set_pos ctx p;
- match e with
- | EConst c ->
- compile_constant ctx c p
- | EBlock [] ->
- write ctx AccNull
- | EBlock el ->
- let locals = ctx.locals in
- let stack = ctx.stack in
- let rec loop(el) =
- match el with
- | [] -> assert false
- | [e] -> compile ctx tail e
- | [e; (ELabel _,_) as f] ->
- compile ctx tail e;
- compile ctx tail f
- | e :: el ->
- compile ctx false e;
- loop el
- in
- loop el;
- if stack < ctx.stack then write ctx (Pop (ctx.stack - stack));
- check_stack ctx stack p;
- ctx.locals <- locals
- | EParenthesis e ->
- compile ctx tail e
- | EField (e,f) ->
- compile ctx false e;
- write ctx (AccField f)
- | ECall (e,a :: b :: c :: d :: x1 :: x2 :: l) when (match e with (EConst (Builtin "array"),_) -> false | _ -> true) ->
- let call = (EConst (Builtin "call"),p) in
- let args = (ECall ((EConst (Builtin "array"),p),(a :: b :: c :: d :: x1 :: x2 :: l)),p) in
- (match e with
- | (EField (e,name) , p2) ->
- let locals = ctx.locals in
- let etmp = (EConst (Ident "$tmp"),p2) in
- compile ctx false (EVars [("$tmp",Some e)],p2);
- compile ctx tail (ECall (call,[(EField (etmp,name),p2);etmp;args]), p);
- write ctx (Pop 1);
- ctx.locals <- locals
- | _ ->
- compile ctx tail (ECall (call,[e; (EConst This,p); args]),p))
- | ECall ((EConst (Builtin b),_),el) ->
- compile_builtin ctx tail b el p
- | ECall ((EField (e,f),_),el) ->
- List.iter (fun e ->
- compile ctx false e;
- write ctx Push;
- ) el;
- compile ctx false e;
- write ctx Push;
- write ctx (AccField f);
- write ctx (ObjCall(List.length el))
- | ECall (e,el) ->
- List.iter (fun e ->
- compile ctx false e;
- write ctx Push;
- ) el;
- compile ctx false e;
- if tail then
- write ctx (TailCall(List.length el,ctx.stack - ctx.limit))
- else
- write ctx (Call(List.length el))
- | EArray (e1,(EConst (Int n),_)) ->
- compile ctx false e1;
- write ctx (if n == 0 then AccIndex0 else if n == 1 then AccIndex1 else AccIndex n)
- | EArray (e1,e2) ->
- compile ctx false e1;
- write ctx Push;
- compile ctx false e2;
- write ctx AccArray
- | EVars vl ->
- List.iter (fun (v,o) ->
- (match o with
- | None -> write ctx AccNull
- | Some e -> compile ctx false e);
- write ctx Push;
- ctx.locals <- PMap.add v ctx.stack ctx.locals;
- ) vl
- | EWhile (econd,e,NormalWhile) ->
- let start = pos ctx in
- if ctx.version >= 2 then write ctx Loop;
- compile ctx false econd;
- let jend = cjmp false ctx in
- let save = save_breaks ctx in
- compile ctx false e;
- process_continues save;
- goto ctx start;
- process_breaks save;
- jend();
- | EWhile (econd,e,DoWhile) ->
- let start = pos ctx in
- if ctx.version >= 2 then write ctx Loop;
- let save = save_breaks ctx in
- compile ctx false e;
- process_continues save;
- compile ctx false econd;
- write ctx (JumpIf (start - pos ctx));
- process_breaks save
- | EIf (e,e1,e2) ->
- let stack = ctx.stack in
- compile ctx false e;
- let jelse = cjmp false ctx in
- compile ctx tail e1;
- check_stack ctx stack p;
- (match e2 with
- | None ->
- jelse()
- | Some e2 ->
- let jend = jmp ctx in
- jelse();
- compile ctx tail e2;
- check_stack ctx stack p;
- jend())
- | ETry (e,v,ecatch) ->
- let trap = trap ctx in
- ctx.traps <- ctx.stack :: ctx.traps;
- compile ctx false e;
- write ctx EndTrap;
- ctx.traps <- (match ctx.traps with [] -> assert false | _ :: l -> l);
- let jend = jmp ctx in
- trap();
- write ctx Push;
- let locals = ctx.locals in
- ctx.locals <- PMap.add v ctx.stack ctx.locals;
- compile ctx tail ecatch;
- write ctx (Pop 1);
- ctx.locals <- locals;
- jend()
- | EBinop (op,e1,e2) ->
- compile_binop ctx tail op e1 e2 p
- | EReturn e ->
- (match e with None -> write ctx AccNull | Some e -> compile ctx (ctx.traps == []) e);
- let stack = ctx.stack in
- List.iter (fun t ->
- if ctx.stack > t then write ctx (Pop(ctx.stack - t));
- write ctx EndTrap;
- ) ctx.traps;
- write ctx (Ret (ctx.stack - ctx.limit));
- ctx.stack <- stack
- | EBreak e ->
- (match e with
- | None -> ()
- | Some e -> compile ctx false e);
- let s = ctx.stack in
- let n = ref (List.length ctx.traps - ctx.loop_traps) in
- List.iter (fun t ->
- if !n > 0 then begin
- decr n;
- if ctx.stack > t then write ctx (Pop(ctx.stack - t));
- write ctx EndTrap;
- end
- ) ctx.traps;
- if ctx.loop_limit <> ctx.stack then write ctx (Pop(ctx.stack - ctx.loop_limit));
- ctx.stack <- s;
- ctx.breaks <- (jmp ctx , p) :: ctx.breaks
- | EContinue ->
- let s = ctx.stack in
- let n = ref (List.length ctx.traps - ctx.loop_traps) in
- List.iter (fun t ->
- if !n > 0 then begin
- decr n;
- if ctx.stack > t then write ctx (Pop(ctx.stack - t));
- write ctx EndTrap;
- end
- ) ctx.traps;
- if ctx.loop_limit <> ctx.stack then write ctx (Pop(ctx.stack - ctx.loop_limit));
- ctx.stack <- s;
- ctx.continues <- (jmp ctx , p) :: ctx.continues
- | EFunction (params,e) ->
- compile_function ctx params e
- | ENext (e1,e2) ->
- compile ctx false e1;
- compile ctx tail e2
- | EObject [] ->
- write ctx AccNull;
- write ctx New
- | EObject fl ->
- let fields = List.sort compare (List.map fst fl) in
- let id = (try
- Hashtbl.find ctx.g.gobjects fields
- with Not_found ->
- let id = global ctx (GlobalVar ("o:" ^ string_of_int (Hashtbl.length ctx.g.gobjects))) in
- Hashtbl.add ctx.g.gobjects fields id;
- id
- ) in
- write ctx (AccGlobal id);
- write ctx New;
- write ctx Push;
- List.iter (fun (f,e) ->
- write ctx Push;
- compile ctx false e;
- write ctx (SetField f);
- write ctx AccStack0;
- ) fl;
- write ctx (Pop 1)
- | ELabel l ->
- let l = (try Hashtbl.find ctx.g.labels l with Not_found -> assert false) in
- if ctx.stack <> l.lstack || List.rev ctx.traps <> l.ltraps then error (Printf.sprintf "Label failure %d %d" ctx.stack l.lstack) p;
- List.iter (fun f -> f()) l.lwait;
- l.lwait <- [];
- l.lpos <- Some (pos ctx)
- | ESwitch (e,[(econd,exec)],eo) ->
- compile ctx tail (EIf ((EBinop ("==",e,econd),p),exec,eo),p)
- | ENeko _ ->
- assert false
- | ESwitch (e,cases,eo) ->
- try
- let ints , size = get_cases_ints cases in
- compile ctx false e;
- write ctx (JumpTable size);
- let tbl = Array.make size None in
- List.iter (fun (i,e) ->
- tbl.(i) <- Some e;
- ) ints;
- let tbl = Array.map (fun e -> (jmp ctx,e)) tbl in
- Array.iter (fun (j,e) ->
- if e == None then j()
- ) tbl;
- (match eo with
- | None -> write ctx AccNull
- | Some e -> compile ctx tail e);
- let jump_end = jmp ctx in
- let tbl = Array.map (fun (j,e) ->
- match e with
- | Some e ->
- j();
- compile ctx tail e;
- jmp ctx
- | None ->
- (fun() -> ())
- ) tbl in
- jump_end();
- Array.iter (fun j -> j()) tbl
- with Exit ->
- compile ctx false e;
- write ctx Push;
- let jumps = List.map (fun (e1,e2) ->
- write ctx AccStack0;
- write ctx Push;
- compile ctx false e1;
- write ctx Eq;
- (cjmp true ctx , e2)
- ) cases in
- (match eo with
- | None -> write ctx AccNull
- | Some e -> compile ctx tail (EBlock [e],p));
- let jump_end = jmp ctx in
- let jumps = List.map (fun (j,e) ->
- j();
- compile ctx tail (EBlock [e],p);
- jmp ctx;
- ) jumps in
- jump_end();
- List.iter (fun j -> j()) jumps;
- write ctx (Pop 1)
- let compile version ast =
- let g = {
- globals = Hashtbl.create 0;
- gobjects = Hashtbl.create 0;
- gtable = DynArray.create();
- functions = [];
- labels = Hashtbl.create 0;
- hfiles = Hashtbl.create 0;
- files = DynArray.create();
- } in
- let ctx = {
- g = g;
- version = version;
- stack = 0;
- loop_limit = 0;
- loop_traps = 0;
- limit = -1;
- locals = PMap.empty;
- ops = DynArray.create();
- breaks = [];
- continues = [];
- env = PMap.empty;
- nenv = 0;
- traps = [];
- pos = DynArray.create();
- curpos = (0,0);
- curfile = "_";
- } in
- if version >= 2 then DynArray.add g.gtable (GlobalVersion version);
- scan_labels ctx true true ast;
- compile ctx false ast;
- check_breaks ctx;
- if g.functions <> [] || Hashtbl.length g.gobjects <> 0 then begin
- let ctxops = ctx.ops in
- let ctxpos = ctx.pos in
- let ops = DynArray.create() in
- let pos = DynArray.create() in
- ctx.pos <- pos;
- ctx.ops <- ops;
- write ctx (Jump 0);
- List.iter (fun (fops,fpos,gid,nargs) ->
- DynArray.set g.gtable gid (GlobalFunction(DynArray.length ops,nargs));
- DynArray.append fops ops;
- DynArray.append fpos pos;
- ) (List.rev g.functions);
- DynArray.set ops 0 (Jump (DynArray.length ops));
- let objects = DynArray.create() in
- Hashtbl.iter (fun fl g -> DynArray.add objects (fl,g)) g.gobjects;
- let objects = DynArray.to_array objects in
- Array.sort (fun (_,g1) (_,g2) -> g1 - g2) objects;
- Array.iter (fun (fl,g) ->
- write ctx AccNull;
- write ctx New;
- write ctx (SetGlobal g);
- List.iter (fun f ->
- write ctx (AccGlobal g);
- write ctx Push;
- write ctx (SetField f);
- ) fl
- ) objects;
- DynArray.append ctxpos pos;
- DynArray.append ctxops ops;
- end;
- DynArray.add g.gtable (GlobalDebug (DynArray.to_array ctx.g.files,DynArray.to_array ctx.pos));
- (DynArray.to_array g.gtable, DynArray.to_array ctx.ops)
|