Jelajahi Sumber

Cross-target exception handling (#9124)

Aleksandr Kuzmenko 5 tahun lalu
induk
melakukan
509b5d5f07
72 mengubah file dengan 3194 tambahan dan 1585 penghapusan
  1. 6 0
      src-json/meta.json
  2. 100 0
      src/context/common.ml
  3. 5 0
      src/core/texpr.ml
  4. 541 0
      src/filters/exceptions.ml
  5. 5 9
      src/filters/filters.ml
  6. 0 212
      src/filters/jsExceptions.ml
  7. 0 187
      src/filters/tryCatchWrapper.ml
  8. 5 3
      src/generators/genjava.ml
  9. 20 1
      src/generators/genjs.ml
  10. 24 108
      src/generators/genjvm.ml
  11. 6 53
      src/generators/genlua.ml
  12. 22 107
      src/generators/genphp7.ml
  13. 8 73
      src/generators/genpy.ml
  14. 2 2
      src/generators/genswf9.ml
  15. 4 1
      src/generators/jvm/jvmSignature.ml
  16. 0 1
      src/macro/eval/evalHash.ml
  17. 4 4
      src/macro/eval/evalStdLib.ml
  18. 1 1
      src/optimization/dce.ml
  19. 6 2
      src/typing/typer.ml
  20. 81 0
      std/cpp/_std/haxe/Exception.hx
  21. 42 0
      std/cpp/_std/haxe/NativeStackTrace.hx
  22. 2 2
      std/cpp/cppia/HostClasses.hx
  23. 0 1
      std/cs/Boot.hx
  24. 0 1
      std/cs/_std/Std.hx
  25. 114 0
      std/cs/_std/haxe/Exception.hx
  26. 60 0
      std/cs/_std/haxe/NativeStackTrace.hx
  27. 0 63
      std/cs/internal/Exceptions.hx
  28. 84 0
      std/eval/_std/haxe/Exception.hx
  29. 32 0
      std/eval/_std/haxe/NativeStackTrace.hx
  30. 96 0
      std/flash/_std/haxe/Exception.hx
  31. 69 0
      std/flash/_std/haxe/NativeStackTrace.hx
  32. 93 337
      std/haxe/CallStack.hx
  33. 110 0
      std/haxe/Exception.hx
  34. 15 0
      std/haxe/NativeStackTrace.hx
  35. 38 0
      std/haxe/ValueException.hx
  36. 79 0
      std/hl/_std/haxe/Exception.hx
  37. 58 0
      std/hl/_std/haxe/NativeStackTrace.hx
  38. 0 1
      std/java/Boot.hx
  39. 0 1
      std/java/_std/Std.hx
  40. 108 0
      std/java/_std/haxe/Exception.hx
  41. 55 0
      std/java/_std/haxe/NativeStackTrace.hx
  42. 0 90
      std/java/internal/Exceptions.hx
  43. 5 5
      std/java/internal/Runtime.hx
  44. 1 21
      std/js/Boot.hx
  45. 153 0
      std/js/_std/haxe/Exception.hx
  46. 137 0
      std/js/_std/haxe/NativeStackTrace.hx
  47. 0 60
      std/jvm/Exception.hx
  48. 0 1
      std/jvm/Jvm.hx
  49. 81 0
      std/lua/_std/haxe/Exception.hx
  50. 54 0
      std/lua/_std/haxe/NativeStackTrace.hx
  51. 81 0
      std/neko/_std/haxe/Exception.hx
  52. 51 0
      std/neko/_std/haxe/NativeStackTrace.hx
  53. 2 16
      std/php/Boot.hx
  54. 1 1
      std/php/Throwable.hx
  55. 0 165
      std/php/_std/haxe/CallStack.hx
  56. 99 0
      std/php/_std/haxe/Exception.hx
  57. 115 0
      std/php/_std/haxe/NativeStackTrace.hx
  58. 0 1
      std/python/Boot.hx
  59. 90 0
      std/python/_std/haxe/Exception.hx
  60. 46 0
      std/python/_std/haxe/NativeStackTrace.hx
  61. 0 37
      std/python/internal/HxException.hx
  62. 2 0
      tests/misc/projects/Issue8303/MainCatch.hx
  63. 1 1
      tests/misc/projects/Issue8303/compile-fail.hxml
  64. 16 1
      tests/misc/projects/Issue8303/compile-fail.hxml.stderr
  65. 1 1
      tests/misc/projects/Issue8303/compile.hxml
  66. 7 7
      tests/optimization/src/TestJs.hx
  67. 2 2
      tests/optimization/src/TestTreGeneration.hx
  68. 2 0
      tests/runci/targets/Flash.hx
  69. 336 0
      tests/unit/src/unit/TestExceptions.hx
  70. 1 0
      tests/unit/src/unit/TestMain.hx
  71. 2 2
      tests/unit/src/unit/issues/Issue4644.hx
  72. 13 4
      tests/unit/src/unitstd/haxe/CallStack.unit.hx

+ 6 - 0
src-json/meta.json

@@ -1253,5 +1253,11 @@
 		"metadata": ":void",
 		"metadata": ":void",
 		"doc": "Use Cpp native `void` return type.",
 		"doc": "Use Cpp native `void` return type.",
 		"platforms": ["cpp"]
 		"platforms": ["cpp"]
+	},
+	{
+		"name": "NeedsExceptionStack",
+		"metadata": ":needsExceptionStack",
+		"doc": "Internally used for some of auto-generated `catch` vars",
+		"internal": true
 	}
 	}
 ]
 ]

+ 100 - 0
src/context/common.ml

@@ -79,6 +79,21 @@ type capture_policy =
 	(** similar to wrap ref, but will only apply to the locals that are declared in loops *)
 	(** similar to wrap ref, but will only apply to the locals that are declared in loops *)
 	| CPLoopVars
 	| CPLoopVars
 
 
+type exceptions_config = {
+	(* Base types which may be thrown from Haxe code without wrapping. *)
+	ec_native_throws : path list;
+	(* Base types which may be caught from Haxe code without wrapping. *)
+	ec_native_catches : path list;
+	(* Path of a native class or interface, which can be used for wildcard catches. *)
+	ec_wildcard_catch : path;
+	(*
+		Path of a native base class or interface, which can be thrown.
+		This type is used to cast `haxe.Exception.thrown(v)` calls to.
+		For example `throw 123` is compiled to `throw (cast Exception.thrown(123):ec_base_throw)`
+	*)
+	ec_base_throw : path;
+}
+
 type platform_config = {
 type platform_config = {
 	(** has a static type system, with not-nullable basic types (Int/Float/Bool) *)
 	(** has a static type system, with not-nullable basic types (Int/Float/Bool) *)
 	pf_static : bool;
 	pf_static : bool;
@@ -106,6 +121,8 @@ type platform_config = {
 	pf_supports_threads : bool;
 	pf_supports_threads : bool;
 	(** target supports Unicode **)
 	(** target supports Unicode **)
 	pf_supports_unicode : bool;
 	pf_supports_unicode : bool;
+	(** exceptions handling config **)
+	pf_exceptions : exceptions_config;
 }
 }
 
 
 class compiler_callbacks = object(self)
 class compiler_callbacks = object(self)
@@ -320,6 +337,12 @@ let default_config =
 		pf_this_before_super = true;
 		pf_this_before_super = true;
 		pf_supports_threads = false;
 		pf_supports_threads = false;
 		pf_supports_unicode = true;
 		pf_supports_unicode = true;
+		pf_exceptions = {
+			ec_native_throws = [];
+			ec_native_catches = [];
+			ec_wildcard_catch = ([],"Dynamic");
+			ec_base_throw = ([],"Dynamic");
+		}
 	}
 	}
 
 
 let get_config com =
 let get_config com =
@@ -335,6 +358,15 @@ let get_config com =
 			pf_capture_policy = CPLoopVars;
 			pf_capture_policy = CPLoopVars;
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 			pf_this_before_super = (get_es_version com) < 6; (* cannot access `this` before `super()` when generating ES6 classes *)
 			pf_this_before_super = (get_es_version com) < 6; (* cannot access `this` before `super()` when generating ES6 classes *)
+			pf_exceptions = {
+				ec_native_throws = [
+					["js";"lib"],"Error";
+					["haxe"],"Exception";
+				];
+				ec_native_catches = [];
+				ec_wildcard_catch = ([],"Dynamic");
+				ec_base_throw = ([],"Dynamic");
+			}
 		}
 		}
 	| Lua ->
 	| Lua ->
 		{
 		{
@@ -359,12 +391,36 @@ let get_config com =
 			pf_capture_policy = CPLoopVars;
 			pf_capture_policy = CPLoopVars;
 			pf_can_skip_non_nullable_argument = false;
 			pf_can_skip_non_nullable_argument = false;
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
+			pf_exceptions = {
+				ec_native_throws = [
+					["flash";"errors"],"Error";
+					["haxe"],"Exception";
+				];
+				ec_native_catches = [
+					["flash";"errors"],"Error";
+					["haxe"],"Exception";
+				];
+				ec_wildcard_catch = ([],"Dynamic");
+				ec_base_throw = ([],"Dynamic");
+			}
 		}
 		}
 	| Php ->
 	| Php ->
 		{
 		{
 			default_config with
 			default_config with
 			pf_static = false;
 			pf_static = false;
 			pf_uses_utf16 = false;
 			pf_uses_utf16 = false;
+			pf_exceptions = {
+				ec_native_throws = [
+					["php"],"Throwable";
+					["haxe"],"Exception";
+				];
+				ec_native_catches = [
+					["php"],"Throwable";
+					["haxe"],"Exception";
+				];
+				ec_wildcard_catch = (["php"],"Throwable");
+				ec_base_throw = (["php"],"Throwable");
+			}
 		}
 		}
 	| Cpp ->
 	| Cpp ->
 		{
 		{
@@ -382,6 +438,18 @@ let get_config com =
 			pf_pad_nulls = true;
 			pf_pad_nulls = true;
 			pf_overload = true;
 			pf_overload = true;
 			pf_supports_threads = true;
 			pf_supports_threads = true;
+			pf_exceptions = {
+				ec_native_throws = [
+					["cs";"system"],"Exception";
+					["haxe"],"Exception";
+				];
+				ec_native_catches = [
+					["cs";"system"],"Exception";
+					["haxe"],"Exception";
+				];
+				ec_wildcard_catch = (["cs";"system"],"Exception");
+				ec_base_throw = (["cs";"system"],"Exception");
+			}
 		}
 		}
 	| Java ->
 	| Java ->
 		{
 		{
@@ -391,6 +459,18 @@ let get_config com =
 			pf_overload = true;
 			pf_overload = true;
 			pf_supports_threads = true;
 			pf_supports_threads = true;
 			pf_this_before_super = false;
 			pf_this_before_super = false;
+			pf_exceptions = {
+				ec_native_throws = [
+					["java";"lang"],"RuntimeException";
+					["haxe"],"Exception";
+				];
+				ec_native_catches = [
+					["java";"lang"],"Throwable";
+					["haxe"],"Exception";
+				];
+				ec_wildcard_catch = (["java";"lang"],"Throwable");
+				ec_base_throw = (["java";"lang"],"RuntimeException");
+			}
 		}
 		}
 	| Python ->
 	| Python ->
 		{
 		{
@@ -398,6 +478,16 @@ let get_config com =
 			pf_static = false;
 			pf_static = false;
 			pf_capture_policy = CPLoopVars;
 			pf_capture_policy = CPLoopVars;
 			pf_uses_utf16 = false;
 			pf_uses_utf16 = false;
+			pf_exceptions = {
+				ec_native_throws = [
+					["python";"Exceptions"],"BaseException";
+				];
+				ec_native_catches = [
+					["python";"Exceptions"],"BaseException";
+				];
+				ec_wildcard_catch = ["python";"Exceptions"],"BaseException";
+				ec_base_throw = ["python";"Exceptions"],"BaseException";
+			}
 		}
 		}
 	| Hl ->
 	| Hl ->
 		{
 		{
@@ -405,6 +495,16 @@ let get_config com =
 			pf_capture_policy = CPWrapRef;
 			pf_capture_policy = CPWrapRef;
 			pf_pad_nulls = true;
 			pf_pad_nulls = true;
 			pf_supports_threads = true;
 			pf_supports_threads = true;
+			pf_exceptions = {
+				ec_native_throws = [
+					["haxe"],"Exception";
+				];
+				ec_native_catches = [
+					["haxe"],"Exception";
+				];
+				ec_wildcard_catch = ([],"Dynamic");
+				ec_base_throw = ([],"Dynamic");
+			}
 		}
 		}
 	| Eval ->
 	| Eval ->
 		{
 		{

+ 5 - 0
src/core/texpr.ml

@@ -517,6 +517,11 @@ module Builder = struct
 	let mk_parent e =
 	let mk_parent e =
 		mk (TParenthesis e) e.etype e.epos
 		mk (TParenthesis e) e.etype e.epos
 
 
+	let ensure_parent e =
+		match e.eexpr with
+		| TParenthesis _ -> e
+		| _ -> mk_parent e
+
 	let mk_return e =
 	let mk_return e =
 		mk (TReturn (Some e)) t_dynamic e.epos
 		mk (TReturn (Some e)) t_dynamic e.epos
 
 

+ 541 - 0
src/filters/exceptions.ml

@@ -0,0 +1,541 @@
+open Globals
+open Ast
+open Type
+open Common
+open Typecore
+open TyperBase
+open Fields
+open Error
+
+let haxe_exception_type_path = (["haxe"],"Exception")
+
+type context = {
+	typer : typer;
+	basic : basic_types;
+	config : exceptions_config;
+	wildcard_catch_type : Type.t;
+	base_throw_type : Type.t;
+	haxe_exception_class : tclass;
+	haxe_exception_type : Type.t;
+	haxe_native_stack_trace : tclass;
+}
+
+let is_dynamic t =
+	match Abstract.follow_with_abstracts t with
+	| TAbstract({ a_path = [],"Dynamic" }, _) -> true
+	| t -> t == t_dynamic
+
+(**
+	Generate `haxe.Exception.method_name(args)`
+*)
+let haxe_exception_static_call ctx method_name args p =
+	let method_field =
+		try PMap.find method_name ctx.haxe_exception_class.cl_statics
+		with Not_found -> error ("haxe.Exception has no field " ^ method_name) p
+	in
+	let return_type =
+		match follow method_field.cf_type with
+		| TFun(_,t) -> t
+		| _ -> error ("haxe.Exception." ^ method_name ^ " is not a function and cannot be called") p
+	in
+	make_static_call ctx.typer ctx.haxe_exception_class method_field (fun t -> t) args return_type p
+
+(**
+	Generate `haxe_exception.method_name(args)`
+*)
+let haxe_exception_instance_call ctx haxe_exception method_name args p =
+	match quick_field haxe_exception.etype method_name with
+	| FInstance (_,_,cf) as faccess ->
+		let efield = { eexpr = TField(haxe_exception,faccess); etype = cf.cf_type; epos = p } in
+		let rt =
+			match follow cf.cf_type with
+			| TFun(_,t) -> t
+			| _ ->
+				error ((s_type (print_context()) haxe_exception.etype) ^ "." ^ method_name ^ " is not a function and cannot be called") p
+		in
+		make_call ctx.typer efield args rt p
+	| _ -> error ((s_type (print_context()) haxe_exception.etype) ^ "." ^ method_name ^ " is expected to be an instance method") p
+
+(**
+	Generate `Std.isOfType(e, t)`
+*)
+let std_is ctx e t p =
+	let t = follow t in
+	let std_cls =
+		match Typeload.load_type_raise ctx.typer ([],"Std") "Std" p with
+		| TClassDecl cls -> cls
+		| _ -> error "Std is expected to be a class" p
+	in
+	let isOfType_field =
+		try PMap.find "isOfType" std_cls.cl_statics
+		with Not_found -> error ("Std has no field isOfType") p
+	in
+	let return_type =
+		match follow isOfType_field.cf_type with
+		| TFun(_,t) -> t
+		| _ -> error ("Std.isOfType is not a function and cannot be called") p
+	in
+	let type_expr = { eexpr = TTypeExpr(module_type_of_type t); etype = t; epos = null_pos } in
+	make_static_call ctx.typer std_cls isOfType_field (fun t -> t) [e; type_expr] return_type p
+
+(**
+	Check if type path of `t` exists in `lst`
+*)
+let is_in_list t lst =
+	match Abstract.follow_with_abstracts t with
+	| TInst(cls,_) ->
+		let rec check cls =
+			List.mem cls.cl_path lst
+			|| List.exists (fun (cls,_) -> check cls) cls.cl_implements
+			|| Option.map_default (fun (cls,_) -> check cls) false cls.cl_super
+		in
+		(match follow t with
+		| TInst (cls, _) -> check cls
+		| _ -> false
+		)
+	| TAbstract({ a_path = path },_)
+	| TEnum({ e_path = path },_) ->
+		List.mem path lst
+	| _ -> false
+
+(**
+	Check if `t` can be thrown without wrapping.
+*)
+let rec is_native_throw cfg t =
+	is_in_list t cfg.ec_native_throws
+
+(**
+	Check if `t` can be caught without wrapping.
+*)
+let rec is_native_catch cfg t =
+	is_in_list t cfg.ec_native_catches
+
+(**
+	Check if `cls` is or extends (if `check_parent=true`) `haxe.Exception`
+*)
+let rec is_haxe_exception_class ?(check_parent=true) cls =
+	cls.cl_path = haxe_exception_type_path
+	|| (check_parent && match cls.cl_super with
+		| None -> false
+		| Some (cls, _) -> is_haxe_exception_class ~check_parent cls
+	)
+
+(**
+	Check if `t` is or extends `haxe.Exception`
+*)
+let is_haxe_exception ?(check_parent=true) (t:Type.t) =
+	match Abstract.follow_with_abstracts t with
+		| TInst (cls, _) -> is_haxe_exception_class ~check_parent cls
+		| _ -> false
+
+(**
+	Check if `v` variable is used in `e` expression
+*)
+let rec is_var_used v e =
+	match e.eexpr with
+	| TLocal v2 -> v == v2
+	| _ -> check_expr (is_var_used v) e
+
+(**
+	Check if `e` contains any throws or try..catches.
+*)
+let rec contains_throw_or_try e =
+	match e.eexpr with
+	| TThrow _ | TTry _ -> true
+	| _ -> check_expr contains_throw_or_try e
+
+(**
+	Returns `true` if `e` has to be wrapped with `haxe.Exception.thrown(e)`
+	to be thrown.
+*)
+let requires_wrapped_throw cfg e =
+	(*
+		Check if `e` is of `haxe.Exception` type directly (not a descendant),
+		but not a `new haxe.Exception(...)` expression.
+		In this case we delegate the decision to `haxe.Exception.thrown(e)`.
+		Because it could happen to be a wrapper for a wildcard catch.
+	*)
+	let is_stored_haxe_exception() =
+		is_haxe_exception ~check_parent:false e.etype
+		&& match e.eexpr with
+			| TNew(_,_,_) -> false
+			| _ -> true
+	in
+	is_stored_haxe_exception()
+	|| (not (is_native_throw cfg e.etype) && not (is_haxe_exception e.etype))
+
+(**
+	Generate a throw of a native exception.
+*)
+let throw_native ctx e_thrown t p =
+	let e_native =
+		if requires_wrapped_throw ctx.config e_thrown then
+			let thrown = haxe_exception_static_call ctx "thrown" [e_thrown] p in
+			if is_dynamic ctx.base_throw_type then thrown
+			else mk_cast thrown ctx.base_throw_type p
+		else
+			e_thrown
+	in
+	mk (TThrow e_native) t p
+
+let set_needs_exception_stack v =
+	if not (Meta.has Meta.NeedsExceptionStack v.v_meta) then
+		v.v_meta <- (Meta.NeedsExceptionStack,[],null_pos) :: v.v_meta
+
+(**
+	Transform user-written `catches` to a set of catches, which would not require
+	special handling in the target generator.
+
+	For example:
+	```
+	} catch(e:SomeNativeError) {
+		doStuff();
+	} catch(e:String) {
+		trace(e);
+	}
+	```
+	is transformed into
+	```
+	} catch(e:SomeNativeError) {
+		doStuff();
+	} catch(etmp:WildCardNativeException) {
+		var ehx:haxe.Exception = haxe.Exception.caught(etmp);
+		if(Std.isOfType(ehx.unwrap(), String)) {
+			var e:String = ehx.unwrap();
+			trace(e);
+		} else {
+			throw etmp;
+		}
+	}
+	```
+*)
+let catch_native ctx catches t p =
+	let rec transform = function
+		| [] -> []
+		(* Keep catches for native exceptions intact *)
+		| (v,_) as current :: rest when (is_native_catch ctx.config v.v_type)
+			(*
+				In case haxe.Exception extends native exception on current target.
+				We don't want it to be generated as a native catch.
+			*)
+			&& not (fast_eq ctx.haxe_exception_type (follow v.v_type)) ->
+			current :: (transform rest)
+		(* Everything else falls into `if(Std.is(e, ExceptionType)`-fest *)
+		| rest ->
+			let catch_var = gen_local ctx.typer ctx.wildcard_catch_type null_pos in
+			let catch_local = mk (TLocal catch_var) catch_var.v_type null_pos in
+			let body =
+				let haxe_exception_var = gen_local ctx.typer ctx.haxe_exception_type null_pos in
+				let haxe_exception_local = mk (TLocal haxe_exception_var) haxe_exception_var.v_type null_pos in
+				let unwrapped_var = gen_local ctx.typer t_dynamic null_pos in
+				let unwrapped_local = mk (TLocal unwrapped_var) unwrapped_var.v_type null_pos in
+				let needs_haxe_exception = ref false
+				and needs_unwrap = ref false in
+				let get_haxe_exception() =
+					needs_haxe_exception := true;
+					haxe_exception_local
+				and unwrap() =
+					needs_haxe_exception := true;
+					needs_unwrap := true;
+					unwrapped_local;
+				in
+				let catch_var_used = ref false in
+				let rec transform = function
+					| (v, body) :: rest ->
+						let current_t = Abstract.follow_with_abstracts v.v_type in
+						let var_used = is_var_used v body in
+						(* catch(e:ExtendsHaxeError) *)
+						if is_haxe_exception current_t then
+							let condition =
+								(* catch(e:haxe.Exception) is a wildcard catch *)
+								if fast_eq ctx.haxe_exception_type current_t then
+									mk (TConst (TBool true)) ctx.basic.tbool v.v_pos
+								else begin
+									std_is ctx (get_haxe_exception()) v.v_type v.v_pos
+								end
+							in
+							let body =
+								if var_used then
+									mk (TBlock [
+										(* var v:ExceptionType = cast haxe_exception_local; *)
+										mk (TVar (v, Some (mk_cast (get_haxe_exception()) v.v_type null_pos))) ctx.basic.tvoid null_pos;
+										body
+									]) body.etype body.epos
+								else
+									body
+							in
+							compose condition body rest
+						(* catch(e:Dynamic) *)
+						else if current_t == t_dynamic then
+							begin
+								set_needs_exception_stack catch_var;
+								(* this is a wildcard catch *)
+								let condition = mk (TConst (TBool true)) ctx.basic.tbool v.v_pos in
+								let body =
+									mk (TBlock [
+										(* var v:Dynamic = haxe_exception_local.unwrap(); *)
+										if var_used then
+											mk (TVar (v, Some (unwrap()))) ctx.basic.tvoid null_pos
+										else
+											mk (TBlock[]) ctx.basic.tvoid null_pos;
+										body
+									]) body.etype body.epos
+								in
+								compose condition body rest
+							end
+						(* catch(e:NativeWildcardException) *)
+						else if fast_eq ctx.wildcard_catch_type current_t then
+							begin
+								set_needs_exception_stack catch_var;
+								(* this is a wildcard catch *)
+								let condition = mk (TConst (TBool true)) ctx.basic.tbool v.v_pos in
+								let body =
+									mk (TBlock [
+										(* var v:NativeWildcardException = catch_var; *)
+										if var_used then
+											mk (TVar (v, Some catch_local)) ctx.basic.tvoid null_pos
+										else
+											mk (TBlock[]) ctx.basic.tvoid null_pos;
+										body
+									]) body.etype body.epos
+								in
+								compose condition body rest
+							end
+						(* catch(e:AnythingElse) *)
+						else begin
+							set_needs_exception_stack catch_var;
+							let condition =
+								catch_var_used := true;
+								(* Std.isOfType(haxe_exception_local.unwrap(), ExceptionType) *)
+								std_is ctx (unwrap()) v.v_type v.v_pos
+							in
+							let body =
+								mk (TBlock [
+									(* var v:ExceptionType = cast haxe_exception_local.unwrap() *)
+									if var_used then
+										mk (TVar (v, Some (mk_cast (unwrap()) v.v_type null_pos))) ctx.basic.tvoid null_pos
+									else
+										mk (TBlock[]) ctx.basic.tvoid null_pos;
+									body
+								]) body.etype body.epos
+							in
+							compose condition body rest
+						end
+					| [] -> mk (TThrow catch_local) t p
+				and compose condition body rest_catches =
+					let else_body =
+						match rest_catches with
+						| [] -> mk (TThrow catch_local) (mk_mono()) p
+						| _ -> transform rest_catches
+					in
+					mk (TIf(condition, body, Some else_body)) t p
+				in
+				let transformed_catches = transform rest in
+				(* haxe.Exception.caught(catch_var) *)
+				let caught = haxe_exception_static_call ctx "caught" [catch_local] null_pos in
+				let exprs = [
+					(* var haxe_exception_local = haxe.Exception.caught(catch_var); *)
+					if !needs_haxe_exception then
+						(mk (TVar (haxe_exception_var, Some caught)) ctx.basic.tvoid null_pos)
+					else
+						mk (TBlock[]) ctx.basic.tvoid null_pos;
+					(* var unwrapped_local = haxe_exception_local.unwrap(); *)
+					if !needs_unwrap then
+						let unwrap = haxe_exception_instance_call ctx haxe_exception_local "unwrap" [] null_pos in
+						mk (TVar (unwrapped_var, Some unwrap)) ctx.basic.tvoid null_pos
+					else
+						mk (TBlock[]) ctx.basic.tvoid null_pos;
+					transformed_catches
+				] in
+				mk (TBlock exprs) t p
+			in (* let body =  *)
+			[(catch_var,body)]
+	in
+	transform catches
+
+(**
+	Transform `throw` and `try..catch` expressions.
+	`rename_locals` is required to deal with the names of temp vars.
+*)
+let filter tctx =
+	let stub e = e in
+	match tctx.com.platform with (* TODO: implement for all targets *)
+	| Php | Js | Java | Cs | Python | Lua | Eval | Neko | Flash | Hl | Cpp ->
+		let config = tctx.com.config.pf_exceptions in
+		let tp (pack,name) =
+			match List.rev pack with
+			| module_name :: pack_rev when not (Ast.is_lower_ident module_name) ->
+				({ tpackage = List.rev pack_rev; tname = module_name; tparams = []; tsub = Some name },null_pos)
+			| _ ->
+				({ tpackage = pack; tname = name; tparams = []; tsub = None },null_pos)
+		in
+		let wildcard_catch_type =
+			let t = Typeload.load_instance tctx (tp config.ec_wildcard_catch) true in
+			if is_dynamic t then t_dynamic
+			else t
+		and base_throw_type =
+			let t = Typeload.load_instance tctx (tp config.ec_base_throw) true in
+			if is_dynamic t then t_dynamic
+			else t
+		and haxe_exception_type, haxe_exception_class =
+			match Typeload.load_instance tctx (tp haxe_exception_type_path) true with
+			| TInst(cls,_) as t -> t,cls
+			| _ -> error "haxe.Exception is expected to be a class" null_pos
+		and haxe_native_stack_trace =
+			match Typeload.load_instance tctx (tp (["haxe"],"NativeStackTrace")) true with
+			| TInst(cls,_) -> cls
+			| TAbstract({ a_impl = Some cls },_) -> cls
+			| _ -> error "haxe.NativeStackTrace is expected to be a class or an abstract" null_pos
+		in
+		let ctx = {
+			typer = tctx;
+			basic = tctx.t;
+			config = config;
+			wildcard_catch_type = wildcard_catch_type;
+			base_throw_type = base_throw_type;
+			haxe_exception_class = haxe_exception_class;
+			haxe_exception_type = haxe_exception_type;
+			haxe_native_stack_trace = haxe_native_stack_trace;
+		} in
+		let rec run e =
+			match e.eexpr with
+			| TThrow e1 ->
+				{ e with eexpr = TThrow (throw_native ctx (run e1) e.etype e.epos) }
+			| TTry(e1,catches) ->
+				let catches =
+					let catches = List.map (fun (v,e) -> (v,run e)) catches in
+					(catch_native ctx catches e.etype e.epos)
+				in
+				{ e with eexpr = TTry(run e1,catches) }
+			| _ ->
+				map_expr run e
+		in
+		(fun e ->
+			if contains_throw_or_try e then run e
+			else stub e
+		)
+	| Cross -> stub
+
+(**
+	Inserts `haxe.NativeStackTrace.saveStack(e)` in non-haxe.Exception catches.
+*)
+let insert_save_stacks tctx =
+	if not (has_feature tctx.com "haxe.NativeStackTrace.exceptionStack") then
+		(fun e -> e)
+	else
+		let native_stack_trace_cls =
+			let tp = { tpackage = ["haxe"]; tname = "NativeStackTrace"; tparams = []; tsub = None } in
+			match Typeload.load_type_def tctx null_pos tp with
+			| TClassDecl cls -> cls
+			| TAbstractDecl { a_impl = Some cls } -> cls
+			| _ -> error "haxe.NativeStackTrace is expected to be a class or an abstract" null_pos
+		in
+		let rec contains_insertion_points e =
+			match e.eexpr with
+			| TTry (e, catches) ->
+				List.exists (fun (v, _) -> Meta.has Meta.NeedsExceptionStack v.v_meta) catches
+				|| contains_insertion_points e
+				|| List.exists (fun (_, e) -> contains_insertion_points e) catches
+			| _ ->
+				check_expr contains_insertion_points e
+		in
+		let save_exception_stack catch_var =
+			(* GOTCHA: `has_feature` always returns `true` if executed before DCE filters *)
+			if has_feature tctx.com "haxe.NativeStackTrace.exceptionStack" then
+				let method_field =
+					try PMap.find "saveStack" native_stack_trace_cls.cl_statics
+					with Not_found -> error ("haxe.NativeStackTrace has no field saveStack") null_pos
+				in
+				let return_type =
+					match follow method_field.cf_type with
+					| TFun(_,t) -> t
+					| _ -> error ("haxe.NativeStackTrace." ^ method_field.cf_name ^ " is not a function and cannot be called") null_pos
+				in
+				let catch_local = mk (TLocal catch_var) catch_var.v_type null_pos in
+				make_static_call tctx native_stack_trace_cls method_field (fun t -> t) [catch_local] return_type null_pos
+			else
+				mk (TBlock[]) tctx.t.tvoid null_pos
+		in
+		let rec run e =
+			match e.eexpr with
+			| TTry (e1, catches) ->
+				let e1 = map_expr run e1 in
+				let catches =
+					List.map (fun ((v, body) as catch) ->
+						if Meta.has Meta.NeedsExceptionStack v.v_meta then
+							let exprs =
+								match body.eexpr with
+								| TBlock exprs ->
+									save_exception_stack v :: exprs
+								| _ ->
+									[save_exception_stack v; body]
+							in
+							(v, { body with eexpr = TBlock exprs })
+						else
+							catch
+					) catches
+				in
+				{ e with eexpr = TTry (e1, catches) }
+			| _ ->
+				map_expr run e
+		in
+		(fun e ->
+			if contains_insertion_points e then run e
+			else e
+		)
+
+(**
+	Adds `this.__shiftStack()` calls to constructors of classes which extend `haxe.Exception`
+*)
+let patch_constructors tctx =
+	let tp = ({ tpackage = fst haxe_exception_type_path; tname = snd haxe_exception_type_path; tparams = []; tsub = None },null_pos) in
+	match Typeload.load_instance tctx tp true with
+	(* Add only if `__shiftStack` method exists *)
+	| TInst(cls,_) when PMap.mem "__shiftStack" cls.cl_fields ->
+		(fun mt ->
+			match mt with
+			| TClassDecl cls when not cls.cl_extern && cls.cl_path <> haxe_exception_type_path && is_haxe_exception_class cls ->
+				let shift_stack p =
+					let t = type_of_module_type mt in
+					let this = { eexpr = TConst(TThis); etype = t; epos = p } in
+					let faccess =
+						try quick_field t "__shiftStack"
+						with Not_found -> error "haxe.Exception has no field __shiftStack" p
+					in
+					match faccess with
+					| FInstance (_,_,cf) ->
+						let efield = { eexpr = TField(this,faccess); etype = cf.cf_type; epos = p } in
+						let rt =
+							match follow cf.cf_type with
+							| TFun(_,t) -> t
+							| _ ->
+								error "haxe.Exception.__shiftStack is not a function and cannot be called" cf.cf_name_pos
+						in
+						make_call tctx efield [] rt p
+					| _ -> error "haxe.Exception.__shiftStack is expected to be an instance method" p
+				in
+				TypeloadFunction.add_constructor tctx cls true cls.cl_name_pos;
+				Option.may (fun cf -> ignore(follow cf.cf_type)) cls.cl_constructor;
+				(match cls.cl_constructor with
+				| Some ({ cf_expr = Some e_ctor } as ctor) ->
+					let rec add e =
+						match e.eexpr with
+						| TFunction _ -> e
+						| TReturn _ -> mk (TBlock [shift_stack e.epos; e]) e.etype e.epos
+						| _ -> map_expr add e
+					in
+					(ctor.cf_expr <- match e_ctor.eexpr with
+						| TFunction fn ->
+							Some { e_ctor with
+								eexpr = TFunction { fn with
+									tf_expr = mk (TBlock [add fn.tf_expr; shift_stack fn.tf_expr.epos]) tctx.t.tvoid fn.tf_expr.epos
+								}
+							}
+						| _ -> assert false
+					)
+				| None -> assert false
+				| _ -> ()
+				)
+			| _ -> ()
+		)
+	| _ -> (fun _ -> ())

+ 5 - 9
src/filters/filters.ml

@@ -827,22 +827,17 @@ let run com tctx main =
 		if defined com Define.AnalyzerOptimize then Tre.run tctx else (fun e -> e);
 		if defined com Define.AnalyzerOptimize then Tre.run tctx else (fun e -> e);
 		Optimizer.reduce_expression tctx;
 		Optimizer.reduce_expression tctx;
 		if Common.defined com Define.OldConstructorInline then Optimizer.inline_constructors tctx else InlineConstructors.inline_constructors tctx;
 		if Common.defined com Define.OldConstructorInline then Optimizer.inline_constructors tctx else InlineConstructors.inline_constructors tctx;
+		Exceptions.filter tctx;
 		CapturedVars.captured_vars com;
 		CapturedVars.captured_vars com;
 	] in
 	] in
 	let filters =
 	let filters =
 		match com.platform with
 		match com.platform with
 		| Cs ->
 		| Cs ->
 			SetHXGen.run_filter com new_types;
 			SetHXGen.run_filter com new_types;
