123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *)
- open Common
- open Type
- type with_type =
- | NoValue
- | Value
- | WithType of t
- | WithTypeResume of t
- type type_patch = {
- mutable tp_type : Ast.complex_type option;
- mutable tp_remove : bool;
- mutable tp_meta : Ast.metadata;
- }
- type current_fun =
- | FunMember
- | FunStatic
- | FunConstructor
- | FunMemberAbstract
- | FunMemberClassLocal
- | FunMemberAbstractLocal
- type macro_mode =
- | MExpr
- | MBuild
- | MMacroType
- type typer_pass =
- | PBuildModule (* build the module structure and setup module type parameters *)
- | PBuildClass (* build the class structure *)
- | PTypeField (* type the class field, allow access to types structures *)
- | PCheckConstraint (* perform late constraint checks with inferred types *)
- | PForce (* usually ensure that lazy have been evaluated *)
- | PFinal (* not used, only mark for finalize *)
- type typer_globals = {
- types_module : (path, path) Hashtbl.t;
- modules : (path , module_def) Hashtbl.t;
- mutable delayed : (typer_pass * (unit -> unit) list) list;
- mutable debug_delayed : (typer_pass * ((unit -> unit) * string * typer) list) list;
- doinline : bool;
- mutable core_api : typer option;
- mutable macros : ((unit -> unit) * typer) option;
- mutable std : module_def;
- mutable hook_generate : (unit -> unit) list;
- type_patches : (path, (string * bool, type_patch) Hashtbl.t * type_patch) Hashtbl.t;
- mutable global_metadata : (string list * Ast.metadata_entry * (bool * bool * bool)) list;
- mutable get_build_infos : unit -> (module_type * t list * Ast.class_field list) option;
- delayed_macros : (unit -> unit) DynArray.t;
- mutable global_using : tclass list;
- (* api *)
- do_inherit : typer -> Type.tclass -> Ast.pos -> Ast.class_flag -> bool;
- do_create : Common.context -> typer;
- do_macro : typer -> macro_mode -> path -> string -> Ast.expr list -> Ast.pos -> Ast.expr option;
- do_load_module : typer -> path -> pos -> module_def;
- do_optimize : typer -> texpr -> texpr;
- do_build_instance : typer -> module_type -> pos -> ((string * t) list * path * (t list -> t));
- }
- and typer_module = {
- curmod : module_def;
- mutable module_types : module_type list;
- mutable module_using : tclass list;
- mutable module_globals : (string, (module_type * string)) PMap.t;
- mutable wildcard_packages : string list list;
- }
- and typer = {
- (* shared *)
- com : context;
- t : basic_types;
- g : typer_globals;
- mutable meta : metadata;
- mutable this_stack : texpr list;
- mutable with_type_stack : with_type list;
- mutable call_argument_stack : Ast.expr list list;
- (* variable *)
- mutable pass : typer_pass;
- (* per-module *)
- mutable m : typer_module;
- (* per-class *)
- mutable curclass : tclass;
- mutable tthis : t;
- mutable type_params : (string * t) list;
- (* per-function *)
- mutable curfield : tclass_field;
- mutable untyped : bool;
- mutable in_super_call : bool;
- mutable in_loop : bool;
- mutable in_display : bool;
- mutable in_macro : bool;
- mutable macro_depth : int;
- mutable curfun : current_fun;
- mutable ret : t;
- mutable locals : (string, tvar) PMap.t;
- mutable opened : anon_status ref list;
- mutable vthis : tvar option;
- (* events *)
- mutable on_error : typer -> string -> pos -> unit;
- }
- type call_error =
- | Not_enough_arguments
- | Too_many_arguments
- | Could_not_unify of error_msg
- | Cannot_skip_non_nullable of string
- and error_msg =
- | Module_not_found of path
- | Type_not_found of path * string
- | Unify of unify_error list
- | Custom of string
- | Unknown_ident of string
- | Stack of error_msg * error_msg
- | Call_error of call_error
- exception Fatal_error of string * Ast.pos
- exception Forbid_package of (string * path * pos) * pos list * string
- exception Error of error_msg * pos
- exception DisplayTypes of t list
- exception DisplayPosition of Ast.pos list
- let make_call_ref : (typer -> texpr -> texpr list -> t -> pos -> texpr) ref = ref (fun _ _ _ _ _ -> assert false)
- let type_expr_ref : (typer -> Ast.expr -> with_type -> texpr) ref = ref (fun _ _ _ -> assert false)
- let type_module_type_ref : (typer -> module_type -> t list option -> pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
- let unify_min_ref : (typer -> texpr list -> t) ref = ref (fun _ _ -> assert false)
- let match_expr_ref : (typer -> Ast.expr -> (Ast.expr list * Ast.expr option * Ast.expr option) list -> Ast.expr option option -> with_type -> Ast.pos -> decision_tree) ref = ref (fun _ _ _ _ _ _ -> assert false)
- let get_pattern_locals_ref : (typer -> Ast.expr -> Type.t -> (string, tvar * pos) PMap.t) ref = ref (fun _ _ _ -> assert false)
- let get_constructor_ref : (typer -> tclass -> t list -> Ast.pos -> (t * tclass_field)) ref = ref (fun _ _ _ _ -> assert false)
- let cast_or_unify_ref : (typer -> t -> texpr -> Ast.pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
- let find_array_access_raise_ref : (typer -> tabstract -> tparams -> texpr -> texpr option -> pos -> (tclass_field * t * t * texpr * texpr option)) ref = ref (fun _ _ _ _ _ _ -> assert false)
- (* Source: http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#OCaml *)
- let levenshtein a b =
- let x = Array.init (String.length a) (fun i -> a.[i]) in
- let y = Array.init (String.length b) (fun i -> b.[i]) in
- let minimum (x:int) y z =
- let m' (a:int) b = if a < b then a else b in
- m' (m' x y) z
- in
- let init_matrix n m =
- let init_col = Array.init m in
- Array.init n (function
- | 0 -> init_col (function j -> j)
- | i -> init_col (function 0 -> i | _ -> 0)
- )
- in
- match Array.length x, Array.length y with
- | 0, n -> n
- | m, 0 -> m
- | m, n ->
- let matrix = init_matrix (m + 1) (n + 1) in
- for i = 1 to m do
- let s = matrix.(i) and t = matrix.(i - 1) in
- for j = 1 to n do
- let cost = abs (compare x.(i - 1) y.(j - 1)) in
- s.(j) <- minimum (t.(j) + 1) (s.(j - 1) + 1) (t.(j - 1) + cost)
- done
- done;
- matrix.(m).(n)
- let string_error_raise s sl msg =
- if sl = [] then msg else
- let cl = List.map (fun s2 -> s2,levenshtein s s2) sl in
- let cl = List.sort (fun (_,c1) (_,c2) -> compare c1 c2) cl in
- let rec loop sl = match sl with
- | (s2,i) :: sl when i <= (min (String.length s) (String.length s2)) / 3 -> s2 :: loop sl
- | _ -> []
- in
- match loop cl with
- | [] -> raise Not_found
- | [s] -> Printf.sprintf "%s (Suggestion: %s)" msg s
- | sl -> Printf.sprintf "%s (Suggestions: %s)" msg (String.concat ", " sl)
- let string_error s sl msg =
- try string_error_raise s sl msg
- with Not_found -> msg
- let string_source t = match follow t with
- | TInst(c,_) -> List.map (fun cf -> cf.cf_name) c.cl_ordered_fields
- | TAnon a -> PMap.fold (fun cf acc -> cf.cf_name :: acc) a.a_fields []
- | TAbstract({a_impl = Some c},_) -> List.map (fun cf -> cf.cf_name) c.cl_ordered_statics
- | _ -> []
- let short_type ctx t =
- let tstr = s_type ctx t in
- if String.length tstr > 150 then String.sub tstr 0 147 ^ "..." else tstr
- let unify_error_msg ctx = function
- | Cannot_unify (t1,t2) ->
- s_type ctx t1 ^ " should be " ^ s_type ctx t2
- | Invalid_field_type s ->
- "Invalid type for field " ^ s ^ " :"
- | Has_no_field (t,n) ->
- string_error n (string_source t) (short_type ctx t ^ " has no field " ^ n)
- | Has_no_runtime_field (t,n) ->
- s_type ctx t ^ "." ^ n ^ " is not accessible at runtime"
- | Has_extra_field (t,n) ->
- short_type ctx t ^ " has extra field " ^ n
- | Invalid_kind (f,a,b) ->
- (match a, b with
- | Var va, Var vb ->
- let name, stra, strb = if va.v_read = vb.v_read then
- "setter", s_access false va.v_write, s_access false vb.v_write
- else if va.v_write = vb.v_write then
- "getter", s_access true va.v_read, s_access true vb.v_read
- else
- "access", "(" ^ s_access true va.v_read ^ "," ^ s_access false va.v_write ^ ")", "(" ^ s_access true vb.v_read ^ "," ^ s_access false vb.v_write ^ ")"
- in
- "Inconsistent " ^ name ^ " for field " ^ f ^ " : " ^ stra ^ " should be " ^ strb
- | _ ->
- "Field " ^ f ^ " is " ^ s_kind a ^ " but should be " ^ s_kind b)
- | Invalid_visibility n ->
- "The field " ^ n ^ " is not public"
- | Not_matching_optional n ->
- "Optional attribute of parameter " ^ n ^ " differs"
- | Cant_force_optional ->
- "Optional parameters can't be forced"
- | Invariant_parameter _ ->
- "Type parameters are invariant"
- | Constraint_failure name ->
- "Constraint check failure for " ^ name
- | Missing_overload (cf, t) ->
- cf.cf_name ^ " has no overload for " ^ s_type ctx t
- | Unify_custom msg ->
- msg
- let rec error_msg = function
- | Module_not_found m -> "Class not found : " ^ Ast.s_type_path m
- | Type_not_found (m,t) -> "Module " ^ Ast.s_type_path m ^ " does not define type " ^ t
- | Unify l ->
- let ctx = print_context() in
- String.concat "\n" (List.map (unify_error_msg ctx) l)
- | Unknown_ident s -> "Unknown identifier : " ^ s
- | Custom s -> s
- | Stack (m1,m2) -> error_msg m1 ^ "\n" ^ error_msg m2
- | Call_error err -> s_call_error err
- and s_call_error = function
- | Not_enough_arguments -> "Not enough arguments"
- | Too_many_arguments -> "Too many arguments"
- | Could_not_unify err -> error_msg err
- | Cannot_skip_non_nullable s -> "Cannot skip non-nullable argument " ^ s
- let pass_name = function
- | PBuildModule -> "build-module"
- | PBuildClass -> "build-class"
- | PTypeField -> "type-field"
- | PCheckConstraint -> "check-constraint"
- | PForce -> "force"
- | PFinal -> "final"
- let display_error ctx msg p = ctx.on_error ctx msg p
- let error msg p = raise (Error (Custom msg,p))
- let make_call ctx e el t p = (!make_call_ref) ctx e el t p
- let type_expr ctx e with_type = (!type_expr_ref) ctx e with_type
- let unify_min ctx el = (!unify_min_ref) ctx el
- let match_expr ctx e cases def with_type p = !match_expr_ref ctx e cases def with_type p
- let make_static_call ctx c cf map args t p =
- let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
- let ethis = mk (TTypeExpr (TClassDecl c)) ta p in
- let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
- let map t = map (apply_params cf.cf_params monos t) in
- let ef = mk (TField (ethis,(FStatic (c,cf)))) (map cf.cf_type) p in
- make_call ctx ef args (map t) p
- let unify ctx t1 t2 p =
- try
- Type.unify t1 t2
- with
- Unify_error l ->
- if not ctx.untyped then display_error ctx (error_msg (Unify l)) p
- let unify_raise ctx t1 t2 p =
- try
- Type.unify t1 t2
- with
- Unify_error l ->
- (* no untyped check *)
- raise (Error (Unify l,p))
- let save_locals ctx =
- let locals = ctx.locals in
- (fun() -> ctx.locals <- locals)
- let add_local ctx n t =
- let v = alloc_var n t in
- ctx.locals <- PMap.add n v ctx.locals;
- v
- let gen_local_prefix = "`"
- let gen_local ctx t =
- (* ensure that our generated local does not mask an existing one *)
- let rec loop n =
- let nv = (if n = 0 then gen_local_prefix else gen_local_prefix ^ string_of_int n) in
- if PMap.mem nv ctx.locals then
- loop (n+1)
- else
- nv
- in
- add_local ctx (loop 0) t
- let is_gen_local v =
- String.unsafe_get v.v_name 0 = String.unsafe_get gen_local_prefix 0
- let not_opened = ref Closed
- let mk_anon fl = TAnon { a_fields = fl; a_status = not_opened; }
- let delay ctx p f =
- let rec loop = function
- | [] -> [p,[f]]
- | (p2,l) :: rest ->
- if p2 = p then
- (p, f :: l) :: rest
- else if p2 < p then
- (p2,l) :: loop rest
- else
- (p,[f]) :: (p2,l) :: rest
- in
- ctx.g.delayed <- loop ctx.g.delayed
- let rec flush_pass ctx p (where:string) =
- match ctx.g.delayed with
- | (p2,l) :: rest when p2 <= p ->
- (match l with
- | [] ->
- ctx.g.delayed <- rest;
- | f :: l ->
- ctx.g.delayed <- (p2,l) :: rest;
- f());
- flush_pass ctx p where
- | _ ->
- ()
- let make_pass ctx f = f
- let init_class_done ctx =
- ctx.pass <- PTypeField
- let exc_protect ctx f (where:string) =
- let rec r = ref (fun() ->
- try
- f r
- with
- | Error (m,p) ->
- raise (Fatal_error ((error_msg m),p))
- ) in
- r
- let fake_modules = Hashtbl.create 0
- let create_fake_module ctx file =
- let file = Common.unique_full_path file in
- let mdep = (try Hashtbl.find fake_modules file with Not_found ->
- let mdep = {
- m_id = alloc_mid();
- m_path = (["$DEP"],file);
- m_types = [];
- m_extra = module_extra file (Common.get_signature ctx.com) (file_time file) MFake;
- } in
- Hashtbl.add fake_modules file mdep;
- mdep
- ) in
- Hashtbl.replace ctx.g.modules mdep.m_path mdep;
- mdep
- (* -------------- debug functions to activate when debugging typer passes ------------------------------- *)
- (*/*
- let delay_tabs = ref ""
- let context_ident ctx =
- if Common.defined ctx.com Common.Define.CoreApi then
- " core "
- else if Common.defined ctx.com Common.Define.Macro then
- "macro "
- else
- " out "
- let debug ctx str =
- if Common.raw_defined ctx.com "cdebug" then prerr_endline (context_ident ctx ^ !delay_tabs ^ str)
- let init_class_done ctx =
- debug ctx ("init_class_done " ^ Ast.s_type_path ctx.curclass.cl_path);
- init_class_done ctx
- let ctx_pos ctx =
- let inf = Ast.s_type_path ctx.m.curmod.m_path in
- let inf = (match snd ctx.curclass.cl_path with "" -> inf | n when n = snd ctx.m.curmod.m_path -> inf | n -> inf ^ "." ^ n) in
- let inf = (match ctx.curfield.cf_name with "" -> inf | n -> inf ^ ":" ^ n) in
- inf
- let pass_infos ctx p =
- let inf = pass_name p ^ " (" ^ ctx_pos ctx ^ ")" in
- let inf = if ctx.pass > p then inf ^ " ??CURPASS=" ^ pass_name ctx.pass else inf in
- inf
- let delay ctx p f =
- let inf = pass_infos ctx p in
- let rec loop = function
- | [] -> [p,[f,inf,ctx]]
- | (p2,l) :: rest ->
- if p2 = p then
- (p, (f,inf,ctx) :: l) :: rest
- else if p2 < p then
- (p2,l) :: loop rest
- else
- (p,[f,inf,ctx]) :: (p2,l) :: rest
- in
- ctx.g.debug_delayed <- loop ctx.g.debug_delayed;
- debug ctx ("add " ^ inf)
- let pending_passes ctx =
- let rec loop acc = function
- | (p,l) :: pl when p < ctx.pass -> loop (acc @ l) pl
- | _ -> acc
- in
- match loop [] ctx.g.debug_delayed with
- | [] -> ""
- | l -> " ??PENDING[" ^ String.concat ";" (List.map (fun (_,i,_) -> i) l) ^ "]"
- let display_error ctx msg p =
- debug ctx ("ERROR " ^ msg);
- display_error ctx msg p
- let make_pass ?inf ctx f =
- let inf = (match inf with None -> pass_infos ctx ctx.pass | Some inf -> inf) in
- (fun v ->
- debug ctx ("run " ^ inf ^ pending_passes ctx);
- let old = !delay_tabs in
- delay_tabs := !delay_tabs ^ "\t";
- let t = (try
- f v
- with
- | Fatal_error (e,p) ->
- delay_tabs := old;
- raise (Fatal_error (e,p))
- | exc when not (Common.raw_defined ctx.com "stack") ->
- debug ctx ("FATAL " ^ Printexc.to_string exc);
- delay_tabs := old;
- raise exc
- ) in
- delay_tabs := old;
- t
- )
- let rec flush_pass ctx p where =
- let rec loop() =
- match ctx.g.debug_delayed with
- | (p2,l) :: rest when p2 <= p ->
- (match l with
- | [] ->
- ctx.g.debug_delayed <- rest
- | (f,inf,ctx2) :: l ->
- ctx.g.debug_delayed <- (p2,l) :: rest;
- match p2 with
- | PTypeField | PBuildClass -> f()
- | _ -> (make_pass ~inf ctx f)());
- loop()
- | _ ->
- ()
- in
- match ctx.g.debug_delayed with
- | (p2,_) :: _ when p2 <= p ->
- let old = !delay_tabs in
- debug ctx ("flush " ^ pass_name p ^ "(" ^ where ^ ")");
- delay_tabs := !delay_tabs ^ "\t";
- loop();
- delay_tabs := old;
- debug ctx "flush-done";
- | _ ->
- ()
- let make_where ctx where =
- where ^ " (" ^ ctx_pos ctx ^ ")"
- let exc_protect ctx f (where:string) =
- let f = make_pass ~inf:(make_where ctx where) ctx f in
- let rec r = ref (fun() ->
- try
- f r
- with
- | Error (m,p) ->
- raise (Fatal_error (error_msg m,p))
- ) in
- r
- */*)
- (* --------------------------------------------------- *)
|