diff --git a/src/codegen/gencommon/gencommon.ml b/src/codegen/gencommon/gencommon.ml index e7fc3ac19b5..bd35aa6a59d 100644 --- a/src/codegen/gencommon/gencommon.ml +++ b/src/codegen/gencommon/gencommon.ml @@ -620,11 +620,11 @@ let new_ctx con = gadd_type = (fun md should_filter -> if should_filter then begin gen.gtypes_list <- md :: gen.gtypes_list; - gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules; + gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_statics = None; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules; Hashtbl.add gen.gtypes (t_path md) md; end else gen.gafter_filters_ended <- (fun () -> gen.gtypes_list <- md :: gen.gtypes_list; - gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules; + gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_statics = None; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules; Hashtbl.add gen.gtypes (t_path md) md; ) :: gen.gafter_filters_ended; ); @@ -685,7 +685,7 @@ let reorder_modules gen = Hashtbl.iter (fun md_path md -> if not (Hashtbl.mem processed md_path) then begin Hashtbl.add processed md_path true; - gen.gmodules <- { m_id = alloc_mid(); m_path = md_path; m_types = List.rev ( Hashtbl.find_all modules md_path ); m_extra = (t_infos md).mt_module.m_extra } :: gen.gmodules + gen.gmodules <- { m_id = alloc_mid(); m_path = md_path; m_types = List.rev ( Hashtbl.find_all modules md_path ); m_statics = None; m_extra = (t_infos md).mt_module.m_extra } :: gen.gmodules end ) modules diff --git a/src/codegen/gencommon/overloadingConstructor.ml b/src/codegen/gencommon/overloadingConstructor.ml index 3d1dfae0b1f..aef06d697fe 100644 --- a/src/codegen/gencommon/overloadingConstructor.ml +++ b/src/codegen/gencommon/overloadingConstructor.ml @@ -284,7 +284,7 @@ let ensure_super_is_first com cf = let init com (empty_ctor_type : t) (empty_ctor_expr : texpr) (follow_type : t -> t) = let basic = com.basic in - let should_change cl = not cl.cl_interface && (not cl.cl_extern || is_hxgen (TClassDecl cl)) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) in + let should_change cl = not cl.cl_interface && (not cl.cl_extern || is_hxgen (TClassDecl cl)) && (match cl.cl_kind with KAbstractImpl _ | KModuleStatics _ -> false | _ -> true) in let msize = List.length com.types in let processed, empty_ctors = Hashtbl.create msize, Hashtbl.create msize in diff --git a/src/codegen/gencommon/reflectionCFs.ml b/src/codegen/gencommon/reflectionCFs.ml index f855b57319a..7986fd46106 100644 --- a/src/codegen/gencommon/reflectionCFs.ml +++ b/src/codegen/gencommon/reflectionCFs.ml @@ -1482,8 +1482,8 @@ struct match md with | TClassDecl ({ cl_interface = true } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path -> cl.cl_implements <- (baseinterface, []) :: cl.cl_implements - | TClassDecl ({ cl_kind = KAbstractImpl _ }) -> - (* don't add any base classes to abstract implementations *) + | TClassDecl ({ cl_kind = KAbstractImpl _ | KModuleStatics _ }) -> + (* don't add any base classes to abstract implementations and module statics *) () | TClassDecl ({ cl_super = None } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path -> cl.cl_super <- Some (baseclass,[]) @@ -1518,7 +1518,7 @@ let has_field_override cl name = let configure ctx baseinterface ~slow_invoke = let run md = (match md with - | TClassDecl ({ cl_extern = false } as cl) when is_hxgen md && ( not cl.cl_interface || cl.cl_path = baseinterface.cl_path ) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) -> + | TClassDecl ({ cl_extern = false } as cl) when is_hxgen md && ( not cl.cl_interface || cl.cl_path = baseinterface.cl_path ) && (match cl.cl_kind with KAbstractImpl _ | KModuleStatics _ -> false | _ -> true) -> if is_some cl.cl_super then begin ignore (has_field_override cl (mk_internal_name "hx" "setField")); ignore (has_field_override cl (mk_internal_name "hx" "setField_f")); diff --git a/src/context/common.ml b/src/context/common.ml index 66c66422a52..9acb818c1a5 100644 --- a/src/context/common.ml +++ b/src/context/common.ml @@ -1027,7 +1027,11 @@ let is_legacy_completion com = match com.json_out with let get_entry_point com = Option.map (fun path -> let m = List.find (fun m -> m.m_path = path) com.modules in - let c = ExtList.List.find_map (fun t -> match t with TClassDecl c when c.cl_path = path -> Some c | _ -> None) m.m_types in + let c = + match m.m_statics with + | Some c when (PMap.mem "main" c.cl_statics) -> c + | _ -> ExtList.List.find_map (fun t -> match t with TClassDecl c when c.cl_path = path -> Some c | _ -> None) m.m_types + in let e = Option.get com.main in (* must be present at this point *) (snd path, c, e) ) com.main_class diff --git a/src/context/display/displayToplevel.ml b/src/context/display/displayToplevel.ml index cb5b337c2d7..96c552617a2 100644 --- a/src/context/display/displayToplevel.ml +++ b/src/context/display/displayToplevel.ml @@ -213,7 +213,7 @@ let collect ctx tk with_type sort = let add_type mt = match mt with - | TClassDecl {cl_kind = KAbstractImpl _} -> () + | TClassDecl {cl_kind = KAbstractImpl _ | KModuleStatics _} -> () | _ -> let path = (t_infos mt).mt_path in let mname = snd (t_infos mt).mt_module.m_path in diff --git a/src/context/display/documentSymbols.ml b/src/context/display/documentSymbols.ml index b45c8e7ddc0..ad6e069fb2b 100644 --- a/src/context/display/documentSymbols.ml +++ b/src/context/display/documentSymbols.ml @@ -42,21 +42,21 @@ let collect_module_symbols with_locals (pack,decls) = expr_opt parent f.f_expr in let is_deprecated meta = Meta.has Meta.Deprecated meta in - let field parent parent_kind cff = - let field_parent = parent ^ "." ^ (fst cff.cff_name) in - let add_field kind = add (fst cff.cff_name) kind cff.cff_pos parent (is_deprecated cff.cff_meta) in - match cff.cff_kind with + let field' parent parent_kind cff_name cff_kind cff_access cff_pos cff_meta = + let field_parent = parent ^ "." ^ (fst cff_name) in + let add_field kind = add (fst cff_name) kind cff_pos parent (is_deprecated cff_meta) in + match cff_kind with | FVar(_,eo) -> add_field ( - if parent_kind = EnumAbstract && not (List.mem_assoc AStatic cff.cff_access) then EnumMember - else if (List.mem_assoc AInline cff.cff_access) then Constant + if parent_kind = EnumAbstract && not (List.mem_assoc AStatic cff_access) then EnumMember + else if (List.mem_assoc AInline cff_access) then Constant else Field ); if with_locals then expr_opt field_parent eo | FFun f -> add_field ( - if fst cff.cff_name = "new" then Constructor - else if ((parent_kind = EnumAbstract or parent_kind = Abstract) && Meta.has_one_of [Meta.Op; Meta.ArrayAccess; Meta.Resolve] cff.cff_meta) then Operator + if fst cff_name = "new" then Constructor + else if ((parent_kind = EnumAbstract or parent_kind = Abstract) && Meta.has_one_of [Meta.Op; Meta.ArrayAccess; Meta.Resolve] cff_meta) then Operator else Method ); if with_locals then func field_parent f @@ -64,13 +64,20 @@ let collect_module_symbols with_locals (pack,decls) = add_field Property; if with_locals then expr_opt field_parent eo in + let field parent parent_kind cff = + field' parent parent_kind cff.cff_name cff.cff_kind cff.cff_access cff.cff_pos cff.cff_meta + in List.iter (fun (td,p) -> - let add_type d kind = - let string_of_path l = String.concat "." l in + let get_decl_path d = let module_name = Path.module_name_of_file p.pfile in let type_name = fst d.d_name in let is_primary_type = type_name = module_name in let type_path = if is_primary_type then pack else pack @ [module_name] in + type_path, type_name + in + let string_of_path l = String.concat "." l in + let add_type d kind = + let type_path, type_name = get_decl_path d in add type_name kind p (string_of_path type_path) (is_deprecated d.d_meta); string_of_path (type_path @ [type_name]) in @@ -98,6 +105,10 @@ let collect_module_symbols with_locals (pack,decls) = let kind = if Meta.has Meta.Enum d.d_meta then EnumAbstract else Abstract in let parent = add_type d kind in List.iter (field parent kind) d.d_data + | EStatic d -> + let path, name = get_decl_path d in + let dotpath = string_of_path (path @ [name]) in + field' dotpath Class d.d_name d.d_data d.d_flags p d.d_meta ) decls; l diff --git a/src/context/display/syntaxExplorer.ml b/src/context/display/syntaxExplorer.ml index d85dfca892b..26b492b6c67 100644 --- a/src/context/display/syntaxExplorer.ml +++ b/src/context/display/syntaxExplorer.ml @@ -97,7 +97,9 @@ let find_in_syntax symbols (pack,decls) = expr_opt f.f_expr and field cff = check KClassField (fst cff.cff_name); - match cff.cff_kind with + field_kind cff.cff_kind + and field_kind cff_kind = + match cff_kind with | FVar(tho,eo) -> Option.may type_hint tho; expr_opt eo @@ -152,6 +154,9 @@ let find_in_syntax symbols (pack,decls) = | AbFrom th | AbTo th | AbOver th -> type_hint th | _ -> () ) d.d_flags; + | EStatic d -> + check KModuleType (fst d.d_name); + field_kind d.d_data ) decls let explore_uncached_modules tctx cs symbols = diff --git a/src/context/typecore.ml b/src/context/typecore.ml index 96851ce9488..37deeecf5dd 100644 --- a/src/context/typecore.ml +++ b/src/context/typecore.ml @@ -359,6 +359,7 @@ let create_fake_module ctx file = m_id = alloc_mid(); m_path = (["$DEP"],file); m_types = []; + m_statics = None; m_extra = module_extra file (Define.get_signature ctx.com.defines) (file_time file) MFake []; } in Hashtbl.add fake_modules key mdep; diff --git a/src/core/ast.ml b/src/core/ast.ml index 73885b78912..d7a78c01341 100644 --- a/src/core/ast.ml +++ b/src/core/ast.ml @@ -321,6 +321,7 @@ type type_def = | EEnum of (enum_flag, enum_constructor list) definition | ETypedef of (enum_flag, type_hint) definition | EAbstract of (abstract_flag, class_field list) definition + | EStatic of (placed_access, class_field_kind) definition | EImport of import | EUsing of placed_name list diff --git a/src/core/display/completionItem.ml b/src/core/display/completionItem.ml index 94f7331b417..85f98117075 100644 --- a/src/core/display/completionItem.ml +++ b/src/core/display/completionItem.ml @@ -20,6 +20,7 @@ module CompletionModuleKind = struct | TypeAlias | Struct | TypeParameter + | Static let to_int = function | Class -> 0 @@ -30,6 +31,7 @@ module CompletionModuleKind = struct | TypeAlias -> 5 | Struct -> 6 | TypeParameter -> 7 + | Static -> 8 end module ImportStatus = struct @@ -154,6 +156,22 @@ module CompletionModuleType = struct has_constructor = ctor; source = Syntax td; } + | EStatic d -> + { + pack = pack; + name = fst d.d_name; + module_name = module_name; + pos = p; + is_private = List.exists (fun (f,_) -> f = APrivate) d.d_flags; + params = d.d_params; + meta = d.d_meta; + doc = d.d_doc; + is_extern = List.exists (fun (f,_) -> f = AExtern) d.d_flags; + is_final = true; + kind = Static; + has_constructor = No; + source = Syntax td; + } | EImport _ | EUsing _ -> raise Exit diff --git a/src/core/json/genjson.ml b/src/core/json/genjson.ml index eeb15062ba0..d3222e8893e 100644 --- a/src/core/json/genjson.ml +++ b/src/core/json/genjson.ml @@ -584,6 +584,7 @@ let generate_class ctx c = | KMacroType -> "KMacroType",None | KGenericBuild _ -> "KGenericBuild",None | KAbstractImpl a -> "KAbstractImpl",Some (abstract_ref ctx a) + | KModuleStatics m -> "KModuleStatics",Some (generate_module_path m.m_path) in generate_adt ctx (Some (["haxe";"macro"],"ClassKind")) ctor args in diff --git a/src/core/tFunctions.ml b/src/core/tFunctions.ml index 4daa5c35cda..7239226b6da 100644 --- a/src/core/tFunctions.ml +++ b/src/core/tFunctions.ml @@ -156,6 +156,7 @@ let null_module = { m_id = alloc_mid(); m_path = [] , ""; m_types = []; + m_statics = None; m_extra = module_extra "" "" 0. MFake []; } diff --git a/src/core/tPrinting.ml b/src/core/tPrinting.ml index c8bbdb96fcd..a9f0a34116a 100644 --- a/src/core/tPrinting.ml +++ b/src/core/tPrinting.ml @@ -429,6 +429,8 @@ let s_class_kind = function "KGenericBuild" | KAbstractImpl a -> Printf.sprintf "KAbstractImpl %s" (s_type_path a.a_path) + | KModuleStatics m -> + Printf.sprintf "KModuleStatics %s" (s_type_path m.m_path) module Printer = struct diff --git a/src/core/tType.ml b/src/core/tType.ml index 6ac3a81c6e6..970332ebdd0 100644 --- a/src/core/tType.ml +++ b/src/core/tType.ml @@ -185,6 +185,7 @@ and tclass_kind = | KMacroType | KGenericBuild of class_field list | KAbstractImpl of tabstract + | KModuleStatics of module_def and metadata = Ast.metadata @@ -312,6 +313,7 @@ and module_def = { m_id : int; m_path : path; mutable m_types : module_type list; + mutable m_statics : tclass option; m_extra : module_def_extra; } diff --git a/src/filters/renameVars.ml b/src/filters/renameVars.ml index 8c37439bc82..1b019ac7950 100644 --- a/src/filters/renameVars.ml +++ b/src/filters/renameVars.ml @@ -29,15 +29,19 @@ let reserve_all_types ri com path_to_name = List.iter (fun mt -> let tinfos = t_infos mt in let native_name = try fst (TypeloadCheck.get_native_name tinfos.mt_meta) with Not_found -> path_to_name tinfos.mt_path in - if native_name = "" then - match mt with - | TClassDecl c -> - List.iter (fun cf -> - let native_name = try fst (TypeloadCheck.get_native_name cf.cf_meta) with Not_found -> cf.cf_name in - reserve_init ri native_name - ) c.cl_ordered_statics; - | _ -> () - else + match mt with + | TClassDecl c when native_name = "" -> + List.iter (fun cf -> + let native_name = try fst (TypeloadCheck.get_native_name cf.cf_meta) with Not_found -> cf.cf_name in + reserve_init ri native_name + ) c.cl_ordered_statics + | TClassDecl { cl_kind = KModuleStatics m; cl_ordered_statics = fl } -> + let prefix = Path.flat_path m.m_path ^ "_" in + List.iter (fun cf -> + let name = try fst (TypeloadCheck.get_native_name cf.cf_meta) with Not_found -> prefix ^ cf.cf_name in + reserve_init ri name + ) fl + | _ -> reserve_init ri native_name ) com.types diff --git a/src/generators/gencs.ml b/src/generators/gencs.ml index cc5633fd430..a2448c627b4 100644 --- a/src/generators/gencs.ml +++ b/src/generators/gencs.ml @@ -3484,6 +3484,14 @@ let generate con = let old_dir = Sys.getcwd() in Sys.chdir gen.gcon.file; let cmd = "haxelib run hxcs hxcs_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in + let cmd = + match gen.gentry_point with + | Some (name,_,_) -> + let name = if gen.gcon.debug then name ^ "-Debug" else name in + cmd ^ " --out " ^ gen.gcon.file ^ "/bin/" ^ name + | _ -> + cmd + in print_endline cmd; if gen.gcon.run_command cmd <> 0 then failwith "Build failed"; Sys.chdir old_dir; diff --git a/src/generators/genjava.ml b/src/generators/genjava.ml index ab28b5e4f90..ba8a901aedb 100644 --- a/src/generators/genjava.ml +++ b/src/generators/genjava.ml @@ -2667,6 +2667,14 @@ let generate con = let old_dir = Sys.getcwd() in Sys.chdir gen.gcon.file; let cmd = "haxelib run hxjava hxjava_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in + let cmd = + match gen.gentry_point with + | Some (name,_,_) -> + let name = if gen.gcon.debug then name ^ "-Debug" else name in + cmd ^ " --out " ^ gen.gcon.file ^ "/" ^ name + | _ -> + cmd + in print_endline cmd; if gen.gcon.run_command cmd <> 0 then failwith "Build failed"; Sys.chdir old_dir; diff --git a/src/generators/genjs.ml b/src/generators/genjs.ml index 7fb89c5be72..0822d02b416 100644 --- a/src/generators/genjs.ml +++ b/src/generators/genjs.ml @@ -151,6 +151,18 @@ let static_field ctx c f = | s -> field s +let module_static m f = + try + fst (TypeloadCheck.get_native_name f.cf_meta) + with Not_found -> + Path.flat_path m.m_path ^ "_" ^ f.cf_name + +let module_static_expose_path mpath f = + try + fst (TypeloadCheck.get_native_name f.cf_meta) + with Not_found -> + (dot_path mpath) ^ "." ^ f.cf_name + let has_feature ctx = Common.has_feature ctx.com let add_feature ctx = Common.add_feature ctx.com @@ -601,6 +613,8 @@ and gen_expr ctx e = spr ctx f.cf_name; | TField (x, (FInstance(_,_,f) | FStatic(_,f) | FAnon(f))) when Meta.has Meta.SelfCall f.cf_meta -> gen_value ctx x; + | TField (_,FStatic ({ cl_kind = KModuleStatics m },f)) -> + spr ctx (module_static m f) | TField (x,f) -> let rec skip e = match e.eexpr with | TCast(e1,None) | TMeta(_,e1) -> skip e1 @@ -1085,6 +1099,31 @@ let path_to_brackets path = let parts = ExtString.String.nsplit path "." in "[\"" ^ (String.concat "\"][\"" parts) ^ "\"]" +let gen_module_statics ctx m c fl = + List.iter (fun f -> + let name = module_static m f in + match f.cf_expr with + | None when not (is_physical_field f) -> + () + | None -> + print ctx "var %s = null" name; + newline ctx + | Some e -> + match e.eexpr with + | TFunction fn -> + ctx.id_counter <- 0; + print ctx "function %s" name; + gen_function ~keyword:"" ctx fn e.epos; + ctx.separator <- false; + newline ctx; + process_expose f.cf_meta (fun () -> module_static_expose_path m.m_path f) (fun s -> + print ctx "$hx_exports%s = %s" (path_to_brackets s) name; + newline ctx + ) + | _ -> + ctx.statics <- (c,f,e) :: ctx.statics + ) fl + let gen_class_static_field ctx c cl_path f = match f.cf_expr with | None | Some { eexpr = TConst TNull } when not (has_feature ctx "Type.getClassFields") -> @@ -1440,10 +1479,14 @@ let generate_class ctx c = (match c.cl_path with | [],"Function" -> abort "This class redefine a native one" c.cl_pos | _ -> ()); - if ctx.es_version >= 6 then - generate_class_es6 ctx c - else - generate_class_es3 ctx c + match c.cl_kind with + | KModuleStatics m -> + gen_module_statics ctx m c c.cl_ordered_statics + | _ -> + if ctx.es_version >= 6 then + generate_class_es6 ctx c + else + generate_class_es3 ctx c let generate_enum ctx e = let p = s_path ctx e.e_path in @@ -1537,9 +1580,16 @@ let generate_enum ctx e = flush ctx let generate_static ctx (c,f,e) = - let cl_path = get_generated_class_path c in - process_expose f.cf_meta (fun () -> (dot_path cl_path) ^ "." ^ f.cf_name) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s)); - print ctx "%s%s = " (s_path ctx cl_path) (static_field ctx c f); + begin + match c.cl_kind with + | KModuleStatics m -> + print ctx "var %s = " (module_static m f); + process_expose f.cf_meta (fun () -> module_static_expose_path m.m_path f) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s)); + | _ -> + let cl_path = get_generated_class_path c in + process_expose f.cf_meta (fun () -> (dot_path cl_path) ^ "." ^ f.cf_name) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s)); + print ctx "%s%s = " (s_path ctx cl_path) (static_field ctx c f); + end; gen_value ctx e; newline ctx @@ -1683,11 +1733,18 @@ let generate com = List.iter ( function | TClassDecl c -> - let path = dot_path c.cl_path in let add s = r := s :: !r in - process_expose c.cl_meta (fun () -> path) add; + let get_expose_path = + match c.cl_kind with + | KModuleStatics m -> + module_static_expose_path m.m_path + | _ -> + let path = dot_path c.cl_path in + process_expose c.cl_meta (fun () -> path) add; + fun f -> path ^ "." ^ f.cf_name + in List.iter (fun f -> - process_expose f.cf_meta (fun () -> path ^ "." ^ f.cf_name) add + process_expose f.cf_meta (fun () -> get_expose_path f) add ) c.cl_ordered_statics | _ -> () ) com.types; diff --git a/src/generators/genjvm.ml b/src/generators/genjvm.ml index 42340aa559a..fa7a30aa4a3 100644 --- a/src/generators/genjvm.ml +++ b/src/generators/genjvm.ml @@ -2799,14 +2799,9 @@ let file_name_and_extension file = let generate com = mkdir_from_path com.file; - let jar_name,manifest_suffix,entry_point = match get_entry_point com with - | Some (jarname,cl,expr) -> - let pack = match fst cl.cl_path with - | [] -> ["haxe";"root"] - | pack -> pack - in - jarname,"\nMain-Class: " ^ (s_type_path (pack,snd cl.cl_path)), Some (cl,expr) - | None -> "jar","",None + let jar_name,entry_point = match get_entry_point com with + | Some (jarname,cl,expr) -> jarname, Some (cl,expr) + | None -> "jar",None in let jar_name = if com.debug then jar_name ^ "-Debug" else jar_name in let jar_dir = add_trailing_slash com.file in @@ -2848,14 +2843,6 @@ let generate com = Some (Printf.sprintf "lib/%s" name) end ) com.native_libs.java_libs in - let manifest_content = - "Manifest-Version: 1.0\n" ^ - (match class_paths with [] -> "" | _ -> "Class-Path: " ^ (String.concat " " class_paths ^ "\n")) ^ - "Created-By: Haxe (Haxe Foundation)" ^ - manifest_suffix ^ - "\n\n" - in - Zip.add_entry manifest_content gctx.jar "META-INF/MANIFEST.MF"; Hashtbl.iter (fun name v -> let filename = Codegen.escape_res_name name true in Zip.add_entry v gctx.jar filename; @@ -2871,4 +2858,14 @@ let generate com = Std.finally (Timer.timer ["generate";"java";"typed interfaces"]) generate_typed_interfaces (); Std.finally (Timer.timer ["generate";"java";"anons"]) generate_anons gctx; Std.finally (Timer.timer ["generate";"java";"typed functions"]) generate_typed_functions gctx; + + let manifest_content = + "Manifest-Version: 1.0\n" ^ + (match class_paths with [] -> "" | _ -> "Class-Path: " ^ (String.concat " " class_paths ^ "\n")) ^ + "Created-By: Haxe (Haxe Foundation)" ^ + (Option.map_default (fun (cl,_) -> "\nMain-Class: " ^ (s_type_path cl.cl_path)) "" entry_point) ^ + "\n\n" + in + Zip.add_entry manifest_content gctx.jar "META-INF/MANIFEST.MF"; + Zip.close_out gctx.jar \ No newline at end of file diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index f38ef5bd379..3ec04646b08 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -674,17 +674,19 @@ and decode_meta_content m = decode_opt_array decode_meta_entry m and decode_doc = opt (fun s -> { doc_own = Some (decode_string s); doc_inherited = [] }) +and decode_class_field_kind v = + match decode_enum v with + | 0, [t;e] -> + FVar (opt decode_ctype t, opt decode_expr e) + | 1, [f] -> + FFun (decode_fun f) + | 2, [get;set; t; e] -> + FProp (decode_placed_name vnull get, decode_placed_name vnull set, opt decode_ctype t, opt decode_expr e) + | _ -> + raise Invalid_expr + and decode_field v = - let fkind = match decode_enum (field v "kind") with - | 0, [t;e] -> - FVar (opt decode_ctype t, opt decode_expr e) - | 1, [f] -> - FFun (decode_fun f) - | 2, [get;set; t; e] -> - FProp (decode_placed_name vnull get, decode_placed_name vnull set, opt decode_ctype t, opt decode_expr e) - | _ -> - raise Invalid_expr - in + let fkind = decode_class_field_kind (field v "kind") in let pos = decode_pos (field v "pos") in { cff_name = (decode_string (field v "name"),decode_pos_default (field v "name_pos") pos); @@ -974,7 +976,7 @@ and encode_class_kind k = let tag, pl = (match k with | KNormal -> 0, [] | KTypeParameter pl -> 1, [encode_tparams pl] - (* KExtension was here *) + | KModuleStatics m -> 2, [encode_string (s_type_path m.m_path)] | KExpr e -> 3, [encode_expr e] | KGeneric -> 4, [] | KGenericInstance (cl, params) -> 5, [encode_clref cl; encode_tparams params] @@ -1473,6 +1475,11 @@ let decode_type_def v = let flags = match opt decode_array tto with None -> flags | Some ta -> (List.map (fun t -> AbTo (decode_ctype t)) ta) @ flags in let flags = match opt decode_ctype tthis with None -> flags | Some t -> (AbOver t) :: flags in EAbstract(mk flags fields) + | 5, [fk;al] -> + let fk = decode_class_field_kind fk in + let al = List.map decode_access (opt_list decode_array al) in + (* let al = if isExtern then (AExtern,pos) :: al else al in *) + EStatic (mk al fk) | _ -> raise Invalid_expr ) in diff --git a/src/syntax/grammar.mly b/src/syntax/grammar.mly index 8fc2976e14e..80aaebb8a5f 100644 --- a/src/syntax/grammar.mly +++ b/src/syntax/grammar.mly @@ -178,6 +178,47 @@ and parse_type_decl mode s = | [< '(Kwd Using,p1) >] -> parse_using s p1 | [< doc = get_doc; meta = parse_meta; c = parse_common_flags; s >] -> match s with parser + | [< '(Kwd Function,p1); name = dollar_ident; pl = parse_constraint_params; '(POpen,_); args = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] -> + let e, p2 = (match s with parser + | [< e = expr; s >] -> + ignore(semicolon s); + Some e, pos e + | [< p = semicolon >] -> None, p + | [< >] -> serror() + ) in + let f = { + f_params = pl; + f_args = args; + f_type = t; + f_expr = e; + } in + (EStatic { + d_name = name; + d_doc = doc_from_string_opt doc; + d_meta = meta; + d_params = pl; + d_flags = List.map decl_flag_to_global_flag c; + d_data = FFun f; + }, punion p1 p2) + | [< '(Kwd Var,p1); name = dollar_ident; s >] -> + let p2,t = + match s with parser + | [< '(POpen,_); i1 = property_ident; '(Comma,_); i2 = property_ident; '(PClose,_) >] -> + let t = popt parse_type_hint s in + let e,p2 = parse_var_field_assignment s in + p2,FProp (i1,i2,t,e) + | [< t = popt parse_type_hint; s >] -> + let e,p2 = parse_var_field_assignment s in + p2,FVar (t,e) + in + (EStatic { + d_name = name; + d_doc = doc_from_string_opt doc; + d_meta = meta; + d_params = []; + d_flags = List.map decl_flag_to_global_flag c; + d_data = t; + }, punion p1 p2) | [< '(Kwd Enum,p1) >] -> begin match s with parser | [< a,p = parse_abstract doc ((Meta.Enum,[],null_pos) :: meta) c >] -> @@ -248,7 +289,21 @@ and parse_type_decl mode s = | [< a,p = parse_abstract doc meta c >] -> EAbstract a,p | [< >] -> - check_type_decl_flag_completion mode c s + match List.rev c with + | (DFinal,p1) :: crest -> + (match s with parser + | [< name = dollar_ident; t = popt parse_type_hint; e,p2 = parse_var_field_assignment >] -> + (EStatic { + d_name = name; + d_doc = doc_from_string_opt doc; + d_meta = meta; + d_params = []; + d_flags = (List.map decl_flag_to_global_flag (List.rev crest)) @ [AFinal,p1]; + d_data = FVar(t,e); + }, punion p1 p2) + | [< >] -> check_type_decl_flag_completion mode c s) + | _ -> + check_type_decl_flag_completion mode c s and parse_class doc meta cflags need_name s = @@ -453,6 +508,9 @@ and parse_common_flags = parser | [< '(Kwd Private,p); l = parse_common_flags >] -> (DPrivate,p) :: l | [< '(Kwd Extern,p); l = parse_common_flags >] -> (DExtern,p) :: l | [< '(Kwd Final,p); l = parse_common_flags >] -> (DFinal,p) :: l + | [< '(Kwd Macro,p); l = parse_common_flags >] -> (DMacro,p) :: l + | [< '(Kwd Dynamic,p); l = parse_common_flags >] -> (DDynamic,p) :: l + | [< '(Kwd Inline,p); l = parse_common_flags >] -> (DInline,p) :: l | [< >] -> [] and parse_meta_argument_expr s = diff --git a/src/syntax/parser.ml b/src/syntax/parser.ml index b43b8889e19..a5db91b93f7 100644 --- a/src/syntax/parser.ml +++ b/src/syntax/parser.ml @@ -38,6 +38,9 @@ type decl_flag = | DPrivate | DExtern | DFinal + | DMacro + | DDynamic + | DInline type type_decl_completion_mode = | TCBeforePackage @@ -98,16 +101,33 @@ let decl_flag_to_class_flag (flag,p) = match flag with | DPrivate -> HPrivate | DExtern -> HExtern | DFinal -> HFinal + | DMacro -> error (Custom "macro on classes is not allowed") p + | DDynamic -> error (Custom "dynamic on classes is not allowed") p + | DInline -> error (Custom "inline on classes is not allowed") p let decl_flag_to_enum_flag (flag,p) = match flag with | DPrivate -> EPrivate | DExtern -> EExtern | DFinal -> error (Custom "final on enums is not allowed") p + | DMacro -> error (Custom "macro on enums is not allowed") p + | DDynamic -> error (Custom "dynamic on enums is not allowed") p + | DInline -> error (Custom "inline on enums is not allowed") p let decl_flag_to_abstract_flag (flag,p) = match flag with | DPrivate -> AbPrivate | DExtern -> AbExtern | DFinal -> error (Custom "final on abstracts is not allowed") p + | DMacro -> error (Custom "macro on abstracts is not allowed") p + | DDynamic -> error (Custom "dynamic on abstracts is not allowed") p + | DInline -> error (Custom "inline on abstracts is not allowed") p + +let decl_flag_to_global_flag (flag,p) = match flag with + | DPrivate -> (APrivate,p) + | DMacro -> (AMacro,p) + | DDynamic -> (ADynamic,p) + | DInline -> (AInline,p) + | DExtern -> error (Custom "extern on module-statics is not allowed") p (* TODO: would be nice to have this actually, but we need some design for it *) + | DFinal -> error (Custom "final on module-statics is not allowed") p module TokenCache = struct let cache = ref (DynArray.create ()) diff --git a/src/typing/fields.ml b/src/typing/fields.ml index 838375617db..f6ccb8226cd 100644 --- a/src/typing/fields.ml +++ b/src/typing/fields.ml @@ -352,6 +352,10 @@ let rec using_field ctx mode e i p = if not !check_constant_struct then raise Not_found; remove_constant_flag e.etype (fun ok -> if ok then using_field ctx mode e i p else raise Not_found) +let check_field_access ctx c f stat p = + if not ctx.untyped && not (can_access ctx c f stat) then + display_error ctx ("Cannot access private field " ^ f.cf_name) p + (* Resolves field [i] on typed expression [e] using the given [mode]. *) let rec type_field cfg ctx e i p mode = let pfield = if (e.epos = p) then p else {p with pmin = p.pmax - (String.length i)} in @@ -437,7 +441,7 @@ let rec type_field cfg ctx e i p mode = display_error ctx "Cannot create closure on super method" p | _ -> display_error ctx "Normal variables cannot be accessed with 'super', use 'this' instead" pfield); - if not (can_access ctx c f false) && not ctx.untyped then display_error ctx ("Cannot access private field " ^ i) pfield; + check_field_access ctx c f false pfield; field_access ctx mode f (match c2 with None -> FAnon f | Some (c,tl) -> FInstance (c,tl,f)) (apply_params c.cl_params params t) e p with Not_found -> try begin match e.eexpr with @@ -535,7 +539,7 @@ let rec type_field cfg ctx e i p mode = (try let c = (match a.a_impl with None -> raise Not_found | Some c -> c) in let f = PMap.find i c.cl_statics in - if not (can_access ctx c f true) && not ctx.untyped then display_error ctx ("Cannot access private field " ^ i) pfield; + check_field_access ctx c f true pfield; let field_type f = if not (Meta.has Meta.Impl f.cf_meta) then begin static_abstract_access_through_instance := true; diff --git a/src/typing/finalization.ml b/src/typing/finalization.ml index 7133434043c..ef077da8c25 100644 --- a/src/typing/finalization.ml +++ b/src/typing/finalization.ml @@ -12,22 +12,37 @@ open Typecore let get_main ctx types = match ctx.com.main_class with | None -> None - | Some cl -> - let t = Typeload.load_type_def ctx null_pos (mk_type_path cl) in - let fmode, ft, r = (match t with - | TEnumDecl _ | TTypeDecl _ | TAbstractDecl _ -> - error ("Invalid -main : " ^ s_type_path cl ^ " is not a class") null_pos - | TClassDecl c -> + | Some path -> + let p = null_pos in + let pack,name = path in + let m = Typeload.load_module ctx (pack,name) p in + let c,f = + let p = ref p in try - let f = PMap.find "main" c.cl_statics in - let t = Type.field_type f in - (match follow t with - | TFun ([],r) -> FStatic (c,f), t, r - | _ -> error ("Invalid -main : " ^ s_type_path cl ^ " has invalid main function") c.cl_pos); - with - Not_found -> error ("Invalid -main : " ^ s_type_path cl ^ " does not have static function main") c.cl_pos - ) in - let emain = type_type ctx cl null_pos in + match m.m_statics with + | None -> + raise Not_found + | Some c -> + p := c.cl_pos; + c, PMap.find "main" c.cl_statics + with Not_found -> try + let t = Typeload.find_type_in_module_raise m name null_pos in + match t with + | TEnumDecl _ | TTypeDecl _ | TAbstractDecl _ -> + error ("Invalid -main : " ^ s_type_path path ^ " is not a class") null_pos + | TClassDecl c -> + p := c.cl_pos; + c, PMap.find "main" c.cl_statics + with Not_found -> + error ("Invalid -main : " ^ s_type_path path ^ " does not have static function main") !p + in + let ft = Type.field_type f in + let fmode, r = + match follow ft with + | TFun ([],r) -> FStatic (c,f), r + | _ -> error ("Invalid -main : " ^ s_type_path path ^ " has invalid main function") c.cl_pos + in + let emain = type_module_type ctx (TClassDecl c) None null_pos in let main = mk (TCall (mk (TField (emain,fmode)) ft null_pos,[])) r null_pos in (* add haxe.EntryPoint.run() call *) let main = (try diff --git a/src/typing/generic.ml b/src/typing/generic.ml index 5d5bcf2afa9..54c32af1ea3 100644 --- a/src/typing/generic.ml +++ b/src/typing/generic.ml @@ -160,6 +160,7 @@ let static_method_container gctx c cf p = m_id = alloc_mid(); m_path = (pack,name); m_types = []; + m_statics = None; m_extra = module_extra (s_type_path (pack,name)) m.m_extra.m_sign 0. MFake m.m_extra.m_check_policy; } in gctx.mg <- Some mg; @@ -205,6 +206,7 @@ let rec build_generic ctx c p tl = m_id = alloc_mid(); m_path = (pack,name); m_types = []; + m_statics = None; m_extra = module_extra (s_type_path (pack,name)) m.m_extra.m_sign 0. MFake m.m_extra.m_check_policy; } in gctx.mg <- Some mg; diff --git a/src/typing/macroContext.ml b/src/typing/macroContext.ml index 57244e8d295..1c63466b2af 100644 --- a/src/typing/macroContext.ml +++ b/src/typing/macroContext.ml @@ -539,20 +539,31 @@ let load_macro_module ctx cpath display p = let load_macro' ctx display cpath f p = let api, mctx = get_macro_context ctx p in let mint = Interp.get_ctx() in - let mpath, sub = (match List.rev (fst cpath) with - | name :: pack when name.[0] >= 'A' && name.[0] <= 'Z' -> (List.rev pack,name), Some (snd cpath) - | _ -> cpath, None - ) in let (meth,mloaded) = try Hashtbl.find mctx.com.cached_macros (cpath,f) with Not_found -> let t = macro_timer ctx ["typing";s_type_path cpath ^ "." ^ f] in - let mloaded,restore = load_macro_module ctx mpath display p in - let mt = Typeload.load_type_def mctx p (mk_type_path ?sub mpath) in - let cl, meth = (match mt with - | TClassDecl c -> - mctx.g.do_finalize mctx; - c, (try PMap.find f c.cl_statics with Not_found -> error ("Method " ^ f ^ " not found on class " ^ s_type_path cpath) p) - | _ -> error "Macro should be called on a class" p + let mpath, sub = (match List.rev (fst cpath) with + | name :: pack when name.[0] >= 'A' && name.[0] <= 'Z' -> (List.rev pack,name), Some (snd cpath) + | _ -> cpath, None ) in + let mloaded,restore = load_macro_module ctx mpath display p in + let cl, meth = + try + if sub <> None then raise Not_found; + match mloaded.m_statics with + | None -> raise Not_found + | Some c -> + mctx.g.do_finalize mctx; + c, PMap.find f c.cl_statics + with Not_found -> + let name = Option.default (snd mpath) sub in + let path = fst mpath, name in + let mt = try List.find (fun t2 -> (t_infos t2).mt_path = path) mloaded.m_types with Not_found -> raise_error (Type_not_found (mloaded.m_path,name,Not_defined)) p in + match mt with + | TClassDecl c -> + mctx.g.do_finalize mctx; + c, (try PMap.find f c.cl_statics with Not_found -> error ("Method " ^ f ^ " not found on class " ^ s_type_path cpath) p) + | _ -> error "Macro should be called on a class" p + in api.MacroApi.current_macro_module <- (fun() -> mloaded); DeprecationCheck.check_cf mctx.com meth p; let meth = (match follow meth.cf_type with TFun (args,ret) -> (args,ret,cl,meth),mloaded | _ -> error "Macro call should be a method" p) in diff --git a/src/typing/typeload.ml b/src/typing/typeload.ml index b6756ccf732..75e0d188254 100644 --- a/src/typing/typeload.ml +++ b/src/typing/typeload.ml @@ -930,7 +930,7 @@ let handle_using ctx path p = | None -> let md = ctx.g.do_load_module ctx (t.tpackage,t.tname) p in let types = List.filter (fun t -> not (t_infos t).mt_private) md.m_types in - types + Option.map_default (fun c -> (TClassDecl c) :: types) types md.m_statics | Some _ -> let t = load_type_def ctx p t in [t] diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index bff4434b9d6..6e5f7648115 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -605,7 +605,7 @@ let is_public (ctx,cctx) access parent = true else match parent with | Some cf -> (has_class_field_flag cf CfPublic) - | _ -> c.cl_extern || c.cl_interface || cctx.extends_public + | _ -> c.cl_extern || c.cl_interface || cctx.extends_public || (match c.cl_kind with KModuleStatics _ -> true | _ -> false) let rec get_parent c name = match c.cl_super with diff --git a/src/typing/typeloadModule.ml b/src/typing/typeloadModule.ml index 7be93448f57..df1977e368a 100644 --- a/src/typing/typeloadModule.ml +++ b/src/typing/typeloadModule.ml @@ -39,6 +39,7 @@ let make_module ctx mpath file loadp = m_id = alloc_mid(); m_path = mpath; m_types = []; + m_statics = None; m_extra = module_extra (Path.get_full_path file) (Define.get_signature ctx.com.defines) (file_time file) (if ctx.in_macro then MMacro else MCode) (get_policy ctx mpath); } in m @@ -192,13 +193,21 @@ end let module_pass_1 ctx m tdecls loadp = let com = ctx.com in let decls = ref [] in - let make_path name priv p = + let statics = ref [] in + let check_name name p = + let error prev_pos = + display_error ctx ("Name " ^ name ^ " is already defined in this module") p; + error "Previous declaration here" prev_pos; + in List.iter (fun (t2,(_,p2)) -> - if snd (t_path t2) = name then begin - display_error ctx ("Type name " ^ name ^ " is already defined in this module") p; - error "Previous declaration here" p2; - end + if snd (t_path t2) = name then error p2 ) !decls; + List.iter (fun (d,p) -> + if fst d.d_name = name then error p + ) !statics + in + let make_path name priv p = + check_name name p; if priv then (fst m.m_path @ ["_" ^ snd m.m_path], name) else (fst m.m_path, name) in let has_declaration = ref false in @@ -210,8 +219,13 @@ let module_pass_1 ctx m tdecls loadp = in let acc = (match fst decl with | EImport _ | EUsing _ -> - if !has_declaration then error "import and using may not appear after a type declaration" p; + if !has_declaration then error "import and using may not appear after a declaration" p; acc + | EStatic d -> + check_name (fst d.d_name) p; + has_declaration := true; + statics := (d,p) :: !statics; + acc; | EClass d -> let name = fst d.d_name in has_declaration := true; @@ -348,6 +362,43 @@ let module_pass_1 ctx m tdecls loadp = decl :: acc in let tdecls = List.fold_left make_decl [] tdecls in + let tdecls = + match !statics with + | [] -> + tdecls + | statics -> + let first_pos = ref null_pos in + let fields = List.map (fun (d,p) -> + first_pos := p; + { + cff_name = d.d_name; + cff_doc = d.d_doc; + cff_pos = p; + cff_meta = d.d_meta; + cff_access = (AStatic,null_pos) :: d.d_flags; + cff_kind = d.d_data; + } + ) statics in + let p = let p = !first_pos in { p with pmax = p.pmin } in + let c = EClass { + d_name = (snd m.m_path) ^ "_Statics_", p; + d_flags = [HPrivate]; + d_data = fields; + d_doc = None; + d_params = []; + d_meta = [] + } in + let tdecls = make_decl tdecls (c,p) in + (match !decls with + | (TClassDecl c,_) :: _ -> + assert (m.m_statics = None); + m.m_statics <- Some c; + c.cl_kind <- KModuleStatics m; + c.cl_final <- true; + | _ -> assert false); + tdecls + + in let decls = List.rev !decls in decls, List.rev tdecls @@ -442,7 +493,8 @@ let init_module_type ctx context_init (decl,p) = let md = ctx.g.do_load_module ctx (List.map fst pack,tname) p_type in let types = md.m_types in let no_private (t,_) = not (t_infos t).mt_private in - let chk_private t p = if (t_infos t).mt_private then error "You can't import a private type" p in + let error_private p = error "Importing private declarations from a module is not allowed" p in + let chk_private t p = if (t_infos t).mt_private then error_private p in let has_name name t = snd (t_infos t).mt_path = name in let get_type tname = let t = (try List.find (has_name tname) types with Not_found -> error (StringError.string_error tname (List.map (fun mt -> snd (t_infos mt).mt_path) types) ("Module " ^ s_type_path md.m_path ^ " does not define type " ^ tname)) p_type) in @@ -490,7 +542,16 @@ let init_module_type ctx context_init (decl,p) = | [] -> (match name with | None -> - ctx.m.module_types <- List.filter no_private (List.map (fun t -> t,p) types) @ ctx.m.module_types + ctx.m.module_types <- List.filter no_private (List.map (fun t -> t,p) types) @ ctx.m.module_types; + Option.may (fun c -> + context_init#add (fun () -> + ignore(c.cl_build()); + List.iter (fun cf -> + if has_class_field_flag cf CfPublic then + ctx.m.module_globals <- PMap.add cf.cf_name (TClassDecl c,cf.cf_name,p) ctx.m.module_globals + ) c.cl_ordered_statics + ); + ) md.m_statics | Some(newname,pname) -> ctx.m.module_types <- (rebind (get_type tname) newname pname,p) :: ctx.m.module_types); | [tsub,p2] -> @@ -501,13 +562,39 @@ let init_module_type ctx context_init (decl,p) = ctx.m.module_types <- ((match name with None -> tsub | Some(n,pname) -> rebind tsub n pname),p) :: ctx.m.module_types with Not_found -> (* this might be a static property, wait later to check *) - let tmain = get_type tname in - context_init#add (fun() -> + let find_main_type_static () = + let tmain = get_type tname in try add_static_init tmain name tsub with Not_found -> + (* TODO: mention module-level declarations in the error message? *) display_error ctx (s_type_path (t_infos tmain).mt_path ^ " has no field or subtype " ^ tsub) p - )) + in + context_init#add (fun() -> + match md.m_statics with + | Some c -> + (try + ignore(c.cl_build()); + let rec loop fl = + match fl with + | [] -> raise Not_found + | cf :: rest -> + if cf.cf_name = tsub then + if not (has_class_field_flag cf CfPublic) then + error_private p + else + let imported_name = match name with None -> tsub | Some (n,pname) -> n in + ctx.m.module_globals <- PMap.add imported_name (TClassDecl c,tsub,p) ctx.m.module_globals; + else + loop rest + in + loop c.cl_ordered_statics + with Not_found -> + find_main_type_static ()) + | None -> + find_main_type_static () + ) + ) | (tsub,p2) :: (fname,p3) :: rest -> (match rest with | [] -> () @@ -808,6 +895,9 @@ let init_module_type ctx context_init (decl,p) = else error "Abstract is missing underlying type declaration" a.a_pos end + | EStatic _ -> + (* nothing to do here as module statics are collected into a special EClass *) + () let module_pass_2 ctx m decls tdecls p = (* here is an additional PASS 1 phase, which define the type parameters for all module types. diff --git a/src/typing/typeloadParse.ml b/src/typing/typeloadParse.ml index cd650c49cf4..2cf64aaeb50 100644 --- a/src/typing/typeloadParse.ml +++ b/src/typing/typeloadParse.ml @@ -123,9 +123,10 @@ let resolve_module_file com m remap p = | (EEnum d,_) :: _ -> d.d_meta | (EAbstract d,_) :: _ -> d.d_meta | (ETypedef d,_) :: _ -> d.d_meta + | (EStatic d,_) :: _ -> d.d_meta | [] -> [] in - let meta = match parse_result with + let meta = match parse_result with | ParseSuccess((_,decls),_,_) -> loop decls | ParseError _ -> [] in @@ -337,6 +338,7 @@ let parse_module ctx m p = | EEnum d -> build EPrivate d | ETypedef d -> build EPrivate d | EAbstract d -> build AbPrivate d + | EStatic d -> build (AStatic,null_pos) d | EImport _ | EUsing _ -> acc ) [(EImport (List.map (fun s -> s,null_pos) (!remap @ [snd m]),INormal),null_pos)] decls) else diff --git a/src/typing/typer.ml b/src/typing/typer.ml index 9c58cba8c46..824a1bac2d9 100644 --- a/src/typing/typer.ml +++ b/src/typing/typer.ml @@ -366,6 +366,15 @@ let rec type_ident_raise ctx i p mode = let e = type_type ctx ctx.curclass.cl_path p in (* check_locals_masking already done in type_type *) field_access ctx mode f (FStatic (ctx.curclass,f)) (field_type ctx ctx.curclass [] f p) e p + with Not_found -> try + (* module-level statics *) + (match ctx.m.curmod.m_statics with + | None -> raise Not_found + | Some c -> + let f = PMap.find i c.cl_statics in + let e = type_module_type ctx (TClassDecl c) None p in + field_access ctx mode f (FStatic (c,f)) (field_type ctx c [] f p) e p + ) with Not_found -> try let wrap e = if mode = MSet then AKNo i diff --git a/src/typing/typerDotPath.ml b/src/typing/typerDotPath.ml index 0720ac64294..0b96741ddef 100644 --- a/src/typing/typerDotPath.ml +++ b/src/typing/typerDotPath.ml @@ -39,22 +39,39 @@ let mk_dot_path_part s p : dot_path_part = let s_dot_path parts = String.concat "." (List.map (fun (s,_,_) -> s) parts) +(** resolve given path against module statics or raise Not_found *) +let resolve_module_static ctx m path p = + match path, m.m_statics with + | [], _ | _, None -> + raise Not_found + | (name,_,p) :: path_rest, Some c -> + let f = PMap.find name c.cl_statics in (* raises Not_found *) + check_field_access ctx c f true p; + let ft = Fields.field_type ctx c [] f p in + let e = type_module_type ctx (TClassDecl c) None p in + (fun mode -> field_access ctx mode f (FStatic (c,f)) ft e p), path_rest + let resolve_module_type ctx m name p = let t = Typeload.find_type_in_module m name in (* raises Not_found *) mk_module_type_access ctx t p let resolve_in_module ctx m path p = - let mname = snd m.m_path in - match path with - | (sname,PUppercase,sp) :: path_rest -> - begin - try - resolve_module_type ctx m sname sp, path_rest - with Not_found -> + try + (* first, try to find module-level static access *) + resolve_module_static ctx m path p + with Not_found -> + (* if there was no module-statics, resolve *) + let mname = snd m.m_path in + match path with + | (sname,PUppercase,sp) :: path_rest -> + begin + try + resolve_module_type ctx m sname sp, path_rest + with Not_found -> + resolve_module_type ctx m mname p, path + end + | _ -> resolve_module_type ctx m mname p, path - end - | _ -> - resolve_module_type ctx m mname p, path (** resolve given qualified module pack+name (and possibly next path part) or raise Not_found *) let resolve_qualified ctx pack name next_path p = diff --git a/std/haxe/macro/Expr.hx b/std/haxe/macro/Expr.hx index bb15ac30982..6bcac29ac99 100644 --- a/std/haxe/macro/Expr.hx +++ b/std/haxe/macro/Expr.hx @@ -953,6 +953,11 @@ enum TypeDefKind { Represents an abstract kind. **/ TDAbstract(tthis:Null, ?from:Array, ?to:Array); + + /** + Represents a module-level static field. + **/ + TDStatic(kind:FieldType, ?access:Array); // ignore TypeDefinition.fields } /** diff --git a/std/haxe/macro/Printer.hx b/std/haxe/macro/Printer.hx index 072b6eef316..a672b278b3b 100644 --- a/std/haxe/macro/Printer.hx +++ b/std/haxe/macro/Printer.hx @@ -377,6 +377,14 @@ class Printer { } ].join("\n") + "\n}"; + case TDStatic(kind, access): + tabs = old; + (access != null && access.length > 0 ? access.map(printAccess).join(" ") + " " : "") + + switch (kind) { + case FVar(type, eo): ((access != null && access.has(AFinal)) ? '' : 'var ') + '${t.name}' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";"; + case FProp(get, set, type, eo): 'var ${t.name}($get, $set)' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";"; + case FFun(func): 'function ${t.name}' + printFunction(func) + switch func.expr { case {expr: EBlock(_)}: ""; case _: ";"; }; + } } tabs = old; return str; diff --git a/std/haxe/macro/Type.hx b/std/haxe/macro/Type.hx index 85e4f9cc8cf..21892380a2c 100644 --- a/std/haxe/macro/Type.hx +++ b/std/haxe/macro/Type.hx @@ -303,11 +303,9 @@ enum ClassKind { KTypeParameter(constraints:Array); /** - A structurally extended class. - - @deprecated + A class containing module statics. **/ - KExtension(cl:Ref, params:Array); + KModuleStatics(module:String); /** A special kind of class to encode expressions into type parameters. diff --git a/tests/misc/projects/Issue7968/compile-fail.hxml.stderr b/tests/misc/projects/Issue7968/compile-fail.hxml.stderr index 70620971f0c..e0afb095837 100644 --- a/tests/misc/projects/Issue7968/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue7968/compile-fail.hxml.stderr @@ -1,2 +1,2 @@ -Foo.hx:2: characters 1-18 : Type name A is already defined in this module +Foo.hx:2: characters 1-18 : Name A is already defined in this module Foo.hx:1: characters 1-16 : Previous declaration here diff --git a/tests/misc/resolution/projects/modulestatics/.gitignore b/tests/misc/resolution/projects/modulestatics/.gitignore new file mode 100644 index 00000000000..c1892f572b6 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/.gitignore @@ -0,0 +1 @@ +/test.js diff --git a/tests/misc/resolution/projects/modulestatics/Duplicate.hx b/tests/misc/resolution/projects/modulestatics/Duplicate.hx new file mode 100644 index 00000000000..944d6b0b708 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/Duplicate.hx @@ -0,0 +1,2 @@ +function C() {} +class C {} diff --git a/tests/misc/resolution/projects/modulestatics/Macro.hx b/tests/misc/resolution/projects/modulestatics/Macro.hx new file mode 100644 index 00000000000..588eda3b894 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/Macro.hx @@ -0,0 +1,58 @@ +import haxe.macro.Expr; + +final calls = []; +final builds = []; + +macro function getCalls() { + return macro $v{calls}; +} + +macro function getBuilds() { + return macro $v{builds}; +} + +function c(name) { + calls.push(name); +} + +function b(name) { + builds.push(name); +} + +function lowerCase() { + c("lowerCase"); +} + +function UpperCase() { + c("UpperCase"); +} + +function build() { + b("build"); + return []; +} + +function Build() { + b("Build"); + return []; +} + +class Macro { + static function lowerCase() { + c("Macro.lowerCase"); + } + + static function UpperCase() { + c("Macro.UpperCase"); + } + + static function build() { + b("Macro.build"); + return []; + } + + static function Build() { + b("Macro.Build"); + return []; + } +} diff --git a/tests/misc/resolution/projects/modulestatics/Main.hx b/tests/misc/resolution/projects/modulestatics/Main.hx new file mode 100644 index 00000000000..c96886dd560 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/Main.hx @@ -0,0 +1,105 @@ +import utest.Assert; + +import RootMod1; +import RootMod3.lowerCase3; +import RootMod3.UpperCase3; +import RootMod4.lowerCase as lowerCase4; +import RootMod4.UpperCase as UpperCase4; +import pack.Mod1; +import ModWithStaticAndClassStatic2; + +class Main extends utest.Test { + function testImportedModule() { + Assert.equals("RootMod1.lowerCase", lowerCase()); + Assert.equals("RootMod1.UpperCase", UpperCase()); + Assert.equals("pack.Mod1.lowerCasePack", lowerCasePack()); + Assert.equals("pack.Mod1.UpperCasePack", UpperCasePack()); + } + + function testUnimportedRootModule() { + Assert.equals("RootMod2.lowerCase", RootMod2.lowerCase()); + Assert.equals("RootMod2.UpperCase", RootMod2.UpperCase()); + } + + function testUnimportedRootModuleWithStd() { + Assert.equals("RootMod2.lowerCase", std.RootMod2.lowerCase()); + Assert.equals("RootMod2.UpperCase", std.RootMod2.UpperCase()); + } + + function testUnimportedPackModule() { + Assert.equals("pack.Mod2.lowerCasePack", pack.Mod2.lowerCasePack()); + Assert.equals("pack.Mod2.UpperCasePack", pack.Mod2.UpperCasePack()); + } + + function testUnimportedPackModuleWithStd() { + Assert.equals("pack.Mod2.lowerCasePack", std.pack.Mod2.lowerCasePack()); + Assert.equals("pack.Mod2.UpperCasePack", std.pack.Mod2.UpperCasePack()); + } + + function testImportedFunction() { + Assert.equals("RootMod3.lowerCase", lowerCase3()); + Assert.equals("RootMod3.UpperCase", UpperCase3()); + } + + function testImportedFunctionAliased() { + Assert.equals("RootMod4.lowerCase", lowerCase4()); + Assert.equals("RootMod4.UpperCase", UpperCase4()); + } + + function testPrivate() { + Assert.equals("ModWithPrivate.f", ModWithPrivate.f()); + } + + function testUnimportedModuleStaticBeforeMainClassStatic() { + Assert.equals("ModWithStaticAndClassStatic.lowerCaseMod", ModWithStaticAndClassStatic.lowerCaseMod()); + Assert.equals("ModWithStaticAndClassStatic.UpperCaseMod", ModWithStaticAndClassStatic.UpperCaseMod()); + } + + function testImportedClassStaticBeforeModuleStatic() { + Assert.equals("ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.lowerCaseMod", ModWithStaticAndClassStatic2.lowerCaseMod()); + Assert.equals("ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.UpperCaseMod", ModWithStaticAndClassStatic2.UpperCaseMod()); + } + + function testModuleWithStaticsResolvesToMainType() { + Assert.equals("ModWithStaticAndClassStatic", Type.getClassName(ModWithStaticAndClassStatic)); + Assert.equals("ModWithStaticAndClassStatic2", Type.getClassName(ModWithStaticAndClassStatic2)); + Assert.equals("pack.Mod2", Type.getClassName(pack.Mod2)); + } + + function testMacro() { + Assert.same([ + "lowerCase", + "UpperCase", + "Macro.lowerCase", + "Macro.UpperCase", + ], Macro.getCalls()); + + // force build + (null : C1); + (null : C2); + (null : C3); + (null : C4); + var builds = Macro.getBuilds(); + builds.sort(Reflect.compare); + trace(builds); + Assert.same([ + "Build", + "Macro.Build", + "Macro.build", + "build", + ], builds); + } + + static function main() { + utest.UTest.run([ + new Main(), + new Wildcard(), + new pack.inner.Test(), + ]); + } +} + +@:build(Macro.build()) private class C1 {} +@:build(Macro.Build()) private class C2 {} +@:build(Macro.Macro.build()) private class C3 {} +@:build(Macro.Macro.Build()) private class C4 {} \ No newline at end of file diff --git a/tests/misc/resolution/projects/modulestatics/ModWithPrivate.hx b/tests/misc/resolution/projects/modulestatics/ModWithPrivate.hx new file mode 100644 index 00000000000..802d0f14101 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/ModWithPrivate.hx @@ -0,0 +1,7 @@ +function f() { + return privateFunc(); +} + +private function privateFunc() { + return "ModWithPrivate.f"; +} diff --git a/tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic.hx b/tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic.hx new file mode 100644 index 00000000000..eef6876e5e1 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic.hx @@ -0,0 +1,17 @@ +function UpperCaseMod() { + return "ModWithStaticAndClassStatic.UpperCaseMod"; +} + +function lowerCaseMod() { + return "ModWithStaticAndClassStatic.lowerCaseMod"; +} + +class ModWithStaticAndClassStatic { + public static function UpperCaseMod() { + return "ModWithStaticAndClassStatic.ModWithStaticAndClassStatic.UpperCaseMod"; + } + + public static function lowerCaseMod() { + return "ModWithStaticAndClassStatic.ModWithStaticAndClassStatic.lowerCaseMod"; + } +} diff --git a/tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic2.hx b/tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic2.hx new file mode 100644 index 00000000000..01f5d25b2b2 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic2.hx @@ -0,0 +1,17 @@ +function UpperCaseMod() { + return "ModWithStaticAndClassStatic2.UpperCaseMod"; +} + +function lowerCaseMod() { + return "ModWithStaticAndClassStatic2.lowerCaseMod"; +} + +class ModWithStaticAndClassStatic2 { + public static function UpperCaseMod() { + return "ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.UpperCaseMod"; + } + + public static function lowerCaseMod() { + return "ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.lowerCaseMod"; + } +} diff --git a/tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClass.hx b/tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClass.hx new file mode 100644 index 00000000000..3de65701c3a --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClass.hx @@ -0,0 +1,5 @@ +class ModuleStaticWithNoMainClass { + static function main() { + trace(RootMod1); + } +} diff --git a/tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClassInPack.hx b/tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClassInPack.hx new file mode 100644 index 00000000000..2b45444373c --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClassInPack.hx @@ -0,0 +1,5 @@ +class ModuleStaticWithNoMainClassInPack { + static function main() { + trace(pack.Mod1); + } +} diff --git a/tests/misc/resolution/projects/modulestatics/PrivateAccess.hx b/tests/misc/resolution/projects/modulestatics/PrivateAccess.hx new file mode 100644 index 00000000000..575760a5b0d --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/PrivateAccess.hx @@ -0,0 +1,5 @@ +class PrivateAccess { + static function main() { + ModWithPrivate.privateFunc(); + } +} diff --git a/tests/misc/resolution/projects/modulestatics/PrivateImport.hx b/tests/misc/resolution/projects/modulestatics/PrivateImport.hx new file mode 100644 index 00000000000..e740d1ec503 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/PrivateImport.hx @@ -0,0 +1,7 @@ +import ModWithPrivate.privateFunc; + +class PrivateImport { + static function main() { + privateFunc(); + } +} diff --git a/tests/misc/resolution/projects/modulestatics/RootMod1.hx b/tests/misc/resolution/projects/modulestatics/RootMod1.hx new file mode 100644 index 00000000000..df1a781ca6d --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/RootMod1.hx @@ -0,0 +1,7 @@ +function lowerCase() { + return "RootMod1.lowerCase"; +} + +function UpperCase() { + return "RootMod1.UpperCase"; +} diff --git a/tests/misc/resolution/projects/modulestatics/RootMod2.hx b/tests/misc/resolution/projects/modulestatics/RootMod2.hx new file mode 100644 index 00000000000..1e955b00ee3 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/RootMod2.hx @@ -0,0 +1,7 @@ +function lowerCase() { + return "RootMod2.lowerCase"; +} + +function UpperCase() { + return "RootMod2.UpperCase"; +} diff --git a/tests/misc/resolution/projects/modulestatics/RootMod3.hx b/tests/misc/resolution/projects/modulestatics/RootMod3.hx new file mode 100644 index 00000000000..9702653ba0b --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/RootMod3.hx @@ -0,0 +1,7 @@ +function lowerCase3() { + return "RootMod3.lowerCase"; +} + +function UpperCase3() { + return "RootMod3.UpperCase"; +} diff --git a/tests/misc/resolution/projects/modulestatics/RootMod4.hx b/tests/misc/resolution/projects/modulestatics/RootMod4.hx new file mode 100644 index 00000000000..58eff29a94a --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/RootMod4.hx @@ -0,0 +1,7 @@ +function lowerCase() { + return "RootMod4.lowerCase"; +} + +function UpperCase() { + return "RootMod4.UpperCase"; +} diff --git a/tests/misc/resolution/projects/modulestatics/Wildcard.hx b/tests/misc/resolution/projects/modulestatics/Wildcard.hx new file mode 100644 index 00000000000..d6776850d23 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/Wildcard.hx @@ -0,0 +1,13 @@ +import pack.inner.*; +import pack.shadow.*; + +import utest.Assert; + +class Wildcard extends utest.Test { + function test() { + Assert.equals("pack.inner.InnerMod.lowerCase", InnerMod.lowerCase()); + Assert.equals("pack.inner.InnerMod.UpperCase", InnerMod.UpperCase()); + Assert.equals("pack.shadow.Test.lowerCase", Test.lowerCase()); + Assert.equals("pack.shadow.Test.UpperCase", Test.UpperCase()); + } +} diff --git a/tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml b/tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml new file mode 100644 index 00000000000..669f5a47c5e --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml @@ -0,0 +1,2 @@ +-main Duplicate +-js duplicateFail.js diff --git a/tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml.stderr b/tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml.stderr new file mode 100644 index 00000000000..bf3373b3c3a --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml.stderr @@ -0,0 +1,2 @@ +Duplicate.hx:2: characters 1-11 : Name C is already defined in this module +Duplicate.hx:1: characters 1-16 : Previous declaration here \ No newline at end of file diff --git a/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml new file mode 100644 index 00000000000..2740eee9ddf --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml @@ -0,0 +1,2 @@ +-main ModuleStaticWithNoMainClass +-js moduleStaticWithNoMainClass.js diff --git a/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml.stderr b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml.stderr new file mode 100644 index 00000000000..1fc85f24204 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml.stderr @@ -0,0 +1 @@ +ModuleStaticWithNoMainClass.hx:3: characters 15-23 : Module RootMod1 does not define type RootMod1 \ No newline at end of file diff --git a/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml new file mode 100644 index 00000000000..9b4e78c5c17 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml @@ -0,0 +1,2 @@ +-main ModuleStaticWithNoMainClassInPack +-js moduleStaticWithNoMainClass.js diff --git a/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml.stderr b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml.stderr new file mode 100644 index 00000000000..92edf852eeb --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml.stderr @@ -0,0 +1 @@ +ModuleStaticWithNoMainClassInPack.hx:3: characters 15-24 : Module pack.Mod1 does not define type Mod1 \ No newline at end of file diff --git a/tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml b/tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml new file mode 100644 index 00000000000..0dd41fe786a --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml @@ -0,0 +1,2 @@ +-main PrivateImport +-js privateFail.js diff --git a/tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml.stderr b/tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml.stderr new file mode 100644 index 00000000000..9a88f828124 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml.stderr @@ -0,0 +1 @@ +PrivateImport.hx:1: characters 1-35 : Importing private declarations from a module is not allowed \ No newline at end of file diff --git a/tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml b/tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml new file mode 100644 index 00000000000..bf779345df6 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml @@ -0,0 +1,2 @@ +-main PrivateAccess +-js privateFail2.js diff --git a/tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml.stderr b/tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml.stderr new file mode 100644 index 00000000000..6a535b544d8 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml.stderr @@ -0,0 +1 @@ +PrivateAccess.hx:3: characters 3-29 : Cannot access private field privateFunc \ No newline at end of file diff --git a/tests/misc/resolution/projects/modulestatics/compile.hxml b/tests/misc/resolution/projects/modulestatics/compile.hxml new file mode 100644 index 00000000000..6397489e490 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/compile.hxml @@ -0,0 +1,8 @@ +-main Main +--macro Macro.lowerCase() +--macro Macro.UpperCase() +--macro Macro.Macro.lowerCase() +--macro Macro.Macro.UpperCase() +-lib utest +-js test.js +-cmd node test.js diff --git a/tests/misc/resolution/projects/modulestatics/pack/InnerMod.hx b/tests/misc/resolution/projects/modulestatics/pack/InnerMod.hx new file mode 100644 index 00000000000..c6df28ac8a1 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/pack/InnerMod.hx @@ -0,0 +1,11 @@ +package pack.inner; + +// this module should NOT shadow InnerMod when called from within pack.inner + +function lowerCase() { + return "pack.InnerMod.lowerCase"; +} + +function UpperCase() { + return "pack.InnerMod.UpperCase"; +} diff --git a/tests/misc/resolution/projects/modulestatics/pack/Mod1.hx b/tests/misc/resolution/projects/modulestatics/pack/Mod1.hx new file mode 100644 index 00000000000..b3f68be5599 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/pack/Mod1.hx @@ -0,0 +1,9 @@ +package pack; + +function lowerCasePack() { + return "pack.Mod1.lowerCasePack"; +} + +function UpperCasePack() { + return "pack.Mod1.UpperCasePack"; +} diff --git a/tests/misc/resolution/projects/modulestatics/pack/Mod2.hx b/tests/misc/resolution/projects/modulestatics/pack/Mod2.hx new file mode 100644 index 00000000000..3aad1474e9d --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/pack/Mod2.hx @@ -0,0 +1,11 @@ +package pack; + +function lowerCasePack() { + return "pack.Mod2.lowerCasePack"; +} + +function UpperCasePack() { + return "pack.Mod2.UpperCasePack"; +} + +class Mod2 {} diff --git a/tests/misc/resolution/projects/modulestatics/pack/inner/InnerMod.hx b/tests/misc/resolution/projects/modulestatics/pack/inner/InnerMod.hx new file mode 100644 index 00000000000..b27762b6466 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/pack/inner/InnerMod.hx @@ -0,0 +1,9 @@ +package pack.inner; + +function lowerCase() { + return "pack.inner.InnerMod.lowerCase"; +} + +function UpperCase() { + return "pack.inner.InnerMod.UpperCase"; +} diff --git a/tests/misc/resolution/projects/modulestatics/pack/inner/Test.hx b/tests/misc/resolution/projects/modulestatics/pack/inner/Test.hx new file mode 100644 index 00000000000..b5c1e748fe5 --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/pack/inner/Test.hx @@ -0,0 +1,31 @@ +package pack.inner; + +import utest.Assert; + +class Test extends utest.Test { + function testUnqualifiedThisPack() { + Assert.equals("pack.inner.InnerMod.lowerCase", pack.inner.InnerMod.lowerCase()); + Assert.equals("pack.inner.InnerMod.UpperCase", pack.inner.InnerMod.UpperCase()); + } + + function testUnqualifiedUpperPack() { + Assert.equals("pack.Mod1.lowerCasePack", Mod1.lowerCasePack()); + Assert.equals("pack.Mod1.UpperCasePack", Mod1.UpperCasePack()); + } + + function testUnqualifiedRootPack() { + Assert.equals("RootMod1.lowerCase", RootMod1.lowerCase()); + Assert.equals("RootMod1.UpperCase", RootMod1.UpperCase()); + } + + function testUnqualifiedRootPackStd() { + Assert.equals("RootMod1.lowerCase", std.RootMod1.lowerCase()); + Assert.equals("RootMod1.UpperCase", std.RootMod1.UpperCase()); + } + + function testUnqualifiedRootPackStdShadowed() { + var RootMod = 1; + Assert.equals("RootMod1.lowerCase", std.RootMod1.lowerCase()); + Assert.equals("RootMod1.UpperCase", std.RootMod1.UpperCase()); + } +} diff --git a/tests/misc/resolution/projects/modulestatics/pack/shadow/Test.hx b/tests/misc/resolution/projects/modulestatics/pack/shadow/Test.hx new file mode 100644 index 00000000000..80d31d6e55d --- /dev/null +++ b/tests/misc/resolution/projects/modulestatics/pack/shadow/Test.hx @@ -0,0 +1,9 @@ +package pack.shadow; + +function lowerCase() { + return "pack.shadow.Test.lowerCase"; +} + +function UpperCase() { + return "pack.shadow.Test.UpperCase"; +} diff --git a/tests/misc/resolution/projects/spec/ModWithPrivate.hx b/tests/misc/resolution/projects/spec/ModWithPrivate.hx new file mode 100644 index 00000000000..c8df714b3a4 --- /dev/null +++ b/tests/misc/resolution/projects/spec/ModWithPrivate.hx @@ -0,0 +1 @@ +private class A {} diff --git a/tests/misc/resolution/projects/spec/PrivateImport.hx b/tests/misc/resolution/projects/spec/PrivateImport.hx new file mode 100644 index 00000000000..0ce317908e3 --- /dev/null +++ b/tests/misc/resolution/projects/spec/PrivateImport.hx @@ -0,0 +1,7 @@ +import ModWithPrivate.A; + +class PrivateImport { + static function main() { + trace(A); + } +} diff --git a/tests/misc/resolution/projects/spec/compile-private-fail.hxml b/tests/misc/resolution/projects/spec/compile-private-fail.hxml new file mode 100644 index 00000000000..0dd41fe786a --- /dev/null +++ b/tests/misc/resolution/projects/spec/compile-private-fail.hxml @@ -0,0 +1,2 @@ +-main PrivateImport +-js privateFail.js diff --git a/tests/misc/resolution/projects/spec/compile-private-fail.hxml.stderr b/tests/misc/resolution/projects/spec/compile-private-fail.hxml.stderr new file mode 100644 index 00000000000..adb545ff525 --- /dev/null +++ b/tests/misc/resolution/projects/spec/compile-private-fail.hxml.stderr @@ -0,0 +1,2 @@ +PrivateImport.hx:1: characters 8-24 : Importing private declarations from a module is not allowed +PrivateImport.hx:5: characters 9-10 : Unknown identifier : A \ No newline at end of file diff --git a/tests/unit/src/unit/TestMain.hx b/tests/unit/src/unit/TestMain.hx index 8d1eaa86ece..a8776710eef 100644 --- a/tests/unit/src/unit/TestMain.hx +++ b/tests/unit/src/unit/TestMain.hx @@ -5,150 +5,149 @@ import utest.Runner; import unit.Test.*; import haxe.ds.List; +final asyncWaits = new Array(); +final asyncCache = new Array Void>(); + @:access(unit.Test) -@:expose("unit.TestMain") +#if js +@:expose("unit.TestMain.main") @:keep -class TestMain { - - static var asyncWaits = new Array(); - static var asyncCache = new Array Void>(); - - static function main() { - #if js - if (js.Browser.supported) { - var oTrace = haxe.Log.trace; - var traceElement = js.Browser.document.getElementById("haxe:trace"); - haxe.Log.trace = function(v, ?infos) { - oTrace(v, infos); - traceElement.innerHTML += infos.fileName + ":" + infos.lineNumber + ": " + StringTools.htmlEscape(v) + "
"; - } +#end +function main() { + #if js + if (js.Browser.supported) { + var oTrace = haxe.Log.trace; + var traceElement = js.Browser.document.getElementById("haxe:trace"); + haxe.Log.trace = function(v, ?infos) { + oTrace(v, infos); + traceElement.innerHTML += infos.fileName + ":" + infos.lineNumber + ": " + StringTools.htmlEscape(v) + "
"; } - #end + } + #end - var verbose = #if ( cpp || neko || php ) Sys.args().indexOf("-v") >= 0 #else false #end; + var verbose = #if ( cpp || neko || php ) Sys.args().indexOf("-v") >= 0 #else false #end; - #if cs //"Turkey Test" - Issue #996 - cs.system.threading.Thread.CurrentThread.CurrentCulture = new cs.system.globalization.CultureInfo('tr-TR'); - cs.Lib.applyCultureChanges(); + #if cs //"Turkey Test" - Issue #996 + cs.system.threading.Thread.CurrentThread.CurrentCulture = new cs.system.globalization.CultureInfo('tr-TR'); + cs.Lib.applyCultureChanges(); + #end + #if neko + if( neko.Web.isModNeko ) + neko.Web.setHeader("Content-Type","text/plain"); + #elseif php + if( php.Web.isModNeko ) + php.Web.setHeader("Content-Type","text/plain"); + #end + #if !macro + trace("Generated at: " + HelperMacros.getCompilationDate()); + #end + trace("START"); + #if flash + var tf : flash.text.TextField = untyped flash.Boot.getTrace(); + tf.selectable = true; + tf.mouseEnabled = true; + #end + var classes = [ + new TestOps(), + new TestBasetypes(), + new TestExceptions(), + new TestBytes(), + new TestIO(), + new TestLocals(), + new TestEReg(), + new TestXML(), + new TestMisc(), + new TestJson(), + new TestResource(), + new TestInt64(), + new TestReflect(), + new TestSerialize(), + new TestSerializerCrossTarget(), + new TestMeta(), + new TestType(), + new TestOrder(), + new TestGADT(), + new TestGeneric(), + new TestArrowFunctions(), + new TestCasts(), + new TestSyntaxModule(), + new TestNull(), + new TestNumericCasts(), + new TestHashMap(), + #if (!no_http && (!azure || !(php && Windows))) + new TestHttp(), + #end + #if !no_pattern_matching + new TestMatch(), + #end + #if cs + new TestCSharp(), + #end + #if java + new TestJava(), + #end + #if lua + new TestLua(), #end - #if neko - if( neko.Web.isModNeko ) - neko.Web.setHeader("Content-Type","text/plain"); - #elseif php - if( php.Web.isModNeko ) - php.Web.setHeader("Content-Type","text/plain"); + #if python + new TestPython(), #end - #if !macro - trace("Generated at: " + HelperMacros.getCompilationDate()); + #if hl + new TestHL(), #end - trace("START"); - #if flash - var tf : flash.text.TextField = untyped flash.Boot.getTrace(); - tf.selectable = true; - tf.mouseEnabled = true; + #if php + new TestPhp(), #end - var classes = [ - new TestOps(), - new TestBasetypes(), - new TestExceptions(), - new TestBytes(), - new TestIO(), - new TestLocals(), - new TestEReg(), - new TestXML(), - new TestMisc(), - new TestJson(), - new TestResource(), - new TestInt64(), - new TestReflect(), - new TestSerialize(), - new TestSerializerCrossTarget(), - new TestMeta(), - new TestType(), - new TestOrder(), - new TestGADT(), - new TestGeneric(), - new TestArrowFunctions(), - new TestCasts(), - new TestSyntaxModule(), - new TestNull(), - new TestNumericCasts(), - new TestHashMap(), - #if (!no_http && (!azure || !(php && Windows))) - new TestHttp(), - #end - #if !no_pattern_matching - new TestMatch(), - #end - #if cs - new TestCSharp(), - #end - #if java - new TestJava(), - #end - #if lua - new TestLua(), - #end - #if python - new TestPython(), - #end - #if hl - new TestHL(), - #end - #if php - new TestPhp(), - #end - #if (java || cs) - new TestOverloads(), - #end - new TestInterface(), - new TestNaN(), - #if ((dce == "full") && !interp) - new TestDCE(), - #end - new TestMapComprehension(), - new TestMacro(), - new TestKeyValueIterator(), - new TestFieldVariance() - //new TestUnspecified(), - //new TestRemoting(), - ]; + #if (java || cs) + new TestOverloads(), + #end + new TestInterface(), + new TestNaN(), + #if ((dce == "full") && !interp) + new TestDCE(), + #end + new TestMapComprehension(), + new TestMacro(), + new TestKeyValueIterator(), + new TestFieldVariance() + //new TestUnspecified(), + //new TestRemoting(), + ]; - for (specClass in unit.UnitBuilder.generateSpec("src/unitstd")) { - classes.push(specClass); - } - TestIssues.addIssueClasses("src/unit/issues", "unit.issues"); - TestIssues.addIssueClasses("src/unit/hxcpp_issues", "unit.hxcpp_issues"); + for (specClass in unit.UnitBuilder.generateSpec("src/unitstd")) { + classes.push(specClass); + } + TestIssues.addIssueClasses("src/unit/issues", "unit.issues"); + TestIssues.addIssueClasses("src/unit/hxcpp_issues", "unit.hxcpp_issues"); - var runner = new Runner(); - for (c in classes) { - runner.addCase(c); - } - var report = Report.create(runner); - report.displayHeader = AlwaysShowHeader; - report.displaySuccessResults = NeverShowSuccessResults; - var success = true; - runner.onProgress.add(function(e) { - for(a in e.result.assertations) { - switch a { - case Success(pos): - case Warning(msg): - case Ignore(reason): - case _: success = false; - } + var runner = new Runner(); + for (c in classes) { + runner.addCase(c); + } + var report = Report.create(runner); + report.displayHeader = AlwaysShowHeader; + report.displaySuccessResults = NeverShowSuccessResults; + var success = true; + runner.onProgress.add(function(e) { + for(a in e.result.assertations) { + switch a { + case Success(pos): + case Warning(msg): + case Ignore(reason): + case _: success = false; } - #if js - if (js.Browser.supported && e.totals == e.done) { - untyped js.Browser.window.success = success; - }; - #end - }); - #if sys - if (verbose) - runner.onTestStart.add(function(test) { - Sys.println(' $test...'); // TODO: need utest success state for this - }); + } + #if js + if (js.Browser.supported && e.totals == e.done) { + untyped js.Browser.window.success = success; + }; #end - runner.run(); - } + }); + #if sys + if (verbose) + runner.onTestStart.add(function(test) { + Sys.println(' $test...'); // TODO: need utest success state for this + }); + #end + runner.run(); } diff --git a/tests/unit/src/unit/TestModuleStatics.hx b/tests/unit/src/unit/TestModuleStatics.hx new file mode 100644 index 00000000000..32d029e714b --- /dev/null +++ b/tests/unit/src/unit/TestModuleStatics.hx @@ -0,0 +1,109 @@ +package unit; + +class TestModuleStatics extends Test { + function testVars() { + eq("finalInit", finalInit); + eq("finalHintInit", finalHintInit); + eq("inlineFinalInit", inlineFinalInit); + eq("inlineFinalHintInit", inlineFinalHintInit); + eq("privateFinalInit", privateFinalInit); + eq("privateFinalHintInit", privateFinalHintInit); + eq("privateInlineFinalInit", privateInlineFinalInit); + eq("privateInlineFinalHintInit", privateInlineFinalHintInit); + eq("inlinePrivateFinalInit", inlinePrivateFinalInit); + eq("inlinePrivateFinalHintInit", inlinePrivateFinalHintInit); + eq("varInit", varInit); + eq("varInitHint", varInitHint); + eq(null, varHint); + eq("inlineVarInit", inlineVarInit); + eq("inlineVarInitHint", inlineVarInitHint); + eq("privateVarInit", privateVarInit); + eq("privateVarInitHint", privateVarInitHint); + eq(null, privateVarHint); + eq("privateInlineVarInit", privateInlineVarInit); + eq("privateInlineVarInitHint", privateInlineVarInitHint); + eq("inlinePrivateVarInit", inlinePrivateVarInit); + eq("inlinePrivateVarInitHint", inlinePrivateVarInitHint); + + + varInit = "1"; + eq("1", varInit); + varInitHint = "2"; + eq("2", varInitHint); + varHint = "3"; + eq("3", varHint); + privateVarInit = "4"; + eq("4", privateVarInit); + privateVarInitHint = "5"; + eq("5", privateVarInitHint); + privateVarHint = "6"; + eq("6", privateVarHint); + } + + function testFuncs() { + eq("func", func()); + eq("privateFunc", privateFunc()); + eq("privateInlineFunc", privateInlineFunc()); + eq("inlinePrivateFunc", inlinePrivateFunc()); + eq("dynamicFunc", dynamicFunc()); + eq("privateDynamicFunc", privateDynamicFunc()); + eq("dynamicPrivateFunc", dynamicPrivateFunc()); + + dynamicFunc = () -> "1"; + eq("1", dynamicFunc()); + privateDynamicFunc = () -> "2"; + eq("2", privateDynamicFunc()); + dynamicPrivateFunc = () -> "3"; + eq("3", dynamicPrivateFunc()); + } + + function testProps() { + eq("prop-get", prop); + prop = "hello"; + eq("hello-set-get", prop); + } + + function testMacroDefined() { + eq(42, mstatics.Funcs.funcA()); + eq(43, mstatics.Funcs.funcB()); + eq(44, mstatics.FuncC.FuncC()); + } +} + +// macro-define the functions +typedef T = TestModuleStaticsMacro; + +final finalInit = "finalInit"; +final finalHintInit:String = "finalHintInit"; +inline final inlineFinalInit = "inlineFinalInit"; +inline final inlineFinalHintInit:String = "inlineFinalHintInit"; +private final privateFinalInit = "privateFinalInit"; +private final privateFinalHintInit:String = "privateFinalHintInit"; +private inline final privateInlineFinalInit = "privateInlineFinalInit"; +private inline final privateInlineFinalHintInit:String = "privateInlineFinalHintInit"; +inline private final inlinePrivateFinalInit = "inlinePrivateFinalInit"; +inline private final inlinePrivateFinalHintInit:String = "inlinePrivateFinalHintInit"; +var varInit = "varInit"; +var varInitHint:String = "varInitHint"; +var varHint:String; +inline var inlineVarInit = "inlineVarInit"; +inline var inlineVarInitHint:String = "inlineVarInitHint"; +private var privateVarInit = "privateVarInit"; +private var privateVarInitHint:String = "privateVarInitHint"; +private var privateVarHint:String; +private inline var privateInlineVarInit = "privateInlineVarInit"; +private inline var privateInlineVarInitHint:String = "privateInlineVarInitHint"; +inline private var inlinePrivateVarInit = "inlinePrivateVarInit"; +inline private var inlinePrivateVarInitHint:String = "inlinePrivateVarInitHint"; + +function func() return "func"; +private function privateFunc() return "privateFunc"; +private inline function privateInlineFunc() return "privateInlineFunc"; +inline private function inlinePrivateFunc() return "inlinePrivateFunc"; +dynamic function dynamicFunc() return "dynamicFunc"; +private dynamic function privateDynamicFunc() return "privateDynamicFunc"; +dynamic private function dynamicPrivateFunc() return "dynamicPrivateFunc"; + +@:isVar var prop(get,set):String = "prop"; +function get_prop() return prop + "-get"; +function set_prop(value) return prop = value + "-set"; diff --git a/tests/unit/src/unit/TestModuleStaticsMacro.hx b/tests/unit/src/unit/TestModuleStaticsMacro.hx new file mode 100644 index 00000000000..df65fc4f1e2 --- /dev/null +++ b/tests/unit/src/unit/TestModuleStaticsMacro.hx @@ -0,0 +1,41 @@ +package unit; + +#if !macro +@:genericBuild(unit.TestModuleStaticsMacro.build()) class TestModuleStaticsMacro {} +#else +import haxe.macro.Context; + +class TestModuleStaticsMacro { + static function build() { + var pos = Context.currentPos(); + + // a bit awkward, but oh well + Context.defineModule("mstatics.Funcs", [ + { + pos: pos, + name: "funcA", + kind: TDStatic(FFun({ret: macro : Int, args: [], expr: macro return 42})), + pack: ["mstatics"], + fields: [] + }, + { + pos: pos, + name: "funcB", + kind: TDStatic(FFun({ret: macro : Int, args: [], expr: macro return 43})), + pack: ["mstatics"], + fields: [] + } + ]); + + Context.defineType({ + pos: pos, + pack: ["mstatics"], + name: "FuncC", + kind: TDStatic(FFun({ret: macro : Int, args: [], expr: macro return 44})), + fields: [] + }); + + return macro : Void; + } +} +#end