diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9fed3c0bfb..48f10d5624a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -105,7 +105,7 @@ jobs: linux-build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: PLATFORM: linux64 OPAMYES: 1 @@ -123,7 +123,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.opam/ - key: ${{ runner.os }}-${{ matrix.ocaml }}-${{ hashFiles('./haxe.opam', './libs/') }} + key: ${{ runner.os }}-${{ matrix.ocaml }}-${{ hashFiles('./haxe.opam', './libs/') }}-1 - name: Install Neko from S3 run: | @@ -148,10 +148,10 @@ jobs: - name: Install dependencies run: | set -ex - sudo add-apt-repository ppa:avsm/ppa -y # provides OPAM 2 - sudo add-apt-repository ppa:haxe/ocaml -y # provides newer version of mbedtls sudo apt-get update -qqy - sudo apt-get install -qqy ocaml-nox opam libpcre2-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build + sudo apt-get install -qqy darcs bubblewrap ocaml-nox libpcre2-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build + curl -sSL https://github.com/ocaml/opam/releases/download/2.3.0/opam-2.3.0-x86_64-linux -o $RUNNER_TEMP/opam + sudo install $RUNNER_TEMP/opam /usr/local/bin/opam - name: Install OCaml libraries if: steps.cache-opam.outputs.cache-hit != 'true' @@ -213,7 +213,7 @@ jobs: linux-test: needs: linux-build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: PLATFORM: linux64 TEST: ${{matrix.target}} @@ -300,7 +300,7 @@ jobs: test-docgen: needs: linux-build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: PLATFORM: linux64 HXCPP_COMPILE_CACHE: ~/hxcache @@ -854,7 +854,7 @@ jobs: deploy: if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' needs: [linux-test, linux-arm64-test, mac-test, windows64-test] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: # this is only needed for to get `COMMIT_DATE`... # maybe https://github.community/t/expose-commit-timestamp-in-the-github-context-data/16460/3 @@ -924,7 +924,7 @@ jobs: deploy_apidoc: if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' needs: [linux-test, linux-arm64-test, mac-test, windows64-test] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Install dependencies run: | diff --git a/Makefile b/Makefile index b56a37ba236..fadab09b49c 100644 --- a/Makefile +++ b/Makefile @@ -66,8 +66,7 @@ NEKO_VERSION_TAG=v$(shell echo "$(NEKO_VERSION)" | sed "s/\./-/g") all: haxe tools haxe: - dune build --profile release src/haxe.exe - cp -f _build/default/src/haxe.exe ./"$(HAXE_OUTPUT)" + dune build --profile release plugin: haxe $(DUNE_COMMAND) build --profile release plugins/$(PLUGIN)/$(PLUGIN).cmxs @@ -234,7 +233,7 @@ package_installer_mac: $(INSTALLER_TMP_DIR)/neko-osx.tar.gz package_unix clean: clean_haxe clean_tools clean_package clean_haxe: - rm -f -r _build $(HAXE_OUTPUT) $(PREBUILD_OUTPUT) + dune clean clean_tools: rm -f $(HAXE_OUTPUT) $(PREBUILD_OUTPUT) $(HAXELIB_OUTPUT) diff --git a/WinSetup.ps1 b/WinSetup.ps1 index 5828e84f3ee..74ce0592102 100644 --- a/WinSetup.ps1 +++ b/WinSetup.ps1 @@ -1,3 +1,9 @@ +# Usage: +# - install Git +# - install Neko +# - checkout haxe git +# - run from command "powershell -noexit -ExecutionPolicy Bypass -File .\WinSetup.ps1" + function Cmd-Path($file) { try { Split-Path -Parent (Get-Command "$file.exe" -ErrorAction Stop).Source } catch { "" } } diff --git a/dune b/dune index 940d0a166fc..7d58e039054 100644 --- a/dune +++ b/dune @@ -1,2 +1,9 @@ (dirs src libs) (data_only_dirs src-json) + +(rule + (action + (copy src/haxe.exe haxe%{ext_exe})) + (mode + (promote (until-clean))) + (alias default)) diff --git a/extra/github-actions/workflows/main.yml b/extra/github-actions/workflows/main.yml index 0774ab0fb4f..c133105aa69 100644 --- a/extra/github-actions/workflows/main.yml +++ b/extra/github-actions/workflows/main.yml @@ -24,7 +24,7 @@ jobs: @import build-windows.yml linux-build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: PLATFORM: linux64 OPAMYES: 1 @@ -42,17 +42,17 @@ jobs: uses: actions/cache@v4 with: path: ~/.opam/ - key: ${{ runner.os }}-${{ matrix.ocaml }}-${{ hashFiles('./haxe.opam', './libs/') }} + key: ${{ runner.os }}-${{ matrix.ocaml }}-${{ hashFiles('./haxe.opam', './libs/') }}-1 @import install-neko-unix.yml - name: Install dependencies run: | set -ex - sudo add-apt-repository ppa:avsm/ppa -y # provides OPAM 2 - sudo add-apt-repository ppa:haxe/ocaml -y # provides newer version of mbedtls sudo apt-get update -qqy - sudo apt-get install -qqy ocaml-nox opam libpcre2-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build + sudo apt-get install -qqy darcs bubblewrap ocaml-nox libpcre2-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build + curl -sSL https://github.com/ocaml/opam/releases/download/2.3.0/opam-2.3.0-x86_64-linux -o $RUNNER_TEMP/opam + sudo install $RUNNER_TEMP/opam /usr/local/bin/opam - name: Install OCaml libraries if: steps.cache-opam.outputs.cache-hit != 'true' @@ -114,7 +114,7 @@ jobs: linux-test: needs: linux-build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: PLATFORM: linux64 TEST: ${{matrix.target}} @@ -183,7 +183,7 @@ jobs: test-docgen: needs: linux-build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: PLATFORM: linux64 HXCPP_COMPILE_CACHE: ~/hxcache @@ -478,7 +478,7 @@ jobs: deploy: if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' needs: [linux-test, linux-arm64-test, mac-test, windows64-test] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: # this is only needed for to get `COMMIT_DATE`... # maybe https://github.community/t/expose-commit-timestamp-in-the-github-context-data/16460/3 @@ -548,7 +548,7 @@ jobs: deploy_apidoc: if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' needs: [linux-test, linux-arm64-test, mac-test, windows64-test] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Install dependencies run: | diff --git a/src-json/define.json b/src-json/define.json index 85559c57aed..8c9e66cbbb2 100644 --- a/src-json/define.json +++ b/src-json/define.json @@ -554,7 +554,8 @@ { "name": "NoDeprecationWarnings", "define": "no-deprecation-warnings", - "doc": "Do not warn if fields annotated with `@:deprecated` are used." + "doc": "Do not warn if fields annotated with `@:deprecated` are used.", + "deprecated": "Use -w to configure warnings. See https://haxe.org/manual/cr-warnings.html for more information." }, { "name": "NoFlashOverride", @@ -791,7 +792,8 @@ { "name": "WarnVarShadowing", "define": "warn-var-shadowing", - "doc": "Warn about shadowing variable declarations." + "doc": "Warn about shadowing variable declarations.", + "deprecated": "Use -w to configure warnings. See https://haxe.org/manual/cr-warnings.html for more information." }, { "name": "NoTre", diff --git a/src-json/warning.json b/src-json/warning.json index 763e6e80b05..0db09c76a01 100644 --- a/src-json/warning.json +++ b/src-json/warning.json @@ -76,7 +76,8 @@ { "name": "WVarShadow", "doc": "A local variable hides another by using the same name", - "parent": "WTyper" + "parent": "WTyper", + "enabled": false }, { "name": "WExternWithExpr", diff --git a/src/compiler/args.ml b/src/compiler/args.ml index fcd72ecb143..e96ada6b3f3 100644 --- a/src/compiler/args.ml +++ b/src/compiler/args.ml @@ -43,7 +43,7 @@ let process_args arg_spec = let parse_args com = let usage = Printf.sprintf "Haxe Compiler %s - (C)2005-2024 Haxe Foundation\nUsage: haxe%s [options] [hxml files and dot paths...]\n" - s_version_full (if Sys.os_type = "Win32" then ".exe" else "") + (s_version_full com.version) (if Sys.os_type = "Win32" then ".exe" else "") in let actx = { classes = [([],"Std")]; @@ -154,7 +154,7 @@ let parse_args com = com.debug <- true; ),"","add debug information to the compiled code"); ("Miscellaneous",["--version"],["-version"],Arg.Unit (fun() -> - raise (Helper.HelpMessage s_version_full); + raise (Helper.HelpMessage (s_version_full com.version)); ),"","print version and exit"); ("Miscellaneous", ["-h";"--help"], ["-help"], Arg.Unit (fun () -> raise (Arg.Help "") diff --git a/src/compiler/compilationCache.ml b/src/compiler/compilationCache.ml index 9d3d4d2300a..c5b4b649c5b 100644 --- a/src/compiler/compilationCache.ml +++ b/src/compiler/compilationCache.ml @@ -234,15 +234,27 @@ class cache = object(self) ) cc#get_modules acc ) contexts [] + method taint_module m_path reason = + Hashtbl.iter (fun _ cc -> + Hashtbl.iter (fun _ m -> + if m.m_path = m_path then m.m_extra.m_cache_state <- MSBad (Tainted reason) + ) cc#get_modules; + Hashtbl.iter (fun _ mc -> + if mc.HxbData.mc_path = m_path then + mc.HxbData.mc_extra.m_cache_state <- match reason, mc.mc_extra.m_cache_state with + | CheckDisplayFile, (MSBad _ as state) -> state + | _ -> MSBad (Tainted reason) + ) cc#get_hxb + ) contexts + method taint_modules file_key reason = Hashtbl.iter (fun _ cc -> Hashtbl.iter (fun _ m -> if Path.UniqueKey.lazy_key m.m_extra.m_file = file_key then m.m_extra.m_cache_state <- MSBad (Tainted reason) ) cc#get_modules; - let open HxbData in Hashtbl.iter (fun _ mc -> - if Path.UniqueKey.lazy_key mc.mc_extra.m_file = file_key then - mc.mc_extra.m_cache_state <- match reason, mc.mc_extra.m_cache_state with + if Path.UniqueKey.lazy_key mc.HxbData.mc_extra.m_file = file_key then + mc.HxbData.mc_extra.m_cache_state <- match reason, mc.HxbData.mc_extra.m_cache_state with | CheckDisplayFile, (MSBad _ as state) -> state | _ -> MSBad (Tainted reason) ) cc#get_hxb diff --git a/src/compiler/compiler.ml b/src/compiler/compiler.ml index 63c65e45a18..1011d027189 100644 --- a/src/compiler/compiler.ml +++ b/src/compiler/compiler.ml @@ -230,7 +230,7 @@ module Setup = struct let setup_common_context ctx = let com = ctx.com in ctx.com.print <- ctx.comm.write_out; - Common.define_value com Define.HaxeVer (Printf.sprintf "%.3f" (float_of_int Globals.version /. 1000.)); + Common.define_value com Define.HaxeVer (Printf.sprintf "%.3f" (float_of_int version /. 1000.)); Common.raw_define com "haxe3"; Common.raw_define com "haxe4"; Common.raw_define com "haxe5"; @@ -296,6 +296,7 @@ let do_type ctx mctx actx display_file_dot_path = Some (MacroContext.call_init_macro ctx.com mctx path) ) mctx (List.rev actx.config_macros) in enter_stage com CInitMacrosDone; + update_platform_config com; (* make sure to adapt all flags changes defined during init macros *) ServerMessage.compiler_stage com; let macros = match mctx with None -> None | Some mctx -> mctx.g.macros in @@ -309,7 +310,10 @@ let do_type ctx mctx actx display_file_dot_path = com.callbacks#run com.error_ext com.callbacks#get_after_init_macros; run_or_diagnose ctx (fun () -> if com.display.dms_kind <> DMNone then DisplayTexpr.check_display_file tctx cs; - List.iter (fun cpath -> ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath null_pos)) (List.rev actx.classes); + List.iter (fun cpath -> + ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath null_pos); + Typecore.flush_pass tctx.g PBuildClass "actx.classes" + ) (List.rev actx.classes); Finalization.finalize tctx; ); end with TypeloadParse.DisplayInMacroBlock -> @@ -418,6 +422,10 @@ let compile ctx actx callbacks = ) (List.rev actx.cmds) end +let make_ice_message com msg backtrace = + let ver = (s_version_full com.version) in + let os_type = if Sys.unix then "unix" else "windows" in + Printf.sprintf "%s\nHaxe: %s; OS type: %s;\n%s" msg ver os_type backtrace let compile_safe ctx f = let com = ctx.com in try @@ -445,6 +453,12 @@ with handle_diagnostics ctx msg null_pos DKCompilerMessage; | Failure msg when not Helper.is_debug_run -> error ctx ("Error: " ^ msg) null_pos + | Globals.Ice (msg,backtrace) when is_diagnostics com -> + let s = make_ice_message com msg backtrace in + handle_diagnostics ctx s null_pos DKCompilerMessage + | Globals.Ice (msg,backtrace) when not Helper.is_debug_run -> + let s = make_ice_message com msg backtrace in + error ctx ("Error: " ^ s) null_pos | Helper.HelpMessage msg -> print_endline msg | Parser.TypePath (p,c,is_import,pos) -> @@ -516,7 +530,14 @@ let compile_ctx callbacks ctx = catch_completion_and_exit ctx callbacks run let create_context comm cs compilation_step params = { - com = Common.create compilation_step cs version params (DisplayTypes.DisplayMode.create !Parser.display_mode); + com = Common.create compilation_step cs { + version = version; + major = version_major; + minor = version_minor; + revision = version_revision; + pre = version_pre; + extra = Version.version_extra; + } params (DisplayTypes.DisplayMode.create !Parser.display_mode); messages = []; has_next = false; has_error = false; diff --git a/src/compiler/dune b/src/compiler/dune index 80d02716907..82d5996bb7f 100644 --- a/src/compiler/dune +++ b/src/compiler/dune @@ -1,5 +1,5 @@ (rule - (deps (env_var ADD_REVISION)) + (deps (env_var ADD_REVISION) (universe)) (targets version.ml) (action (with-stdout-to version.ml (run ../prebuild.exe version))) ) \ No newline at end of file diff --git a/src/compiler/hxb/hxbReader.ml b/src/compiler/hxb/hxbReader.ml index e710c8033d0..077cd339c3e 100644 --- a/src/compiler/hxb/hxbReader.ml +++ b/src/compiler/hxb/hxbReader.ml @@ -1589,6 +1589,7 @@ class hxb_reader a.a_read <- self#read_option (fun () -> self#read_field_ref); a.a_write <- self#read_option (fun () -> self#read_field_ref); a.a_call <- self#read_option (fun () -> self#read_field_ref); + a.a_constructor <- self#read_option (fun () -> self#read_field_ref); a.a_ops <- self#read_list (fun () -> let i = read_byte ch in @@ -2078,8 +2079,7 @@ class hxb_reader ^ "Attach the following information:" in let backtrace = Printexc.raw_backtrace_to_string backtrace in - let s = Printf.sprintf "%s\nHaxe: %s\n%s" msg s_version_full backtrace in - failwith s + raise (Globals.Ice (msg, backtrace)) method private read_chunk_data kind = let path = String.concat "_" (ExtLib.String.nsplit (s_type_path mpath) ".") in diff --git a/src/compiler/hxb/hxbWriter.ml b/src/compiler/hxb/hxbWriter.ml index 8bd88f42bed..9337b697707 100644 --- a/src/compiler/hxb/hxbWriter.ml +++ b/src/compiler/hxb/hxbWriter.ml @@ -452,8 +452,7 @@ module HxbWriter = struct ^ "Attach the following information:" in let backtrace = Printexc.raw_backtrace_to_string backtrace in - let s = Printf.sprintf "%s\nHaxe: %s\n%s" msg s_version_full backtrace in - failwith s + raise (Globals.Ice (msg, backtrace)) let in_nested_scope writer = match writer.field_stack with | [] -> false (* can happen for cl_init and in EXD *) @@ -1918,6 +1917,7 @@ module HxbWriter = struct Chunk.write_option writer.chunk a.a_read (write_field_ref writer c CfrStatic ); Chunk.write_option writer.chunk a.a_write (write_field_ref writer c CfrStatic); Chunk.write_option writer.chunk a.a_call (write_field_ref writer c CfrStatic); + Chunk.write_option writer.chunk a.a_constructor (write_field_ref writer c CfrStatic); Chunk.write_list writer.chunk a.a_ops (fun (op, cf) -> Chunk.write_u8 writer.chunk (binop_index op); diff --git a/src/context/common.ml b/src/context/common.ml index c9daa3ed671..2cb50c073fe 100644 --- a/src/context/common.ml +++ b/src/context/common.ml @@ -356,7 +356,7 @@ type context = { is_macro_context : bool; mutable json_out : json_api option; (* config *) - version : int; + version : compiler_version; mutable args : string list; mutable display : DisplayTypes.DisplayMode.settings; mutable debug : bool; @@ -968,7 +968,7 @@ let init_platform com = end; (* Set the source header, unless the user has set one already or the platform sets a custom one *) if not (defined com Define.SourceHeader) && (com.platform <> Hl) then - define_value com Define.SourceHeader ("Generated by Haxe " ^ s_version_full); + define_value com Define.SourceHeader ("Generated by Haxe " ^ (s_version_full com.version)); let forbid acc p = if p = name || PMap.mem p acc then acc else PMap.add p Forbidden acc in com.package_rules <- List.fold_left forbid com.package_rules ("java" :: (List.map platform_name platforms)); update_platform_config com; diff --git a/src/context/display/displayJson.ml b/src/context/display/displayJson.ml index 1960cd20052..a7fb80594b8 100644 --- a/src/context/display/displayJson.ml +++ b/src/context/display/displayJson.ml @@ -179,11 +179,11 @@ let handler = hctx.send_result (JObject [ "methods",jarray methods; "haxeVersion",jobject [ - "major",jint version_major; - "minor",jint version_minor; - "patch",jint version_revision; - "pre",(match version_pre with None -> jnull | Some pre -> jstring pre); - "build",(match Version.version_extra with None -> jnull | Some(_,build) -> jstring build); + "major",jint hctx.com.version.major; + "minor",jint hctx.com.version.minor; + "patch",jint hctx.com.version.revision; + "pre",(match hctx.com.version.pre with None -> jnull | Some pre -> jstring pre); + "build",(match hctx.com.version.extra with None -> jnull | Some(_,build) -> jstring build); ]; "protocolVersion",jobject [ "major",jint 0; diff --git a/src/context/display/importHandling.ml b/src/context/display/importHandling.ml index 1b16393165e..84c96d6b54c 100644 --- a/src/context/display/importHandling.ml +++ b/src/context/display/importHandling.ml @@ -113,8 +113,9 @@ let init_import ctx path mode p = let check_alias mt name pname = if not (name.[0] >= 'A' && name.[0] <= 'Z') then raise_typing_error "Type aliases must start with an uppercase letter" pname; - if ctx.m.is_display_file && DisplayPosition.display_position#enclosed_in pname then - DisplayEmitter.display_alias ctx name (type_of_module_type mt) pname; + (* Imports from import.hx should not match display position from current file *) + if ctx.m.is_display_file && DisplayPosition.display_position#enclosed_in pname && (Path.UniqueKey.create pname.pfile) = (Path.UniqueKey.lazy_key ctx.m.curmod.m_extra.m_file) then + DisplayEmitter.display_alias ctx name (type_of_module_type mt) pname in let add_static_init t name s = match resolve_typedef t with diff --git a/src/context/typecore.ml b/src/context/typecore.ml index 43e197ea079..1dfdb2630a5 100644 --- a/src/context/typecore.ml +++ b/src/context/typecore.ml @@ -416,7 +416,7 @@ let save_locals ctx = let add_local ctx k n t p = let v = alloc_var k n t p in - if Define.defined ctx.com.defines Define.WarnVarShadowing && n <> "_" then begin + if n <> "_" then begin match k with | VUser _ -> begin try diff --git a/src/core/abstract.ml b/src/core/abstract.ml index c7f9cb2e064..58ea0c92aae 100644 --- a/src/core/abstract.ml +++ b/src/core/abstract.ml @@ -155,7 +155,7 @@ let rec follow_with_forward_ctor ?(build=false) t = match follow t with | TAbstract(a,tl) as t -> if build then build_abstract a; if Meta.has Meta.ForwardNew a.a_meta && not (match a.a_impl with - | Some c -> PMap.mem "_new" c.cl_statics + | Some c -> a.a_constructor <> None | None -> false ) then follow_with_forward_ctor (get_underlying_type ~return_first:true a tl) diff --git a/src/core/display/completionItem.ml b/src/core/display/completionItem.ml index 6501b672d74..bc99f668fe2 100644 --- a/src/core/display/completionItem.ml +++ b/src/core/display/completionItem.ml @@ -182,14 +182,11 @@ module CompletionModuleType = struct raise Exit let of_module_type mt = - let actor a = match a.a_impl with - | None -> No - | Some c -> - try - let cf = PMap.find "_new" c.cl_statics in - if (has_class_flag c CExtern) || (has_class_field_flag cf CfPublic) then Yes else YesButPrivate - with Not_found -> - No + let actor a = match a.a_impl,a.a_constructor with + | Some c,Some cf -> + if (has_class_flag c CExtern) || (has_class_field_flag cf CfPublic) then Yes else YesButPrivate + | _ -> + No in let ctor c = try diff --git a/src/core/globals.ml b/src/core/globals.ml index 3c474d8512d..9df8f008a50 100644 --- a/src/core/globals.ml +++ b/src/core/globals.ml @@ -24,6 +24,15 @@ type platform = | Eval | CustomTarget of string +type compiler_version = { + version: int; + major: int; + minor: int; + revision: int; + pre: string option; + extra: (string * string) option; +} + let version = 5000 let version_major = version / 1000 let version_minor = (version mod 1000) / 100 @@ -135,14 +144,17 @@ let s_version = let pre = Option.map_default (fun pre -> "-" ^ pre) "" version_pre in Printf.sprintf "%d.%d.%d%s" version_major version_minor version_revision pre -let s_version_full = - match Version.version_extra with +let s_version_full v = + match v.extra with | Some (_,build) -> s_version ^ "+" ^ build | _ -> s_version let patch_string_pos p s = { p with pmin = p.pmax - String.length s } +(* msg * backtrace *) +exception Ice of string * string + (** Terminates compiler process and prints user-friendly instructions about filing an issue. Usage: `die message __LOC__`, where `__LOC__` is a built-in ocaml constant @@ -163,10 +175,8 @@ let die ?p msg ml_loc = try snd (ExtString.String.split backtrace "\n") with ExtString.Invalid_string -> backtrace in - let ver = s_version_full - and os_type = if Sys.unix then "unix" else "windows" in - let s = Printf.sprintf "%s\nHaxe: %s; OS type: %s;\n%s\n%s" msg ver os_type ml_loc backtrace in - failwith s + let backtrace = ml_loc ^ "\n" ^ backtrace in + raise (Ice (msg,backtrace)) let dump_callstack () = print_endline (Printexc.raw_backtrace_to_string (Printexc.get_callstack 200)) diff --git a/src/core/inheritDoc.ml b/src/core/inheritDoc.ml index 25af705d208..3c96ae0f619 100644 --- a/src/core/inheritDoc.ml +++ b/src/core/inheritDoc.ml @@ -181,7 +181,7 @@ and get_target_doc ctx e_target = | TAbstract ({ a_impl = Some c }, _) -> let c_opt, cf = let field_name = - if field_name = "new" then "_new" + if field_name = "new" then "_hx_new" else field_name in get_class_field c field_name diff --git a/src/core/tFunctions.ml b/src/core/tFunctions.ml index 3073a4fcf98..b9b8cebf3c5 100644 --- a/src/core/tFunctions.ml +++ b/src/core/tFunctions.ml @@ -299,6 +299,7 @@ let null_abstract = { a_read = None; a_write = None; a_call = None; + a_constructor = None; a_extern = false; a_enum = false; } diff --git a/src/core/tOther.ml b/src/core/tOther.ml index d7331a625ac..551952cf41a 100644 --- a/src/core/tOther.ml +++ b/src/core/tOther.ml @@ -300,6 +300,7 @@ let mk_abstract m path pos name_pos = a_this = mk_mono(); a_read = None; a_write = None; + a_constructor = None; a_extern = false; a_enum = false; a_call = None; diff --git a/src/core/tPrinting.ml b/src/core/tPrinting.ml index 0d44f74ca2e..f009efb12bc 100644 --- a/src/core/tPrinting.ml +++ b/src/core/tPrinting.ml @@ -637,8 +637,11 @@ module Printer = struct let s_module_tainting_reason = function | CheckDisplayFile -> "check_display_file" + | DefineType -> "define_type" + | DefineModule -> "define_module" | ServerInvalidate -> "server/invalidate" | ServerInvalidateFiles -> "server_invalidate_files" + | ServerInvalidateModule -> "server_invalidate_module" let s_module_skip_reason reason = let rec loop stack = function diff --git a/src/core/tType.ml b/src/core/tType.ml index bf111332242..3dd5b7ea3e9 100644 --- a/src/core/tType.ml +++ b/src/core/tType.ml @@ -32,8 +32,11 @@ type module_check_policy = type module_tainting_reason = | CheckDisplayFile + | DefineType + | DefineModule | ServerInvalidate | ServerInvalidateFiles + | ServerInvalidateModule type module_skip_reason = | DependencyDirty of path * module_skip_reason @@ -382,6 +385,7 @@ and tabstract = { mutable a_read : tclass_field option; mutable a_write : tclass_field option; mutable a_call : tclass_field option; + mutable a_constructor : tclass_field option; mutable a_extern : bool; mutable a_enum : bool; } @@ -511,10 +515,11 @@ type flag_tclass_field = | CfUsed (* Marker for DCE *) | CfMaybeUsed (* Marker for DCE *) | CfNoLookup (* Field cannot be accessed by-name. *) + | CfAbstractConstructor (* Order has to match declaration for printing*) let flag_tclass_field_names = [ - "CfPublic";"CfStatic";"CfExtern";"CfFinal";"CfModifiesThis";"CfOverride";"CfAbstract";"CfOverload";"CfImpl";"CfEnum";"CfGeneric";"CfDefault";"CfPostProcessed";"CfUsed";"CfMaybeUsed";"CfNoLookup" + "CfPublic";"CfStatic";"CfExtern";"CfFinal";"CfModifiesThis";"CfOverride";"CfAbstract";"CfOverload";"CfImpl";"CfEnum";"CfGeneric";"CfDefault";"CfPostProcessed";"CfUsed";"CfMaybeUsed";"CfNoLookup";"CfAbstractConstructor" ] type flag_tenum = diff --git a/src/filters/localStatic.ml b/src/filters/localStatic.ml index 9c3cbb28bdb..d67bc64a13e 100644 --- a/src/filters/localStatic.ml +++ b/src/filters/localStatic.ml @@ -27,19 +27,49 @@ let promote_local_static lsctx run v eo = let no_local_in_static p = raise_typing_error "Accessing local variables in static initialization is not allowed" p in - let rec loop e = match e.eexpr with + let declared_vars = Hashtbl.create 0 in + let declare v = Hashtbl.add declared_vars v.v_id () in + let rec loop in_function in_loop e = + let loop' = loop in_function in_loop in + match e.eexpr with | TLocal v when has_var_flag v VStatic -> run e - | TFunction _ | TLocal _ -> + | TLocal v when not (Hashtbl.mem declared_vars v.v_id) -> no_local_in_static e.epos + | TVar(v,eo) -> + let eo = Option.map loop' eo in + declare v; + {e with eexpr = TVar(v,eo)} + | TFunction tf -> + let args = List.map (fun (v,eo) -> + declare v; + let eo = Option.map loop' eo in + (v,eo) + ) tf.tf_args in + let e1 = loop true in_loop tf.tf_expr in + {e with eexpr = TFunction {tf with tf_args = args;tf_expr = e1}} + | TTry(e1,catches) -> + let e1 = loop' e1 in + let catches = List.map (fun (v,e) -> + declare v; + let e = loop' e in + (v,e) + ) catches in + {e with eexpr = TTry(e1,catches)} + | TWhile(e1,e2,flag) -> + let e1 = loop' e1 in + let e2 = loop in_function true e2 in + {e with eexpr = TWhile(e1,e2,flag)} | TConst (TThis | TSuper) -> raise_typing_error "Accessing `this` in static initialization is not allowed" e.epos - | TReturn _ | TBreak | TContinue -> + | TReturn _ when not in_function -> + raise_typing_error "This kind of control flow in static initialization is not allowed" e.epos + | TBreak | TContinue when not in_loop -> raise_typing_error "This kind of control flow in static initialization is not allowed" e.epos | _ -> - map_expr loop e + map_expr loop' e in - let e = loop e in + let e = loop false false e in cf.cf_expr <- Some e end; lsctx.added_fields <- cf :: lsctx.added_fields; diff --git a/src/generators/gctx.ml b/src/generators/gctx.ml index 2785bcf1cef..220ce22818e 100644 --- a/src/generators/gctx.ml +++ b/src/generators/gctx.ml @@ -22,7 +22,7 @@ type t = { basic : basic_types; debug : bool; file : string; - version : int; + version : compiler_version; features : (string,bool) Hashtbl.t; modules : Type.module_def list; main : context_main; diff --git a/src/generators/gencpp.ml b/src/generators/gencpp.ml index ef0f58455ab..af67cf5e148 100644 --- a/src/generators/gencpp.ml +++ b/src/generators/gencpp.ml @@ -305,7 +305,7 @@ let generate_source ctx = match has_class_flag class_def CInterface with | true -> let (slots, iface) = CppRetyper.tcpp_interface_from_tclass ctx acc.slots class_def in - if native_gen then (has_boot_field class_def, NativeInterface iface, slots, acc.ids) else (has_boot_field class_def, ManagedInterface iface, acc.slots, acc.ids) + if native_gen then (has_boot_field class_def, NativeInterface iface, slots, acc.ids) else (has_boot_field class_def, ManagedInterface iface, slots, acc.ids) | false -> let (slots, ids, cls) = CppRetyper.tcpp_class_from_tclass ctx acc.ids acc.slots class_def [] in if native_gen then (has_tcpp_class_flag cls Boot, NativeClass cls, slots, ids) else (has_tcpp_class_flag cls Boot, ManagedClass cls, slots, ids) in diff --git a/src/generators/genhl.ml b/src/generators/genhl.ml index 622992fdada..728e17c1517 100644 --- a/src/generators/genhl.ml +++ b/src/generators/genhl.ml @@ -1086,18 +1086,14 @@ let rec eval_to ctx e (t:ttype) = let r = alloc_tmp ctx t in op ctx (OFloat (r,alloc_float ctx (Int32.to_float i))); r - (* this causes a bug with NG, to be reviewed later | TConst (TInt i), HF32 -> let r = alloc_tmp ctx t in - let bits = Int32.bits_of_float (Int32.to_float i) in - op ctx (OFloat (r,alloc_float ctx (Int64.float_of_bits (Int64.of_int32 bits)))); + op ctx (OFloat (r, alloc_float ctx (Int32.to_float i))); r | TConst (TFloat f), HF32 -> let r = alloc_tmp ctx t in - let bits = Int32.bits_of_float (float_of_string f) in - op ctx (OFloat (r,alloc_float ctx (Int64.float_of_bits (Int64.of_int32 bits)))); + op ctx (OFloat (r, alloc_float ctx (float_of_string f))); r - *) | _ -> let r = eval_expr ctx e in cast_to ctx r t e.epos @@ -1242,7 +1238,7 @@ and cast_to ?(force=false) ctx (r:reg) (t:ttype) p = let r = alloc_tmp ctx (HNull t) in op ctx (OToDyn (r,tmp)); r - | HNull ((HUI8 | HUI16 | HI32 | HI64) as it), (HF32 | HF64) -> + | HNull ((HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64) as it), (HF32 | HF64) -> let i = alloc_tmp ctx it in op ctx (OSafeCast (i,r)); let tmp = alloc_tmp ctx t in @@ -3427,7 +3423,7 @@ and make_fun ?gen_content ctx name fidx f cthis cparent = regs = DynArray.to_array ctx.m.mregs.arr; code = DynArray.to_array ctx.m.mops; debug = make_debug ctx ctx.m.mdebug; - assigns = Array.of_list (List.rev ctx.m.massign); + assigns = Array.of_list (List.sort (fun (_,p1) (_,p2) -> p1 - p2) (List.rev ctx.m.massign)); } in ctx.m <- old; Hashtbl.add ctx.defined_funs fidx (); @@ -4296,9 +4292,9 @@ let generate com = let gnames = Array.make (Array.length code.globals) "" in PMap.iter (fun n i -> gnames.(i) <- n) ctx.cglobals.map; if not (Gctx.defined com Define.SourceHeader) then begin - let version_major = com.version / 1000 in - let version_minor = (com.version mod 1000) / 100 in - let version_revision = (com.version mod 100) in + let version_major = com.version.major in + let version_minor = com.version.minor in + let version_revision = com.version.revision in Gctx.define_value com Define.SourceHeader (Printf.sprintf "Generated by HLC %d.%d.%d (HL v%d)" version_major version_minor version_revision code.version); end; Hl2c.write_c com com.file code gnames; diff --git a/src/generators/genswf.ml b/src/generators/genswf.ml index 8ab35db9221..d5fce627a81 100644 --- a/src/generators/genswf.ml +++ b/src/generators/genswf.ml @@ -207,7 +207,7 @@ let build_swc_catalog com types = let x = node "swc" ["xmlns","http://www.adobe.com/flash/swccatalog/9"] [ node "versions" [] [ node "swc" ["version","1.2"] []; - node "haxe" ["version",Printf.sprintf "%d.%.2d" (com.version/10000) (com.version mod 10000)] []; + node "haxe" ["version",Printf.sprintf "%d.%.2d" (com.Gctx.version.version/10000) (com.version.version mod 10000)] []; ]; node "features" [] [ node "feature-script-deps" [] []; diff --git a/src/generators/hl2c.ml b/src/generators/hl2c.ml index bb7b389b7d0..4c910bf51a3 100644 --- a/src/generators/hl2c.ml +++ b/src/generators/hl2c.ml @@ -1109,7 +1109,7 @@ let generate_function ctx f = let expr = (if fid = 0 then reg r else (match rtype r with | HObj o | HStruct o -> let name, t = resolve_field o (fid - 1) in - Printf.sprintf "%s->%s" (reg r) name + Printf.sprintf "&%s->%s" (reg r) name | _ -> Globals.die "" __LOC__ )) in @@ -1463,7 +1463,7 @@ let write_c com file (code:code) gnames = let bnames = Array.map (fun b -> "bytes$" ^ short_digest (Digest.to_hex (Digest.bytes b))) code.bytes in let ctx = { - version = com.Gctx.version; + version = com.Gctx.version.version; out = Buffer.create 1024; tabs = ""; hlcode = code; @@ -1556,18 +1556,28 @@ let write_c com file (code:code) gnames = sexpr "static struct _%s %s = {%s}" (ctype t) name (String.concat "," fields); ) code.constants; line ""; - line "void hl_init_roots() {"; + line "void hl_init_roots_constants() {"; block ctx; let is_const = Hashtbl.create 0 in Array.iter (fun (g,fields) -> sexpr "%s = &const_%s" gnames.(g) gnames.(g); Hashtbl.add is_const g true; ) code.constants; + unblock ctx; + line "}"; + line "void hl_init_roots_globals() {"; + block ctx; Array.iteri (fun i t -> if is_ptr t && not (Hashtbl.mem is_const i) then sexpr "hl_add_root((void**)&%s)" gnames.(i); ) code.globals; unblock ctx; line "}"; + line "void hl_init_roots() {"; + block ctx; + expr "hl_init_roots_constants()"; + expr "hl_init_roots_globals()"; + unblock ctx; + line "}"; let output_bytes f str = for i = 0 to String.length str - 1 do diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index 6424f147132..2a9d9af2b49 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -2238,7 +2238,7 @@ let macro_api ccom get_api = "get_configuration", vfun0 (fun() -> let com = ccom() in encode_obj [ - "version", vint com.version; + "version", vint com.version.version; "args", encode_array (List.map encode_string com.args); "debug", vbool com.debug; "verbose", vbool com.verbose; @@ -2323,6 +2323,18 @@ let macro_api ccom get_api = (get_api()).add_module_check_policy filter policy (decode_bool recursive); vnull ); + "server_invalidate_module", vfun1 (fun p -> + let mpath = parse_path (decode_string p) in + let com = ccom() in + (try + ignore(com.module_lut#find mpath); + let msg = "Cannot invalidate loaded module " ^ (s_type_path mpath) in + let pos = get_api_call_pos() in + compiler_error (Error.make_error (Custom msg) pos) + with Not_found -> + com.cs#taint_module mpath ServerInvalidateModule); + vnull + ); "server_invalidate_files", vfun1 (fun a -> let com = ccom() in let cs = com.cs in diff --git a/src/optimization/analyzer.ml b/src/optimization/analyzer.ml index 9c8d2e3d7fb..3da02893842 100644 --- a/src/optimization/analyzer.ml +++ b/src/optimization/analyzer.ml @@ -1009,11 +1009,17 @@ module Run = struct match e.eexpr with | TFunction tf -> let get_t t = if ExtType.is_void t then tf.tf_type else t in + let doesnt_like_complex_expressions = match actx.com.platform with + | Cpp | Hl | Jvm | Php | Flash -> + true + | _ -> + false + in let rec loop e = match e.eexpr with | TBlock [e1] -> loop e1 (* If there's a complex expression, keep the function and generate a call to it. *) - | TBlock _ | TIf _ | TSwitch _ | TTry _ when actx.com.platform = Cpp || actx.com.platform = Hl -> + | TBlock _ | TIf _ | TSwitch _ | TTry _ when doesnt_like_complex_expressions -> raise Exit (* Remove generated return *) | TReturn (Some e) -> diff --git a/src/optimization/inline.ml b/src/optimization/inline.ml index 1f67d0a33e1..d501ef12a1a 100644 --- a/src/optimization/inline.ml +++ b/src/optimization/inline.ml @@ -232,7 +232,7 @@ let inline_default_config cf t = let inline_config cls_opt cf call_args return_type = match cls_opt with | Some ({cl_kind = KAbstractImpl _}) when has_class_field_flag cf CfImpl -> - let t = if cf.cf_name = "_new" then + let t = if has_class_field_flag cf CfAbstractConstructor then return_type else if call_args = [] then raise_typing_error "Invalid abstract implementation function" cf.cf_pos @@ -580,7 +580,7 @@ class inline_state ctx ethis params cf f p = object(self) (match follow ethis.etype with | TAnon a -> (match !(a.a_status) with | ClassStatics {cl_kind = KAbstractImpl a } when has_class_field_flag cf CfImpl -> - if cf.cf_name <> "_new" then begin + if not (has_class_field_flag cf CfAbstractConstructor) then begin (* the first argument must unify with a_this for abstract implementation functions *) let tb = (TFun(("",false,map_type a.a_this) :: (List.tl tl),tret)) in unify_raise mt tb p diff --git a/src/typing/callUnification.ml b/src/typing/callUnification.ml index f0b08ee2b44..8dd2914c26e 100644 --- a/src/typing/callUnification.ml +++ b/src/typing/callUnification.ml @@ -232,7 +232,7 @@ let unify_field_call ctx fa el_typed el p inline = cfl,Some c,false,TClass.get_map_function c tl,(fun t -> t) | FHAbstract(a,tl,c) -> let map = apply_params a.a_params tl in - let tmap = if fa.fa_field.cf_name = "_new" (* TODO: BAD BAD BAD BAD *) then (fun t -> t) else (fun t -> map a.a_this) in + let tmap = if has_class_field_flag fa.fa_field CfAbstractConstructor then (fun t -> t) else (fun t -> map a.a_this) in expand_overloads fa.fa_field,Some c,true,map,tmap in let is_forced_inline = is_forced_inline co fa.fa_field in diff --git a/src/typing/calls.ml b/src/typing/calls.ml index f5b9a6a7b0f..2d6bbf0fb7d 100644 --- a/src/typing/calls.ml +++ b/src/typing/calls.ml @@ -54,7 +54,7 @@ let make_call ctx e params t ?(force_inline=false) p = (match cl, ctx.c.curclass.cl_kind, params with | Some c, KAbstractImpl _, { eexpr = TLocal { v_meta = v_meta } } :: _ when c == ctx.c.curclass -> if - f.cf_name <> "_new" + not (has_class_field_flag f CfAbstractConstructor) && has_meta Meta.This v_meta && has_class_field_flag f CfModifiesThis then diff --git a/src/typing/fieldAccess.ml b/src/typing/fieldAccess.ml index 2f47ebda5d8..a6c86ac4143 100644 --- a/src/typing/fieldAccess.ml +++ b/src/typing/fieldAccess.ml @@ -163,8 +163,15 @@ let get_constructor_access c tl p = | _ -> c, tl in let cf, fh = match c.cl_kind with - | KAbstractImpl a -> PMap.find "_new" c.cl_statics, FHAbstract(a,tl,c) - | _ -> Type.get_constructor c, FHInstance(c,tl) + | KAbstractImpl a -> + begin match a.a_constructor with + | None -> + raise Not_found + | Some cf -> + cf,FHAbstract(a,tl,c) + end + | _ -> + Type.get_constructor c, FHInstance(c,tl) in create e_static cf fh false p with Not_found -> diff --git a/src/typing/macroContext.ml b/src/typing/macroContext.ml index e9faaed82c4..b272ef47d67 100644 --- a/src/typing/macroContext.ml +++ b/src/typing/macroContext.ml @@ -464,13 +464,21 @@ let make_macro_api ctx mctx p = | _ -> false in let add is_macro ctx = - let mdep = Option.map_default (fun s -> TypeloadModule.load_module ~origin:MDepFromMacro ctx (parse_path s) pos) ctx.m.curmod mdep in - let mnew = TypeloadModule.type_module ctx.com ctx.g ~dont_check_path:(has_native_meta) mpath (ctx.com.file_keys#generate_virtual mpath ctx.com.compilation_step) [tdef,pos] pos in - mnew.m_extra.m_kind <- if is_macro then MMacro else MFake; - add_dependency mnew mdep MDepFromMacro; - add_dependency mdep mnew MDepFromMacroDefine; - ctx.com.module_nonexistent_lut#clear; - in + try + let m = ctx.com.module_lut#find mpath in + let pos = { pfile = (Path.UniqueKey.lazy_path m.m_extra.m_file); pmin = 0; pmax = 0 } in + Interp.compiler_error (make_error ~sub:[ + make_error ~depth:1 (Custom "Previously defined here") pos + ] (Custom (Printf.sprintf "Cannot redefine module %s" (s_type_path mpath))) p); + with Not_found -> + ctx.com.cs#taint_module mpath DefineType; + let mdep = Option.map_default (fun s -> TypeloadModule.load_module ~origin:MDepFromMacro ctx (parse_path s) pos) ctx.m.curmod mdep in + let mnew = TypeloadModule.type_module ctx.com ctx.g ~dont_check_path:(has_native_meta) mpath (ctx.com.file_keys#generate_virtual mpath ctx.com.compilation_step) [tdef,pos] pos in + mnew.m_extra.m_kind <- if is_macro then MMacro else MFake; + add_dependency mnew mdep MDepFromMacro; + add_dependency mdep mnew MDepFromMacroDefine; + ctx.com.module_nonexistent_lut#clear; + in add false ctx; (* if we are adding a class which has a macro field, we also have to add it to the macro context (issue #1497) *) if not ctx.com.is_macro_context then match tdef with @@ -496,12 +504,13 @@ let make_macro_api ctx mctx p = let m = ctx.com.module_lut#find mpath in if m != ctx.m.curmod then begin let pos = { pfile = (Path.UniqueKey.lazy_path m.m_extra.m_file); pmin = 0; pmax = 0 } in - raise_typing_error_ext (make_error ~sub:[ + Interp.compiler_error (make_error ~sub:[ make_error ~depth:1 (Custom "Previously defined here") pos ] (Custom (Printf.sprintf "Cannot redefine module %s" (s_type_path mpath))) p); end else - ignore(TypeloadModule.type_types_into_module ctx.com ctx.g m types pos) + ignore(TypeloadModule.type_types_into_module ctx.com ctx.g ctx.m.curmod types pos) with Not_found -> + ctx.com.cs#taint_module mpath DefineModule; let mnew = TypeloadModule.type_module ctx.com ctx.g mpath (ctx.com.file_keys#generate_virtual mpath ctx.com.compilation_step) types pos in mnew.m_extra.m_kind <- MFake; add_dependency mnew ctx.m.curmod MDepFromMacro; diff --git a/src/typing/operators.ml b/src/typing/operators.ml index e8a54066e14..b893dd290aa 100644 --- a/src/typing/operators.ml +++ b/src/typing/operators.ml @@ -650,42 +650,38 @@ let process_lhs_expr ctx name e_lhs = let e = vr#get_expr name e_lhs in e,vr -let type_assign_op ctx op e1 e2 with_type p = - let field_rhs_by_name op name ev with_type = +type 'a assign_op_api = { + akno_fallback : unit -> texpr; + type_rhs : texpr -> expr -> 'a; + to_texpr : value_reference -> 'a -> (texpr -> texpr) -> texpr; + generate : value_reference -> texpr -> texpr -> texpr; + assign : value_reference -> texpr -> 'a -> texpr; +} + +let handle_assign_op ctx api e1 e2 with_type p = + let field_rhs_by_name name ev with_type = let access_get = type_field_default_cfg ctx ev name p MGet with_type in let e_get = acc_get ctx access_get in - e_get.etype,type_binop2 ctx op e_get e2 true WithType.value p + e_get,api.type_rhs e_get e2 in - let field_rhs op cf ev = - field_rhs_by_name op cf.cf_name ev (WithType.with_type cf.cf_type) + let field_rhs cf ev = + field_rhs_by_name cf.cf_name ev (WithType.with_type cf.cf_type) in - let assign vr e r_rhs = - if BinopResult.needs_assign r_rhs then check_assign ctx e; + let set vr fa e_lhs r_rhs el = let assign e_rhs = - let e_rhs = AbstractCast.cast_or_unify ctx e.etype e_rhs p in - match e_rhs.eexpr with - | TBinop(op',e1',e2') when op = op' && Texpr.equal e e1' -> - mk (TBinop(OpAssignOp op',e1',e2')) e.etype p - | _ -> - mk (TBinop(OpAssign,e,e_rhs)) e.etype p - in - let e = BinopResult.to_texpr vr r_rhs assign in - vr#to_texpr e - in - let set vr fa t_lhs r_rhs el = - let assign e_rhs = - let e_rhs = AbstractCast.cast_or_unify ctx t_lhs e_rhs p in + let e_rhs = AbstractCast.cast_or_unify ctx e_lhs.etype e_rhs p in let dispatcher = new call_dispatcher ctx (MSet (Some e2)) with_type p in dispatcher#accessor_call fa (el @ [e_rhs]) []; in - let e = BinopResult.to_texpr vr r_rhs assign in - vr#to_texpr e + let e = api.to_texpr vr r_rhs assign in + api.generate vr e_lhs e in let rec loop acc = match acc with | AKNo(_,p) -> (* try abstract operator overloading *) begin try - type_non_assign_op ctx op e1 e2 true true with_type p + api.akno_fallback(); + (* type_non_assign_op ctx op e1 e2 true true with_type p *) with Not_found -> raise_typing_error "This expression cannot be accessed for writing" p end @@ -695,23 +691,23 @@ let type_assign_op ctx op e1 e2 with_type p = raise_typing_error "Invalid operation" p | AKExpr e -> let e,vr = process_lhs_expr ctx "lhs" e in - let e_rhs = type_binop2 ctx op e e2 true WithType.value p in - assign vr e e_rhs + let e_rhs = api.type_rhs e e2 in + api.assign vr e e_rhs | AKField fa -> let vr = new value_reference ctx in let ef = vr#get_expr_part "fh" fa.fa_on in - let _,e_rhs = field_rhs op fa.fa_field ef in + let _,e_rhs = field_rhs fa.fa_field ef in let e_lhs = FieldAccess.get_field_expr {fa with fa_on = ef} FWrite in - assign vr e_lhs e_rhs + api.assign vr e_lhs e_rhs | AKAccessor fa -> let vr = new value_reference ctx in let ef = vr#get_expr_part "fh" fa.fa_on in - let t_lhs,e_rhs = field_rhs op fa.fa_field ef in - set vr {fa with fa_on = ef} t_lhs e_rhs [] + let e_lhs,e_rhs = field_rhs fa.fa_field ef in + set vr {fa with fa_on = ef} e_lhs e_rhs [] | AKUsingAccessor sea -> let fa = sea.se_access in let ef,vr = process_lhs_expr ctx "fh" sea.se_this in - let t_lhs,e_rhs = field_rhs op fa.fa_field ef in + let t_lhs,e_rhs = field_rhs fa.fa_field ef in set vr sea.se_access t_lhs e_rhs [ef] | AKAccess(a,tl,c,ebase,ekey) -> let cf_get,tf_get,r_get,ekey = AbstractCast.find_array_read_access ctx a tl ekey p in @@ -724,16 +720,16 @@ let type_assign_op ctx op e1 e2 with_type p = in let ebase = maybe_bind_to_temp "base" ebase in let ekey = maybe_bind_to_temp "key" ekey in - let eget = mk_array_get_call ctx (cf_get,tf_get,r_get,ekey) c ebase p in - let eget = type_binop2 ctx op eget e2 true WithType.value p in - let eget = BinopResult.to_texpr vr eget (fun e -> e) in + let eread = mk_array_get_call ctx (cf_get,tf_get,r_get,ekey) c ebase p in + let eget = api.type_rhs eread e2 in + let eget = api.to_texpr vr eget (fun e -> e) in unify ctx eget.etype r_get p; let cf_set,tf_set,r_set,ekey,eget = AbstractCast.find_array_write_access ctx a tl ekey eget p in let et = type_module_type ctx (TClassDecl c) p in let e = match cf_set.cf_expr,cf_get.cf_expr with - | None,None -> + (* | None,None -> let ea = mk (TArray(ebase,ekey)) r_get p in - mk (TBinop(OpAssignOp op,ea,type_expr ctx e2 (WithType.with_type r_get))) r_set p + mk (TBinop(OpAssignOp op,ea,type_expr ctx e2 (WithType.with_type r_get))) r_set p *) | Some _,Some _ -> let ef_set = mk (TField(et,(FStatic(c,cf_set)))) tf_set p in let el = [make_call ctx ef_set [ebase;ekey;eget] r_set p] in @@ -745,20 +741,89 @@ let type_assign_op ctx op e1 e2 with_type p = raise_typing_error "Invalid array access getter/setter combination" p in save(); - vr#to_texpr e + api.generate vr eread e | AKResolve(sea,name) -> let e,vr = process_lhs_expr ctx "fh" sea.se_this in - let t_lhs,r_rhs = field_rhs_by_name op name e WithType.value in + let e_lhs,r_rhs = field_rhs_by_name name e WithType.value in let assign e_rhs = let e_name = Texpr.Builder.make_string ctx.t name null_pos in (new call_dispatcher ctx (MCall [e2]) with_type p)#field_call sea.se_access [sea.se_this;e_name;e_rhs] [] in - let e = BinopResult.to_texpr vr r_rhs assign in - vr#to_texpr e + let e = api.to_texpr vr r_rhs assign in + api.generate vr e_lhs e in let with_type = with_type_or_value with_type in loop (!type_access_ref ctx (fst e1) (snd e1) (MSet (Some e2)) with_type) +let type_assign_op ctx op e1 e2 with_type p = + let api = { + akno_fallback = (fun () -> + type_non_assign_op ctx op e1 e2 true true with_type p + ); + type_rhs = (fun e_lhs e2 -> + type_binop2 ctx op e_lhs e2 true WithType.value p + ); + to_texpr = (fun vr br assign -> + BinopResult.to_texpr vr br assign + ); + generate = (fun vr e_lhs e -> + vr#to_texpr e + ); + assign = (fun vr e_lhs r_rhs -> + let assign e_rhs = + if BinopResult.needs_assign r_rhs then check_assign ctx e_lhs; + let e_rhs = AbstractCast.cast_or_unify ctx e_lhs.etype e_rhs p in + match e_rhs.eexpr with + | TBinop(op',e1',e2') when op = op' && Texpr.equal e_lhs e1' -> + mk (TBinop(OpAssignOp op',e1',e2')) e_lhs.etype p + | _ -> + mk (TBinop(OpAssign,e_lhs,e_rhs)) e_lhs.etype p + in + let e = BinopResult.to_texpr vr r_rhs assign in + vr#to_texpr e + ) + } in + handle_assign_op ctx api e1 e2 with_type p + +let type_op_null_coal_assign ctx e1 e2 with_type p = + let hack = ref (fun e -> e) in + let gen vr e1 t2 e_assign = + let e1,eelse,tif = match with_type with + | WithType.NoValue -> + e1,None,ctx.t.tvoid + | _ -> + let e1 = vr#as_var "tmp" e1 in + (* The t2 is here so that `anything ??= 2` doesn't become Null *) + e1,Some e1,t2 + in + let e_null = Texpr.Builder.make_null e1.etype e1.epos in + let e_null = Texpr.Builder.binop OpEq e1 e_null ctx.t.tbool e1.epos in + let e = mk (TIf(e_null,e_assign,eelse)) tif e1.epos in + vr#to_texpr e + in + let api = { + akno_fallback = (fun () -> + raise Not_found + ); + type_rhs = (fun e_lhs e2 -> + type_expr ctx e2 (WithType.WithType(e_lhs.etype,None)) + ); + to_texpr = (fun vr e assign -> + hack := assign; + e + ); + generate = (fun vr e_lhs e -> + gen vr e_lhs e.etype (!hack e) + ); + assign = (fun vr e_lhs e_rhs -> + let assign e_rhs = + let e_rhs = AbstractCast.cast_or_unify ctx e_lhs.etype e_rhs p in + mk (TBinop(OpAssign,e_lhs,e_rhs)) e_lhs.etype p + in + gen vr e_lhs e_rhs.etype (assign e_rhs) + ) + } in + handle_assign_op ctx api e1 e2 with_type p let type_binop ctx op e1 e2 is_assign_op with_type p = match op with @@ -919,6 +984,9 @@ let type_unop ctx op flag e with_type p = | AKAccess(a,tl,c,ebase,ekey) -> begin try (match op with Increment | Decrement -> () | _ -> raise Not_found); + let v_base = alloc_var VGenerated "tmp" ebase.etype ebase.epos in + let evar_base = mk (TVar(v_base, Some ebase)) ctx.com.basic.tvoid ebase.epos in + let ebase = mk (TLocal v_base) ebase.etype ebase.epos in let v_key = alloc_var VGenerated "tmp" ekey.etype ekey.epos in let evar_key = mk (TVar(v_key,Some ekey)) ctx.com.basic.tvoid ekey.epos in let ekey = mk (TLocal v_key) ekey.etype ekey.epos in @@ -932,7 +1000,7 @@ let type_unop ctx op flag e with_type p = let e_op = mk (TBinop((if op = Increment then OpAdd else OpSub),ev_get,e_one)) ev_get.etype p in (* set *) let e_set = mk_array_set_call ctx (AbstractCast.find_array_write_access_raise ctx a tl ekey e_op p) c ebase p in - let el = evar_key :: evar_get :: e_set :: (if flag = Postfix then [ev_get] else []) in + let el = evar_base :: evar_key :: evar_get :: e_set :: (if flag = Postfix then [ev_get] else []) in mk (TBlock el) e_set.etype p with Not_found -> let e = mk_array_get_call ctx (AbstractCast.find_array_read_access ctx a tl ekey p) c ebase p in diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index c8746b34081..483325d770a 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -54,6 +54,7 @@ type field_init_ctx = { is_abstract : bool; is_macro : bool; is_abstract_member : bool; + is_abstract_constructor : bool; is_display_field : bool; is_field_debug : bool; is_generic : bool; @@ -271,7 +272,7 @@ let transform_abstract_field com this_t a_t a f = ); f_type = Some a_t; } in - { f with cff_name = "_new",pos f.cff_name; cff_kind = FFun fu; cff_meta = meta } + { f with cff_name = "_hx_new",pos f.cff_name; cff_kind = FFun fu; cff_meta = meta } | FFun fu when not stat -> if Meta.has Meta.From f.cff_meta then raise_typing_error "@:from cast functions must be static" f.cff_pos; { f with cff_kind = FFun fu } @@ -529,6 +530,7 @@ let create_field_context ctx cctx cff is_display_file display_modifier = is_field_debug = cctx.is_class_debug || Meta.has (Meta.Custom ":debug.typeload") cff.cff_meta; display_modifier = display_modifier; is_abstract_member = is_abstract_member; + is_abstract_constructor = is_abstract_member && fst cff.cff_name = "_hx_new"; is_generic = Meta.has Meta.Generic cff.cff_meta; field_kind = field_kind; do_bind = (((not ((has_class_flag c CExtern) || !is_extern) || is_inline) && not is_abstract && not (has_class_flag c CInterface)) || field_kind = CfrInit); @@ -631,7 +633,7 @@ let check_field_display ctx fctx c cf = let scope, cf = match c.cl_kind with | KAbstractImpl _ -> if has_class_field_flag cf CfImpl then - (if cf.cf_name = "_new" then + (if fctx.is_abstract_constructor then CFSConstructor, {cf with cf_name = "new"} else CFSMember, cf) @@ -927,6 +929,7 @@ let create_variable (ctx,cctx,fctx) c f cf t eo p = cf let check_abstract (ctx,cctx,fctx) a c cf fd t ret p = + if fctx.is_abstract_constructor && a.a_constructor = None (* TODO: this is pretty dumb, it deals with the overload case *) then a.a_constructor <- Some cf; let m = mk_mono() in let ta = TAbstract(a,List.map (fun _ -> mk_mono()) a.a_params) in let tthis = if fctx.is_abstract_member || Meta.has Meta.To cf.cf_meta then monomorphs a.a_params a.a_this else a.a_this in @@ -978,10 +981,11 @@ let check_abstract (ctx,cctx,fctx) a c cf fd t ret p = cf.cf_meta <- (Meta.MultiType,[],null_pos) :: cf.cf_meta; let r = make_lazy ctx.g t (fun () -> let args = if is_multitype_cast then begin - let ctor = try - PMap.find "_new" c.cl_statics - with Not_found -> - raise_typing_error "Constructor of multi-type abstract must be defined before the individual @:to-functions are" cf.cf_pos + let ctor = match a.a_constructor with + | Some cf -> + cf + | None -> + raise_typing_error "Constructor of multi-type abstract must be defined before the individual @:to-functions are" cf.cf_pos in (* delay ctx PFinal (fun () -> unify ctx m tthis f.cff_pos); *) let args = match follow (monomorphs a.a_params ctor.cf_type) with @@ -1087,7 +1091,7 @@ let check_abstract (ctx,cctx,fctx) a c cf fd t ret p = | _ -> (); in List.iter check_meta cf.cf_meta; - if cf.cf_name = "_new" && Meta.has Meta.MultiType a.a_meta then fctx.do_bind <- false; + if fctx.is_abstract_constructor && Meta.has Meta.MultiType a.a_meta then fctx.do_bind <- false; if fd.f_expr = None then begin if fctx.is_inline then missing_expression ctx.com fctx "Inline functions must have an expression" cf.cf_pos; if fd.f_type = None then raise_typing_error ("Functions without expressions must have an explicit return type") cf.cf_pos; @@ -1160,7 +1164,7 @@ let setup_args_ret ctx cctx fctx name fd p = maybe_use_property_type fd.f_type (fun () -> match Lazy.force mk with MKGetter | MKSetter -> true | _ -> false) def end in let abstract_this = match cctx.abstract with - | Some a when fctx.is_abstract_member && name <> "_new" (* TODO: this sucks *) && not fctx.is_macro -> + | Some a when fctx.is_abstract_member && not fctx.is_abstract_constructor && not fctx.is_macro -> Some a.a_this | _ -> None @@ -1271,6 +1275,7 @@ let create_method (ctx,cctx,fctx) c f cf fd p = add_class_field_flag cf CfAbstract; end; if fctx.is_abstract_member then add_class_field_flag cf CfImpl; + if fctx.is_abstract_constructor then add_class_field_flag cf CfAbstractConstructor; if fctx.is_generic then add_class_field_flag cf CfGeneric; begin match fctx.default with | Some p -> diff --git a/src/typing/typeloadModule.ml b/src/typing/typeloadModule.ml index 6db894ffcbd..d523b3485f4 100644 --- a/src/typing/typeloadModule.ml +++ b/src/typing/typeloadModule.ml @@ -197,6 +197,7 @@ module ModuleLevel = struct a_read = None; a_write = None; a_call = None; + a_constructor = None; a_extern = List.mem AbExtern d.d_flags; a_enum = List.mem AbEnum d.d_flags || p_enum_meta <> None; } in diff --git a/src/typing/typer.ml b/src/typing/typer.ml index af4a6cb994e..d40c70c2cbd 100644 --- a/src/typing/typer.ml +++ b/src/typing/typer.ml @@ -1479,7 +1479,7 @@ and type_array_comprehension ctx e with_type p = ]) v.v_type p and type_return ?(implicit=false) ctx e with_type p = - let is_abstract_ctor = ctx.e.curfun = FunMemberAbstract && ctx.f.curfield.cf_name = "_new" in + let is_abstract_ctor = ctx.e.curfun = FunMemberAbstract && has_class_field_flag ctx.f.curfield CfAbstractConstructor in match e with | None when is_abstract_ctor -> let e_cast = mk (TCast(get_this ctx p,None)) ctx.e.ret p in @@ -1872,15 +1872,21 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = | _ -> follow_null tmin in let e1_null_t = if is_nullable e1.etype then e1.etype else ctx.t.tnull e1.etype in - let e1 = vr#as_var (Option.default "tmp" (WithType.get_expected_name with_type)) {e1 with etype = e1_null_t} in + let var_name = match WithType.get_expected_name with_type with + | None + (* TODO: why does this happen? *) + | Some "" -> + "tmp" + | Some name -> + name + in + let e1 = vr#as_var var_name {e1 with etype = e1_null_t} in let e_null = Builder.make_null e1_null_t e1.epos in let e_cond = mk (TBinop(OpNotEq,e1,e_null)) ctx.t.tbool e1.epos in let e_if = mk (TIf(e_cond,cast e1,Some e2)) iftype p in vr#to_texpr e_if | EBinop (OpAssignOp OpNullCoal,e1,e2) -> - let e_cond = EBinop(OpNotEq,e1,(EConst(Ident "null"), p)) in - let e_if = EIf ((e_cond, p),e1,Some e2) in - type_assign ctx e1 (e_if, p) with_type p + type_op_null_coal_assign ctx e1 e2 with_type p | EBinop (op,e1,e2) -> type_binop ctx op e1 e2 false with_type p | EBlock [] when (match with_type with diff --git a/src/typing/typerBase.ml b/src/typing/typerBase.ml index 0fc1ee39603..6adf51fce69 100644 --- a/src/typing/typerBase.ml +++ b/src/typing/typerBase.ml @@ -188,7 +188,7 @@ let assign_to_this_is_allowed ctx = | KAbstractImpl _ -> (match ctx.f.curfield.cf_kind with | Method MethInline -> true - | Method _ when ctx.f.curfield.cf_name = "_new" -> true + | Method _ when has_class_field_flag ctx.f.curfield CfAbstractConstructor -> true | _ -> false ) | _ -> false @@ -269,7 +269,7 @@ let rec s_access_kind acc = | AKUsingField sea -> Printf.sprintf "AKUsingField(%s)" (s_static_extension_access sea) | AKUsingAccessor sea -> Printf.sprintf "AKUsingAccessor(%s)" (s_static_extension_access sea) | AKAccess(a,tl,c,e1,e2) -> Printf.sprintf "AKAccess(%s, [%s], %s, %s, %s)" (s_type_path a.a_path) (String.concat ", " (List.map st tl)) (s_type_path c.cl_path) (se e1) (se e2) - | AKResolve(_) -> "" + | AKResolve(sea,name) -> Printf.sprintf "AKResolve(%s, %s)" (s_static_extension_access sea) name and s_safe_nav_access sn = let st = s_type (print_context()) in diff --git a/src/typing/typerDisplay.ml b/src/typing/typerDisplay.ml index 64d70a4b1ba..96f950da141 100644 --- a/src/typing/typerDisplay.ml +++ b/src/typing/typerDisplay.ml @@ -619,7 +619,7 @@ let handle_display ctx e_ast dk mode with_type = | TClassDecl c -> has_constructor c | TAbstractDecl a -> (match Abstract.follow_with_forward_ctor ~build:true (TAbstract(a,extract_param_types a.a_params)) with | TInst(c,_) -> has_constructor c - | TAbstract({a_impl = Some c},_) -> PMap.mem "_new" c.cl_statics + | TAbstract(a,_) -> a.a_constructor <> None | _ -> false) | _ -> false end diff --git a/std/haxe/io/Bytes.hx b/std/haxe/io/Bytes.hx index fdfdf4290fe..ae197b33166 100644 --- a/std/haxe/io/Bytes.hx +++ b/std/haxe/io/Bytes.hx @@ -404,8 +404,6 @@ class Bytes { interpreted with the given `encoding` (UTF-8 by default). **/ public function getString(pos:Int, len:Int, ?encoding:Encoding):String { - if (encoding == null) - encoding == UTF8; #if !neko if (pos < 0 || len < 0 || pos + len > length) throw Error.OutsideBounds; diff --git a/std/haxe/macro/CompilationServer.hx b/std/haxe/macro/CompilationServer.hx index 921611cf386..18cfd589c64 100644 --- a/std/haxe/macro/CompilationServer.hx +++ b/std/haxe/macro/CompilationServer.hx @@ -69,6 +69,17 @@ class CompilationServer { }); } + /** + Invalidates a module, removing it from the cache. + + If the module has already been loaded in current context, a + `haxe.macro.Expr.Error` compiler error will be raised which can be + caught using `try ... catch`. + **/ + static public function invalidateModule(path:String) { + @:privateAccess Compiler.load("server_invalidate_module", 1)(path); + } + /** Invalidates all files given in `filePaths`, removing them from the cache. **/ diff --git a/std/haxe/macro/Compiler.hx b/std/haxe/macro/Compiler.hx index b574d1b3362..e4d7e085269 100644 --- a/std/haxe/macro/Compiler.hx +++ b/std/haxe/macro/Compiler.hx @@ -51,14 +51,12 @@ class Compiler { return macro $v{haxe.macro.Context.definedValue(key)}; } - #if (neko || (macro && hl) || (macro && eval)) + #if macro static var ident = ~/^[A-Za-z_][A-Za-z0-9_]*$/; static var path = ~/^[A-Za-z_][A-Za-z0-9_.]*$/; public static function allowPackage(v:String) { - #if (neko || eval) load("allow_package", 1)(v); - #end } /** @@ -67,48 +65,30 @@ class Compiler { Usage of this function outside of initialization macros is deprecated and may cause compilation server issues. **/ public static function define(flag:String, ?value:String) { - #if (neko || eval) Context.assertInitMacro(); load("define", 2)(flag, value); - #end } - #if (!neko && !eval) - private static function addGlobalMetadataImpl(pathFilter:String, meta:String, recursive:Bool, toTypes:Bool, toFields:Bool) {} - #end - /** Add a class path where ".hx" source files or packages (sub-directories) can be found. Usage of this function outside of initialization macros is deprecated and may cause compilation server issues. **/ public static function addClassPath(path:String) { - #if (neko || eval) Context.assertInitMacro(); load("add_class_path", 1)(path); - #end } public static function getOutput():String { - #if (neko || eval) return load("get_output", 0)(); - #else - return null; - #end } public static function setOutput(fileOrDir:String) { - #if (neko || eval) load("set_output", 1)(fileOrDir); - #end } public static function getDisplayPos():Null<{file:String, pos:Int}> { - #if (neko || eval) return load("get_display_pos", 0)(); - #else - return null; - #end } /** @@ -117,11 +97,7 @@ class Compiler { Usage of this function outside a macro context returns `null`. **/ public static function getConfiguration():Null { - #if (neko || eval) return load("get_configuration", 0)(); - #else - return null; - #end } /** @@ -130,9 +106,7 @@ class Compiler { Usage of this function outside a macro context does nothing. **/ public static function setPlatformConfiguration(config:PlatformConfig):Void { - #if (neko || eval) load("set_platform_configuration", 1)(config); - #end } /** @@ -141,10 +115,8 @@ class Compiler { Usage of this function outside of initialization macros is deprecated and may cause compilation server issues. **/ public static function addNativeLib(name:String) { - #if (neko || eval) Context.assertInitMacro(); load("add_native_lib", 1)(name); - #end } /** @@ -367,11 +339,7 @@ class Compiler { through `Context.getType`. **/ public static function addGlobalMetadata(pathFilter:String, meta:String, ?recursive:Bool = true, ?toTypes:Bool = true, ?toFields:Bool = false) { - #if (neko || eval) load("add_global_metadata_impl", 5)(pathFilter, meta, recursive, toTypes, toFields); - #else - addGlobalMetadataImpl(pathFilter, meta, recursive, toTypes, toFields); - #end } @:deprecated @@ -406,80 +374,39 @@ class Compiler { Register a custom metadata for documentation and completion purposes **/ public static function registerCustomMetadata(meta:MetadataDescription, ?source:String):Void { - #if (neko || eval) load("register_metadata_impl", 2)(meta, source); - #end } /** Register a custom define for documentation purposes **/ public static function registerCustomDefine(define:DefineDescription, ?source:String):Void { - #if (neko || eval) load("register_define_impl", 2)(define, source); - #end } /** Change the default JS output by using a custom generator callback **/ public static function setCustomJSGenerator(callb:JSGenApi->Void) { - #if (neko || eval) load("set_custom_js_generator", 1)(callb); - #end } - #if (neko || eval) static inline function load(f, nargs):Dynamic { return @:privateAccess Context.load(f, nargs); } - #end /** Clears cached results of file lookups **/ public static function flushDiskCache() { - #if (neko || eval) load("flush_disk_cache", 0)(); - #end } - #end - - #if (js || lua || macro) - /** - Embed a JavaScript or Lua file at compile time (can be called by `--macro` or within an `__init__` method). - **/ - public static #if !macro macro #end function includeFile(file:String, position:IncludePosition = Top) { - return switch ((position : String).toLowerCase()) { - case Inline: - if (Context.getLocalModule() == "") - Context.error("Cannot use inline mode when includeFile is called by `--macro`", Context.currentPos()); - - var f = try sys.io.File.getContent(Context.resolvePath(file)) catch (e:Dynamic) Context.error(Std.string(e), Context.currentPos()); - var p = Context.currentPos(); - if (Context.defined("js")) { - macro @:pos(p) js.Syntax.plainCode($v{f}); - } else { - macro @:pos(p) untyped __lua__($v{f}); - } - case Top | Closure: - @:privateAccess Context.includeFile(file, position); - macro {}; - case _: - Context.error("unknown includeFile position: " + position, Context.currentPos()); - } - } - #end /** Gets the current hxb writer configuration, if any. **/ static public function getHxbWriterConfiguration():Null { - #if macro return load("get_hxb_writer_config", 0)(); - #else - return null; - #end } /** @@ -500,10 +427,35 @@ class Compiler { @see haxe.hxb.WriterConfig **/ static public function setHxbWriterConfiguration(config:Null) { - #if macro load("set_hxb_writer_config", 1)(config); - #end } + #end + + #if (js || lua || macro) + /** + Embed a JavaScript or Lua file at compile time (can be called by `--macro` or within an `__init__` method). + **/ + public static #if !macro macro #end function includeFile(file:String, position:IncludePosition = Top) { + return switch ((position : String).toLowerCase()) { + case Inline: + if (Context.getLocalModule() == "") + Context.error("Cannot use inline mode when includeFile is called by `--macro`", Context.currentPos()); + + var f = try sys.io.File.getContent(Context.resolvePath(file)) catch (e:Dynamic) Context.error(Std.string(e), Context.currentPos()); + var p = Context.currentPos(); + if (Context.defined("js")) { + macro @:pos(p) js.Syntax.plainCode($v{f}); + } else { + macro @:pos(p) untyped __lua__($v{f}); + } + case Top | Closure: + @:privateAccess Context.includeFile(file, position); + macro {}; + case _: + Context.error("unknown includeFile position: " + position, Context.currentPos()); + } + } + #end } enum abstract IncludePosition(String) from String to String { diff --git a/std/haxe/macro/Context.hx b/std/haxe/macro/Context.hx index 5343c3e1f41..1c00929a759 100644 --- a/std/haxe/macro/Context.hx +++ b/std/haxe/macro/Context.hx @@ -42,7 +42,7 @@ enum Message { - `haxe.macro.TypeTools` **/ class Context { - #if (neko || eval || display) + #if eval /** Displays a compilation error `msg` at the given `Position` `pos` and aborts the current macro call. @@ -663,6 +663,10 @@ class Context { /** Defines a new type from `TypeDefinition` `t`. + If a matching module has already been loaded in current context, a + `haxe.macro.Expr.Error` compiler error will be raised which can be + caught using `try ... catch`. + If `moduleDependency` is given and is not `null`, it should contain a module path that will be used as a dependency for the newly defined module instead of the current module. @@ -695,6 +699,10 @@ class Context { Defines a new module as `modulePath` with several `TypeDefinition` `types`. This is analogous to defining a .hx file. + If a matching module has already been loaded in current context, a + `haxe.macro.Expr.Error` compiler error will be raised which can be + caught using `try ... catch`. + The individual `types` can reference each other and any identifier respects the `imports` and `usings` as usual, expect that imports are not allowed to have `.*` wildcards or `as s` shorthands. @@ -871,13 +879,7 @@ class Context { @:allow(haxe.macro.TypedExprTools) @:allow(haxe.macro.PositionTools) static function load(f:String, nargs:Int):Dynamic { - #if neko - return neko.Lib.load("macro", f, nargs); - #elseif eval return eval.vm.Context.callMacroApi(f); - #else - return Reflect.makeVarArgs(function(_) return throw "Can't be called outside of macro"); - #end } private static function includeFile(file:String, position:String) { diff --git a/std/haxe/macro/MacroStringTools.hx b/std/haxe/macro/MacroStringTools.hx index 4922a6f0d9b..29a9a05b5c4 100644 --- a/std/haxe/macro/MacroStringTools.hx +++ b/std/haxe/macro/MacroStringTools.hx @@ -37,9 +37,7 @@ class MacroStringTools { elements. **/ static public function formatString(s:String, pos:Position):Expr { - #if (neko || eval) return Context.load("format_string", 2)(s, pos); - #end } /** diff --git a/std/haxe/macro/TypeTools.hx b/std/haxe/macro/TypeTools.hx index 38949bf172f..fba26955338 100644 --- a/std/haxe/macro/TypeTools.hx +++ b/std/haxe/macro/TypeTools.hx @@ -258,19 +258,9 @@ class TypeTools { throw 'Incompatible arguments: ${typeParameters.length} type parameters and ${concreteTypes.length} concrete types'; else if (typeParameters.length == 0) return t; - #if (neko || eval) return Context.load("apply_params", 3)(typeParameters, concreteTypes, t); - #else - return applyParams(typeParameters, concreteTypes, t); - #end } - #if !neko - private static function applyParams(typeParameters:Array, concreteTypes:Array, t:Type):Type { - return null; - } - #end - /** Transforms `t` by calling `f` on each of its subtypes. @@ -359,11 +349,7 @@ class TypeTools { Converts type `t` to a human-readable String representation. **/ static public function toString(t:Type):String { - #if (neko || eval) return Context.load("s_type", 1)(t); - #else - return null; - #end } /** @@ -377,22 +363,14 @@ class TypeTools { Converts type `t` to `haxe.macro.Type.ModuleType`. **/ static public function toModuleType(t:Type):ModuleType { - #if (neko || eval) return Context.load("type_to_module_type", 1)(t); - #else - return null; - #end } /** Creates a type from the `haxe.macro.Type.ModuleType` argument. **/ static public function fromModuleType(mt:ModuleType):Type { - #if (neko || eval) return Context.load("module_type_to_type", 1)(mt); - #else - return null; - #end } /** diff --git a/std/js/html/webgl/extension/WEBGLPolygonMode.hx b/std/js/html/webgl/extension/WEBGLPolygonMode.hx new file mode 100644 index 00000000000..870718e1f4d --- /dev/null +++ b/std/js/html/webgl/extension/WEBGLPolygonMode.hx @@ -0,0 +1,42 @@ +/* + * Copyright (C)2005-2025 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. + */ + +package js.html.webgl.extension; + +/** + This extension exposes the ANGLE_polygon_mode functionality to WebGL. + There are no WebGL-specific behavioral changes. +**/ +@:native("WEBGL_polygon_mode ") +extern class WEBGLPolygonMode { + + static inline var POLYGON_MODE_WEBGL : Int = 2880; + static inline var POLYGON_OFFSET_LINE_WEBGL : Int = 10754; + static inline var LINE_WEBGL : Int = 6913; + static inline var FILL_WEBGL : Int = 6914; + + /** + Face must be FRONT_AND_BACK. + Mode must be LINE_WEBGL or FILL_WEBGL (default). + **/ + function polygonModeWEBGL( face : Int, mode : Int ) : Void; +} \ No newline at end of file diff --git a/tests/misc/cpp/projects/Issue12027/Main.hx b/tests/misc/cpp/projects/Issue12027/Main.hx new file mode 100644 index 00000000000..418ba8f58b4 --- /dev/null +++ b/tests/misc/cpp/projects/Issue12027/Main.hx @@ -0,0 +1,21 @@ +function main() { + foo(new Foo()); +} + +function foo(foo:Null) { + (foo ?? throw "hello")[0]++; +} + +abstract Foo(Int) { + public function new() { + this = 0; + } + + @:op([]) function get(i:Int) { + return this; + } + + @:op([]) function set(i:Int, val:Int) { + return this; + } +} diff --git a/tests/misc/cpp/projects/Issue12027/compile.hxml b/tests/misc/cpp/projects/Issue12027/compile.hxml new file mode 100644 index 00000000000..cf54a5789e3 --- /dev/null +++ b/tests/misc/cpp/projects/Issue12027/compile.hxml @@ -0,0 +1,2 @@ +-m Main +-cpp out \ No newline at end of file diff --git a/tests/misc/js/projects/Issue11985/.gitignore b/tests/misc/js/projects/Issue11985/.gitignore new file mode 100644 index 00000000000..daa602947e4 --- /dev/null +++ b/tests/misc/js/projects/Issue11985/.gitignore @@ -0,0 +1 @@ +test.js diff --git a/tests/misc/js/projects/Issue11985/Macro.hx b/tests/misc/js/projects/Issue11985/Macro.hx new file mode 100644 index 00000000000..91912d131ab --- /dev/null +++ b/tests/misc/js/projects/Issue11985/Macro.hx @@ -0,0 +1,3 @@ +function init() { + haxe.macro.Compiler.define("js-unflatten", "1"); +} diff --git a/tests/misc/js/projects/Issue11985/Main.hx b/tests/misc/js/projects/Issue11985/Main.hx new file mode 100644 index 00000000000..13a8385a7be --- /dev/null +++ b/tests/misc/js/projects/Issue11985/Main.hx @@ -0,0 +1,4 @@ +function main() { + haxe.Log.trace("hello"); + var haxe = "haxe"; +} diff --git a/tests/misc/js/projects/Issue11985/compile.hxml b/tests/misc/js/projects/Issue11985/compile.hxml new file mode 100644 index 00000000000..7f508a0588c --- /dev/null +++ b/tests/misc/js/projects/Issue11985/compile.hxml @@ -0,0 +1,4 @@ +-main Main +--macro Macro.init() +-js test.js +-cmd "node test.js" diff --git a/tests/misc/projects/Issue10845/compile-fail.hxml.stderr b/tests/misc/projects/Issue10845/compile-fail.hxml.stderr index 410e41470d2..3e383980408 100644 --- a/tests/misc/projects/Issue10845/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue10845/compile-fail.hxml.stderr @@ -1,6 +1,6 @@ Main.hx:21: characters 3-10 : Cannot modify abstract value of final field Main.hx:22: characters 3-10 : Cannot modify abstract value of final local -Main.hx:24: characters 3-8 : Cannot modify abstract value of final field +Main.hx:24: characters 3-13 : Cannot modify abstract value of final field Main.hx:25: characters 3-13 : Cannot modify abstract value of final local Main.hx:29: characters 3-8 : This expression cannot be accessed for writing Main.hx:30: characters 3-8 : Cannot assign to final diff --git a/tests/misc/projects/Issue11004/build.hxml.stdout b/tests/misc/projects/Issue11004/build.hxml.stdout index 800b6c23fee..399b8e53256 100644 --- a/tests/misc/projects/Issue11004/build.hxml.stdout +++ b/tests/misc/projects/Issue11004/build.hxml.stdout @@ -1 +1 @@ -Bar.hx:27: @:storedTypedExpr 3 computed this_ident as: foo.bar \ No newline at end of file +Bar.hx:27: @:storedTypedExpr 1 computed this_ident as: foo.bar diff --git a/tests/misc/projects/Issue11545/compile.hxml.stdout b/tests/misc/projects/Issue11545/compile.hxml.stdout index 91b293171ec..4a5fa1c12f4 100644 --- a/tests/misc/projects/Issue11545/compile.hxml.stdout +++ b/tests/misc/projects/Issue11545/compile.hxml.stdout @@ -1,3 +1,3 @@ Macro.hx:21: main -Macro.hx:21: _new +Macro.hx:21: _hx_new Main.hx:4: abc diff --git a/tests/misc/projects/Issue11740/Baz.hx b/tests/misc/projects/Issue11740/Baz.hx new file mode 100644 index 00000000000..bf370233bc4 --- /dev/null +++ b/tests/misc/projects/Issue11740/Baz.hx @@ -0,0 +1,6 @@ +import foo.Foo; + +class Baz { + function baz(data:foo.FooData) {} +} + diff --git a/tests/misc/projects/Issue11740/Macro.macro.hx b/tests/misc/projects/Issue11740/Macro.macro.hx new file mode 100644 index 00000000000..803e9371469 --- /dev/null +++ b/tests/misc/projects/Issue11740/Macro.macro.hx @@ -0,0 +1,17 @@ +import haxe.macro.Context; + +class Macro { + public static function build() { + trace("build FooData"); + + Context.defineType({ + pos : Context.currentPos(), + name : "FooData", + pack : ["foo"], + kind : TDClass(), + fields : [], + }); + + return null; + } +} diff --git a/tests/misc/projects/Issue11740/Main.hx b/tests/misc/projects/Issue11740/Main.hx new file mode 100644 index 00000000000..63590c32ad4 --- /dev/null +++ b/tests/misc/projects/Issue11740/Main.hx @@ -0,0 +1,5 @@ +import foo.Foo; + +function main() { + trace(Baz); +} diff --git a/tests/misc/projects/Issue11740/compile1.hxml b/tests/misc/projects/Issue11740/compile1.hxml new file mode 100644 index 00000000000..7d61297e2cb --- /dev/null +++ b/tests/misc/projects/Issue11740/compile1.hxml @@ -0,0 +1,2 @@ +-main Main +--interp diff --git a/tests/misc/projects/Issue11740/compile1.hxml.stdout b/tests/misc/projects/Issue11740/compile1.hxml.stdout new file mode 100644 index 00000000000..513fc838c7d --- /dev/null +++ b/tests/misc/projects/Issue11740/compile1.hxml.stdout @@ -0,0 +1,2 @@ +Macro.macro.hx:5: build FooData +Main.hx:4: Class diff --git a/tests/misc/projects/Issue11740/compile2.hxml b/tests/misc/projects/Issue11740/compile2.hxml new file mode 100644 index 00000000000..660cee52f25 --- /dev/null +++ b/tests/misc/projects/Issue11740/compile2.hxml @@ -0,0 +1,3 @@ +-main Main +Baz +--interp diff --git a/tests/misc/projects/Issue11740/compile2.hxml.stdout b/tests/misc/projects/Issue11740/compile2.hxml.stdout new file mode 100644 index 00000000000..513fc838c7d --- /dev/null +++ b/tests/misc/projects/Issue11740/compile2.hxml.stdout @@ -0,0 +1,2 @@ +Macro.macro.hx:5: build FooData +Main.hx:4: Class diff --git a/tests/misc/projects/Issue11740/foo/Foo.hx b/tests/misc/projects/Issue11740/foo/Foo.hx new file mode 100644 index 00000000000..4dc77078eb7 --- /dev/null +++ b/tests/misc/projects/Issue11740/foo/Foo.hx @@ -0,0 +1,4 @@ +package foo; + +@:build(Macro.build()) +class Foo {} diff --git a/tests/optimization/src/issues/Issue11931.hx b/tests/optimization/src/issues/Issue11931.hx new file mode 100644 index 00000000000..27558ea20ab --- /dev/null +++ b/tests/optimization/src/issues/Issue11931.hx @@ -0,0 +1,17 @@ +package issues; + +class Issue11931 { + @:js(' + var arr = []; + var tmp = arr[0]; + issues_Issue11931.use(tmp == null ? arr[0] = [] : tmp); + ') + static function test() { + var arr = []; + var e = arr[0] ??= []; + use(e); + } + + @:pure(false) + static function use(v:Array) {} +} diff --git a/tests/optimization/src/issues/Issue3713.hx b/tests/optimization/src/issues/Issue3713.hx index 9b52d9c2730..94c56c4d538 100644 --- a/tests/optimization/src/issues/Issue3713.hx +++ b/tests/optimization/src/issues/Issue3713.hx @@ -19,7 +19,7 @@ private class C { class Issue3713 { @:js(' - var b = BImpl._new(); + var b = BImpl._hx_new(); var c_x = 1; ') @:analyzer(no_const_propagation, no_local_dce) diff --git a/tests/server/src/cases/issues/Issue12001.hx b/tests/server/src/cases/issues/Issue12001.hx new file mode 100644 index 00000000000..08d4eedb1bc --- /dev/null +++ b/tests/server/src/cases/issues/Issue12001.hx @@ -0,0 +1,129 @@ +package cases.issues; + +import utest.Async; + +class Issue12001 extends TestCase { + function testDefineType(_) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro.hx")); + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty", "--macro", "Macro.defineType()"]; + runHaxe(args); + assertSuccess(); + + // Nothing is loading Foo, so no redefinition issue + runHaxe(args); + assertSuccess(); + } + + function testRedefineTypeCatchError(_) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro.hx")); + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty", "--macro", "Macro.redefineTypeCatchError()"]; + runHaxe(args); + assertSuccess(); + + runHaxe(args); + assertSuccess(); + assertHasPrint("Macro.hx:56: TInst(Foobar,[])"); + assertHasPrint("Macro.hx:69: Cannot redefine module Foobar"); + } + + @:async + @:timeout(3000) + function testRedefineType(async:Async) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro.hx")); + vfs.putContent("Main.hx", getTemplate("issues/Issue12001/Main.hx")); + var args = ["-main", "Main", "--interp", "--macro", "Macro.defineType()"]; + var i = 0; + function test() { + // Was failing with nightlies (HxbFailure) + runHaxe(args, () -> { + assertSuccess(); + assertHasPrint("Foo.test() = " + i); + if (++i >= 5) async.done(); + else test(); + }); + } + test(); + } + + function testDefineModule(_) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro.hx")); + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty", "--macro", "Macro.defineModule()"]; + runHaxe(args); + assertSuccess(); + + // Nothing is loading Bar, so no redefinition issue + runHaxe(args); + assertSuccess(); + } + + function testRedefineModuleCatchError(_) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro.hx")); + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty", "--macro", "Macro.redefineModuleCatchError()"]; + runHaxe(args); + assertSuccess(); + + runHaxe(args); + assertSuccess(); + assertHasPrint("Macro.hx:77: TInst(Foobaz,[])"); + assertHasPrint("Macro.hx:90: Cannot redefine module Foobaz"); + } + + @:async + @:timeout(3000) + function testRedefineModule(async:Async) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro.hx")); + vfs.putContent("Main.hx", getTemplate("issues/Issue12001/Main1.hx")); + var args = ["-main", "Main", "--interp", "--macro", "Macro.defineModule()"]; + var i = 0; + function test() { + // Was failing with nightlies (HxbFailure) + runHaxe(args, () -> { + assertSuccess(); + assertHasPrint("Bar.test() = " + i); + if (++i >= 5) async.done(); + else test(); + }); + } + test(); + } + + @:async + @:timeout(3000) + function testRedefineAfterTyping(async:Async) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro.hx")); + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty", "--interp", "--macro", "Macro.hookRedefine()"]; + var i = 0; + function test() { + runHaxe(args, () -> { + assertSuccess(); + // Newest version is being included + assertHasPrint("Baz.test() = " + i); + if (++i >= 5) async.done(); + else test(); + }); + } + test(); + } + + function testInvalidateError(_) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro1.hx")); + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty", "--interp", "--macro", "Macro.hookInvalidateError()"]; + runHaxe(args); + assertErrorMessage("Cannot invalidate loaded module Empty"); + } + + function testInvalidateCaughtError(_) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue12001/Macro1.hx")); + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty", "--interp", "--macro", "Macro.hookInvalidateCatch()"]; + runHaxe(args); + assertSuccess(); + assertHasPrint("Cannot invalidate loaded module Empty"); + } +} diff --git a/tests/server/src/cases/issues/Issue12018.hx b/tests/server/src/cases/issues/Issue12018.hx new file mode 100644 index 00000000000..f853f0fbba1 --- /dev/null +++ b/tests/server/src/cases/issues/Issue12018.hx @@ -0,0 +1,18 @@ +package cases.issues; + +class Issue12018 extends TestCase { + function test(_) { + var content = getTemplate("issues/Issue12018/Main.hx"); + var transform = Markers.parse(content); + + vfs.putContent("Main.hx", transform.source); + vfs.putContent("import.hx", getTemplate("issues/Issue12018/import.hx")); + var args = ["-main", "Main"]; + runHaxe(args); + assertSuccess(); + + runHaxeJsonCb(args, DisplayMethods.Hover, {file: new FsPath("Main.hx"), offset: transform.offset(1)}, (res) -> { + Assert.equals(res.item.args.path.typeName, "Main"); + }); + } +} diff --git a/tests/server/test/templates/csSafeTypeBuilding/Macro.macro.hx b/tests/server/test/templates/csSafeTypeBuilding/Macro.macro.hx index 9c33cbf6dc1..47857385baf 100644 --- a/tests/server/test/templates/csSafeTypeBuilding/Macro.macro.hx +++ b/tests/server/test/templates/csSafeTypeBuilding/Macro.macro.hx @@ -11,23 +11,19 @@ class Macro { @:persistent static var generated = new Map(); - #if config.getType - static function isAlive(name:String):Bool { + static function isAlive(name:String, ct:ComplexType, pos:Position):Bool { // Null check is just there to make it a one liner // Basically returning true if no exception is caught + #if config.getType return try Context.getType(name) != null catch(s:String) { if (s != 'Type not found \'$name\'') throw s; false; }; - } - #else - static function isAlive(ct:ComplexType, pos:Position):Bool { - // Null check is just there to make it a one liner - // Basically returning true if no exception is caught + #else return try Context.resolveType(ct, pos) != null catch(e) false; + #end } - #end public static function buildFoo() { var from = '[${Context.getLocalModule()}] '; @@ -41,11 +37,7 @@ class Macro { var ct = TPath({pack: [], name: key}); if (generated.exists(key)) { - #if config.getType - if (isAlive(key)) { - #else - if (isAlive(ct, pos)) { - #end + if (isAlive(key, ct, pos)) { print('Reusing previously generated type for $key.'); return ct; } diff --git a/tests/server/test/templates/issues/Issue12001/Macro.hx b/tests/server/test/templates/issues/Issue12001/Macro.hx new file mode 100644 index 00000000000..d0b92aa8366 --- /dev/null +++ b/tests/server/test/templates/issues/Issue12001/Macro.hx @@ -0,0 +1,93 @@ +import haxe.macro.Context; +import haxe.macro.Expr.Error; + +@:persistent var i = 0; +function defineType() { + Context.onAfterInitMacros(() -> { + Context.defineType({ + pos: Context.currentPos(), + pack: [], + name: "Foo", + kind: TDClass(null, null, false, false, false), + fields: (macro class Foo { + public static function test() Sys.println("Foo.test() = " + $v{i++}); + }).fields + }); + }); +} + +@:persistent var j = 0; +function defineModule() { + Context.onAfterInitMacros(() -> { + Context.defineModule("Bar", [{ + pos: Context.currentPos(), + pack: [], + name: "Bar", + kind: TDClass(null, null, false, false, false), + fields: (macro class Bar { + public static function test() Sys.println("Bar.test() = " + $v{j++}); + }).fields + }]); + }); +} + +@:persistent var k = 0; +function hookRedefine() { + var generated = false; + Context.onAfterTyping((_) -> { + if (generated) return; + generated = true; + + Context.defineModule("Baz", [{ + pos: Context.currentPos(), + pack: [], + name: "Baz", + kind: TDClass(null, null, false, false, false), + fields: (macro class Baz { + public static function __init__() Sys.println("Baz.test() = " + $v{k++}); + }).fields + }]); + }); +} + +@:persistent var l = 0; +function redefineTypeCatchError() { + Context.onAfterInitMacros(() -> { + if (l > 0) trace(Context.getType("Foobar")); + + try { + l++; + Context.defineType({ + pos: Context.currentPos(), + pack: [], + name: "Foobar", + kind: TDClass(null, null, false, false, false), + fields: [] + }); + } catch (e:Error) { + if (l == 0) throw e; + trace(e.message); + } + }); +} + +@:persistent var m = 0; +function redefineModuleCatchError() { + Context.onAfterInitMacros(() -> { + if (m > 0) trace(Context.getType("Foobaz")); + + try { + m++; + Context.defineModule("Foobaz", [{ + pos: Context.currentPos(), + pack: [], + name: "Foobaz", + kind: TDClass(null, null, false, false, false), + fields: [] + }]); + } catch (e:Error) { + if (m == 0) throw e; + trace(e.message); + } + }); +} diff --git a/tests/server/test/templates/issues/Issue12001/Macro1.hx b/tests/server/test/templates/issues/Issue12001/Macro1.hx new file mode 100644 index 00000000000..5eacfba32c6 --- /dev/null +++ b/tests/server/test/templates/issues/Issue12001/Macro1.hx @@ -0,0 +1,19 @@ +import haxe.macro.CompilationServer; +import haxe.macro.Context; +import haxe.macro.Expr.Error; + +function hookInvalidateError() { + Context.onAfterTyping((_) -> { + CompilationServer.invalidateModule("Empty"); + }); +} + +function hookInvalidateCatch() { + Context.onAfterTyping((_) -> { + try { + CompilationServer.invalidateModule("Empty"); + } catch (e:Error) { + Sys.println(e.message); + } + }); +} diff --git a/tests/server/test/templates/issues/Issue12001/Main.hx b/tests/server/test/templates/issues/Issue12001/Main.hx new file mode 100644 index 00000000000..30367f8cb3d --- /dev/null +++ b/tests/server/test/templates/issues/Issue12001/Main.hx @@ -0,0 +1,3 @@ +function main() { + Foo.test(); +} diff --git a/tests/server/test/templates/issues/Issue12001/Main1.hx b/tests/server/test/templates/issues/Issue12001/Main1.hx new file mode 100644 index 00000000000..0b97ed04e59 --- /dev/null +++ b/tests/server/test/templates/issues/Issue12001/Main1.hx @@ -0,0 +1,3 @@ +function main() { + Bar.test(); +} diff --git a/tests/server/test/templates/issues/Issue12018/Main.hx b/tests/server/test/templates/issues/Issue12018/Main.hx new file mode 100644 index 00000000000..61eecd8adb5 --- /dev/null +++ b/tests/server/test/templates/issues/Issue12018/Main.hx @@ -0,0 +1,6 @@ +// don't remove +class Ma{-1-}in { + static function main() { + HaxeJson.parse("{}"); + } +} diff --git a/tests/server/test/templates/issues/Issue12018/import.hx b/tests/server/test/templates/issues/Issue12018/import.hx new file mode 100644 index 00000000000..79ddfdfc960 --- /dev/null +++ b/tests/server/test/templates/issues/Issue12018/import.hx @@ -0,0 +1 @@ +import haxe.Json as HaxeJson; diff --git a/tests/unit/src/unit/TestNullCoalescing.hx b/tests/unit/src/unit/TestNullCoalescing.hx index 76eb4895fbc..bf1c02de5c2 100644 --- a/tests/unit/src/unit/TestNullCoalescing.hx +++ b/tests/unit/src/unit/TestNullCoalescing.hx @@ -4,6 +4,90 @@ private class A {} private class B extends A {} private class C extends A {} +private class NullCoalClass { + @:isVar public var field(get, set):String; + + public var getCounter = 0; + public var setCounter = 0; + + public function new() {} + + public function get_field() { + getCounter++; + return field; + } + + public function set_field(v:String) { + setCounter++; + return field = v; + } +} + +private typedef NullCoalAbstractData = { + var field:String; + var getCounter:Int; + var setCounter:Int; +} + +private abstract NullCoalAbstract(NullCoalAbstractData) { + public var field(get, set):String; + + public function new() { + this = { + field: null, + getCounter: 0, + setCounter: 0 + } + } + + public function getGetCounter() { + return this.getCounter; + } + + public function getSetCounter() { + return this.setCounter; + } + + public function get_field() { + this.getCounter++; + return this.field; + } + + public function set_field(v:String) { + this.setCounter++; + return this.field = v; + } +} + +private abstract NullCoalAbstractResolve(NullCoalAbstractData) { + public function new() { + this = { + field: null, + getCounter: 0, + setCounter: 0 + } + } + + public function getGetCounter() { + return this.getCounter; + } + + public function getSetCounter() { + return this.setCounter; + } + + @:op(a.b) public function readResolve(field:String) { + this.getCounter++; + return Reflect.field(this, field); + } + + @:op(a.b) public function writeResolve(field:String, v:T) { + this.setCounter++; + Reflect.setField(this, field, v); + return Reflect.field(this, field); + } +} + @:nullSafety(StrictThreaded) class TestNullCoalescing extends Test { final nullInt:Null = null; @@ -19,6 +103,7 @@ class TestNullCoalescing extends Test { } function test() { + count = 0; eq(true, 0 != 1 ?? 2); var a = call() ?? "default"; eq(count, 1); @@ -53,6 +138,7 @@ class TestNullCoalescing extends Test { eq(nullInt ?? 2, 2); eq(nullInt ?? (2 : Null) ?? 3 + 100, 2); eq(nullInt ?? nullInt ?? 3, 3); + f(HelperMacros.isNullable(nullInt ?? nullInt ?? 3)); final i:Null = 1; final arr:Array = [i ?? 2]; @@ -73,29 +159,6 @@ class TestNullCoalescing extends Test { final di3:Null = 2; eq(di ?? di2 ?? di3, 2); - var a:Null = null; - a ??= 5; - eq(a, 5); - var a:Null = null; - eq(a ??= 5, 5); - eq(a, 5); - var a = "default"; - eq(a ??= "5", "default"); - - count = 0; - var a = call(); - eq(count, 1); - a ??= call(); - eq(count, 1); - - var a:Null = null; - final b = a ??= call(); - final c = a ??= call(); - eq(count, 2); - eq(a, "_"); - eq(b, "_"); - eq(c, "_"); - final a:Null = ({} : Dynamic).x; eq(a ?? 2, 2); @@ -143,4 +206,161 @@ class TestNullCoalescing extends Test { f(HelperMacros.isNullable(notNullF)); f(HelperMacros.isNullable(notNullF2)); } + + function testAssignOp() { + count = 0; + var a:Null = null; + a ??= 5; + eq(a, 5); + t(HelperMacros.isNullable(a ??= null)); + f(HelperMacros.isNullable(a ??= 5)); + var a:Null = null; + eq(a ??= 5, 5); + eq(a, 5); + var a = "default"; + eq(a ??= "5", "default"); + + count = 0; + var a = call(); + eq(count, 1); + a ??= call(); + eq(count, 1); + + var a:Null = null; + final b = a ??= call(); + final c = a ??= call(); + eq(count, 2); + eq(a, "_"); + eq(b, "_"); + eq(c, "_"); + + final map:Map> = []; + var array1 = []; + map["foo"] ??= array1; + eq(map["foo"], array1); + map["foo"] ??= []; + eq(map["foo"], array1); + + // test typing + #if !macro + var a = mut() ?? mut(); + eq(2, getMut()); + resetMut(); + + var a:Null = 0; + mutAssignLeft(a) ??= mut() ?? mut(); + eq(3, getMut()); + resetMut(); + + var a:Null = 0; + final b = a ??= mut(); + eq(1, getMut()); + resetMut(); + + var a:Null = 0; + mutAssignLeft(a) ??= 1; + eq(1, getMut()); + resetMut(); + + // field + var obj = getObj(); + obj.field ??= "value"; + eq("value", obj.field ?? "fail"); + + var value = obj.field ??= "value2"; + eq("value", obj.field ?? "fail"); + eq("value", value); + + mutAssignLeft(obj.field) ??= "not value"; + eq(1, getMut()); + eq("value", obj.field ?? "fail"); + resetMut(); + + // accessor + var obj = new NullCoalClass(); + obj.field ??= "value"; + eq(1, obj.getCounter); + eq(1, obj.setCounter); + eq("value", obj.field ?? "fail"); + + var value = obj.field ??= "value2"; + eq(3, obj.getCounter); + eq(1, obj.setCounter); + eq("value", obj.field ?? "fail"); + eq("value", value); + + mutAssignLeft(obj.field) ??= "not value"; + eq(5, obj.getCounter); + eq(1, obj.setCounter); + eq(1, getMut()); + eq("value", obj.field ?? "fail"); + resetMut(); + + // static extension accessor + var obj = new NullCoalAbstract(); + obj.field ??= "value"; + eq(1, obj.getGetCounter()); + eq(1, obj.getSetCounter()); + eq("value", obj.field ?? "fail"); + + var value = obj.field ??= "value2"; + eq(3, obj.getGetCounter()); + eq(1, obj.getSetCounter()); + eq("value", obj.field ?? "fail"); + eq("value", value); + + mutAssignLeft(obj.field) ??= "not value"; + eq(5, obj.getGetCounter()); + eq(1, obj.getSetCounter()); + eq(1, getMut()); + eq("value", obj.field ?? "fail"); + resetMut(); + + // resolve + var obj = new NullCoalAbstractResolve(); + obj.field ??= "value"; + eq(1, obj.getGetCounter()); + eq(1, obj.getSetCounter()); + eq("value", obj.field ?? "fail"); + + var value = obj.field ??= "value2"; + eq(3, obj.getGetCounter()); + eq(1, obj.getSetCounter()); + eq("value", obj.field ?? "fail"); + eq("value", value); + + // TODO: this fails at the moment with some "not enough arguments error" + // mutAssignLeft(obj.field) ??= "not value"; + // eq(5, obj.getGetCounter()); + // eq(1, obj.getSetCounter()); + // eq(1, getMut()); + // eq("value", obj.field ?? "fail"); + // resetMut(); + #end + } + + static var mutI = 0; + + static function getObj():{field:Null} { + return {field: null} + } + + static macro function mut() { + mutI++; + return macro mutI; + } + + static macro function getMut() { + return macro $v{mutI}; + } + + static macro function resetMut() { + mutI = 0; + return macro $v{mutI}; + } + + static macro function mutAssignLeft(e:haxe.macro.Expr) { + mutI++; + return e; + } } diff --git a/tests/unit/src/unit/issues/Issue10965.hx b/tests/unit/src/unit/issues/Issue10965.hx index 0ab14b2acea..a9af8f03cda 100644 --- a/tests/unit/src/unit/issues/Issue10965.hx +++ b/tests/unit/src/unit/issues/Issue10965.hx @@ -1,7 +1,7 @@ package unit.issues; class Issue10965 extends Test { - #if jvm + #if (jvm || hl || cpp) function testNeg() { var a:Single = 1; var c = -a; diff --git a/tests/unit/src/unit/issues/Issue11647.hx b/tests/unit/src/unit/issues/Issue11647.hx new file mode 100644 index 00000000000..44636483a57 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11647.hx @@ -0,0 +1,15 @@ +package unit.issues; + +class Issue11647 extends Test { + #if (java || hl || cpp) + function test() { + foo(); + } + + function foo(a : Single = 10.0, b:Single = 11.0, c:Single = 12.0) : Void { + feq(10, a); + feq(11, b); + feq(12, c); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue11919.hx b/tests/unit/src/unit/issues/Issue11919.hx new file mode 100644 index 00000000000..d845dbe93d8 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11919.hx @@ -0,0 +1,61 @@ +package unit.issues; + +import unit.Test; + +private class Func { + public var f:Int->Int; + + public function new(f:Int->Int) { + this.f = f; + } +} + +class Issue11919 extends Test { + static function getInt() { + return 5; + } + + function test() { + static var localFunction = new Func(a -> a); + eq(2, localFunction.f(2)); + + static var declaredLocal = { + var f = getInt(); + f; + } + eq(5, declaredLocal); + + static var caughtVar = { + try { + throw "foo"; + } catch (s:String) { + s; + } + } + eq("foo", caughtVar); + + static var loopBreak = { + var acc = 0; + for (i in 0...getInt()) { + acc += i; + if (i == 2) { + break; + } + } + acc; + } + eq(3, loopBreak); + + static var loopContinue = { + var acc = 0; + for (i in 0...getInt()) { + if (i & 1 == 0) { + continue; + } + acc += i; + } + acc; + } + eq(4, loopContinue); + } +} diff --git a/tests/unit/src/unit/issues/Issue11990.hx b/tests/unit/src/unit/issues/Issue11990.hx new file mode 100644 index 00000000000..acba9ccb85a --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11990.hx @@ -0,0 +1,14 @@ +package unit.issues; + +class Issue11990 extends unit.Test { + public function test() { + #if (cpp || jvm || hl) + var x:Single = std.Math.NaN; + eq(true, x != x); + var x:Float = std.Math.NaN; + eq(true, x != x); + #else + noAssert(); + #end + } +} diff --git a/tests/unit/src/unit/issues/Issue11998.hx b/tests/unit/src/unit/issues/Issue11998.hx new file mode 100644 index 00000000000..3578f83c1e8 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11998.hx @@ -0,0 +1,15 @@ +package unit.issues; + +class Issue11998 extends unit.Test { + static var caughtVar = { + try { + throw "foo"; + } catch (s:String) { + s; + } + } + + public function test() { + eq("foo", caughtVar); + } +} diff --git a/tests/unit/src/unit/issues/Issue12000.hx b/tests/unit/src/unit/issues/Issue12000.hx new file mode 100644 index 00000000000..117a04ed5f3 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue12000.hx @@ -0,0 +1,28 @@ +package unit.issues; + +private class Foo { + public var m : Map; + public var length : Int; + public function new() { + } +} + +class Issue12000 extends unit.Test { + var obj = new Foo(); + + #if hl + public function test() { + untyped $prefetch(obj, 0); + untyped $prefetch(obj, 1); + untyped $prefetch(obj, 2); + untyped $prefetch(obj, 3); + untyped $prefetch(obj, 4); + untyped $prefetch(obj.length, 0); + untyped $prefetch(obj.length, 1); + untyped $prefetch(obj.length, 2); + untyped $prefetch(obj.length, 3); + untyped $prefetch(obj.length, 4); + noAssert(); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue12028.hx b/tests/unit/src/unit/issues/Issue12028.hx new file mode 100644 index 00000000000..211fdf7a43f --- /dev/null +++ b/tests/unit/src/unit/issues/Issue12028.hx @@ -0,0 +1,38 @@ +package unit.issues; + +private typedef StateHandler = { + public function onUpdate():T; +} + +class Issue12028 extends Test { + #if !lua + function testMakeVarArgsInDynamic() { + var func = function(args:Array):String { + return args.length >= 1 ? args[0] : ""; + }; + + var f2 = Reflect.makeVarArgs(func); + eq("a", f2("a")); // Important for repro + eq("b", foo(f2)); + eq("d", Reflect.callMethod(null, f2, ["d"])); + } + + function foo(func:Dynamic) { + return func("b", "c"); + } + + function testVoid2Dyn() { + var handlers:StateHandler = { + onUpdate: function():Bool { + return true; + } + } + foo2(handlers); + noAssert(); + } + + function foo2(handlers:StateHandler):Null { + return handlers.onUpdate(); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue12041.hx b/tests/unit/src/unit/issues/Issue12041.hx new file mode 100644 index 00000000000..2c2ed2fe9dc --- /dev/null +++ b/tests/unit/src/unit/issues/Issue12041.hx @@ -0,0 +1,47 @@ +package unit.issues; + +class Issue12041 extends Test { + #if (java || hl || cpp) + @:analyzer(ignore) + function testCastNullFloat() { + var fnull : Null = null; + var snull : Null = null; + var f0 : Float = snull; + feq(f0, 0); + var s0 : Single = fnull; + feq(s0, 0); + } + + @:analyzer(ignore) + function testSingleOp() { + var s1 : Single = 10.0; + feq(10.0, s1); + var s2 : Single = 0.3; + feq(0.3, s2); + var f1 : Float = 10.0; + var f2 : Float = 0.3; + var a : Single = s1 + s2; + feq(10.3, a); + var a : Single = s1 - s2; + feq(9.7, a); + var a : Single = s1 * s2; + feq(3.0, a); + var a : Single = s1 / s2; + feq(33.3333333333, a); + var a : Single = s1 / (f2 : Single); + feq(33.3333333333, a); + var a : Single = (f1 : Single) / s2; + feq(33.3333333333, a); + var a : Single = (f1 : Single) / (f2 : Single); + feq(33.3333333333, a); + } + + @:analyzer(ignore) + function testSingleFromInt() { + var s1 : Single = 10; + feq(10.0, s1); + var s2 : Single = 3; + feq(3.0, s2); + } + #end +}