-			filters @ [
-				TryCatchWrapper.configure_cs com
-			]
+			filters
 		| Java when not (Common.defined com Jvm)->
 		| Java when not (Common.defined com Jvm)->
 			SetHXGen.run_filter com new_types;
 			SetHXGen.run_filter com new_types;
-			filters @ [
-				TryCatchWrapper.configure_java com
-			]
-		| Js ->
-			filters @ [JsExceptions.init tctx];
+			filters
 		| _ -> filters
 		| _ -> filters
 	in
 	in
 	let t = filter_timer detail_times ["expr 1"] in
 	let t = filter_timer detail_times ["expr 1"] in
@@ -917,7 +912,9 @@ let run com tctx main =
 	t();
 	t();
 	com.stage <- CDceDone;
 	com.stage <- CDceDone;
 	(* PASS 3: type filters post-DCE *)
 	(* PASS 3: type filters post-DCE *)
+	List.iter (run_expression_filters tctx [Exceptions.insert_save_stacks tctx]) new_types;
 	let type_filters = [
 	let type_filters = [
+		Exceptions.patch_constructors;
 		check_private_path;
 		check_private_path;
 		apply_native_paths;
 		apply_native_paths;
 		add_rtti;
 		add_rtti;
@@ -930,7 +927,6 @@ let run com tctx main =
 	] in
 	] in
 	let type_filters = match com.platform with
 	let type_filters = match com.platform with
 		| Cs -> type_filters @ [ fun _ t -> InterfaceProps.run t ]
 		| Cs -> type_filters @ [ fun _ t -> InterfaceProps.run t ]
-		| Js -> JsExceptions.inject_callstack com type_filters
 		| _ -> type_filters
 		| _ -> type_filters
 	in
 	in
 	let t = filter_timer detail_times ["type 3"] in
 	let t = filter_timer detail_times ["type 3"] in

+ 0 - 212
src/filters/jsExceptions.ml

@@ -1,212 +0,0 @@
-(*
-	The Haxe Compiler
-	Copyright (C) 2005-2019  Haxe Foundation
-
-	This program is free software; you can redistribute it and/or
-	modify it under the terms of the GNU General Public License
-	as published by the Free Software Foundation; either version 2
-	of the License, or (at your option) any later version.
-
-	This program is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-	GNU General Public License for more details.
-
-	You should have received a copy of the GNU General Public License
-	along with this program; if not, write to the Free Software
-	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *)
-
-(*
-	This filter handles everything related to exceptions for the JavaScript target:
-
-	- wrapping non-js.Error types in HaxeError on throwing
-	- unwrapping HaxeError on catch
-	- transforming series of catches into a single catch with Std.is checks (optimized)
-	- re-throwing caught exception with js.Lib.rethrow
-	- storing caught exception in haxe.CallStack.lastException (if haxe.CallStack is used)
-
-	Basically it translates this:
-
-	 try throw "fail"
-	 catch (e:String) { trace(e); js.Lib.rethrow(); }
-	 catch (e:Bool) {}
-
-	into something like this (JS):
-
-	 try {
-		 throw new HaxeError("fail");
-	 } catch (e) {
-		 haxe.CallStack.lastException = e;
-		 var e1 = (e instanceof HaxeError) e.val : e;
-		 if (typeof e1 == "string") {
-			 trace(e1);
-			 throw e;
-		 } else if (typeof e1 == "boolean") {
-		 } else {
-			 throw e;
-		 }
-	 }
-*)
-
-open Common
-open Type
-open Typecore
-open Texpr.Builder
-
-let follow = Abstract.follow_with_abstracts
-
-let rec is_js_error c =
-	match c with
-	| { cl_path = ["js";"lib"],"Error" } -> true
-	| { cl_super = Some (csup,_) } -> is_js_error csup
-	| _ -> false
-
-let find_cl com path =
-	ExtList.List.find_map (function
-		| TClassDecl c when c.cl_path = path -> Some c
-		| _ -> None
-	) com.types
-
-let init ctx =
-	let cJsError = find_cl ctx.com (["js";"lib"],"Error") in
-	let cHaxeError = find_cl ctx.com (["js";"_Boot"],"HaxeError") in
-	let cStd = find_cl ctx.com ([],"Std") in
-	let cBoot = find_cl ctx.com (["js"],"Boot") in
-	let cSyntax = find_cl ctx.com (["js"],"Syntax") in
-
-	let dynamic_wrap e =
-		let eHaxeError = make_static_this cHaxeError e.epos in
-		fcall eHaxeError "wrap" [e] (TInst (cJsError, [])) e.epos
-	in
-
-	let static_wrap e =
-		{ e with eexpr = TNew (cHaxeError,[],[e]); etype = TInst (cHaxeError,[]) }
-	in
-
-	let rec loop vrethrow e =
-		match e.eexpr with
-		| TThrow eexc ->
-			let eexc = loop vrethrow eexc in
-			let eexc =
-				match follow eexc.etype with
-				| TDynamic _ | TMono _ ->
-					(match eexc.eexpr with
-					| TConst (TInt _ | TFloat _ | TString _ | TBool _ | TNull) -> static_wrap eexc
-					| _ -> dynamic_wrap eexc)
-				| TInst (c,_) when (is_js_error c) ->
-					eexc
-				| _ ->
-					static_wrap eexc
-			in
-			{ e with eexpr = TThrow eexc }
-
-		| TCall ({ eexpr = TField (_, FStatic ({ cl_path = ["js"],"Lib" }, { cf_name = "getOriginalException" })) }, _) ->
-			(match vrethrow with
-			| Some erethrowvar -> erethrowvar
-			| None -> abort "js.Lib.getOriginalException can only be called inside a catch block" e.epos)
-
-		| TCall ({ eexpr = TField (_, FStatic ({ cl_path = ["js"],"Lib" }, { cf_name = "rethrow" })) }, _) ->
-			(match vrethrow with
-			| Some erethrowvar -> { e with eexpr = TThrow erethrowvar }
-			| None -> abort "js.Lib.rethrow can only be called inside a catch block" e.epos)
-
-		| TTry (etry, catches) ->
-			let etry = loop vrethrow etry in
-
-			let catchall_name, catchall_kind = match catches with [(v,_)] -> v.v_name, (VUser TVOCatchVariable) | _ -> "e", VGenerated in
-			let vcatchall = alloc_var catchall_kind catchall_name t_dynamic e.epos in
-			let ecatchall = make_local vcatchall e.epos in
-			let erethrow = mk (TThrow ecatchall) t_dynamic e.epos in
-
-			let eSyntax = make_static_this cSyntax e.epos in
-			let eHaxeError = make_static_this cHaxeError e.epos in
-			let eInstanceof = fcall eSyntax "instanceof" [ecatchall;eHaxeError] ctx.com.basic.tbool e.epos in
-			let eVal = field { ecatchall with etype = TInst (cHaxeError,[]) } "val" t_dynamic e.epos in
-			let eunwrap = mk (TIf (eInstanceof, eVal, Some (ecatchall))) t_dynamic e.epos in
-
-			let vunwrapped = alloc_var catchall_kind catchall_name t_dynamic e.epos in
-			vunwrapped.v_kind <- VGenerated;
-			let eunwrapped = make_local vunwrapped e.epos in
-
-			let ecatch = List.fold_left (fun acc (v,ecatch) ->
-				let ecatch = loop (Some ecatchall) ecatch in
-
-				(* it's not really compiler-generated, but it kind of is, since it was used as catch identifier and we add a TVar for it *)
-				v.v_kind <- VGenerated;
-
-				match follow v.v_type with
-				| TDynamic _ ->
-					{ ecatch with
-						eexpr = TBlock [
-							mk (TVar (v, Some eunwrapped)) ctx.com.basic.tvoid ecatch.epos;
-							ecatch;
-						]
-					}
-				| t ->
-					let etype = make_typeexpr (module_type_of_type t) e.epos in
-					let args = [eunwrapped;etype] in
-					let echeck =
-						match Inline.api_inline ctx cStd "isOfType" args e.epos with
-						| Some e -> e
-						| None ->
-							let eBoot = make_static_this cBoot e.epos in
-							fcall eBoot "__instanceof" [eunwrapped;etype] ctx.com.basic.tbool e.epos
-					in
-					let ecatch = { ecatch with
-						eexpr = TBlock [
-							mk (TVar (v, Some eunwrapped)) ctx.com.basic.tvoid ecatch.epos;
-							ecatch;
-						]
-					} in
-					mk (TIf (echeck, ecatch, Some acc)) e.etype e.epos
-			) erethrow (List.rev catches) in
-
-			let ecatch = { ecatch with
-				eexpr = TBlock [
-					mk (TVar (vunwrapped, Some eunwrap)) ctx.com.basic.tvoid e.epos;
-					ecatch;
-				]
-			} in
-			{ e with eexpr = TTry (etry, [(vcatchall,ecatch)]) }
-		| _ ->
-			Type.map_expr (loop vrethrow) e
-	in
-	loop None
-
-let inject_callstack com type_filters =
-	let cCallStack =
-		if Common.has_dce com then
-			if Common.has_feature com "haxe.CallStack.lastException" then
-				Some (find_cl com (["haxe"],"CallStack"))
-			else
-				None
-		else
-			try Some (find_cl com (["haxe"],"CallStack")) with Not_found -> None
-	in
-	match cCallStack with
-	| Some cCallStack ->
-		let run mt e =
-			let rec loop e =
-				match e.eexpr with
-				| TTry (etry,[(v,ecatch)]) ->
-					let etry = loop etry in
-					let ecatch = loop ecatch in
-					add_dependency (t_infos mt).mt_module cCallStack.cl_module;
-					let eCallStack = make_static_this cCallStack ecatch.epos in
-					let elastException = field eCallStack "lastException" t_dynamic ecatch.epos in
-					let elocal = make_local v ecatch.epos in
-					let eStoreException = mk (TBinop (Ast.OpAssign, elastException, elocal)) ecatch.etype ecatch.epos in
-					let ecatch = Type.concat eStoreException ecatch in
-					{ e with eexpr = TTry (etry,[(v,ecatch)]) }
-				| TTry _ ->
-					(* this should be handled by the filter above *)
-					assert false
-				| _ ->
-					Type.map_expr loop e
-			in
-			loop e
-		in
-		type_filters @ [ fun ctx t -> FiltersCommon.run_expression_filters ctx [run t] t ]
-	| None ->
-		type_filters

+ 0 - 187
src/filters/tryCatchWrapper.ml

@@ -1,187 +0,0 @@
-(*
-	The Haxe Compiler
-	Copyright (C) 2005-2019  Haxe Foundation
-
-	This program is free software; you can redistribute it and/or
-	modify it under the terms of the GNU General Public License
-	as published by the Free Software Foundation; either version 2
-	of the License, or (at your option) any later version.
-
-	This program is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-	GNU General Public License for more details.
-
-	You should have received a copy of the GNU General Public License
-	along with this program; if not, write to the Free Software
-	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*)
-open Globals
-open Common
-open Ast
-open Type
-open Codegen
-open Texpr.Builder
-
-(* ******************************************* *)
-(* Try / Catch + throw native types handling *)
-(* ******************************************* *)
-(*
-	Some languages/vm's do not support throwing any kind of value. For them, only
-	special kinds of objects can be thrown. Because of this, we must wrap some throw
-	statements with an expression, and also we must unwrap it on the catch() phase, and
-	maybe manually test with Std.is()
-*)
-
-(*
-	should_wrap : does the type should be wrapped? This of course works on the reverse way, so it tells us if the type should be unwrapped as well
-	wrap_throw : the wrapper for throw (throw expr->returning wrapped expression)
-	unwrap_expr : the other way around : given the catch var (maybe will need casting to wrapper_type) , return the unwrap expr
-	rethrow_expr : how to rethrow ane exception in the platform
-	catchall_type : the class used for catchall (e:Dynamic)
-	wrapper_type : the wrapper type, so we can test if exception is of type 'wrapper'
-	catch_map : maps the catch expression to include some intialization code (e.g. setting up Stack.exceptionStack)
-	gen_typecheck : generate Std.is (or similar) check expression for given expression and type
-*)
-let init com (should_wrap:t->bool) (wrap_throw:texpr->texpr) (unwrap_expr:texpr->texpr) (rethrow_expr:texpr->texpr) (catchall_type:t) (wrapper_type:t) (catch_map:tvar->texpr->texpr) (gen_typecheck:texpr->t->pos->texpr) =
-	let rec run e =
-		match e.eexpr with
-		| TThrow texpr when should_wrap texpr.etype ->
-			wrap_throw (run texpr)
-		| TTry (ttry, catches) ->
-			let nowrap_catches, must_wrap_catches, catchall = List.fold_left (fun (nowrap_catches, must_wrap_catches, catchall) (v, catch) ->
-				(* first we'll see if the type is Dynamic (catchall) *)
-				match follow v.v_type with
-				| TDynamic _ ->
-					assert (Option.is_none catchall);
-					(nowrap_catches, must_wrap_catches, Some(v, run catch))
-				(* see if we should unwrap it *)
-				| _ when should_wrap (follow v.v_type) ->
-					(nowrap_catches, (v,run catch) :: must_wrap_catches, catchall)
-				| _ ->
-					((v,catch_map v (run catch)) :: nowrap_catches, must_wrap_catches, catchall)
-			) ([], [], None) catches in
-
-			(* temp (?) fix for https://github.com/HaxeFoundation/haxe/issues/4134 *)
-			let must_wrap_catches = List.rev must_wrap_catches in
-
-			(*
-				1st catch all nowrap "the easy way"
-				2nd see if there are any must_wrap or catchall. If there is,
-					do a catchall first with a temp var.
-					then get catchall var (as dynamic) (or create one), and declare it = catchall exception
-					then test if it is of type wrapper_type. If it is, unwrap it
-					then start doing Std.is() tests for each catch type
-					if there is a catchall in the end, end with it. If there isn't, rethrow
-			*)
-			let dyn_catch = match catchall, must_wrap_catches with
-			| Some (v,c), _
-			| _, (v, c) :: _ ->
-				let pos = c.epos in
-
-				let temp_var = alloc_var VGenerated "catchallException" catchall_type pos in
-				let temp_local = make_local temp_var pos in
-				let catchall_var = alloc_var VGenerated "realException" t_dynamic pos in
-				let catchall_local = make_local catchall_var pos in
-
-				(* if it is of type wrapper_type, unwrap it *)
-				let catchall_expr = mk (TIf (gen_typecheck temp_local wrapper_type pos, unwrap_expr temp_local, Some temp_local)) t_dynamic pos in
-				let catchall_decl = mk (TVar (catchall_var, Some catchall_expr)) com.basic.tvoid pos in
-
-				let rec loop must_wrap_catches =
-					match must_wrap_catches with
-					| (vcatch,catch) :: tl ->
-						mk (TIf (gen_typecheck catchall_local vcatch.v_type catch.epos,
-							     mk (TBlock [(mk (TVar (vcatch, Some(mk_cast (* TODO: this should be a fast non-dynamic cast *) catchall_local vcatch.v_type pos))) com.basic.tvoid catch.epos); catch]) catch.etype catch.epos,
-							     Some (loop tl))
-						) catch.etype catch.epos
-					| [] ->
-						match catchall with
-						| Some (v,s) ->
-							Type.concat (mk (TVar (v, Some catchall_local)) com.basic.tvoid pos) s
-						| None ->
-							mk_block (rethrow_expr temp_local)
-				in
-				[(temp_var, catch_map temp_var { e with eexpr = TBlock [catchall_decl; loop must_wrap_catches] })]
-			| _ ->
-				[]
-			in
-			{ e with eexpr = TTry(run ttry, (List.rev nowrap_catches) @ dyn_catch) }
-		| _ ->
-			Type.map_expr run e
-	in
-	run
-
-let find_class com path =
-	let mt = List.find (fun mt -> match mt with TClassDecl c -> c.cl_path = path | _ -> false) com.types in
-	match mt with TClassDecl c -> c | _ -> assert false
-
-let configure_cs com =
-	let base_exception = find_class com (["cs";"system"], "Exception") in
-	let base_exception_t = TInst(base_exception, []) in
-	let hx_exception = find_class com (["cs";"internal";"_Exceptions"], "HaxeException") in
-	let hx_exception_t = TInst (hx_exception, []) in
-	let exc_cl = find_class com (["cs";"internal"],"Exceptions") in
-	let rec is_exception t =
-		match follow t with
-		| TInst (cl,_) -> is_parent base_exception cl
-		| _ -> false
-	in
-	let e_rethrow = mk (TIdent "__rethrow__") t_dynamic null_pos in
-	let should_wrap t = not (is_exception t) in
-	let wrap_throw expr =
-		match expr.eexpr with
-		| TIdent "__rethrow__" ->
-			make_throw expr expr.epos
-		| _ ->
-			let e_hxexception = make_static_this hx_exception expr.epos in
-			let e_wrap = fcall e_hxexception "wrap" [expr] base_exception_t expr.epos in
-			make_throw e_wrap expr.epos
-	in
-	let unwrap_expr local_to_unwrap = field (mk_cast local_to_unwrap hx_exception_t local_to_unwrap.epos) "obj" t_dynamic local_to_unwrap.epos in
-	let rethrow_expr rethrow = make_throw e_rethrow rethrow.epos in
-	let catch_map v e =
-		let e_exc = make_static_this exc_cl e.epos in
-		let e_field = field e_exc "exception" base_exception_t e.epos in
-		let e_setstack = binop OpAssign e_field (make_local v e.epos) v.v_type e.epos in
-		Type.concat e_setstack e
-	in
-	let std_cl = find_class com ([],"Std") in
-	let gen_typecheck e t pos =
-		let std = make_static_this std_cl pos in
-		let e_type = make_typeexpr (module_type_of_type t) pos in
-		fcall std "isOfType" [e; e_type] com.basic.tbool pos
-	in
-	init com should_wrap wrap_throw unwrap_expr rethrow_expr base_exception_t hx_exception_t catch_map gen_typecheck
-
-let configure_java com =
-	let base_exception = find_class com (["java"; "lang"], "Throwable") in
-	let base_exception_t = TInst (base_exception, []) in
-	let hx_exception = find_class com (["java";"internal";"_Exceptions"], "HaxeException") in
-	let hx_exception_t = TInst (hx_exception, []) in
-	let exc_cl = find_class com (["java";"internal"],"Exceptions") in
-	let rec is_exception t =
-		match follow t with
-		| TInst (cl,_) -> is_parent base_exception cl
-		| _ -> false
-	in
-	let should_wrap t = not (is_exception t) in
-	let wrap_throw expr =
-		let e_hxexception = make_static_this hx_exception expr.epos in
-		let e_wrap = fcall e_hxexception "wrap" [expr] base_exception_t expr.epos in
-		make_throw e_wrap expr.epos
-	in
-	let unwrap_expr local_to_unwrap = field (mk_cast local_to_unwrap hx_exception_t local_to_unwrap.epos) "obj" t_dynamic local_to_unwrap.epos in
-	let rethrow_expr exc = { exc with eexpr = TThrow exc } in
-	let catch_map v e =
-		let exc = make_static_this exc_cl e.epos in
-		let e_setstack = fcall exc "setException" [make_local v e.epos] com.basic.tvoid e.epos in
-		Type.concat e_setstack e;
-	in
-	let std_cl = find_class com ([],"Std") in
-	let gen_typecheck e t pos =
-		let std = make_static_this std_cl pos in
-		let e_type = make_typeexpr (module_type_of_type t) pos in
-		fcall std "is" [e; e_type] com.basic.tbool pos
-	in
-	init com should_wrap wrap_throw unwrap_expr rethrow_expr base_exception_t hx_exception_t catch_map gen_typecheck

+ 5 - 3
src/generators/genjava.ml

@@ -905,11 +905,13 @@ let rec handle_throws gen cf =
 			Type.iter iter e
 			Type.iter iter e
 		with | Exit -> (* needs typed exception to be caught *)
 		with | Exit -> (* needs typed exception to be caught *)
 			let throwable = get_cl (get_type gen (["java";"lang"],"Throwable")) in
 			let throwable = get_cl (get_type gen (["java";"lang"],"Throwable")) in
+			let cast_cl = get_cl (get_type gen (["java";"lang"],"RuntimeException")) in
 			let catch_var = alloc_var "typedException" (TInst(throwable,[])) in
 			let catch_var = alloc_var "typedException" (TInst(throwable,[])) in
 			let rethrow = mk_local catch_var e.epos in
 			let rethrow = mk_local catch_var e.epos in
-			let hx_exception = get_cl (get_type gen (["haxe";"lang"], "HaxeException")) in
-			let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], t_dynamic)) rethrow.epos in
-			let wrapped = { rethrow with eexpr = TThrow { rethrow with eexpr = TCall(wrap_static, [rethrow]) }; } in
+			let hx_exception = get_cl (get_type gen (["haxe"], "Exception")) in
+			let wrap_static = mk_static_field_access (hx_exception) "thrown" (TFun([("obj",false,t_dynamic)], t_dynamic)) rethrow.epos in
+			let thrown_value = mk_cast (TInst(cast_cl,[])) { rethrow with eexpr = TCall(wrap_static, [rethrow]) } in
+			let wrapped = { rethrow with eexpr = TThrow thrown_value; } in
 			let map_throws cl =
 			let map_throws cl =
 				let var = alloc_var "typedException" (TInst(cl,List.map (fun _ -> t_dynamic) cl.cl_params)) in
 				let var = alloc_var "typedException" (TInst(cl,List.map (fun _ -> t_dynamic) cl.cl_params)) in
 				var, { tf.tf_expr with eexpr = TThrow (mk_local var e.epos) }
 				var, { tf.tf_expr with eexpr = TThrow (mk_local var e.epos) }

+ 20 - 1
src/generators/genjs.ml

@@ -61,6 +61,7 @@ type ctx = {
 	mutable type_accessor : module_type -> string;
 	mutable type_accessor : module_type -> string;
 	mutable separator : bool;
 	mutable separator : bool;
 	mutable found_expose : bool;
 	mutable found_expose : bool;
+	mutable catch_vars : texpr list;
 }
 }
 
 
 type object_store = {
 type object_store = {
@@ -403,6 +404,21 @@ let rec gen_call ctx e el in_value =
 		spr ctx ")";
 		spr ctx ")";
 	| TField (_, FStatic ({ cl_path = ["js"],"Syntax" }, { cf_name = meth })), args ->
 	| TField (_, FStatic ({ cl_path = ["js"],"Syntax" }, { cf_name = meth })), args ->
 		gen_syntax ctx meth args e.epos
 		gen_syntax ctx meth args e.epos
+	| TField (_, FStatic ({ cl_path = ["js"],"Lib" }, { cf_name = "rethrow" })), [] ->
+		(match ctx.catch_vars with
+			| e :: _ ->
+				spr ctx "throw ";
+				gen_value ctx e
+			| _ ->
+				abort "js.Lib.rethrow can only be called inside a catch block" e.epos
+		)
+	| TField (_, FStatic ({ cl_path = ["js"],"Lib" }, { cf_name = "getOriginalException" })), [] ->
+		(match ctx.catch_vars with
+			| e :: _ ->
+				gen_value ctx e
+			| _ ->
+				abort "js.Lib.getOriginalException can only be called inside a catch block" e.epos
+		)
 	| TIdent "__new__", args ->
 	| TIdent "__new__", args ->
 		print_deprecation_message ctx.com "__new__ is deprecated, use js.Syntax.construct instead" e.epos;
 		print_deprecation_message ctx.com "__new__ is deprecated, use js.Syntax.construct instead" e.epos;
 		gen_syntax ctx "construct" args e.epos
 		gen_syntax ctx "construct" args e.epos
@@ -711,7 +727,9 @@ and gen_expr ctx e =
 		gen_expr ctx etry;
 		gen_expr ctx etry;
 		check_var_declaration v;
 		check_var_declaration v;
 		print ctx " catch( %s ) " v.v_name;
 		print ctx " catch( %s ) " v.v_name;
-		gen_expr ctx ecatch
+		ctx.catch_vars <- (mk (TLocal v) v.v_type v.v_pos) :: ctx.catch_vars;
+		gen_expr ctx ecatch;
+		ctx.catch_vars <- List.tl ctx.catch_vars
 	| TTry _ ->
 	| TTry _ ->
 		abort "Unhandled try/catch, please report" e.epos
 		abort "Unhandled try/catch, please report" e.epos
 	| TSwitch (e,cases,def) ->
 	| TSwitch (e,cases,def) ->
@@ -1591,6 +1609,7 @@ let alloc_ctx com es_version =
 		type_accessor = (fun _ -> assert false);
 		type_accessor = (fun _ -> assert false);
 		separator = false;
 		separator = false;
 		found_expose = false;
 		found_expose = false;
+		catch_vars = [];
 	} in
 	} in
 
 
 	ctx.type_accessor <- (fun t ->
 	ctx.type_accessor <- (fun t ->

+ 24 - 108
src/generators/genjvm.ml

@@ -51,6 +51,7 @@ exception HarderFailure of string
 type generation_context = {
 type generation_context = {
 	com : Common.context;
 	com : Common.context;
 	jar : Zip.out_file;
 	jar : Zip.out_file;
+	t_runtime_exception : Type.t;
 	entry_point : (tclass * texpr) option;
 	entry_point : (tclass * texpr) option;
 	t_exception : Type.t;
 	t_exception : Type.t;
 	t_throwable : Type.t;
 	t_throwable : Type.t;
@@ -294,45 +295,26 @@ let type_unifies a b =
 
 
 let follow = Abstract.follow_with_abstracts
 let follow = Abstract.follow_with_abstracts
 
 
-class haxe_exception gctx (t : Type.t) = object(self)
-	val native_exception =
-		if follow t == t_dynamic then
-			throwable_sig,false
-		else if type_unifies t gctx.t_exception then
-			jsignature_of_type gctx t,true
-		else
-			haxe_exception_sig,false
-
-	val mutable native_exception_path = None
+class haxe_exception gctx (t : Type.t) =
+	let is_haxe_exception = Exceptions.is_haxe_exception t
+	and native_type = jsignature_of_type gctx t in
+object(self)
+	val native_path = (match native_type with TObject(path,_) -> path | _ -> assert false)
 
 
 	method is_assignable_to (exc2 : haxe_exception) =
 	method is_assignable_to (exc2 : haxe_exception) =
-		match self#is_native_exception,exc2#is_native_exception with
-		| true, true ->
-			(* Native exceptions are assignable if they unify *)
+		match self#is_haxe_exception,exc2#is_haxe_exception with
+		| true, true | false, false ->
 			type_unifies t exc2#get_type
 			type_unifies t exc2#get_type
-		| false,false ->
-			(* Haxe exceptions are always assignable to each other *)
-			true
+		(* `haxe.Exception` is assignable to java.lang.RuntimeException/Exception/Throwable *)
 		| false,true ->
 		| false,true ->
-			(* Haxe exception is assignable to native only if caught type is java.lang.Exception/Throwable *)
-			let exc2_native_exception_type = exc2#get_native_exception_type in
-			exc2_native_exception_type = throwable_sig || exc2_native_exception_type = exception_sig
+			List.mem exc2#get_native_type [throwable_sig; exception_sig; runtime_exception_sig]
 		| _ ->
 		| _ ->
-			(* Native to Haxe is never assignable *)
 			false
 			false
 
 
-	method is_native_exception = snd native_exception
-	method get_native_exception_type = fst native_exception
-
-	method get_native_exception_path =
-		match native_exception_path with
-		| None ->
-			let path = (match (fst native_exception) with TObject(path,_) -> path | _ -> assert false) in
-			native_exception_path <- Some path;
-			path
-		| Some path ->
-			path
+	method is_haxe_exception = is_haxe_exception
 
 
+	method get_native_type = native_type
+	method get_native_path = native_path
 	method get_type = t
 	method get_type = t
 end
 end
 
 
@@ -1629,11 +1611,6 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 
 
 	(* exceptions *)
 	(* exceptions *)
 
 
-	method throw vt =
-		jm#expect_reference_type;
-		jm#invokestatic (["haxe";"jvm"],"Exception") "wrap" (method_sig [object_sig] (Some exception_sig));
-		code#athrow;
-
 	method try_catch ret e1 catches =
 	method try_catch ret e1 catches =
 		let restore = jm#start_branch in
 		let restore = jm#start_branch in
 		let fp_from = code#get_fp in
 		let fp_from = code#get_fp in
@@ -1648,17 +1625,6 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 		let term_try = jm#is_terminated in
 		let term_try = jm#is_terminated in
 		let r_try = jm#maybe_make_jump in
 		let r_try = jm#maybe_make_jump in
 		let fp_to = code#get_fp in
 		let fp_to = code#get_fp in
-		let unwrap () =
-			code#dup;
-			code#instanceof haxe_exception_path;
-			jm#if_then_else
-				(code#if_ CmpEq)
-				(fun () ->
-					jm#cast haxe_exception_sig;
-					jm#getfield (["haxe";"jvm"],"Exception") "value" object_sig;
-				)
-				(fun () -> jm#cast object_sig);
-		in
 		let start_exception_block path jsig =
 		let start_exception_block path jsig =
 			restore();
 			restore();
 			let fp_target = code#get_fp in
 			let fp_target = code#get_fp in
@@ -1671,8 +1637,6 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 			};
 			};
 			code#get_stack#push jsig;
 			code#get_stack#push jsig;
 			jm#add_stack_frame;
 			jm#add_stack_frame;
-			jm#get_code#dup;
-			jm#invokestatic haxe_exception_path "setException" (method_sig [throwable_sig] None);
 		in
 		in
 		let run_catch_expr v e =
 		let run_catch_expr v e =
 			let pop_scope = jm#push_scope in
 			let pop_scope = jm#push_scope in
@@ -1683,63 +1647,15 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 			jm#is_terminated
 			jm#is_terminated
 		in
 		in
 		let add_catch (exc,v,e) =
 		let add_catch (exc,v,e) =
-			start_exception_block exc#get_native_exception_path exc#get_native_exception_type;
-			if not exc#is_native_exception then begin
-				unwrap();
-				self#cast v.v_type
-			end;
+			start_exception_block exc#get_native_path exc#get_native_type;
 			let term = run_catch_expr v e in
 			let term = run_catch_expr v e in
 			let r = jm#maybe_make_jump in
 			let r = jm#maybe_make_jump in
 			term,r
 			term,r
 		in
 		in
-		let commit_instanceof_checks excl =
-			start_exception_block throwable_path throwable_sig;
-			let pop_scope = jm#push_scope in
-			let _,load,save = jm#add_local "exc" throwable_sig VarWillInit in
-			code#dup;
-			save();
-			unwrap();
-			let restore = jm#start_branch in
-			let rl = ref [] in
-			let rec loop excl = match excl with
-				| [] ->
-					code#pop;
-					load();
-					code#athrow;
-				| (_,v,e) :: excl ->
-					code#dup;
-					let path = match self#vtype (self#mknull v.v_type) with TObject(path,_) -> path | _ -> assert false in
-					if path = object_path then begin
-						code#pop;
-						restore();
-						let term = run_catch_expr v e in
-						rl := (term,ref 0) :: !rl;
-					end else begin
-						code#instanceof path;
-						jm#if_then_else
-							(code#if_ CmpEq)
-							(fun () ->
-								restore();
-								self#cast v.v_type;
-								let term = run_catch_expr v e in
-								rl := (term,ref 0) :: !rl;
-							)
-							(fun () -> loop excl)
-					end
-			in
-			loop excl;
-			pop_scope();
-			!rl
-		in
 		let rec loop acc excl = match excl with
 		let rec loop acc excl = match excl with
 			| (exc,v,e) :: excl ->
 			| (exc,v,e) :: excl ->
-				if List.exists (fun (exc',_,_) -> exc'#is_assignable_to exc) excl || excl = [] && not exc#is_native_exception then begin
-					let res = commit_instanceof_checks ((exc,v,e) :: excl) in
-					acc @ res
-				end else begin
-					let res = add_catch (exc,v,e) in
-					loop (res :: acc) excl
-				end
+				let res = add_catch (exc,v,e) in
+				loop (res :: acc) excl
 			| [] ->
 			| [] ->
 				acc
 				acc
 		in
 		in
@@ -2027,14 +1943,13 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 			self#cast e.etype;
 			self#cast e.etype;
 		| TThrow e1 ->
 		| TThrow e1 ->
 			self#texpr rvalue_any e1;
 			self#texpr rvalue_any e1;
-			let exc = new haxe_exception gctx e1.etype in
-			if not (List.exists (fun exc' -> exc#is_assignable_to exc') caught_exceptions) then jm#add_thrown_exception exc#get_native_exception_path;
-			if not exc#is_native_exception then begin
-				let vt = self#vtype (self#mknull e1.etype) in
-				self#throw vt
-			end else begin
-				code#athrow;
-			end
+			if not (Exceptions.is_haxe_exception e1.etype) && not (type_unifies e1.etype gctx.t_runtime_exception) then begin
+				let exc = new haxe_exception gctx e1.etype in
+				if not (List.exists (fun exc' -> exc#is_assignable_to exc') caught_exceptions) then
+					jm#add_thrown_exception exc#get_native_path;
+			end;
+			code#athrow;
+			jm#set_terminated true
 		| TObjectDecl fl ->
 		| TObjectDecl fl ->
 			let td = gctx.anon_identification#identify true e.etype in
 			let td = gctx.anon_identification#identify true e.etype in
 			begin match follow e.etype,td with
 			begin match follow e.etype,td with
@@ -2897,6 +2812,7 @@ let generate com =
 	let gctx = {
 	let gctx = {
 		com = com;
 		com = com;
 		jar = Zip.open_out jar_path;
 		jar = Zip.open_out jar_path;
+		t_runtime_exception = TInst(resolve_class com (["java";"lang"],"RuntimeException"),[]);
 		entry_point = entry_point;
 		entry_point = entry_point;
 		t_exception = TInst(resolve_class com (["java";"lang"],"Exception"),[]);
 		t_exception = TInst(resolve_class com (["java";"lang"],"Exception"),[]);
 		t_throwable = TInst(resolve_class com (["java";"lang"],"Throwable"),[]);
 		t_throwable = TInst(resolve_class com (["java";"lang"],"Throwable"),[]);

+ 6 - 53
src/generators/genlua.ml

@@ -940,7 +940,6 @@ and gen_expr ?(local=true) ctx e = begin
         println ctx "local _hx_status, _hx_result = pcall(function() ";
         println ctx "local _hx_status, _hx_result = pcall(function() ";
         let b = open_block ctx in
         let b = open_block ctx in
         gen_expr ctx e;
         gen_expr ctx e;
-        let vname = temp ctx in
         b();
         b();
         println ctx "return _hx_pcall_default";
         println ctx "return _hx_pcall_default";
         println ctx "end)";
         println ctx "end)";
@@ -953,58 +952,12 @@ and gen_expr ?(local=true) ctx e = begin
                 println ctx "  break";
                 println ctx "  break";
         println ctx "elseif not _hx_status then ";
         println ctx "elseif not _hx_status then ";
         let bend = open_block ctx in
         let bend = open_block ctx in
-        newline ctx;
-        print ctx "local %s = _hx_result" vname;
-        let last = ref false in
-        let else_block = ref false in
-        List.iter (fun (v,e) ->
-            if !last then () else
-                let t = (match follow v.v_type with
-                    | TEnum (e,_) -> Some (TEnumDecl e)
-                    | TInst (c,_) -> Some (TClassDecl c)
-                    | TAbstract (a,_) -> Some (TAbstractDecl a)
-                    | TFun _
-                    | TLazy _
-                    | TType _
-                    | TAnon _ ->
-                        assert false
-                    | TMono _
-                    | TDynamic _ ->
-                        None
-                ) in
-                match t with
-                | None ->
-                    last := true;
-                    if !else_block then print ctx "";
-                    if vname <> v.v_name then begin
-                        newline ctx;
-                        print ctx "local %s = %s" v.v_name vname;
-                    end;
-                    gen_block_element ctx e;
-                    if !else_block then begin
-                        newline ctx;
-                        print ctx " end ";
-                    end
-                | Some t ->
-                    if not !else_block then newline ctx;
-                    print ctx "if( %s.__instanceof(%s," (ctx.type_accessor (TClassDecl { null_class with cl_path = ["lua"],"Boot" })) vname;
-                    gen_value ctx (mk (TTypeExpr t) (mk_mono()) e.epos);
-                    spr ctx ") ) then ";
-                    let bend = open_block ctx in
-                    if vname <> v.v_name then begin
-                        newline ctx;
-                        print ctx "local %s = %s" v.v_name vname;
-                    end;
-                    gen_block_element ctx e;
-                    bend();
-                    newline ctx;
-                    spr ctx "else";
-                    else_block := true
-        ) catchs;
-        if not !last then begin
-            println ctx " _G.error(%s)" vname;
-            spr ctx "end";
-        end;
+        (match catchs with
+        | [v,e] ->
+            print ctx "  local %s = _hx_result;" v.v_name;
+            gen_block_element ctx e;
+        | _ -> assert false
+        );
         bend();
         bend();
         newline ctx;
         newline ctx;
         println ctx "elseif _hx_result ~= _hx_pcall_default then";
         println ctx "elseif _hx_result ~= _hx_pcall_default then";

+ 22 - 107
src/generators/genphp7.ml

@@ -85,14 +85,6 @@ let hashtbl_keys tbl = Hashtbl.fold (fun key _ lst -> key :: lst) tbl []
 *)
 *)
 let diff_lists list1 list2 = List.filter (fun x -> not (List.mem x list2)) list1
 let diff_lists list1 list2 = List.filter (fun x -> not (List.mem x list2)) list1
 
 
-(**
-	Type path for native PHP Exception class
-*)
-let native_exception_path = ([], "Throwable")
-(**
-	Type path for Haxe exceptions wrapper
-*)
-let hxexception_type_path = (["php"; "_Boot"], "HxException")
 (**
 (**
 	Type path of `php.Boot`
 	Type path of `php.Boot`
 *)
 *)
@@ -310,7 +302,7 @@ let is_function_type t = match follow t with TFun _ -> true | _ -> false
 *)
 *)
 let is_syntax_extern expr =
 let is_syntax_extern expr =
 	match expr.eexpr with
 	match expr.eexpr with
-		| TField ({ eexpr = TTypeExpr (TClassDecl { cl_path = path }) }, _) when path = syntax_type_path -> true
+		| TField ({ eexpr = TTypeExpr (TClassDecl { cl_path = path }) }, _) -> path = syntax_type_path
 		| _ -> false
 		| _ -> false
 
 
 (**
 (**
@@ -527,34 +519,6 @@ let get_full_type_name ?(escape=false) ?(omit_first_slash=false) (type_path:path
 	else
 	else
 		name
 		name
 
 
-(**
-	Check if `target` is or implements native PHP `Throwable` interface
-*)
-let rec is_native_exception (target:Type.t) =
-	match follow target with
-		| TInst ({ cl_path = path }, _) when path = native_exception_path -> true
-		| TInst ({ cl_super = parent ; cl_implements = interfaces ; cl_path = path }, _) ->
-			let (parent, params) =
-				match parent with
-					| Some (parent, params) -> (Some parent, params)
-					| None -> (None, [])
-			in
-			let found = ref false in
-			List.iter
-				(fun (cls, params) ->
-					if not !found then
-						found := is_native_exception (TInst (cls, params))
-				)
-				interfaces;
-			if !found then
-				true
-			else
-				(match parent with
-					| Some parent -> is_native_exception (TInst (parent, params))
-					| None -> false
-				)
-		| _ -> false
-
 (**
 (**
 	@return Short type name. E.g. returns "Test" for (["example"], "Test")
 	@return Short type name. E.g. returns "Test" for (["example"], "Test")
 *)
 *)
@@ -1624,10 +1588,7 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 				| TCall (target, [arg1; arg2]) when is_std_is target -> self#write_expr_std_is target arg1 arg2
 				| TCall (target, [arg1; arg2]) when is_std_is target -> self#write_expr_std_is target arg1 arg2
 				| TCall (_, [arg]) when is_native_struct_array_cast expr && is_object_declaration arg ->
 				| TCall (_, [arg]) when is_native_struct_array_cast expr && is_object_declaration arg ->
 					(match (reveal_expr arg).eexpr with TObjectDecl fields -> self#write_assoc_array_decl fields | _ -> fail self#pos __POS__)
 					(match (reveal_expr arg).eexpr with TObjectDecl fields -> self#write_assoc_array_decl fields | _ -> fail self#pos __POS__)
-				| TCall ({ eexpr = TIdent name}, args) when is_magic expr ->
-					let msg = "untyped " ^ name ^ " is deprecated. Use php.Syntax instead." in
-					DeprecationCheck.warn_deprecation ctx.pgc_common msg self#pos;
-					self#write_expr_magic name args
+				| TCall ({ eexpr = TIdent name}, args) when is_magic expr -> self#write_expr_magic name args
 				| TCall ({ eexpr = TField (expr, access) }, args) when is_string expr -> self#write_expr_call_string expr access args
 				| TCall ({ eexpr = TField (expr, access) }, args) when is_string expr -> self#write_expr_call_string expr access args
 				| TCall (expr, args) when is_syntax_extern expr -> self#write_expr_call_syntax_extern expr args
 				| TCall (expr, args) when is_syntax_extern expr -> self#write_expr_call_syntax_extern expr args
 				| TCall (target, args) when is_sure_var_field_access target -> self#write_expr_call (parenthesis target) args
 				| TCall (target, args) when is_sure_var_field_access target -> self#write_expr_call (parenthesis target) args
@@ -1791,7 +1752,7 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 			vars#captured used_vars;
 			vars#captured used_vars;
 			self#write " ";
 			self#write " ";
 			if List.length used_vars > 0 then begin
 			if List.length used_vars > 0 then begin
-				self#write " use (";
+				self#write "use (";
 				write_args self#write (fun name -> self#write ("&$" ^ name)) used_vars;
 				write_args self#write (fun name -> self#write ("&$" ^ name)) used_vars;
 				self#write ") "
 				self#write ") "
 			end;
 			end;
@@ -1864,12 +1825,16 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 			and exprs = match expr.eexpr with TBlock exprs -> exprs | _ -> [expr] in
 			and exprs = match expr.eexpr with TBlock exprs -> exprs | _ -> [expr] in
 			let write_body () =
 			let write_body () =
 				let write_expr expr =
 				let write_expr expr =
-					if not ctx.pgc_skip_line_directives && not (is_block expr) then
+					if not ctx.pgc_skip_line_directives && not (is_block expr) && expr.epos <> null_pos then
 						if self#write_pos expr then self#write_indentation;
 						if self#write_pos expr then self#write_indentation;
-					self#write_expr expr;
 					match expr.eexpr with
 					match expr.eexpr with
-						| TBlock _ | TIf _ | TTry _ | TSwitch _ | TWhile (_, _, NormalWhile) -> self#write "\n"
-						| _ -> self#write ";\n"
+						| TBlock _ ->
+							self#write_as_block ~inline:true expr
+						| _ ->
+							self#write_expr expr;
+							match expr.eexpr with
+								| TBlock _ | TIf _ | TTry _ | TSwitch _ | TWhile (_, _, NormalWhile) -> self#write "\n"
+								| _ -> self#write ";\n"
 				in
 				in
 				let write_expr_with_indent expr =
 				let write_expr_with_indent expr =
 					self#write_indentation;
 					self#write_indentation;
@@ -1930,73 +1895,21 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 		*)
 		*)
 		method write_expr_throw expr =
 		method write_expr_throw expr =
 			self#write "throw ";
 			self#write "throw ";
-			if is_native_exception expr.etype then
-				self#write_expr expr
-			else if sure_extends_extern expr.etype || is_dynamic_type expr.etype then
-				begin
-					self#write "(is_object($__hx__throw = ";
-					self#write_expr expr;
-					self#write (") && $__hx__throw instanceof \\Throwable ? $__hx__throw : new " ^ (self#use hxexception_type_path) ^ "($__hx__throw))")
-				end
-			else
-				begin
-					self#write ("new " ^ (self#use hxexception_type_path) ^ "(");
-					self#write_expr expr;
-					self#write ")"
-				end
+			self#write_expr expr
 		(**
 		(**
 			Writes try...catch to output buffer
 			Writes try...catch to output buffer
 		*)
 		*)
 		method write_expr_try_catch try_expr catches =
 		method write_expr_try_catch try_expr catches =
-			let catching_dynamic = ref false in
-			let haxe_exception = self#use hxexception_type_path
-			and first_catch = ref true in
-			let write_catch (var, expr) =
-				let dynamic = ref false in
-				(match follow var.v_type with
-					| TInst ({ cl_path = ([], "String") }, _) -> self#write "if (is_string($__hx__real_e)) {\n"
-					| TAbstract ({ a_path = ([], "Float") }, _) -> self#write "if (is_float($__hx__real_e)) {\n"
-					| TAbstract ({ a_path = ([], "Int") }, _) -> self#write "if (is_int($__hx__real_e)) {\n"
-					| TAbstract ({ a_path = ([], "Bool") }, _) -> self#write "if (is_bool($__hx__real_e)) {\n"
-					| TDynamic _ ->
-						dynamic := true;
-						catching_dynamic := true;
-						if not !first_catch then self#write "{\n"
-					| vtype -> self#write ("if ($__hx__real_e instanceof " ^ (self#use_t vtype) ^ ") {\n")
-				);
-				if !dynamic && !first_catch then
-					begin
-						self#write ("$" ^ var.v_name ^ " = $__hx__real_e;\n");
-						self#write_indentation;
-						self#write_as_block ~inline:true expr;
-					end
-				else
-					begin
-						self#indent_more;
-						self#write_statement ("$" ^ var.v_name ^ " = $__hx__real_e");
-						self#write_indentation;
-						self#write_as_block ~inline:true expr;
-						self#indent_less;
-						self#write_with_indentation "}";
-					end;
-				if not !dynamic then self#write " else ";
-				first_catch := false;
-			in
 			self#write "try ";
 			self#write "try ";
 			self#write_as_block try_expr;
 			self#write_as_block try_expr;
-			self#write " catch (\\Throwable $__hx__caught_e) {\n";
-			self#indent_more;
-			if has_feature ctx.pgc_common "haxe.CallStack.exceptionStack"  then
-				self#write_statement ((self#use (["haxe"], "CallStack")) ^ "::saveExceptionTrace($__hx__caught_e)");
-			self#write_statement ("$__hx__real_e = ($__hx__caught_e instanceof " ^ haxe_exception ^ " ? $__hx__caught_e->e : $__hx__caught_e)");
-			self#write_indentation;
-			List.iter write_catch catches;
-			if not !catching_dynamic then
-				self#write " throw $__hx__caught_e;\n"
-			else
-				(match catches with [_] -> () | _ -> self#write "\n");
-			self#indent_less;
-			self#write_with_indentation "}"
+			let rec traverse = function
+				| [] -> ()
+				| (v,body) :: rest ->
+					self#write (" catch(" ^ (self#use_t v.v_type) ^ " $" ^ v.v_name ^ ") ");
+					self#write_as_block body;
+					traverse rest
+			in
+			traverse catches
 		(**
 		(**
 			Writes TCast to output buffer
 			Writes TCast to output buffer
 		*)
 		*)
@@ -2014,6 +1927,8 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 			@see http://old.haxe.org/doc/advanced/magic#php-magic
 			@see http://old.haxe.org/doc/advanced/magic#php-magic
 		*)
 		*)
 		method write_expr_magic name args =
 		method write_expr_magic name args =
+			let msg = "untyped " ^ name ^ " is deprecated. Use php.Syntax instead." in
+			DeprecationCheck.warn_deprecation ctx.pgc_common msg self#pos;
 			let error = ("Invalid arguments for " ^ name ^ " magic call") in
 			let error = ("Invalid arguments for " ^ name ^ " magic call") in
 			match args with
 			match args with
 				| [] -> fail ~msg:error self#pos __POS__
 				| [] -> fail ~msg:error self#pos __POS__

+ 8 - 73
src/generators/genpy.ml

@@ -1393,19 +1393,7 @@ module Printer = struct
 			| TContinue ->
 			| TContinue ->
 				"continue"
 				"continue"
 			| TThrow e1 ->
 			| TThrow e1 ->
-				let rec is_native_exception t =
-					match Abstract.follow_with_abstracts t with
-					| TInst ({ cl_path = [],"BaseException" }, _) ->
-						true
-					| TInst ({ cl_super = Some csup }, _) ->
-						is_native_exception (TInst(fst csup, snd csup))
-					| _ ->
-						false
-				in
-				if is_native_exception e1.etype then
-					Printf.sprintf "raise %s" (print_expr pctx e1)
-				else
-					Printf.sprintf "raise _HxException(%s)" (print_expr pctx e1)
+				Printf.sprintf "raise %s" (print_expr pctx e1)
 			| TCast(e1,None) ->
 			| TCast(e1,None) ->
 				print_expr pctx e1
 				print_expr pctx e1
 			| TMeta((Meta.Custom ":ternaryIf",_,_),{eexpr = TIf(econd,eif,Some eelse)}) ->
 			| TMeta((Meta.Custom ":ternaryIf",_,_),{eexpr = TIf(econd,eif,Some eelse)}) ->
@@ -1484,70 +1472,17 @@ module Printer = struct
 				do_default()
 				do_default()
 
 
 	and print_try pctx e1 catches =
 	and print_try pctx e1 catches =
-		let has_catch_all = List.exists (fun (v,_) -> match follow v.v_type with
-			| TDynamic _ -> true
-			| _ -> false
-		) catches in
-		let has_only_catch_all = has_catch_all && begin match catches with
-			| [_] -> true
-			| _ -> false
-		end in
-		let print_catch pctx i (v,e) =
-			let is_empty_expr = begin match e.eexpr with
-				| TBlock [] -> true
-				| _ -> false
-			end in
-			let indent = pctx.pc_indent in
-			(* Don't generate assignment to catch variable when catch expression is an empty block *)
-			let assign = if is_empty_expr then "" else Printf.sprintf "%s = _hx_e1\n%s" v.v_name indent in
-			let handle_base_type bt =
-				let t = print_base_type bt in
-				let print_custom_check t_str =
-					Printf.sprintf "if %s:\n%s    %s    %s" t_str indent assign (print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e)
-				in
-				let print_type_check t_str =
-					print_custom_check ("isinstance(_hx_e1, " ^ t_str ^ ")")
-				in
-				let res = match t with
-				| "str" -> print_type_check "str"
-				| "Bool" -> print_type_check "bool"
-				| "Int" -> print_custom_check "(isinstance(_hx_e1, int) and not isinstance(_hx_e1, bool))" (* for historic reasons bool extends int *)
-				| "Float" -> print_type_check "float"
-				| t -> print_type_check t
-				in
-				if i > 0 then
-					indent ^ "el" ^ res
-				else
-					res
-			in
-			match follow v.v_type with
-				| TDynamic _ ->
-					begin if has_only_catch_all then
-						Printf.sprintf "%s%s" assign (print_expr pctx e)
-					else
-						(* Dynamic is always the last block *)
-						Printf.sprintf "%selse:\n    %s%s    %s" indent indent assign (print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e)
-					end
-				| TInst(c,_) ->
-					handle_base_type (t_infos (TClassDecl c))
-				| TEnum(en,_) ->
-					handle_base_type (t_infos (TEnumDecl en))
-				| TAbstract(a,_) ->
-					handle_base_type (t_infos (TAbstractDecl a))
-				| _ ->
-					assert false
+		let print_catch pctx (v,e) =
+			let s_type = print_module_type (module_type_of_type v.v_type)
+			and s_expr = pctx.pc_indent ^ (print_expr pctx e) in
+			Printf.sprintf "except %s as %s:\n%s" s_type v.v_name s_expr;
 		in
 		in
 		let indent = pctx.pc_indent in
 		let indent = pctx.pc_indent in
 		let print_expr_indented e = print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e in
 		let print_expr_indented e = print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e in
 		let try_str = Printf.sprintf "try:\n%s    %s\n%s" indent (print_expr_indented e1) indent in
 		let try_str = Printf.sprintf "try:\n%s    %s\n%s" indent (print_expr_indented e1) indent in
-		let except = if has_feature pctx "has_throw" then
-			Printf.sprintf "except Exception as _hx_e:\n%s    _hx_e1 = _hx_e.val if isinstance(_hx_e, _HxException) else _hx_e\n%s    " indent indent
-		else
-			Printf.sprintf "except Exception as _hx_e:\n%s    _hx_e1 = _hx_e\n%s    " indent indent
-		in
-		let catch_str = String.concat (Printf.sprintf "\n") (ExtList.List.mapi (fun i catch -> print_catch {pctx with pc_indent = "    " ^ pctx.pc_indent} i catch) catches) in
-		let except_end = if not has_catch_all then Printf.sprintf "\n%s    else:\n%s        raise _hx_e" indent indent else "" in
-		Printf.sprintf "%s%s%s%s" try_str except catch_str except_end
+		let catch_pctx = {pctx with pc_indent = "    " ^ pctx.pc_indent} in
+		let catch_str = String.concat (Printf.sprintf "\n") (List.map (print_catch catch_pctx) catches) in
+		Printf.sprintf "%s%s" try_str catch_str
 
 
 	and print_call2 pctx e1 el =
 	and print_call2 pctx e1 el =
 		let id = print_expr pctx e1 in
 		let id = print_expr pctx e1 in

+ 2 - 2
src/generators/genswf9.ml

@@ -1048,7 +1048,7 @@ let rec gen_expr_content ctx retval e =
 		gen_constant ctx c e.etype e.epos
 		gen_constant ctx c e.etype e.epos
 	| TThrow e ->
 	| TThrow e ->
 		ctx.infos.icond <- true;
 		ctx.infos.icond <- true;
-		if has_feature ctx.com "haxe.CallStack.exceptionStack" then begin
+		if has_feature ctx.com "haxe.CallStack.exceptionStack" && not (Exceptions.is_haxe_exception e.etype) then begin
 			getvar ctx (VGlobal (type_path ctx (["flash"],"Boot")));
 			getvar ctx (VGlobal (type_path ctx (["flash"],"Boot")));
 			let id = type_path ctx (["flash";"errors"],"Error") in
 			let id = type_path ctx (["flash";"errors"],"Error") in
 			write ctx (HFindPropStrict id);
 			write ctx (HFindPropStrict id);
@@ -1212,7 +1212,7 @@ let rec gen_expr_content ctx retval e =
 					| _ -> Type.iter call_loop e
 					| _ -> Type.iter call_loop e
 				in
 				in
 				let has_call = (try call_loop e; false with Exit -> true) in
 				let has_call = (try call_loop e; false with Exit -> true) in
-				if has_call && has_feature ctx.com "haxe.CallStack.exceptionStack" then begin
+				if has_call && has_feature ctx.com "haxe.CallStack.exceptionStack" && not (Exceptions.is_haxe_exception v.v_type) then begin
 					getvar ctx (gen_local_access ctx v e.epos Read);
 					getvar ctx (gen_local_access ctx v e.epos Read);
 					write ctx (HAsType (type_path ctx (["flash";"errors"],"Error")));
 					write ctx (HAsType (type_path ctx (["flash";"errors"],"Error")));
 					let j = jump ctx J3False in
 					let j = jump ctx J3False in

+ 4 - 1
src/generators/jvm/jvmSignature.ml

@@ -85,7 +85,7 @@ module NativeSignatures = struct
 	let haxe_dynamic_object_path = ["haxe";"jvm"],"DynamicObject"
 	let haxe_dynamic_object_path = ["haxe";"jvm"],"DynamicObject"
 	let haxe_dynamic_object_sig = TObject(haxe_dynamic_object_path,[])
 	let haxe_dynamic_object_sig = TObject(haxe_dynamic_object_path,[])
 
 
-	let haxe_exception_path = ["haxe";"jvm"],"Exception"
+	let haxe_exception_path = ["haxe"],"Exception"
 	let haxe_exception_sig = TObject(haxe_exception_path,[])
 	let haxe_exception_sig = TObject(haxe_exception_path,[])
 
 
 	let haxe_object_path = ["haxe";"jvm"],"Object"
 	let haxe_object_path = ["haxe";"jvm"],"Object"
@@ -97,6 +97,9 @@ module NativeSignatures = struct
 	let exception_path = (["java";"lang"],"Exception")
 	let exception_path = (["java";"lang"],"Exception")
 	let exception_sig = TObject(exception_path,[])
 	let exception_sig = TObject(exception_path,[])
 
 
+	let runtime_exception_path = (["java";"lang"],"RuntimeException")
+	let runtime_exception_sig = TObject(runtime_exception_path,[])
+
 	let retention_path = (["java";"lang";"annotation"],"Retention")
 	let retention_path = (["java";"lang";"annotation"],"Retention")
 	let retention_sig = TObject(retention_path,[])
 	let retention_sig = TObject(retention_path,[])
 
 

+ 0 - 1
src/macro/eval/evalHash.ml

@@ -107,7 +107,6 @@ let key_haxe_macro_DisplayKind = hash "haxe.macro.DisplayKind"
 let key_haxe_macro_Message = hash "haxe.macro.Message"
 let key_haxe_macro_Message = hash "haxe.macro.Message"
 let key_haxe_macro_FunctionKind = hash "haxe.macro.FunctionKind"
 let key_haxe_macro_FunctionKind = hash "haxe.macro.FunctionKind"
 let key_haxe_macro_StringLiteralKind = hash "haxe.macro.StringLiteralKind"
 let key_haxe_macro_StringLiteralKind = hash "haxe.macro.StringLiteralKind"
-let key_haxe_CallStack = hash "haxe.CallStack"
 let key___init__ = hash "__init__"
 let key___init__ = hash "__init__"
 let key_new = hash "new"
 let key_new = hash "new"
 let key_questionmark = hash "?"
 let key_questionmark = hash "?"

+ 4 - 4
src/macro/eval/evalStdLib.ml

@@ -537,7 +537,7 @@ module StdBytesBuffer = struct
 	)
 	)
 end
 end
 
 
-module StdCallStack = struct
+module StdNativeStackTrace = struct
 	let make_stack envs =
 	let make_stack envs =
 		let l = DynArray.create () in
 		let l = DynArray.create () in
 		List.iter (fun (pos,kind) ->
 		List.iter (fun (pos,kind) ->
@@ -3326,9 +3326,9 @@ let init_standard_library builtins =
 		"addBytes",StdBytesBuffer.addBytes;
 		"addBytes",StdBytesBuffer.addBytes;
 		"getBytes",StdBytesBuffer.getBytes;
 		"getBytes",StdBytesBuffer.getBytes;
 	];
 	];
-	init_fields builtins (["haxe"],"CallStack") [
-		"getCallStack",StdCallStack.getCallStack;
-		"getExceptionStack",StdCallStack.getExceptionStack;
+	init_fields builtins (["haxe"],"NativeStackTrace") [
+		"_callStack",StdNativeStackTrace.getCallStack;
+		"exceptionStack",StdNativeStackTrace.getExceptionStack;
 	] [];
 	] [];
 	init_fields builtins (["haxe";"zip"],"Compress") [
 	init_fields builtins (["haxe";"zip"],"Compress") [
 		"run",StdCompress.run;
 		"run",StdCompress.run;

+ 1 - 1
src/optimization/dce.ml

@@ -111,7 +111,7 @@ let mk_keep_meta pos =
 let rec keep_field dce cf c is_static =
 let rec keep_field dce cf c is_static =
 	Meta.has_one_of (Meta.Used :: keep_metas) cf.cf_meta
 	Meta.has_one_of (Meta.Used :: keep_metas) cf.cf_meta
 	|| cf.cf_name = "__init__"
 	|| cf.cf_name = "__init__"
-	|| not (is_physical_field cf)
+	|| has_class_field_flag cf CfExtern
 	|| (not is_static && overrides_extern_field cf c)
 	|| (not is_static && overrides_extern_field cf c)
 	|| (
 	|| (
 		cf.cf_name = "new"
 		cf.cf_name = "new"

+ 6 - 2
src/typing/typer.ml

@@ -1772,11 +1772,14 @@ and type_try ctx e1 catches with_type p =
 			let unreachable () =
 			let unreachable () =
 				display_error ctx "This block is unreachable" p;
 				display_error ctx "This block is unreachable" p;
 				let st = s_type (print_context()) in
 				let st = s_type (print_context()) in
-				display_error ctx (Printf.sprintf "%s can be assigned to %s, which is handled here" (st t) (st v.v_type)) e.epos
+				display_error ctx (Printf.sprintf "%s can be caught to %s, which is handled here" (st t) (st v.v_type)) e.epos
 			in
 			in
 			begin try
 			begin try
 				begin match follow t,follow v.v_type with
 				begin match follow t,follow v.v_type with
-					| TDynamic _, TDynamic _ ->
+					| _, TDynamic _
+					| _, TInst({ cl_path = ["haxe"],"Error"},_) ->
+						unreachable()
+					| _, TInst({ cl_path = path },_) when path = ctx.com.config.pf_exceptions.ec_wildcard_catch ->
 						unreachable()
 						unreachable()
 					| TDynamic _,_ ->
 					| TDynamic _,_ ->
 						()
 						()
@@ -2651,6 +2654,7 @@ let rec create com =
 		| [TClassDecl c2 ] -> ctx.g.global_using <- (c1,c1.cl_pos) :: (c2,c2.cl_pos) :: ctx.g.global_using
 		| [TClassDecl c2 ] -> ctx.g.global_using <- (c1,c1.cl_pos) :: (c2,c2.cl_pos) :: ctx.g.global_using
 		| _ -> assert false);
 		| _ -> assert false);
 	| _ -> assert false);
 	| _ -> assert false);
+	ignore(TypeloadModule.load_module ctx (["haxe"],"Exception") null_pos);
 	ctx.g.complete <- true;
 	ctx.g.complete <- true;
 	ctx
 	ctx
 
 

+ 81 - 0
std/cpp/_std/haxe/Exception.hx

@@ -0,0 +1,81 @@
+package haxe;
+
+//TODO: extend ::std::exception
+@:coreApi
+class Exception {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionMessage:String;
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:Array<String>;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
+	@:noCompletion var __nativeException:Any;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		__exceptionMessage = message;
+		__previousException = previous;
+		if(native != null) {
+			__nativeStack = NativeStackTrace.exceptionStack();
+			__nativeException = native;
+		} else {
+			__nativeStack = NativeStackTrace.callStack();
+			__nativeException = this;
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return __exceptionMessage;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null: __exceptionStack = NativeStackTrace.toHaxe(__nativeStack, __skipStack);
+			case s: s;
+		}
+	}
+}
+

+ 42 - 0
std/cpp/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,42 @@
+package haxe;
+
+import haxe.CallStack.StackItem;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(exception:Any):Void {
+	}
+
+	@:noDebug //Do not mess up the exception stack
+	static public function callStack():Array<String> {
+		return untyped __global__.__hxcpp_get_call_stack(true);
+	}
+
+	@:noDebug //Do not mess up the exception stack/
+	static public function exceptionStack():Array<String> {
+		return untyped __global__.__hxcpp_get_exception_stack();
+	}
+
+	static public function toHaxe(native:Array<String>, skip:Int = 0):Array<StackItem> {
+		var stack:Array<String> = native;
+		var m = new Array<StackItem>();
+		for (i in 0...stack.length) {
+			if(skip > i) {
+				continue;
+			}
+			var words = stack[i].split("::");
+			if (words.length == 0)
+				m.push(CFunction)
+			else if (words.length == 2)
+				m.push(Method(words[0], words[1]));
+			else if (words.length == 4)
+				m.push(FilePos(Method(words[0], words[1]), words[2], Std.parseInt(words[3])));
+		}
+		return m;
+	}
+}

+ 2 - 2
std/cpp/cppia/HostClasses.hx

@@ -85,7 +85,7 @@ class HostClasses {
 		"haxe.ds.ObjectMap",
 		"haxe.ds.ObjectMap",
 		"haxe.ds.StringMap",
 		"haxe.ds.StringMap",
 		"haxe.ds.BalancedTree",
 		"haxe.ds.BalancedTree",
-		"haxe.CallStack",
+		"haxe.NativeStackTrace",
 		"haxe.Serializer",
 		"haxe.Serializer",
 		"haxe.Unserializer",
 		"haxe.Unserializer",
 		"haxe.Resource",
 		"haxe.Resource",
@@ -117,7 +117,7 @@ class HostClasses {
 		"haxe.io.StringInput",
 		"haxe.io.StringInput",
 		"haxe.xml.Parser",
 		"haxe.xml.Parser",
 		"haxe.Json",
 		"haxe.Json",
-		"haxe.CallStack",
+		"haxe.NativeStackTrace",
 		"haxe.Resource",
 		"haxe.Resource",
 		"haxe.Utf8",
 		"haxe.Utf8",
 		"haxe.Int64",
 		"haxe.Int64",

+ 0 - 1
std/cs/Boot.hx

@@ -22,7 +22,6 @@
 
 
 package cs;
 package cs;
 
 
-import cs.internal.Exceptions;
 import cs.internal.FieldLookup;
 import cs.internal.FieldLookup;
 import cs.internal.Function;
 import cs.internal.Function;
 import cs.internal.HxObject;
 import cs.internal.HxObject;

+ 0 - 1
std/cs/_std/Std.hx

@@ -22,7 +22,6 @@
 
 
 import cs.Boot;
 import cs.Boot;
 import cs.Lib;
 import cs.Lib;
-import cs.internal.Exceptions;
 
 
 @:coreApi @:nativeGen class Std {
 @:coreApi @:nativeGen class Std {
 	public static inline function is(v:Dynamic, t:Dynamic):Bool {
 	public static inline function is(v:Dynamic, t:Dynamic):Bool {

+ 114 - 0
std/cs/_std/haxe/Exception.hx

@@ -0,0 +1,114 @@
+package haxe;
+
+import cs.system.Exception as CsException;
+import cs.system.diagnostics.StackTrace;
+
+@:coreApi
+class Exception extends NativeException {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:StackTrace;
+	@:noCompletion var __ownStack:Bool;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
+	@:noCompletion var __nativeException:CsException;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static public function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else if(Std.isOfType(value, CsException)) {
+			return new Exception((value:CsException).Message, null, value);
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static public function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else if(Std.isOfType(value, CsException)) {
+			return value;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		super(message, previous);
+		this.__previousException = previous;
+
+		if(native != null && Std.isOfType(native, CsException)) {
+			__nativeException = native;
+			if(__nativeException.StackTrace == null) {
+				__nativeStack = new StackTrace(1, true);
+				__ownStack = true;
+			} else {
+				__nativeStack = new StackTrace(__nativeException, true);
+				__ownStack = false;
+			}
+		} else {
+			__nativeException = cast this;
+			__nativeStack = new StackTrace(1, true);
+			__ownStack = true;
+		}
+	}
+
+	public function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		if(__ownStack) __skipStack++;
+	}
+
+	function get_message():String {
+		return this.Message;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = NativeStackTrace.toHaxe(__nativeStack, __skipStack);
+			case s: s;
+		}
+	}
+}
+
+@:dox(hide)
+@:nativeGen
+@:noCompletion
+@:native('System.Exception')
+private extern class NativeException {
+	@:noCompletion private function new(message:String, innerException:NativeException):Void;
+	@:noCompletion @:skipReflection private final Data:cs.system.collections.IDictionary;
+	@:noCompletion @:skipReflection private var HelpLink:String;
+	@:noCompletion @:skipReflection private final InnerException:cs.system.Exception;
+	@:noCompletion @:skipReflection private final Message:String;
+	@:noCompletion @:skipReflection private var Source:String;
+	@:noCompletion @:skipReflection private final StackTrace:String;
+	@:noCompletion @:skipReflection private final TargetSite:cs.system.reflection.MethodBase;
+	@:overload @:noCompletion @:skipReflection private function GetBaseException():cs.system.Exception;
+	@:overload @:noCompletion @:skipReflection private function GetObjectData(info:cs.system.runtime.serialization.SerializationInfo, context:cs.system.runtime.serialization.StreamingContext):Void;
+	@:overload @:noCompletion @:skipReflection private function GetType():cs.system.Type;
+	@:overload @:noCompletion @:skipReflection private function ToString():cs.system.String;
+}

+ 60 - 0
std/cs/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,60 @@
+package haxe;
+
+import haxe.CallStack.StackItem;
+import cs.system.diagnostics.StackTrace;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	@:meta(System.ThreadStaticAttribute)
+	static var exception:Null<cs.system.Exception>;
+
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(e:Any):Void {
+		exception = e;
+	}
+
+	static public inline function callStack():StackTrace {
+		return new StackTrace(1, true);
+	}
+
+	static public function exceptionStack():Null<StackTrace> {
+		return switch exception {
+			case null: null;
+			case e: new StackTrace(e, true);
+		}
+	}
+
+	static public function toHaxe(native:Null<StackTrace>, skip:Int = 0):Array<StackItem> {
+		var stack = [];
+		if(native == null) {
+			return stack;
+		}
+		var cnt = 0;
+		for (i in 0...native.FrameCount) {
+			var frame = native.GetFrame(i);
+			var m = frame.GetMethod();
+
+			if (m == null) {
+				continue;
+			}
+			if(skip > cnt++) {
+				continue;
+			}
+
+			var method = StackItem.Method(m.ReflectedType.ToString(), m.Name);
+
+			var fileName = frame.GetFileName();
+			var lineNumber = frame.GetFileLineNumber();
+
+			if (fileName != null || lineNumber >= 0)
+				stack.push(FilePos(method, fileName, lineNumber));
+			else
+				stack.push(method);
+		}
+		return stack;
+	}
+}

+ 0 - 63
std/cs/internal/Exceptions.hx

@@ -1,63 +0,0 @@
-/*
- * Copyright (C)2005-2019 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 cs.internal;
-
-import cs.system.Exception;
-
-@:nativeGen @:keep @:native("haxe.lang.Exceptions") class Exceptions {
-	@:allow(haxe.CallStack)
-	@:meta(System.ThreadStaticAttribute)
-	static var exception:cs.system.Exception;
-}
-
-// should NOT be usable inside Haxe code
-
-@:classCode('override public string Message { get { return this.toString(); } }\n\n')
-@:nativeGen @:keep @:native("haxe.lang.HaxeException") private class HaxeException extends Exception {
-	private var obj:Dynamic;
-
-	public function new(obj:Dynamic) {
-		super();
-
-		if (Std.isOfType(obj, HaxeException)) {
-			var _obj:HaxeException = cast obj;
-			obj = _obj.getObject();
-		}
-		this.obj = obj;
-	}
-
-	public function getObject():Dynamic {
-		return obj;
-	}
-
-	public function toString():String {
-		return Std.string(obj);
-	}
-
-	public static function wrap(obj:Dynamic):Exception {
-		if (Std.isOfType(obj, Exception))
-			return obj;
-
-		return new HaxeException(obj);
-	}
-}

+ 84 - 0
std/eval/_std/haxe/Exception.hx

@@ -0,0 +1,84 @@
+package haxe;
+
+@:coreApi
+class Exception {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionMessage:String;
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:CallStack;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
+	@:noCompletion var __nativeException:Any;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		__exceptionMessage = message;
+		__previousException = previous;
+		if(native != null) {
+			__nativeStack = NativeStackTrace.exceptionStack();
+			__nativeException = native;
+		} else {
+			__nativeStack = NativeStackTrace.callStack();
+			__nativeException = this;
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return __exceptionMessage;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = if(__skipStack > 0) {
+					__nativeStack.asArray().slice(__skipStack);
+				} else {
+					__nativeStack;
+				}
+			case s: s;
+		}
+	}
+}

+ 32 - 0
std/eval/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,32 @@
+package haxe;
+
+import haxe.CallStack.StackItem;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(exception:Any):Void {
+	}
+
+	static public function callStack():Array<StackItem> {
+		return _callStack();
+	}
+
+	//implemented in the compiler
+	static function _callStack():Array<StackItem> {
+		return null;
+	}
+
+	//implemented in the compiler
+	static public function exceptionStack():Array<StackItem> {
+		return null;
+	}
+
+	static public inline function toHaxe(stack:Array<StackItem>, skip:Int = 0):Array<StackItem> {
+		return skip > 0 ? stack.slice(skip) : stack;
+	}
+}

+ 96 - 0
std/flash/_std/haxe/Exception.hx

@@ -0,0 +1,96 @@
+package haxe;
+
+import flash.errors.Error;
+
+@:coreApi
+class Exception extends NativeException {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:String;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int;
+	@:noCompletion var __nativeException:Error;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else if(Std.isOfType(value, Error)) {
+			return new Exception((value:Error).message, null, value);
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else if(Std.isOfType(value, Error)) {
+			return value;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		super(message);
+		__previousException = previous;
+		if(native != null && Std.isOfType(native, Error)) {
+			__nativeException = native;
+			__nativeStack = NativeStackTrace.normalize((native:Error).getStackTrace());
+		} else {
+			__nativeException = cast this;
+			__nativeStack = NativeStackTrace.callStack();
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return (cast this:Error).message;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = NativeStackTrace.toHaxe(__nativeStack, __skipStack);
+			case s: s;
+		}
+	}
+}
+
+@:dox(hide)
+@:native('flash.errors.Error')
+extern class NativeException {
+	@:noCompletion @:flash.property private var errorID(get,never):Int;
+	// @:noCompletion private var message:Dynamic;
+	@:noCompletion private var name:Dynamic;
+	@:noCompletion private function new(?message:Dynamic, id:Dynamic = 0):Void;
+	@:noCompletion private function getStackTrace():String;
+	@:noCompletion private function get_errorID():Int;
+}

+ 69 - 0
std/flash/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,69 @@
+package haxe;
+
+import flash.errors.Error;
+import haxe.CallStack.StackItem;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+@:allow(haxe.Exception)
+class NativeStackTrace {
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(e:Any):Void {
+	}
+
+	static public inline function callStack():String {
+		return normalize(new Error().getStackTrace(), 1);
+	}
+
+	static public function exceptionStack():String {
+		var err:Null<Error> = untyped flash.Boot.lastError;
+		return err == null ? '' : normalize(err.getStackTrace());
+	}
+
+	static public function toHaxe(native:String, skip:Int = 0):Array<StackItem> {
+		var a = new Array();
+		var r = ~/at ([^\/]+?)\$?(\/[^\(]+)?\(\)(\[(.*?):([0-9]+)\])?/;
+		var rlambda = ~/^MethodInfo-([0-9]+)$/g;
+		var cnt = 0;
+		while (r.match(native)) {
+			native = r.matchedRight();
+			if(skip > cnt++) {
+				continue;
+			}
+			var cl = r.matched(1).split("::").join(".");
+			var meth = r.matched(2);
+			var item;
+			if (meth == null) {
+				if (rlambda.match(cl))
+					item = LocalFunction(Std.parseInt(rlambda.matched(1)));
+				else
+					item = Method(cl, "new");
+			} else
+				item = Method(cl, meth.substring(1));
+			if (r.matched(3) != null)
+				item = FilePos(item, r.matched(4), Std.parseInt(r.matched(5)));
+			a.push(item);
+		}
+		return a;
+	}
+
+	static function normalize(stack:String, skipItems:Int = 0):String {
+		switch (stack:String).substring(0, 6) {
+			case 'Error:' | 'Error\n': skipItems += 1;
+			case _:
+		}
+		return skipLines(stack, skipItems);
+	}
+
+	static function skipLines(stack:String, skip:Int, pos:Int = 0):String {
+		return if(skip > 0) {
+			pos = stack.indexOf('\n', pos);
+			return pos < 0 ? '' : skipLines(stack, --skip, pos + 1);
+		} else {
+			return stack.substring(pos);
+		}
+	}
+}

+ 93 - 337
std/haxe/CallStack.hx

@@ -36,234 +36,122 @@ enum StackItem {
 /**
 /**
 	Get information about the call stack.
 	Get information about the call stack.
 **/
 **/
-class CallStack {
-	#if js
-	static var lastException:js.lib.Error;
-
-	static function getStack(e:js.lib.Error):Array<StackItem> {
-		if (e == null)
-			return [];
-		// https://v8.dev/docs/stack-trace-api
-		var oldValue = V8Error.prepareStackTrace;
-		V8Error.prepareStackTrace = function(error, callsites) {
-			var stack = [];
-			for (site in callsites) {
-				if (wrapCallSite != null)
-					site = wrapCallSite(site);
-				var method = null;
-				var fullName = site.getFunctionName();
-				if (fullName != null) {
-					var idx = fullName.lastIndexOf(".");
-					if (idx >= 0) {
-						var className = fullName.substr(0, idx);
-						var methodName = fullName.substr(idx + 1);
-						method = Method(className, methodName);
-					}
-				}
-				var fileName = site.getFileName();
-				var fileAddr = fileName == null ? -1 : fileName.indexOf("file:");
-				if (wrapCallSite != null && fileAddr > 0)
-					fileName = fileName.substr(fileAddr + 6);
-				stack.push(FilePos(method, fileName, site.getLineNumber(), site.getColumnNumber()));
-			}
-			return stack;
-		}
-		var a = makeStack(e.stack);
-		V8Error.prepareStackTrace = oldValue;
-		return a;
-	}
-
-	// support for source-map-support module
-	@:noCompletion
-	public static var wrapCallSite:V8CallSite->V8CallSite;
-	#end
-
-	#if eval
-	static function getCallStack() {
-		return [];
-	}
-
-	static function getExceptionStack() {
-		return [];
-	}
-	#end
+@:allow(haxe.Exception)
+@:using(haxe.CallStack)
+abstract CallStack(Array<StackItem>) from Array<StackItem> {
+	/**
+		The length of this stack.
+	**/
+	public var length(get,never):Int;
+	inline function get_length():Int return this.length;
 
 
 	/**
 	/**
 		Return the call stack elements, or an empty array if not available.
 		Return the call stack elements, or an empty array if not available.
 	**/
 	**/
 	public static function callStack():Array<StackItem> {
 	public static function callStack():Array<StackItem> {
-		#if neko
-		var a = makeStack(untyped __dollar__callstack());
-		a.shift(); // remove Stack.callStack()
-		return a;
-		#elseif flash
-		var a = makeStack(new flash.errors.Error().getStackTrace());
-		a.shift(); // remove Stack.callStack()
-		return a;
-		#elseif cpp
-		var s:Array<String> = untyped __global__.__hxcpp_get_call_stack(true);
-		return makeStack(s);
-		#elseif js
-		try {
-			throw new js.lib.Error();
-		} catch (e:Dynamic) {
-			var a = getStack(js.Lib.getOriginalException());
-			a.shift(); // remove Stack.callStack()
-			return a;
-		}
-		#elseif java
-		var stack = [];
-		for (el in java.lang.Thread.currentThread().getStackTrace()) {
-			var className = el.getClassName();
-			var methodName = el.getMethodName();
-			var fileName = el.getFileName();
-			var lineNumber = el.getLineNumber();
-			var method = Method(className, methodName);
-			if (fileName != null || lineNumber >= 0) {
-				stack.push(FilePos(method, fileName, lineNumber));
-			} else {
-				stack.push(method);
-			}
-		}
-		stack.shift();
-		stack.shift();
-		stack.pop();
-		return stack;
-		#elseif cs
-		return makeStack(new cs.system.diagnostics.StackTrace(1, true));
-		#elseif python
-		var stack = [];
-		var infos = python.lib.Traceback.extract_stack();
-		infos.pop();
-		infos.reverse();
-		for (elem in infos)
-			stack.push(FilePos(Method(null, elem._3), elem._1, elem._2));
-		return stack;
-		#elseif lua
-		var stack = [];
-		var infos = lua.Debug.traceback();
-		var luastack = infos.split("\n").slice(2, -1);
-		for (s in luastack) {
-			var parts = s.split(":");
-			var file = parts[0];
-			var line = parts[1];
-			var method = if(parts.length <= 2) {
-				null;
-			} else {
-				var methodPos = parts[2].indexOf("'");
-				if(methodPos < 0) {
-					null;
-				} else {
-					Method(null, parts[2].substring(methodPos + 1, parts[2].length - 1));
-				}
-			}
-			stack.push(FilePos(method, file, Std.parseInt(line)));
-		}
-		return stack;
-		#elseif hl
-		try {
-			throw null;
-		} catch (e:Dynamic) {
-			var st = _getExceptionStack();
-			return makeStack(st.length > 2 ? st.sub(2, st.length - 2) : st);
-		}
-		#elseif eval
-		return getCallStack();
-		#else
-		return []; // Unsupported
-		#end
-	}
-
-	#if hl
-	@:hlNative("std", "exception_stack") static function _getExceptionStack():hl.NativeArray<hl.Bytes> {
-		return null;
+		return NativeStackTrace.toHaxe(NativeStackTrace.callStack());
 	}
 	}
-	#end
 
 
 	/**
 	/**
 		Return the exception stack : this is the stack elements between
 		Return the exception stack : this is the stack elements between
 		the place the last exception was thrown and the place it was
 		the place the last exception was thrown and the place it was
 		caught, or an empty array if not available.
 		caught, or an empty array if not available.
+
+		May not work if catch type was a derivative from `haxe.Exception`.
 	**/
 	**/
-	#if cpp
-	@:noDebug /* Do not mess up the exception stack */
-	#end
 	public static function exceptionStack():Array<StackItem> {
 	public static function exceptionStack():Array<StackItem> {
-		#if neko
-		return makeStack(untyped __dollar__excstack());
-		#elseif hl
-		return makeStack(_getExceptionStack());
-		#elseif flash
-		var err:flash.errors.Error = untyped flash.Boot.lastError;
-		if (err == null)
-			return new Array();
-		var a = makeStack(err.getStackTrace());
-		var c = callStack();
-		var i = c.length - 1;
-		while (i > 0) {
-			if (Std.string(a[a.length - 1]) == Std.string(c[i]))
-				a.pop();
-			else
-				break;
-			i--;
-		}
-		return a;
-		#elseif cpp
-		var s:Array<String> = untyped __global__.__hxcpp_get_exception_stack();
-		return makeStack(s);
-		#elseif java
-		var stack = [];
-		switch (#if jvm jvm.Exception #else java.internal.Exceptions #end.currentException()) {
-			case null:
-			case current:
-				for (el in current.getStackTrace()) {
-					var className = el.getClassName();
-					var methodName = el.getMethodName();
-					var fileName = el.getFileName();
-					var lineNumber = el.getLineNumber();
-					var method = Method(className, methodName);
-					if (fileName != null || lineNumber >= 0) {
-						stack.push(FilePos(method, fileName, lineNumber));
-					} else {
-						stack.push(method);
-					}
-				}
-		}
-		return stack;
-		#elseif cs
-		return cs.internal.Exceptions.exception == null ? [] : makeStack(new cs.system.diagnostics.StackTrace(cs.internal.Exceptions.exception, true));
-		#elseif python
-		var stack = [];
-		var exc = python.lib.Sys.exc_info();
-		if (exc._3 != null) {
-			var infos = python.lib.Traceback.extract_tb(exc._3);
-			infos.reverse();
-			for (elem in infos)
-				stack.push(FilePos(Method(null, elem._3), elem._1, elem._2));
-		}
-		return stack;
-		#elseif js
-		return getStack(lastException);
-		#elseif eval
-		return getExceptionStack();
-		#else
-		return []; // Unsupported
-		#end
+		var eStack:CallStack = NativeStackTrace.toHaxe(NativeStackTrace.exceptionStack());
+		return eStack.subtract(callStack()).asArray();
 	}
 	}
 
 
 	/**
 	/**
 		Returns a representation of the stack as a printable string.
 		Returns a representation of the stack as a printable string.
 	**/
 	**/
-	public static function toString(stack:Array<StackItem>) {
+	static public function toString(stack:CallStack):String {
 		var b = new StringBuf();
 		var b = new StringBuf();
-		for (s in stack) {
-			b.add("\nCalled from ");
+		for (s in stack.asArray()) {
+			b.add('\nCalled from ');
 			itemToString(b, s);
 			itemToString(b, s);
 		}
 		}
 		return b.toString();
 		return b.toString();
 	}
 	}
 
 
-	private static function itemToString(b:StringBuf, s) {
+	/**
+		Returns a range of entries of current stack from the beginning to the the
+		common part of this and `stack`.
+	**/
+	public function subtract(stack:CallStack):CallStack {
+		var startIndex = -1;
+		var i = -1;
+		while(++i < this.length) {
+			for(j in 0...stack.length) {
+				if(equalItems(this[i], stack[j])) {
+					if(startIndex < 0) {
+						startIndex = i;
+					}
+					++i;
+					if(i >= this.length) break;
+				} else {
+					startIndex = -1;
+				}
+			}
+			if(startIndex >= 0) break;
+		}
+		return startIndex >= 0 ? this.slice(0, startIndex) : this;
+	}
+
+	/**
+		Make a copy of the stack.
+	**/
+	public inline function copy():CallStack {
+		return this.copy();
+	}
+
+	@:arrayAccess public inline function get(index:Int):StackItem {
+		return this[index];
+	}
+
+	inline function asArray():Array<StackItem> {
+		return this;
+	}
+
+	static function equalItems(item1:Null<StackItem>, item2:Null<StackItem>):Bool {
+		return switch([item1, item2]) {
+			case [null, null]: true;
+			case [CFunction, CFunction]: true;
+			case [Module(m1), Module(m2)]:
+				m1 == m2;
+			case [FilePos(item1, file1, line1, col1), FilePos(item2, file2, line2, col2)]:
+				file1 == file2 && line1 == line2 && col1 == col2 && equalItems(item1, item2);
+			case [Method(class1, method1), Method(class2, method2)]:
+				class1 == class2 && method1 == method2;
+			case [LocalFunction(v1), LocalFunction(v2)]:
+				v1 == v2;
+			case _: false;
+		}
+	}
+
+	static function exceptionToString(e:Exception):String {
+		if(e.previous == null) {
+			return 'Exception: ${e.message}${e.stack}';
+		}
+		var result = '';
+		var e:Null<Exception> = e;
+		var prev:Null<Exception> = null;
+		while(e != null) {
+			if(prev == null) {
+				result = 'Exception: ${e.message}${e.stack}' + result;
+			} else {
+				var prevStack = @:privateAccess e.stack.subtract(prev.stack);
+				result = 'Exception: ${e.message}${prevStack}\n\nNext ' + result;
+			}
+			prev = e;
+			e = e.previous;
+		}
+		return result;
+	}
+
+	static function itemToString(b:StringBuf, s) {
 		switch (s) {
 		switch (s) {
 			case CFunction:
 			case CFunction:
 				b.add("a C function");
 				b.add("a C function");
@@ -293,136 +181,4 @@ class CallStack {
 				b.add(n);
 				b.add(n);
 		}
 		}
 	}
 	}
-
-	#if cpp
-	@:noDebug /* Do not mess up the exception stack */
-	#end
-	private static function makeStack(s #if cs:cs.system.diagnostics.StackTrace #elseif hl:hl.NativeArray<hl.Bytes> #else:Dynamic #end) {
-		#if neko
-		var a = new Array();
-		var l = untyped __dollar__asize(s);
-		var i = 0;
-		while (i < l) {
-			var x = s[i++];
-			if (x == null)
-				a.unshift(CFunction);
-			else if (untyped __dollar__typeof(x) == __dollar__tstring)
-				a.unshift(Module(new String(x)));
-			else
-				a.unshift(FilePos(null, new String(untyped x[0]), untyped x[1]));
-		}
-		return a;
-		#elseif flash
-		var a = new Array();
-		var r = ~/at ([^\/]+?)\$?(\/[^\(]+)?\(\)(\[(.*?):([0-9]+)\])?/;
-		var rlambda = ~/^MethodInfo-([0-9]+)$/g;
-		while (r.match(s)) {
-			var cl = r.matched(1).split("::").join(".");
-			var meth = r.matched(2);
-			var item;
-			if (meth == null) {
-				if (rlambda.match(cl))
-					item = LocalFunction(Std.parseInt(rlambda.matched(1)));
-				else
-					item = Method(cl, "new");
-			} else
-				item = Method(cl, meth.substr(1));
-			if (r.matched(3) != null)
-				item = FilePos(item, r.matched(4), Std.parseInt(r.matched(5)));
-			a.push(item);
-			s = r.matchedRight();
-		}
-		return a;
-		#elseif cpp
-		var stack:Array<String> = s;
-		var m = new Array<StackItem>();
-		for (func in stack) {
-			var words = func.split("::");
-			if (words.length == 0)
-				m.push(CFunction)
-			else if (words.length == 2)
-				m.push(Method(words[0], words[1]));
-			else if (words.length == 4)
-				m.push(FilePos(Method(words[0], words[1]), words[2], Std.parseInt(words[3])));
-		}
-		return m;
-		#elseif js
-		if (s == null) {
-			return [];
-		} else if (js.Syntax.typeof(s) == "string") {
-			// Return the raw lines in browsers that don't support prepareStackTrace
-			var stack:Array<String> = s.split("\n");
-			if (stack[0] == "Error")
-				stack.shift();
-			var m = [];
-			var rie10 = ~/^   at ([A-Za-z0-9_. ]+) \(([^)]+):([0-9]+):([0-9]+)\)$/;
-			for (line in stack) {
-				if (rie10.match(line)) {
-					var path = rie10.matched(1).split(".");
-					var meth = path.pop();
-					var file = rie10.matched(2);
-					var line = Std.parseInt(rie10.matched(3));
-					var column = Std.parseInt(rie10.matched(4));
-					m.push(FilePos(meth == "Anonymous function" ? LocalFunction() : meth == "Global code" ? null : Method(path.join("."), meth), file, line,
-						column));
-				} else
-					m.push(Module(StringTools.trim(line))); // A little weird, but better than nothing
-			}
-			return m;
-		} else {
-			return cast s;
-		}
-		#elseif cs
-		var stack = [];
-		for (i in 0...s.FrameCount) {
-			var frame = s.GetFrame(i);
-			var m = frame.GetMethod();
-
-			if (m == null) {
-				continue;
-			}
-			var method = StackItem.Method(m.ReflectedType.ToString(), m.Name);
-
-			var fileName = frame.GetFileName();
-			var lineNumber = frame.GetFileLineNumber();
-
-			if (fileName != null || lineNumber >= 0)
-				stack.push(FilePos(method, fileName, lineNumber));
-			else
-				stack.push(method);
-		}
-		return stack;
-		#elseif hl
-		var stack = [];
-		var r = ~/^([A-Za-z0-9.$_]+)\.([~A-Za-z0-9_]+(\.[0-9]+)?)\((.+):([0-9]+)\)$/;
-		var r_fun = ~/^fun\$([0-9]+)\((.+):([0-9]+)\)$/;
-		for (i in 0...s.length - 1) {
-			var str = @:privateAccess String.fromUCS2(s[i]);
-			if (r.match(str))
-				stack.push(FilePos(Method(r.matched(1), r.matched(2)), r.matched(4), Std.parseInt(r.matched(5))));
-			else if (r_fun.match(str))
-				stack.push(FilePos(LocalFunction(Std.parseInt(r_fun.matched(1))), r_fun.matched(2), Std.parseInt(r_fun.matched(3))));
-			else
-				stack.push(Module(str));
-		}
-		return stack;
-		#else
-		return null;
-		#end
-	}
-}
-
-#if js
-// https://v8.dev/docs/stack-trace-api
-@:native("Error")
-private extern class V8Error {
-	static var prepareStackTrace:(error:js.lib.Error, structuredStackTrace:Array<V8CallSite>)->Any;
-}
-
-typedef V8CallSite = {
-	function getFunctionName():String;
-	function getFileName():String;
-	function getLineNumber():Int;
-	function getColumnNumber():Int;
-}
-#end
+}

+ 110 - 0
std/haxe/Exception.hx

@@ -0,0 +1,110 @@
+package haxe;
+
+/**
+	Base class for exceptions.
+
+	If this class (or derivatives) is used to catch an exception, then
+	`haxe.CallStack.exceptionStack()` will not return a stack for the exception
+	caught. Use `haxe.Exception.stack` property instead:
+	```haxe
+	try {
+		throwSomething();
+	} catch(e:Exception) {
+		trace(e.stack);
+	}
+	```
+
+	Custom exceptions should extend this class:
+	```haxe
+	class MyException extends haxe.Exception {}
+	//...
+	throw new MyException('terrible exception');
+	```
+
+	`haxe.Exception` is also a wildcard type to catch any exception:
+	```haxe
+	try {
+		throw 'Catch me!';
+	} catch(e:haxe.Exception) {
+		trace(e.message); // Output: Catch me!
+	}
+	```
+
+	To rethrow an exception just throw it again.
+	Haxe will try to rethrow an original native exception whenever possible.
+	```haxe
+	try {
+		var a:Array<Int> = null;
+		a.push(1); // generates target-specific null-pointer exception
+	} catch(e:haxe.Exception) {
+		throw e; // rethrows native exception instead of haxe.Exception
+	}
+	```
+**/
+extern class Exception {
+	/**
+		Exception message.
+	**/
+	public var message(get,never):String;
+	private function get_message():String;
+
+	/**
+		The call stack at the moment of the exception creation.
+	**/
+	public var stack(get,never):CallStack;
+	private function get_stack():CallStack;
+
+	/**
+		Contains an exception, which was passed to `previous` constructor argument.
+	**/
+	public var previous(get,never):Null<Exception>;
+	private function get_previous():Null<Exception>;
+
+	/**
+		Native exception, which caused this exception.
+	**/
+	public var native(get,never):Any;
+	final private function get_native():Any;
+
+	/**
+		Used internally for wildcard catches like `catch(e:Exception)`.
+	**/
+	static private function caught(value:Any):Exception;
+
+	/**
+		Used internally for wrapping non-throwable values for `throw` expressions.
+	**/
+	static private function thrown(value:Any):Any;
+
+	/**
+		Create a new Exception instance.
+
+		The `native` argument is for internal usage only.
+		There is no need to provide `native` argument manually and no need to keep it
+		upon extending `haxe.Exception` unless you know what you're doing.
+	**/
+	public function new(message:String, ?previous:Exception, ?native:Any):Void;
+
+	/**
+		Extract an originally thrown value.
+
+		Used internally for catching non-native exceptions.
+		Do _not_ override unless you know what you are doing.
+	**/
+	private function unwrap():Any;
+
+	/**
+		Exception description.
+
+		Includes message, stack and the previous exception (if set).
+	**/
+	public function toString():String;
+
+	/**
+		If this field is defined in a target implementation, then a call to this
+		field will be generated automatically in every constructor of derived classes
+		to make exception stacks point to derived constructor invocations instead of
+		`super` calls.
+	**/
+	// @:noCompletion @:ifFeature("haxe.Exception.stack") private function __shiftStack():Void;
+}

+ 15 - 0
std/haxe/NativeStackTrace.hx

@@ -0,0 +1,15 @@
+package haxe;
+
+import haxe.CallStack.StackItem;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+extern class NativeStackTrace {
+	static public function saveStack(exception:Any):Void;
+	static public function callStack():Any;
+	static public function exceptionStack():Any;
+	static public function toHaxe(nativeStackTrace:Any, skip:Int = 0):Array<StackItem>;
+}

+ 38 - 0
std/haxe/ValueException.hx

@@ -0,0 +1,38 @@
+package haxe;
+
+/**
+	An exception containing arbitrary value.
+
+	This class is automatically used for throwing values, which don't extend `haxe.Exception`
+	or native exception type.
+	For example:
+	```haxe
+	throw "Terrible error";
+	```
+	will be compiled to
+	```haxe
+	throw new ValueException("Terrible error");
+	```
+**/
+class ValueException extends Exception {
+	/**
+		Thrown value.
+	**/
+	public var value(default,null):Any;
+
+	public function new(value:Any, ?previous:Exception, ?native:Any):Void {
+		super(#if js js.Syntax.code('String({0})', value) #else Std.string(value) #end, previous, native);
+		this.value = value;
+	}
+
+	/**
+		Extract an originally thrown value.
+
+		This method must return the same value on subsequent calls.
+		Used internally for catching non-native exceptions.
+		Do _not_ override unless you know what you are doing.
+	**/
+	override function unwrap():Any {
+		return value;
+	}
+}

+ 79 - 0
std/hl/_std/haxe/Exception.hx

@@ -0,0 +1,79 @@
+package haxe;
+
+@:coreApi
+class Exception {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionMessage:String;
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:hl.NativeArray<hl.Bytes>;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
+	@:noCompletion var __nativeException:Any;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		__exceptionMessage = message;
+		__previousException = previous;
+		if(native != null) {
+			__nativeStack = NativeStackTrace.exceptionStack();
+			__nativeException = native;
+		} else {
+			__nativeStack = NativeStackTrace.callStack();
+			__nativeException = this;
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return __exceptionMessage;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null: __exceptionStack = NativeStackTrace.toHaxe(__nativeStack, __skipStack);
+			case s: s;
+		}
+	}
+}

+ 58 - 0
std/hl/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,58 @@
+package haxe;
+
+import hl.NativeArray;
+import hl.Bytes;
+import haxe.CallStack.StackItem;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(exception:Any):Void {
+	}
+
+	@:hlNative("std", "exception_stack")
+	static public function exceptionStack():NativeArray<Bytes> {
+		return null;
+	}
+
+	//TODO: implement in hashlink like `exceptionStack`
+	static public function callStack():NativeArray<Bytes> {
+		var stack:NativeArray<Bytes> = try {
+			throw new Exception('', null, 'stack');
+		} catch (e:Exception) {
+			exceptionStack();
+		}
+		var skip = 1;
+		for(i in 0...stack.length - 1) {
+			var s = @:privateAccess String.fromUCS2(stack[i]);
+			if(s.indexOf('NativeStackTrace.callStack') < 0) {
+				break;
+			}
+			skip++;
+		}
+		return skip < stack.length ? stack.sub(skip, stack.length - skip) : stack;
+	}
+
+	static public function toHaxe(native:NativeArray<Bytes>, skip:Int = 0):Array<StackItem> {
+		var stack = [];
+		var r = ~/^([A-Za-z0-9.$_]+)\.([~A-Za-z0-9_]+(\.[0-9]+)?)\((.+):([0-9]+)\)$/;
+		var r_fun = ~/^fun\$([0-9]+)\((.+):([0-9]+)\)$/;
+		for (i in 0...native.length - 1) {
+			if(skip > i) {
+				continue;
+			}
+			var str = @:privateAccess String.fromUCS2(native[i]);
+			if (r.match(str))
+				stack.push(FilePos(Method(r.matched(1), r.matched(2)), r.matched(4), Std.parseInt(r.matched(5))));
+			else if (r_fun.match(str))
+				stack.push(FilePos(LocalFunction(Std.parseInt(r_fun.matched(1))), r_fun.matched(2), Std.parseInt(r_fun.matched(3))));
+			else
+				stack.push(Module(str));
+		}
+		return stack;
+	}
+}

+ 0 - 1
std/java/Boot.hx

@@ -22,7 +22,6 @@
 
 
 package java;
 package java;
 
 
-import java.internal.Exceptions;
 import java.internal.Function;
 import java.internal.Function;
 import java.internal.HxObject;
 import java.internal.HxObject;
 import java.internal.Runtime;
 import java.internal.Runtime;

+ 0 - 1
std/java/_std/Std.hx

@@ -22,7 +22,6 @@
 
 
 import java.Boot;
 import java.Boot;
 import java.Lib;
 import java.Lib;
-import java.internal.Exceptions;
 
 
 @:coreApi @:nativeGen class Std {
 @:coreApi @:nativeGen class Std {
 	public static inline function is(v:Dynamic, t:Dynamic):Bool {
 	public static inline function is(v:Dynamic, t:Dynamic):Bool {

+ 108 - 0
std/java/_std/haxe/Exception.hx

@@ -0,0 +1,108 @@
+package haxe;
+
+import java.NativeArray;
+import java.lang.Throwable;
+import java.lang.RuntimeException;
+import java.lang.StackTraceElement;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+@:coreApi
+class Exception extends NativeException {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeException:Throwable;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else if(Std.isOfType(value, Throwable)) {
+			return new Exception((value:Throwable).getMessage(), null, value);
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			var native = (value:Exception).__nativeException;
+			return Std.isOfType(native, RuntimeException) ? native : value;
+		} else if(Std.isOfType(value, RuntimeException)) {
+			return value;
+		} else if(Std.isOfType(value, Throwable)) {
+			return new Exception((value:Throwable).getMessage(), null, value);
+		} else {
+			var e = new ValueException(value);
+			var stack = e.getStackTrace();
+			if(stack.length > 1) {
+				e.setStackTrace(java.util.Arrays.copyOfRange(stack, 1, stack.length));
+			}
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		super(message, cast previous);
+		__previousException = previous;
+		if(native != null && Std.isOfType(native, Throwable)) {
+			__nativeException = native;
+			setStackTrace(__nativeException.getStackTrace());
+		} else {
+			__nativeException = cast this;
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	override public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	function get_message():String {
+		return this.getMessage();
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = NativeStackTrace.toHaxe(__nativeException.getStackTrace());
+			case s: s;
+		}
+	}
+}
+
+@:dox(hide)
+@:noCompletion
+@:native('java.lang.RuntimeException')
+private extern class NativeException {
+	@:noCompletion private function new(?message:String, ?cause:Throwable):Void;
+
+	@:noCompletion @:skipReflection private function addSuppressed (param1:Throwable):Void;
+	@:noCompletion @:skipReflection private function fillInStackTrace ():Throwable;
+	@:noCompletion @:skipReflection private function getCause ():Throwable;
+	@:noCompletion @:skipReflection private function getLocalizedMessage ():String;
+	@:noCompletion @:skipReflection private function getMessage ():String;
+	@:noCompletion @:skipReflection private function getStackTrace ():NativeArray<StackTraceElement>;
+	@:noCompletion @:skipReflection private function getSuppressed ():NativeArray<Throwable>;
+	@:noCompletion @:skipReflection private function initCause (param1:Throwable):Throwable;
+	@:noCompletion @:skipReflection @:overload private function printStackTrace (param1:PrintWriter):Void;
+	@:noCompletion @:skipReflection @:overload private function printStackTrace ():Void;
+	@:noCompletion @:skipReflection @:overload private function printStackTrace (param1:PrintStream):Void;
+	@:noCompletion @:skipReflection private function setStackTrace (param1:NativeArray<StackTraceElement>):Void;
+	@:noCompletion @:skipReflection private function toString ():String;
+}

+ 55 - 0
std/java/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,55 @@
+package haxe;
+
+import java.NativeArray;
+import java.lang.ThreadLocal;
+import java.lang.Throwable;
+import java.lang.Thread;
+import java.lang.StackTraceElement;
+import haxe.CallStack.StackItem;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	static var exception = new ThreadLocal<Throwable>();
+
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(e:Throwable):Void {
+		exception.set(e);
+	}
+
+	static public function callStack():NativeArray<StackTraceElement> {
+		var stack = Thread.currentThread().getStackTrace();
+		return stack.length <= 3 ? stack : java.util.Arrays.copyOfRange(stack, 3, stack.length);
+	}
+
+	static public function exceptionStack():NativeArray<StackTraceElement> {
+		return switch exception.get() {
+			case null: new NativeArray(0);
+			case e: e.getStackTrace();
+		}
+	}
+
+	static public function toHaxe(native:NativeArray<StackTraceElement>, skip:Int = 0):Array<StackItem> {
+		var stack = [];
+		for (i in 0...native.length) {
+			if(skip > i) {
+				continue;
+			}
+			var el = native[i];
+			var className = el.getClassName();
+			var methodName = el.getMethodName();
+			var fileName = el.getFileName();
+			var lineNumber = el.getLineNumber();
+			var method = Method(className, methodName);
+			if (fileName != null || lineNumber >= 0) {
+				stack.push(FilePos(method, fileName, lineNumber));
+			} else {
+				stack.push(method);
+			}
+		}
+		return stack;
+	}
+}

+ 0 - 90
std/java/internal/Exceptions.hx

@@ -1,90 +0,0 @@
-/*
- * Copyright (C)2005-2019 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 java.internal;
-
-import java.lang.Throwable;
-import java.lang.RuntimeException;
-import java.lang.Exception;
-
-@:native("haxe.lang.Exceptions")
-class Exceptions {
-	private static var exception = new java.lang.ThreadLocal<java.lang.Throwable>();
-
-	@:keep private static function setException(exc:Throwable) {
-		exception.set(exc);
-	}
-
-	public static function currentException() {
-		return exception.get();
-	}
-}
-
-@:classCode("public static final long serialVersionUID = 5956463319488556322L;")
-@:nativeGen @:keep @:native("haxe.lang.HaxeException") private class HaxeException extends RuntimeException {
-	private var obj:Dynamic;
-
-	public function new(obj:Dynamic, msg:String, cause:Throwable) {
-		super(msg, cause);
-
-		if (Std.isOfType(obj, HaxeException)) {
-			var _obj:HaxeException = cast obj;
-			obj = _obj.getObject();
-		}
-
-		this.obj = obj;
-	}
-
-	public function getObject():Dynamic {
-		return obj;
-	}
-
-	#if !debug
-	@:overload override public function fillInStackTrace():Throwable {
-		return this;
-	}
-	#end
-
-	@:overload override public function toString():String {
-		return "Haxe Exception: " + obj;
-	}
-
-	@:overload override public function getMessage():String {
-		return switch (super.getMessage()) {
-			case null: Std.string(obj);
-			case var message: message;
-		}
-	}
-
-	public static function wrap(obj:Dynamic):RuntimeException {
-		var ret:RuntimeException = null;
-		if (Std.isOfType(obj, RuntimeException))
-			ret = obj;
-		else if (Std.isOfType(obj, String))
-			ret = new HaxeException(obj, obj, null);
-		else if (Std.isOfType(obj, Throwable))
-			ret = new HaxeException(obj, Std.string(obj), obj);
-		else
-			ret = new HaxeException(obj, Std.string(obj), null);
-		return ret;
-	}
-}

+ 5 - 5
std/java/internal/Runtime.hx

@@ -294,7 +294,7 @@ package java.internal;
 		}
 		}
 
 
 		if (throwErrors)
 		if (throwErrors)
-			throw HaxeException.wrap(t);
+			throw (java.lang.RuntimeException)haxe.Exception.thrown(t);
 
 
 		return null;
 		return null;
 	}
 	}
@@ -331,7 +331,7 @@ package java.internal;
 		}
 		}
 		catch (Throwable t)
 		catch (Throwable t)
 		{
 		{
-			throw HaxeException.wrap(t);
+			throw (java.lang.RuntimeException)haxe.Exception.thrown(t);
 		}
 		}
 	')
 	')
 	public static function slowSetField(obj:Dynamic, field:String, value:Dynamic):Dynamic {
 	public static function slowSetField(obj:Dynamic, field:String, value:Dynamic):Dynamic {
@@ -421,7 +421,7 @@ package java.internal;
 
 
 		java.lang.reflect.Method found;
 		java.lang.reflect.Method found;
 		if (ms.length == 0 || (found = ms[0]) == null)
 		if (ms.length == 0 || (found = ms[0]) == null)
-			throw haxe.lang.HaxeException.wrap("No compatible method found for: " + field);
+			throw (java.lang.RuntimeException)haxe.Exception.thrown("No compatible method found for: " + field);
 
 
 		if (hasNumber)
 		if (hasNumber)
 		{
 		{
@@ -471,12 +471,12 @@ package java.internal;
 
 
 		catch (java.lang.reflect.InvocationTargetException e)
 		catch (java.lang.reflect.InvocationTargetException e)
 		{
 		{
-			throw haxe.lang.HaxeException.wrap(e.getCause());
+			throw (java.lang.RuntimeException)haxe.Exception.thrown(e.getCause());
 		}
 		}
 
 
 		catch (Throwable t)
 		catch (Throwable t)
 		{
 		{
-			throw haxe.lang.HaxeException.wrap(t);
+			throw (java.lang.RuntimeException)haxe.Exception.thrown(t);
 		}
 		}
 	')
 	')
 	public static function slowCallField(obj:Dynamic, field:String, args:java.NativeArray<Dynamic>):Dynamic {
 	public static function slowCallField(obj:Dynamic, field:String, args:java.NativeArray<Dynamic>):Dynamic {

+ 1 - 21
std/js/Boot.hx

@@ -24,26 +24,6 @@ package js;
 
 
 import js.Syntax; // import it here so it's always available in the compiler
 import js.Syntax; // import it here so it's always available in the compiler
 
 
-private class HaxeError extends js.lib.Error {
-	var val:Dynamic;
-
-	@:pure
-	public function new(val:Dynamic) {
-		super();
-		this.val = val;
-		if ((cast js.lib.Error).captureStackTrace)
-			(cast js.lib.Error).captureStackTrace(this, HaxeError);
-	}
-
-	public static function wrap(val:Dynamic):js.lib.Error {
-		return if (js.Syntax.instanceof(val, js.lib.Error)) val else new HaxeError(val);
-	}
-
-	static function __init__() {
-		js.Syntax.code("try{Object.defineProperty({0}.prototype, \"message\", {get: function(){return String(this.val)}})}catch(e){}", HaxeError);
-	}
-}
-
 @:dox(hide)
 @:dox(hide)
 class Boot {
 class Boot {
 	static inline function isClass(o:Dynamic):Bool {
 	static inline function isClass(o:Dynamic):Bool {
@@ -179,7 +159,7 @@ class Boot {
 		return __interfLoop(cc.__super__, cl);
 		return __interfLoop(cc.__super__, cl);
 	}
 	}
 
 
-	@:ifFeature("typed_catch") @:pure private static function __instanceof(o:Dynamic, cl:Dynamic) {
+	@:pure private static function __instanceof(o:Dynamic, cl:Dynamic) {
 		if (cl == null)
 		if (cl == null)
 			return false;
 			return false;
 		switch (cl) {
 		switch (cl) {

+ 153 - 0
std/js/_std/haxe/Exception.hx

@@ -0,0 +1,153 @@
+package haxe;
+
+import js.lib.Error;
+
+@:coreApi
+class Exception extends NativeException {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:ifFeature("haxe.Exception.get_stack")
+	@:noCompletion var __skipStack:Int;
+	@:noCompletion var __exceptionStack(get,set):Null<CallStack>;
+	@:noCompletion var __nativeException:Any;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.isOfType(value, Exception)) {
+			return value;
+		} else if(Std.isOfType(value, Error)) {
+			return new Exception((cast value:Error).message, null, value);
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else if(Std.isOfType(value, Error)) {
+			return value;
+		} else {
+			var e = new ValueException(value);
+			untyped __feature__("haxe.Exception.get_stack", e.__shiftStack());
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		super(message);
+		(cast this).message = message;
+		__previousException = previous;
+		__nativeException = native != null ? native : this;
+		untyped __feature__('haxe.Exception.stack', {
+			__skipStack = 0;
+			var old = js.Syntax.code('Error.prepareStackTrace');
+			js.Syntax.code('Error.prepareStackTrace = function(e) { return e.stack; }');
+			if(Std.isOfType(native, Error)) {
+				(cast this).stack = native.stack;
+			} else {
+				var e:Error = null;
+				if ((cast Error).captureStackTrace) {
+					(cast Error).captureStackTrace(this, Exception);
+					e = cast this;
+				} else {
+					e = new Error();
+				}
+				(cast this).stack = e.stack;
+			}
+			js.Syntax.code('Error.prepareStackTrace = {0}', old);
+		});
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return (cast this:Error).message;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = NativeStackTrace.toHaxe(NativeStackTrace.normalize((cast this).stack), __skipStack);
+			case s: s;
+		}
+	}
+
+	function setProperty(name:String, value:Any):Void {
+		try {
+			js.lib.Object.defineProperty(this, name, {value:value});
+		} catch(e:Exception) {
+			js.Syntax.code('{0}[{1}] = {2}', this, name, value);
+		}
+	}
+
+	inline function get___exceptionStack():CallStack {
+		return (cast this).__exceptionStack;
+	}
+
+	inline function set___exceptionStack(value:CallStack):CallStack {
+		setProperty('__exceptionStack', value);
+		return value;
+	}
+
+	inline function get___skipStack():Int {
+		return (cast this).__skipStack;
+	}
+
+	inline function set___skipStack(value:Int):Int {
+		setProperty('__skipStack', value);
+		return value;
+	}
+
+	inline function get___nativeException():Any {
+		return (cast this).__nativeException;
+	}
+
+	inline function set___nativeException(value:Any):Any {
+		setProperty('__nativeException', value);
+		return value;
+	}
+
+	inline function get___previousException():Null<Exception> {
+		return (cast this).__previousException;
+	}
+
+	inline function set___previousException(value:Null<Exception>):Null<Exception> {
+		setProperty('__previousException', value);
+		return value;
+	}
+}
+
+@:dox(hide)
+@:noCompletion
+@:native('Error')
+private extern class NativeException {
+	// private var message:String; //redefined in haxe.Exception
+	@:noCompletion private var name:String;
+	// private var stack(default, null):String; //redefined in haxe.Exception
+
+	private function new(?message:String):Void;
+}

+ 137 - 0
std/js/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,137 @@
+package haxe;
+
+import js.Syntax;
+import js.lib.Error;
+import haxe.CallStack.StackItem;
+
+// https://v8.dev/docs/stack-trace-api
+@:native("Error")
+private extern class V8Error {
+	static var prepareStackTrace:(error:Error, structuredStackTrace:Array<V8CallSite>)->Any;
+}
+
+typedef V8CallSite = {
+	function getFunctionName():String;
+	function getFileName():String;
+	function getLineNumber():Int;
+	function getColumnNumber():Int;
+}
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+@:allow(haxe.Exception)
+class NativeStackTrace {
+	static var lastError:Error;
+
+	// support for source-map-support module
+	@:noCompletion
+	public static var wrapCallSite:V8CallSite->V8CallSite;
+
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(e:Error):Void {
+		lastError = e;
+	}
+
+	static public function callStack():Any {
+		return normalize(tryHaxeStack(new Error()), 2);
+	}
+
+	static public function exceptionStack():Any {
+		return normalize(tryHaxeStack(lastError));
+	}
+
+	static public function toHaxe(s:Null<Any>, skip:Int = 0):Array<StackItem> {
+		if (s == null) {
+			return [];
+		} else if (Syntax.typeof(s) == "string") {
+			// Return the raw lines in browsers that don't support prepareStackTrace
+			var stack:Array<String> = (s:String).split("\n");
+			if (stack[0] == "Error")
+				stack.shift();
+			var m = [];
+			for (i in 0...stack.length) {
+				if(skip > i) continue;
+				var line = stack[i];
+				var matched:Null<Array<String>> = Syntax.code('{0}.match(/^    at ([A-Za-z0-9_. ]+) \\(([^)]+):([0-9]+):([0-9]+)\\)$/)', line);
+				if (matched != null) {
+					var path = matched[1].split(".");
+					var meth = path.pop();
+					var file = matched[2];
+					var line = Std.parseInt(matched[3]);
+					var column = Std.parseInt(matched[4]);
+					m.push(FilePos(meth == "Anonymous function" ? LocalFunction() : meth == "Global code" ? null : Method(path.join("."), meth), file, line,
+						column));
+				} else
+					m.push(Module(StringTools.trim(line))); // A little weird, but better than nothing
+			}
+			return m;
+		} else if(skip > 0 && Syntax.code('Array.isArray({0})', s)) {
+			return (s:Array<StackItem>).slice(skip);
+		} else {
+			return cast s;
+		}
+	}
+
+	static function tryHaxeStack(e:Null<Error>):Any {
+		if (e == null) {
+			return [];
+		}
+		// https://v8.dev/docs/stack-trace-api
+		var oldValue = V8Error.prepareStackTrace;
+		V8Error.prepareStackTrace = prepareHxStackTrace;
+		var stack = e.stack;
+		V8Error.prepareStackTrace = oldValue;
+		return stack;
+	}
+
+	static function prepareHxStackTrace(e:Error, callsites:Array<V8CallSite>):Any {
+		var stack = [];
+		for (site in callsites) {
+			if (wrapCallSite != null)
+				site = wrapCallSite(site);
+			var method = null;
+			var fullName = site.getFunctionName();
+			if (fullName != null) {
+				var idx = fullName.lastIndexOf(".");
+				if (idx >= 0) {
+					var className = fullName.substring(0, idx - 1);
+					var methodName = fullName.substring(idx + 1);
+					method = Method(className, methodName);
+				}
+			}
+			var fileName = site.getFileName();
+			var fileAddr = fileName == null ? -1 : fileName.indexOf("file:");
+			if (wrapCallSite != null && fileAddr > 0)
+				fileName = fileName.substring(fileAddr + 6);
+			stack.push(FilePos(method, fileName, site.getLineNumber(), site.getColumnNumber()));
+		}
+		return stack;
+	}
+
+	static function normalize(stack:Any, skipItems:Int = 0):Any {
+		if(Syntax.code('Array.isArray({0})', stack) && skipItems > 0) {
+			return (stack:Array<StackItem>).slice(skipItems);
+		} else if(Syntax.typeof(stack) == "string") {
+			switch (stack:String).substring(0, 6) {
+				case 'Error:' | 'Error\n': skipItems += 1;
+				case _:
+			}
+			return skipLines(stack, skipItems);
+		} else {
+			//nothing we can do
+			return stack;
+		}
+	}
+
+	static function skipLines(stack:String, skip:Int, pos:Int = 0):String {
+		return if(skip > 0) {
+			pos = stack.indexOf('\n', pos);
+			return pos < 0 ? '' : skipLines(stack, --skip, pos + 1);
+		} else {
+			return stack.substring(pos);
+		}
+	}
+}

+ 0 - 60
std/jvm/Exception.hx

@@ -1,60 +0,0 @@
-/*
- * Copyright (C)2005-2019 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 jvm;
-
-@:keep
-@:native('haxe.jvm.Exception')
-class Exception<T> extends java.lang.Exception {
-	static public var exception = new java.lang.ThreadLocal<java.lang.Throwable>();
-
-	static public function setException(exc:java.lang.Throwable) {
-		exception.set(exc);
-	}
-
-	static public function currentException() {
-		return exception.get();
-	}
-
-	public var value:T;
-
-	public function new(value:T) {
-		super();
-		this.value = value;
-	}
-
-	@:overload override public function toString() {
-		return Std.string(value);
-	}
-
-	public function unwrap() {
-		return value;
-	}
-
-	static public function wrap<T>(t:Null<T>) {
-		if (Jvm.instanceof(t, java.lang.Exception)) {
-			return (cast t : java.lang.Exception);
-		} else {
-			return new Exception(t);
-		}
-	}
-}

+ 0 - 1
std/jvm/Jvm.hx

@@ -31,7 +31,6 @@ import java.Init;
 import java.NativeArray;
 import java.NativeArray;
 import jvm.DynamicObject;
 import jvm.DynamicObject;
 import jvm.EmptyConstructor;
 import jvm.EmptyConstructor;
-import jvm.Exception;
 import jvm.Object;
 import jvm.Object;
 import jvm.annotation.ClassReflectionInformation;
 import jvm.annotation.ClassReflectionInformation;
 import jvm.annotation.EnumReflectionInformation;
 import jvm.annotation.EnumReflectionInformation;

+ 81 - 0
std/lua/_std/haxe/Exception.hx

@@ -0,0 +1,81 @@
+package haxe;
+
+@:coreApi
+class Exception {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionMessage:String;
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:Array<String>;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
+	@:noCompletion var __nativeException:Any;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		__exceptionMessage = message;
+		__previousException = previous;
+		if(native != null) {
+			__nativeException = native;
+			__nativeStack = NativeStackTrace.exceptionStack();
+		} else {
+			__nativeException = this;
+			__nativeStack = NativeStackTrace.callStack();
+			__skipStack = 1;
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return __exceptionMessage;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = NativeStackTrace.toHaxe(__nativeStack, __skipStack);
+			case s: s;
+		}
+	}
+}

+ 54 - 0
std/lua/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,54 @@
+package haxe;
+
+import haxe.CallStack.StackItem;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(exception:Any):Void {
+	}
+
+	static public function callStack():Array<String> {
+		return switch lua.Debug.traceback() {
+			case null: [];
+			case s: s.split('\n').slice(3);
+		}
+	}
+
+	static public function exceptionStack():Array<String> {
+		return []; //Not implemented. Maybe try xpcal instead of pcal in genlua.
+	}
+
+	static public function toHaxe(native:Array<String>, skip:Int = 0):Array<StackItem> {
+		var stack = [];
+		var cnt = -1;
+		for (item in native) {
+			var parts = item.substr(1).split(":"); //`substr` to skip a tab at the beginning of a line
+			var file = parts[0];
+			if(file == '[C]') {
+				continue;
+			}
+			++cnt;
+			if(skip > cnt) {
+				continue;
+			}
+			var line = parts[1];
+			var method = if(parts.length <= 2) {
+				null;
+			} else {
+				var methodPos = parts[2].indexOf("'");
+				if(methodPos < 0) {
+					null;
+				} else {
+					Method(null, parts[2].substring(methodPos + 1, parts[2].length - 1));
+				}
+			}
+			stack.push(FilePos(method, file, Std.parseInt(line)));
+		}
+		return stack;
+	}
+}

+ 81 - 0
std/neko/_std/haxe/Exception.hx

@@ -0,0 +1,81 @@
+package haxe;
+
+@:coreApi
+class Exception {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionMessage:String;
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:Any;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
+	@:noCompletion var __nativeException:Any;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		__exceptionMessage = message;
+		__previousException = previous;
+		if(native != null) {
+			__nativeStack = NativeStackTrace.exceptionStack();
+			__nativeException = native;
+		} else {
+			__nativeStack = NativeStackTrace.callStack();
+			__shiftStack();
+			__nativeException = this;
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return __exceptionMessage;
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = NativeStackTrace.toHaxe(__nativeStack, __skipStack);
+			case s: s;
+		}
+	}
+}

+ 51 - 0
std/neko/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,51 @@
+package haxe;
+
+import haxe.CallStack.StackItem;
+
+private typedef NativeTrace = {
+	final skip:Int;
+	final stack:Dynamic;
+}
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(exception:Any):Void {
+	}
+
+	static public inline function callStack():NativeTrace {
+		return { skip:1, stack:untyped __dollar__callstack() };
+	}
+
+	static public function exceptionStack():NativeTrace {
+		return { skip:0, stack:untyped __dollar__excstack() };
+	}
+
+	static public function toHaxe(native:NativeTrace, skip:Int = 0):Array<StackItem> {
+		skip += native.skip;
+		var a = new Array();
+		var l = untyped __dollar__asize(native.stack);
+		var i = 0;
+		while (i < l) {
+			var x = native.stack[l - i - 1];
+			//skip all CFunctions until we skip required amount of hx entries
+			if(x == null && skip > i) {
+				skip++;
+			}
+			if(skip > i++) {
+				continue;
+			}
+			if (x == null)
+				a.push(CFunction);
+			else if (untyped __dollar__typeof(x) == __dollar__tstring)
+				a.push(Module(new String(x)));
+			else
+				a.push(FilePos(null, new String(untyped x[0]), untyped x[1]));
+		}
+		return a;
+	}
+}

+ 2 - 16
std/php/Boot.hx

@@ -315,7 +315,7 @@ class Boot {
 
 
 	/**
 	/**
 		Implementation for `cast(value, Class<Dynamic>)`
 		Implementation for `cast(value, Class<Dynamic>)`
-		@throws HxException if `value` cannot be casted to this type
+		@throws haxe.ValueError if `value` cannot be casted to this type
 	**/
 	**/
 	public static function typedCast(hxClass:HxClass, value:Dynamic):Dynamic {
 	public static function typedCast(hxClass:HxClass, value:Dynamic):Dynamic {
 		if (value == null)
 		if (value == null)
@@ -1014,18 +1014,4 @@ private class HxClosure {
 	public function callWith(newThis:Dynamic, args:NativeArray):Dynamic {
 	public function callWith(newThis:Dynamic, args:NativeArray):Dynamic {
 		return Global.call_user_func_array(getCallback(newThis), args);
 		return Global.call_user_func_array(getCallback(newThis), args);
 	}
 	}
-}
-
-/**
-	Special exception which is used to wrap non-throwable values
-**/
-@:keep
-@:dox(hide)
-private class HxException extends Exception {
-	var e:Dynamic;
-
-	public function new(e:Dynamic):Void {
-		this.e = e;
-		super(Boot.stringify(e));
-	}
-}
+}

+ 1 - 1
std/php/Throwable.hx

@@ -35,4 +35,4 @@ extern interface Throwable {
 	function getTrace():NativeIndexedArray<NativeAssocArray<Dynamic>>; // an array of the backtrace
 	function getTrace():NativeIndexedArray<NativeAssocArray<Dynamic>>; // an array of the backtrace
 	function getTraceAsString():String; // formatted string of trace
 	function getTraceAsString():String; // formatted string of trace
 	@:phpMagic function __toString():String; // formatted string for display
 	@:phpMagic function __toString():String; // formatted string for display
-}
+}

+ 0 - 165
std/php/_std/haxe/CallStack.hx

@@ -1,165 +0,0 @@
-/*
- * Copyright (C)2005-2019 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 haxe;
-
-import php.*;
-
-private typedef NativeTrace = NativeIndexedArray<NativeAssocArray<Dynamic>>;
-
-enum StackItem {
-	CFunction;
-	Module(m:String);
-	FilePos(s:Null<StackItem>, file:String, line:Int, ?column:Null<Int>);
-	Method(classname:Null<String>, method:String);
-	LocalFunction(?v:Int);
-}
-
-class CallStack {
-	/**
-		If defined this function will be used to transform call stack entries.
-		@param String - generated php file name.
-		@param Int - Line number in generated file.
-	**/
-	static public var mapPosition:String->Int->Null<{?source:String, ?originalLine:Int}>;
-
-	@:ifFeature("haxe.CallStack.exceptionStack")
-	static var lastExceptionTrace:NativeTrace;
-
-	public static function callStack():Array<StackItem> {
-		return makeStack(Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS));
-	}
-
-	public static function exceptionStack():Array<StackItem> {
-		return makeStack(lastExceptionTrace == null ? new NativeIndexedArray() : lastExceptionTrace);
-	}
-
-	public static function toString(stack:Array<StackItem>) {
-		var b = new StringBuf();
-		for (s in stack) {
-			b.add("\nCalled from ");
-			itemToString(b, s);
-		}
-		return b.toString();
-	}
-
-	static function itemToString(b:StringBuf, s) {
-		switch (s) {
-			case CFunction:
-				b.add("a C function");
-			case Module(m):
-				b.add("module ");
-				b.add(m);
-			case FilePos(s, file, line, _):
-				if (s != null) {
-					itemToString(b, s);
-					b.add(" (");
-				}
-				b.add(file);
-				b.add(" line ");
-				b.add(line);
-				if (s != null)
-					b.add(")");
-			case Method(cname, meth):
-				b.add(cname == null ? "<unknown>" : cname);
-				b.add(".");
-				b.add(meth);
-			case LocalFunction(n):
-				b.add("local function");
-		}
-	}
-
-	@:ifFeature("haxe.CallStack.exceptionStack")
-	static function saveExceptionTrace(e:Throwable):Void {
-		lastExceptionTrace = e.getTrace();
-
-		// Reduce exception stack to the place where exception was caught
-		var currentTrace = Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS);
-		var count = Global.count(currentTrace);
-
-		for (i in -(count - 1)...1) {
-			var exceptionEntry:NativeAssocArray<Dynamic> = Global.end(lastExceptionTrace);
-
-			if (!Global.isset(exceptionEntry['file']) || !Global.isset(currentTrace[-i]['file'])) {
-				Global.array_pop(lastExceptionTrace);
-			} else if (currentTrace[-i]['file'] == exceptionEntry['file'] && currentTrace[-i]['line'] == exceptionEntry['line']) {
-				Global.array_pop(lastExceptionTrace);
-			} else {
-				break;
-			}
-		}
-
-		// Remove arguments from trace to avoid blocking some objects from GC
-		var count = Global.count(lastExceptionTrace);
-		for (i in 0...count) {
-			lastExceptionTrace[i]['args'] = new NativeArray();
-		}
-
-		var thrownAt = new NativeAssocArray<Dynamic>();
-		thrownAt['function'] = '';
-		thrownAt['line'] = e.getLine();
-		thrownAt['file'] = e.getFile();
-		thrownAt['class'] = '';
-		thrownAt['args'] = new NativeArray();
-		Global.array_unshift(lastExceptionTrace, thrownAt);
-	}
-
-	static function makeStack(native:NativeTrace):Array<StackItem> {
-		var result = [];
-		var count = Global.count(native);
-
-		for (i in 0...count) {
-			var entry = native[i];
-			var item = null;
-
-			if (i + 1 < count) {
-				var next = native[i + 1];
-
-				if (!Global.isset(next['function']))
-					next['function'] = '';
-				if (!Global.isset(next['class']))
-					next['class'] = '';
-
-				if ((next['function'] : String).indexOf('{closure}') >= 0) {
-					item = LocalFunction();
-				} else if (Global.strlen(next['class']) > 0 && Global.strlen(next['function']) > 0) {
-					var cls = Boot.getClassName(next['class']);
-					item = Method(cls, next['function']);
-				}
-			}
-			if (Global.isset(entry['file'])) {
-				if (mapPosition != null) {
-					var pos = mapPosition(entry['file'], entry['line']);
-					if (pos != null && pos.source != null && pos.originalLine != null) {
-						entry['file'] = pos.source;
-						entry['line'] = pos.originalLine;
-					}
-				}
-				result.push(FilePos(item, entry['file'], entry['line']));
-			} else if (item != null) {
-				result.push(item);
-			}
-		}
-
-		return result;
-	}
-}

+ 99 - 0
std/php/_std/haxe/Exception.hx

@@ -0,0 +1,99 @@
+package haxe;
+
+import php.Throwable;
+import php.NativeAssocArray;
+import php.NativeIndexedArray;
+
+@:coreApi
+class Exception extends NativeException {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeException:Throwable;
+	@:noCompletion var __skipStack:Int = 0;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else if(Std.isOfType(value, Throwable)) {
+			return new Exception((value:Throwable).getMessage(), null, value);
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else if(Std.isOfType(value, Throwable)) {
+			return value;
+		} else {
+			var e = new ValueException(value);
+			e.__skipStack = 1;
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		super(message, 0, previous);
+		this.__previousException = previous;
+		if(native != null && Std.isOfType(native, Throwable)) {
+			__nativeException = native;
+		} else {
+			__nativeException = cast this;
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	function get_message():String {
+		return this.getMessage();
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				var nativeTrace = NativeStackTrace.complementTrace(__nativeException.getTrace(), native);
+				__exceptionStack = NativeStackTrace.toHaxe(nativeTrace, __skipStack);
+			case s: s;
+		}
+	}
+}
+
+@:dox(hide)
+@:noCompletion
+@:native('Exception')
+private extern class NativeException {
+	@:noCompletion private function new(?message:String, ?code:Int, ?previous:NativeException):Void;
+
+	@:noCompletion private var code:Int;
+	@:noCompletion private var file:String;
+	@:noCompletion private var line:Int;
+
+	@:noCompletion final private function getPrevious():Throwable;
+	@:noCompletion private function getMessage():String;
+	@:noCompletion private function getCode():Int;
+	@:noCompletion private function getFile():String;
+	@:noCompletion private function getLine():Int;
+	@:noCompletion private function getTrace():NativeIndexedArray<NativeAssocArray<Dynamic>>;
+	@:noCompletion private function getTraceAsString():String;
+	@:noCompletion @:phpMagic private function __toString():String;
+}

+ 115 - 0
std/php/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,115 @@
+package haxe;
+
+import php.*;
+import haxe.CallStack.StackItem;
+
+private typedef NativeTrace = NativeIndexedArray<NativeAssocArray<Dynamic>>;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+@:allow(haxe.Exception)
+class NativeStackTrace {
+	/**
+		If defined this function will be used to transform call stack entries.
+		@param String - generated php file name.
+		@param Int - Line number in generated file.
+	**/
+	static public var mapPosition:String->Int->Null<{?source:String, ?originalLine:Int}>;
+
+	static var lastExceptionTrace:Null<NativeTrace>;
+
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public function saveStack(e:Throwable) {
+		var nativeTrace = e.getTrace();
+
+		// Reduce exception stack to the place where exception was caught
+		var currentTrace = Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS);
+		var count = Global.count(currentTrace);
+
+		for (i in -(count - 1)...1) {
+			var exceptionEntry:NativeAssocArray<Dynamic> = Global.end(nativeTrace);
+
+			if (!Global.isset(exceptionEntry['file']) || !Global.isset(currentTrace[-i]['file'])) {
+				Global.array_pop(nativeTrace);
+			} else if (currentTrace[-i]['file'] == exceptionEntry['file'] && currentTrace[-i]['line'] == exceptionEntry['line']) {
+				Global.array_pop(nativeTrace);
+			} else {
+				break;
+			}
+		}
+
+		// Remove arguments from trace to avoid blocking some objects from GC
+		var count = Global.count(nativeTrace);
+		for (i in 0...count) {
+			nativeTrace[i]['args'] = new NativeArray();
+		}
+
+		lastExceptionTrace = complementTrace(nativeTrace, e);
+	}
+
+	static public inline function callStack():NativeTrace {
+		return Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS);
+	}
+
+	static public function exceptionStack():NativeTrace {
+		return lastExceptionTrace == null ? new NativeIndexedArray() : lastExceptionTrace;
+	}
+
+	static public function toHaxe(native:NativeTrace, skip:Int = 0):Array<StackItem> {
+		var result = [];
+		var count = Global.count(native);
+
+		for (i in 0...count) {
+			if(skip > i) {
+				continue;
+			}
+
+			var entry = native[i];
+			var item = null;
+
+			if (i + 1 < count) {
+				var next = native[i + 1];
+
+				if (!Global.isset(next['function']))
+					next['function'] = '';
+				if (!Global.isset(next['class']))
+					next['class'] = '';
+
+				if ((next['function'] : String).indexOf('{closure}') >= 0) {
+					item = LocalFunction();
+				} else if (Global.strlen(next['class']) > 0 && Global.strlen(next['function']) > 0) {
+					var cls = Boot.getClassName(next['class']);
+					item = Method(cls, next['function']);
+				}
+			}
+			if (Global.isset(entry['file'])) {
+				if (mapPosition != null) {
+					var pos = mapPosition(entry['file'], entry['line']);
+					if (pos != null && pos.source != null && pos.originalLine != null) {
+						entry['file'] = pos.source;
+						entry['line'] = pos.originalLine;
+					}
+				}
+				result.push(FilePos(item, entry['file'], entry['line']));
+			} else if (item != null) {
+				result.push(item);
+			}
+		}
+
+		return result;
+	}
+
+	static function complementTrace(nativeTrace:NativeTrace, e:Throwable):NativeTrace {
+		var thrownAt = new NativeAssocArray<Dynamic>();
+		thrownAt['function'] = '';
+		thrownAt['line'] = e.getLine();
+		thrownAt['file'] = e.getFile();
+		thrownAt['class'] = '';
+		thrownAt['args'] = new NativeArray();
+		Global.array_unshift(nativeTrace, thrownAt);
+		return nativeTrace;
+	}
+}

+ 0 - 1
std/python/Boot.hx

@@ -28,7 +28,6 @@ import python.internal.Internal;
 import python.internal.StringImpl;
 import python.internal.StringImpl;
 import python.internal.EnumImpl;
 import python.internal.EnumImpl;
 import python.internal.HxOverrides;
 import python.internal.HxOverrides;
-import python.internal.HxException;
 import python.internal.AnonObject;
 import python.internal.AnonObject;
 import python.internal.UBuiltins;
 import python.internal.UBuiltins;
 import python.lib.Inspect;
 import python.lib.Inspect;

+ 90 - 0
std/python/_std/haxe/Exception.hx

@@ -0,0 +1,90 @@
+package haxe;
+
+import python.Exceptions.BaseException;
+import python.Exceptions.Exception in PyException;
+import python.lib.Traceback;
+import python.internal.UBuiltins;
+
+private typedef PyStackItem = python.Tuple.Tuple4<String, Int, String, String>;
+
+@:coreApi
+class Exception extends PyException {
+	public var message(get,never):String;
+	public var stack(get,never):CallStack;
+	public var previous(get,never):Null<Exception>;
+	public var native(get,never):Any;
+
+	@:noCompletion var __exceptionStack:Null<CallStack>;
+	@:noCompletion var __nativeStack:Array<PyStackItem>;
+	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
+	@:noCompletion var __nativeException:BaseException;
+	@:noCompletion var __previousException:Null<Exception>;
+
+	static function caught(value:Any):Exception {
+		if(Std.is(value, Exception)) {
+			return value;
+		} else if(Std.isOfType(value, BaseException)) {
+			return new Exception(UBuiltins.str(value), null, value);
+		} else {
+			return new ValueException(value, null, value);
+		}
+	}
+
+	static function thrown(value:Any):Any {
+		if(Std.isOfType(value, Exception)) {
+			return (value:Exception).native;
+		} else if(Std.isOfType(value, BaseException)) {
+			return value;
+		} else {
+			var e = new ValueException(value);
+			e.__shiftStack();
+			return e;
+		}
+	}
+
+	public function new(message:String, ?previous:Exception, ?native:Any) {
+		super(message);
+		this.__previousException = previous;
+		if(native != null && Std.isOfType(native, BaseException)) {
+			__nativeException = native;
+			__nativeStack = NativeStackTrace.exceptionStack();
+		} else {
+			__nativeException = cast this;
+			__nativeStack = NativeStackTrace.callStack();
+		}
+	}
+
+	function unwrap():Any {
+		return __nativeException;
+	}
+
+	public function toString():String {
+		return inline CallStack.exceptionToString(this);
+	}
+
+	@:noCompletion
+	@:ifFeature("haxe.Exception.get_stack")
+	inline function __shiftStack():Void {
+		__skipStack++;
+	}
+
+	function get_message():String {
+		return UBuiltins.str(this);
+	}
+
+	function get_previous():Null<Exception> {
+		return __previousException;
+	}
+
+	final function get_native():Any {
+		return __nativeException;
+	}
+
+	function get_stack():CallStack {
+		return switch __exceptionStack {
+			case null:
+				__exceptionStack = NativeStackTrace.toHaxe(__nativeStack, __skipStack);
+			case s: s;
+		}
+	}
+}

+ 46 - 0
std/python/_std/haxe/NativeStackTrace.hx

@@ -0,0 +1,46 @@
+package haxe;
+
+import haxe.CallStack.StackItem;
+
+private typedef NativeTrace = Array<python.Tuple.Tuple4<String, Int, String, String>>;
+
+/**
+	Do not use manually.
+**/
+@:dox(hide)
+@:noCompletion
+class NativeStackTrace {
+	@:ifFeature('haxe.NativeStackTrace.exceptionStack')
+	static public inline function saveStack(exception:Any):Void {
+	}
+
+	static public inline function callStack():NativeTrace {
+		var infos = python.lib.Traceback.extract_stack();
+		infos.pop();
+		infos.reverse();
+		return infos;
+	}
+
+	static public function exceptionStack():NativeTrace {
+		var exc = python.lib.Sys.exc_info();
+		if (exc._3 != null) {
+			var infos = python.lib.Traceback.extract_tb(exc._3);
+			infos.reverse();
+			return infos;
+		} else {
+			return [];
+		}
+	}
+
+	static public function toHaxe(native:NativeTrace, skip:Int = 0):Array<StackItem> {
+		var stack = [];
+		for(i in 0...native.length) {
+			if(skip > i) {
+				continue;
+			}
+			var elem = native[i];
+			stack.push(FilePos(Method(null, elem._3), elem._1, elem._2));
+		}
+		return stack;
+	}
+}

+ 0 - 37
std/python/internal/HxException.hx

@@ -1,37 +0,0 @@
-/*
- * Copyright (C)2005-2019 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 python.internal;
-
-@:ifFeature("has_throw")
-@:native("_HxException")
-class HxException extends python.Exceptions.Exception {
-	@:ifFeature("has_throw")
-	public var val:Dynamic;
-
-	@:ifFeature("has_throw")
-	public function new(val:Dynamic) {
-		var message = UBuiltins.str(val);
-		super(message);
-		this.val = val;
-	}
-}

+ 2 - 0
tests/misc/projects/Issue8303/MainCatch.hx

@@ -6,6 +6,8 @@ class MainCatch {
 	static function test() {
 	static function test() {
 		function log() {
 		function log() {
 			log();
 			log();
+			//prevent tail recursion elimination
+			return 0;
 		}
 		}
 		try {
 		try {
 			log();
 			log();

+ 1 - 1
tests/misc/projects/Issue8303/compile-fail.hxml

@@ -1,3 +1,3 @@
 -main Main
 -main Main
--D eval-call-stack-depth=5
+-D eval-call-stack-depth=20
 --interp
 --interp

+ 16 - 1
tests/misc/projects/Issue8303/compile-fail.hxml.stderr

@@ -2,5 +2,20 @@ Uncaught exception Stack overflow
 Main.hx:1: character 1 : Called from here
 Main.hx:1: character 1 : Called from here
 Main.hx:8: characters 4-9 : Called from here
 Main.hx:8: characters 4-9 : Called from here
 Main.hx:8: characters 4-9 : Called from here
 Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
+Main.hx:8: characters 4-9 : Called from here
 Main.hx:10: characters 3-8 : Called from here
 Main.hx:10: characters 3-8 : Called from here
-Main.hx:3: characters 3-9 : Called from here
+Main.hx:3: characters 3-9 : Called from here

+ 1 - 1
tests/misc/projects/Issue8303/compile.hxml

@@ -1,3 +1,3 @@
 -main MainCatch
 -main MainCatch
--D eval-call-stack-depth=5
+-D eval-call-stack-depth=20
 --interp
 --interp

+ 7 - 7
tests/optimization/src/TestJs.hx

@@ -62,7 +62,7 @@ class TestJs {
 		return v + v2;
 		return v + v2;
 	}
 	}
 
 
-	@:js("var a = [];var tmp;try {tmp = a[0];} catch( e ) {((e) instanceof js__$Boot_HaxeError);tmp = null;}tmp;")
+	@:js("var a = [];var tmp;try {tmp = a[0];} catch( _g3 ) {tmp = null;}tmp;")
 	@:analyzer(no_local_dce)
 	@:analyzer(no_local_dce)
 	static function testInlineWithComplexExpr() {
 	static function testInlineWithComplexExpr() {
 		var a = [];
 		var a = [];
@@ -167,27 +167,27 @@ class TestJs {
 		var vRand = new Inl(Math.random());
 		var vRand = new Inl(Math.random());
 	}
 	}
 
 
-	@:js("try {throw new js__$Boot_HaxeError(false);} catch( e ) {}")
+	@:js("try {throw haxe_Exception.thrown(false);} catch( _g9 ) {}")
 	static function testNoHaxeErrorUnwrappingWhenNotRequired() {
 	static function testNoHaxeErrorUnwrappingWhenNotRequired() {
 		try throw false catch (e:Dynamic) {}
 		try throw false catch (e:Dynamic) {}
 	}
 	}
 
 
-	@:js('try {throw new js__$Boot_HaxeError(false);} catch( e ) {TestJs.use(((e) instanceof js__$Boot_HaxeError) ? e.val : e);}')
+	@:js('try {throw haxe_Exception.thrown(false);} catch( _g12 ) {TestJs.use(haxe_Exception.caught(_g12).unwrap());}')
 	static function testHaxeErrorUnwrappingWhenUsed() {
 	static function testHaxeErrorUnwrappingWhenUsed() {
 		try throw false catch (e:Dynamic) use(e);
 		try throw false catch (e:Dynamic) use(e);
 	}
 	}
 
 
-	@:js('try {throw new js__$Boot_HaxeError(false);} catch( e ) {if(typeof(((e) instanceof js__$Boot_HaxeError) ? e.val : e) != "boolean") {throw e;}}')
+	@:js('try {throw haxe_Exception.thrown(false);} catch( _g15 ) {if(typeof(haxe_Exception.caught(_g15).unwrap()) != "boolean") {throw _g15;}}')
 	static function testHaxeErrorUnwrappingWhenTypeChecked() {
 	static function testHaxeErrorUnwrappingWhenTypeChecked() {
 		try throw false catch (e:Bool) {};
 		try throw false catch (e:Bool) {};
 	}
 	}
 
 
-	@:js('try {throw new js__$Boot_HaxeError(false);} catch( e ) {if(typeof(((e) instanceof js__$Boot_HaxeError) ? e.val : e) == "boolean") {TestJs.use(e);} else {throw e;}}')
+	@:js('try {throw haxe_Exception.thrown(false);} catch( _g18 ) {if(typeof(haxe_Exception.caught(_g18).unwrap()) == "boolean") {TestJs.use(_g18);} else {throw _g18;}}')
 	static function testGetOriginalException() {
 	static function testGetOriginalException() {
 		try throw false catch (e:Bool) use(js.Lib.getOriginalException());
 		try throw false catch (e:Bool) use(js.Lib.getOriginalException());
 	}
 	}
 
 
-	@:js('try {throw new js__$Boot_HaxeError(false);} catch( e ) {if(typeof(((e) instanceof js__$Boot_HaxeError) ? e.val : e) == "boolean") {throw e;} else {throw e;}}')
+	@:js('try {throw haxe_Exception.thrown(false);} catch( _g21 ) {if(typeof(haxe_Exception.caught(_g21).unwrap()) == "boolean") {throw _g21;} else {throw _g21;}}')
 	static function testRethrow() {
 	static function testRethrow() {
 		try throw false catch (e:Bool) js.Lib.rethrow();
 		try throw false catch (e:Bool) js.Lib.rethrow();
 	}
 	}
@@ -362,7 +362,7 @@ class TestJs {
 	@:js('
 	@:js('
 		TestJs.getInt();
 		TestJs.getInt();
 		if(TestJs.getInt() != 0) {
 		if(TestJs.getInt() != 0) {
-			throw new js__$Boot_HaxeError("meh");
+			throw haxe_Exception.thrown("meh");
 		}
 		}
 	')
 	')
 	static function testIfInvert() {
 	static function testIfInvert() {

+ 2 - 2
tests/optimization/src/TestTreGeneration.hx

@@ -131,10 +131,10 @@ class TestTreGeneration {
 		while(true) {
 		while(true) {
 			try {
 			try {
 				if(n <= 0) {
 				if(n <= 0) {
-					throw new js__$Boot_HaxeError("exit");
+					throw haxe_Exception.thrown("exit");
 				}
 				}
 				return TestTreGeneration.testTryCancelsTre(n - 1);
 				return TestTreGeneration.testTryCancelsTre(n - 1);
-			} catch( e ) {
+			} catch( _g24 ) {
 				if(n == 0) {
 				if(n == 0) {
 					n -= 1;
 					n -= 1;
 					continue;
 					continue;

+ 2 - 0
tests/runci/targets/Flash.hx

@@ -144,6 +144,8 @@ class Flash {
 				break;
 				break;
 			}
 			}
 		}
 		}
+		traceProcess.kill();
+		traceProcess.close();
 		Sys.command("cat", [flashlogPath]);
 		Sys.command("cat", [flashlogPath]);
 		return success;
 		return success;
 	}
 	}

+ 336 - 0
tests/unit/src/unit/TestExceptions.hx

@@ -0,0 +1,336 @@
+package unit;
+
+import haxe.Exception;
+import haxe.ValueException;
+import haxe.CallStack;
+import utest.Assert;
+
+private enum EnumError {
+	EError;
+}
+
+private abstract AbstrString(String) from String {}
+private abstract AbstrException(CustomHaxeException) from CustomHaxeException {}
+
+private class CustomHaxeException extends Exception {}
+
+#if php
+private class CustomNativeException extends php.Exception {}
+#elseif js
+private class CustomNativeException extends js.lib.Error {}
+#elseif flash
+private class CustomNativeException extends flash.errors.Error {}
+#elseif java
+private class CustomNativeException extends java.lang.RuntimeException {}
+#elseif cs
+private class CustomNativeException extends cs.system.Exception {}
+#elseif python
+private class CustomNativeException extends python.Exceptions.Exception {}
+#elseif (lua || eval || neko || hl || cpp)
+private class CustomNativeException { public function new(m:String) {} }
+#end
+
+#if java
+private class NativeExceptionBase extends java.lang.RuntimeException {}
+private class NativeExceptionChild extends NativeExceptionBase {}
+private class NativeExceptionOther extends java.lang.RuntimeException {}
+#end
+
+private class NoConstructorValueException extends ValueException {}
+
+private class WithConstructorValueException extends ValueException {
+	public function new(value:Any, ?previous:Exception, ?native:Any) {
+		super(value, previous, native);
+	}
+}
+
+private typedef ItemData = {?file:String, ?line:Int, ?method:String}
+
+class TestExceptions extends Test {
+	/** Had to move to instance var because of https://github.com/HaxeFoundation/haxe/issues/9174 */
+	var rethrown:Bool = false;
+
+	public function testWildCardCatch() {
+		try {
+			throw 123;
+		} catch(e:Dynamic) {
+			eq(123, e);
+		}
+
+		try {
+			throw 123;
+		} catch(e:Exception) {
+			eq('123', e.message);
+			t(Std.isOfType(e, ValueException));
+		}
+	}
+
+	public function testWildCardCatch_rethrow() {
+		var thrown = new CustomHaxeException('');
+		rethrown = false;
+		try {
+			try {
+				throw thrown;
+			} catch(e:Exception) {
+				rethrown = true;
+				throw e;
+			}
+		} catch(e:CustomHaxeException) {
+			eq(thrown, e);
+			t(rethrown);
+		}
+
+		var thrown = new CustomNativeException('');
+		rethrown = false;
+		try {
+			try {
+				throw thrown;
+			} catch(e:Exception) {
+				rethrown = true;
+				throw e;
+			}
+		} catch(e:CustomNativeException) {
+			eq(thrown, e);
+			t(rethrown);
+		}
+	}
+
+	public function testSpecificCatch_propagatesUnrelatedExceptions() {
+		var propagated = false;
+		try {
+			try {
+				throw new ValueException('hello');
+			} catch(e:CustomHaxeException) {
+				assert();
+			}
+			assert();
+		} catch(e:ValueException) {
+			propagated = true;
+		}
+		t(propagated);
+	}
+
+	public function testCatchAbstract() {
+		var a:AbstrString = 'hello';
+		try {
+			throw a;
+		} catch(e:AbstrString) {
+			eq(a, e);
+		}
+
+		var a:AbstrException = new CustomHaxeException('');
+		try {
+			throw a;
+		} catch(e:AbstrException) {
+			eq(a, e);
+		}
+	}
+
+	public function testValueException() {
+		try {
+			throw 123;
+		} catch(e:ValueException) {
+			eq(123, e.value);
+		}
+		try {
+			throw 123;
+		} catch(e:Int) {
+			eq(123, e);
+		}
+
+		try {
+			throw EError;
+		} catch(e:ValueException) {
+			eq('EError', e.message);
+		}
+		try {
+			throw EError;
+		} catch(e:EnumError) {
+			eq(EError, e);
+		}
+
+		try {
+			throw 'string';
+		} catch(e:ValueException) {
+			eq('string', e.value);
+		}
+		try {
+			throw 'string';
+		} catch(e:String) {
+			eq('string', e);
+		}
+	}
+
+	public function testCustomNativeException() {
+		var thrown = new CustomNativeException('');
+		rethrown = false;
+		try {
+			try {
+				throw thrown;
+			} catch(e:CustomNativeException) {
+				eq(thrown, e);
+				rethrown = true;
+				throw e;
+			}
+		} catch(e:CustomNativeException) {
+			eq(thrown, e);
+			t(rethrown);
+		}
+	}
+
+	public function testCustomNativeException_thrownAsDynamic() {
+		var thrown:Any = new CustomNativeException('');
+		rethrown = false;
+		try {
+			try {
+				throw thrown;
+			} catch(e:CustomNativeException) {
+				eq(thrown, e);
+				rethrown = true;
+				throw e;
+			}
+		} catch(e:CustomNativeException) {
+			eq(thrown, e);
+			t(rethrown);
+		}
+	}
+
+	public function testCustomHaxeException() {
+		var thrown = new CustomHaxeException('');
+		rethrown = false;
+		try {
+			try {
+				throw thrown;
+			} catch(e:CustomHaxeException) {
+				eq(thrown, e);
+				rethrown = true;
+				throw e;
+			}
+		} catch(e:CustomHaxeException) {
+			eq(thrown, e);
+			t(rethrown);
+		}
+	}
+
+	public function testCustomHaxeException_thrownAsDynamic() {
+		var thrown:Any = new CustomHaxeException('');
+		rethrown = false;
+		try {
+			try {
+				throw thrown;
+			} catch(e:CustomHaxeException) {
+				eq(thrown, e);
+				rethrown = true;
+				throw e;
+			}
+		} catch(e:CustomHaxeException) {
+			eq(thrown, e);
+			t(rethrown);
+		}
+	}
+
+	public function testExceptionStack() {
+		var data = [
+			'_without_ throws' => stacksWithoutThrowLevel1(),
+			'_with_ throws' => stacksWithThrowLevel1()
+		];
+		for(label => stacks in data) {
+			Assert.isTrue(stacks.length > 1, '$label: wrong stacks.length');
+			var expected = null;
+			var lineShift = 0;
+			for(s in stacks) {
+				if(expected == null) {
+					expected = stackItemData(s[0]);
+				} else {
+					var actual = stackItemData(s[0]);
+					if(expected.line != actual.line) {
+						if(lineShift == 0) {
+							lineShift = actual.line - expected.line;
+						}
+						expected.line += lineShift;
+					}
+					Assert.same(expected, actual, '$label: $expected is expected, but got $actual');
+				}
+			}
+		}
+	}
+
+	function stacksWithoutThrowLevel1() {
+		return stacksWithoutThrowLevel2();
+	}
+
+	function stacksWithoutThrowLevel2():Array<CallStack> {
+		var result:Array<CallStack> = [];
+		// It's critical for `testExceptionStack` test to keep the following lines
+		// order with no additional code in between.
+		result.push(CallStack.callStack());
+		result.push(new Exception('').stack);
+		result.push(new ValueException('').stack);
+		result.push(new WithConstructorValueException('').stack);
+		result.push(new NoConstructorValueException('').stack);
+		result.push(@:privateAccess (Exception.thrown(''):Exception).stack);
+		return result;
+	}
+
+	function stacksWithThrowLevel1() {
+		return stacksWithThrowLevel2();
+	}
+
+	function stacksWithThrowLevel2():Array<CallStack> {
+		var result:Array<CallStack> = [];
+		// It's critical for `testExceptionStack` test to keep the following lines
+		// order with no additional code in between.
+		result.push(try throw new Exception('') catch(e:Exception) e.stack);
+		result.push(try throw new ValueException('') catch(e:Exception) e.stack);
+		result.push(try throw new WithConstructorValueException('') catch(e:Exception) e.stack);
+		result.push(try throw new NoConstructorValueException('') catch(e:Exception) e.stack);
+		result.push(try throw @:privateAccess (Exception.thrown(''):Exception) catch(e:Exception) e.stack);
+		return result;
+	}
+
+	function stackItemData(item:StackItem):ItemData {
+		var result:ItemData = {};
+		switch item {
+			case FilePos(s, f, l, _):
+				result.file = f;
+				result.line = l;
+				switch s {
+					case Method(_, m): result.method = m;
+					case _:
+				}
+			case _:
+		}
+		return result;
+	}
+
+#if java
+	function testCatchChain() {
+		eq("caught NativeExceptionChild: msg", raise(() -> throw new NativeExceptionChild("msg")));
+		eq("caught NativeExceptionBase: msg", raise(() -> throw new NativeExceptionBase("msg")));
+		eq("caught String: msg", raise(() -> throw "msg"));
+		eq("caught NativeExceptionOther: msg", raise(() -> throw new NativeExceptionOther("msg")));
+		eq("caught Int: 12", raise(() -> throw 12));
+		eq("caught Throwable: 12.1", raise(() -> throw 12.1));
+		eq("caught Throwable: false", raise(() -> throw false));
+		eq("caught Throwable: msg", raise(() -> throw new java.lang.Exception("msg")));
+	}
+
+	function raise<T>(f:Void -> String) {
+		return try {
+			f();
+		} catch(e:NativeExceptionChild) {
+			'caught NativeExceptionChild: ${e.getMessage()}';
+		} catch(e:NativeExceptionBase) {
+			'caught NativeExceptionBase: ${e.getMessage()}';
+		} catch(e:String) {
+			'caught String: $e';
+		} catch(e:NativeExceptionOther) {
+			'caught NativeExceptionOther: ${e.getMessage()}';
+		} catch(e:Int) {
+			'caught Int: $e';
+ 		} catch(e:java.lang.Throwable) {
+			'caught Throwable: ${e.getMessage()}';
+		}
+	}
+#end
+}

+ 1 - 0
tests/unit/src/unit/TestMain.hx

@@ -50,6 +50,7 @@ class TestMain {
 		var classes = [
 		var classes = [
 			new TestOps(),
 			new TestOps(),
 			new TestBasetypes(),
 			new TestBasetypes(),
+			new TestExceptions(),
 			new TestBytes(),
 			new TestBytes(),
 			new TestIO(),
 			new TestIO(),
 			new TestLocals(),
 			new TestLocals(),

+ 2 - 2
tests/unit/src/unit/issues/Issue4644.hx

@@ -14,9 +14,9 @@ class Issue4644 extends Test {
 			() -> throw (new js.lib.Error():Dynamic),
 			() -> throw (new js.lib.Error():Dynamic),
 			b -> isHaxeError = b,
 			b -> isHaxeError = b,
 			#if js_unflatten
 			#if js_unflatten
-			js.Syntax.code("js._Boot.HaxeError")
+			js.Syntax.code("haxe.Exception")
 			#else
 			#else
-			js.Syntax.code("js__$Boot_HaxeError")
+			js.Syntax.code("haxe_Exception")
 			#end
 			#end
 		);
 		);
 		f(isHaxeError);
 		f(isHaxeError);

+ 13 - 4
tests/unit/src/unitstd/haxe/CallStack.unit.hx

@@ -4,17 +4,26 @@ var stack = haxe.CallStack.callStack();
 var stack = haxe.CallStack.exceptionStack();
 var stack = haxe.CallStack.exceptionStack();
 (stack is Array) == true;
 (stack is Array) == true;
 
 
-try {
+function throw2() {
 	throw false;
 	throw false;
+}
+function throw1() {
+	throw2();
+}
+try {
+	throw1();
 } catch (_:Dynamic) {
 } catch (_:Dynamic) {
 	var stack = haxe.CallStack.exceptionStack();
 	var stack = haxe.CallStack.exceptionStack();
 	(stack is Array) == true;
 	(stack is Array) == true;
+	#if !lua
+	stack.length > 0;
+	#end
 }
 }
 #if js
 #if js
-var old = @:privateAccess haxe.CallStack.lastException;
-@:privateAccess haxe.CallStack.lastException = null;
+var old = @:privateAccess haxe.NativeStackTrace.lastError;
+@:privateAccess haxe.NativeStackTrace.lastError = null;
 var stack = haxe.CallStack.exceptionStack();
 var stack = haxe.CallStack.exceptionStack();
 (stack is Array) == true;
 (stack is Array) == true;
 stack.length == 0;
 stack.length == 0;
-@:privateAccess haxe.CallStack.lastException = old;
+@:privateAccess haxe.NativeStackTrace.lastError = old;
 #end
 #end