Преглед на файлове

Allow setting haxe.Exception.stack (#12213)

* allow setting Exception.stack

* add simple test

* or maybe this just works?

* adjust to hxcpp change
Simon Krajewski преди 3 месеца
родител
ревизия
fc9f5b4ce4

+ 24 - 1
src/macro/eval/evalExceptions.ml

@@ -130,7 +130,30 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p =
 				| l when p' = null_pos -> l (* If the exception position is null_pos, we're "probably" in a built-in function. *)
 				| _ :: l -> l (* Otherwise, ignore topmost frame position. *)
 			in
-			get_exc_error_stack ctx stack
+			let def () =
+				get_exc_error_stack ctx stack
+			in
+			begin match v with
+			| VInstance vi when is v key_haxe_Exception ->
+				begin try
+					let vf = instance_field vi key_custom_stack in
+					begin match vf with
+					| VVector vv ->
+						Array.to_list vv |> List.map (fun v -> match v with
+							| VEnumValue {enpos = Some p} ->
+								p
+							| _ ->
+								die "" __LOC__
+						)
+					| _ ->
+						def();
+					end
+				with Not_found ->
+					def ()
+				end
+			| _ ->
+				def();
+			end
 		in
 
 		if is v key_haxe_macro_Error then begin

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

@@ -48,6 +48,7 @@ let key_error = hash "error"
 let key_exception_message = hash "__exceptionMessage"
 let key_native_exception = hash "__nativeException"
 let key_native_stack = hash "__nativeStack"
+let key_custom_stack = hash "__customStack"
 let key_Array = hash "Array"
 let key_eval_Vector = hash "eval.Vector"
 let key_String = hash "String"

+ 3 - 3
src/macro/eval/evalStackTrace.ml

@@ -10,14 +10,14 @@ let make_stack envs =
 	List.iter (fun (pos,kind) ->
 		let file_pos s =
 			let line1,col1,_,_ = Lexer.get_pos_coords pos in
-			encode_enum_value key_haxe_StackItem 2 [|s;create_unknown pos.pfile;vint line1;vint col1|] None
+			encode_enum_value key_haxe_StackItem 2 [|s;create_unknown pos.pfile;vint line1;vint col1|] (Some pos)
 		in
 		match kind with
 		| EKLocalFunction i ->
-			let local_function = encode_enum_value key_haxe_StackItem 4 [|vint i|] None in
+			let local_function = encode_enum_value key_haxe_StackItem 4 [|vint i|] (Some pos) in
 			DynArray.add l (file_pos local_function);
 		| EKMethod(st,sf) ->
-			let local_function = encode_enum_value key_haxe_StackItem 3 [|create_unknown (rev_hash st); create_unknown (rev_hash sf)|] None in
+			let local_function = encode_enum_value key_haxe_StackItem 3 [|create_unknown (rev_hash st); create_unknown (rev_hash sf)|] (Some pos) in
 			DynArray.add l (file_pos local_function);
 		| EKMacro(st,sf) ->
 			()

+ 7 - 1
src/macro/eval/evalValue.ml

@@ -238,7 +238,7 @@ and venum_value = {
 	eindex : int;
 	eargs : value array;
 	epath : int;
-	enpos : pos option;
+	mutable enpos : pos option;
 }
 
 and vthread = {
@@ -356,3 +356,9 @@ let s_expr_pretty e = (Type.s_expr_pretty false "" false (Type.s_type (Type.prin
 let rec vresolve v = match v with
 	| VLazy f -> vresolve (Lazy.force f)
 	| _ -> v
+
+let associate_enum_value_pos ve p = match ve with
+	| VEnumValue ve ->
+		ve.enpos <- Some p
+	| _ ->
+		()

+ 8 - 1
src/macro/macroApi.ml

@@ -174,6 +174,8 @@ module type InterpApi = sig
 	val handle_decoding_error : (string -> unit) -> value -> Type.t -> (string * int) list
 
 	val get_api_call_pos : unit -> pos
+
+	val associate_enum_value_pos : value -> pos -> unit
 end
 
 let s_type_path = Globals.s_type_path
@@ -2435,6 +2437,11 @@ let macro_api ccom get_api =
 		"set_hxb_writer_config", vfun1 (fun v ->
 			(get_api()).set_hxb_writer_config v;
 			vnull
-		)
+		);
+		"associate_enum_value_pos",vfun2 (fun ve vp ->
+			let p = decode_pos vp in
+			associate_enum_value_pos ve p;
+			vnull;
+		);
 	]
 end

+ 8 - 1
std/cpp/_std/haxe/Exception.hx

@@ -4,7 +4,7 @@ package haxe;
 @:coreApi
 class Exception {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -14,6 +14,7 @@ class Exception {
 	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
 	@:noCompletion var __nativeException:Any;
 	@:noCompletion var __previousException:Null<Exception>;
+	@:noCompletion var _hx_customStack:Null<String>;
 
 	static function caught(value:Any):Exception {
 		if(Std.isOfType(value, Exception)) {
@@ -90,5 +91,11 @@ class Exception {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		_hx_customStack = CallStack.toString(stack);
+
+		return __exceptionStack = stack;
+	}
 }
 

+ 16 - 1
std/eval/_std/haxe/Exception.hx

@@ -3,7 +3,7 @@ package haxe;
 @:coreApi
 class Exception {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -13,6 +13,7 @@ class Exception {
 	@:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0;
 	@:noCompletion var __nativeException:Any;
 	@:noCompletion var __previousException:Null<Exception>;
+	@:noCompletion var __customStack:haxe.ds.Vector<Dynamic>;
 
 	static function caught(value:Any):Exception {
 		if(Std.isOfType(value, Exception)) {
@@ -95,4 +96,18 @@ class Exception {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		var items = stack.asArray();
+		var a:Array<Dynamic> = [];
+		for (item in items) {
+			switch (item) {
+				case FilePos(Method(_), _):
+					a.push(item);
+				case _:
+			}
+		}
+		__customStack = haxe.ds.Vector.fromArrayCopy(a);
+		return __nativeStack = __exceptionStack = stack;
+	}
 }

+ 5 - 1
std/flash/_std/haxe/Exception.hx

@@ -5,7 +5,7 @@ import flash.errors.Error;
 @:coreApi
 class Exception extends NativeException {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -86,6 +86,10 @@ class Exception extends NativeException {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		return __exceptionStack = stack;
+	}
 }
 
 @:dox(hide)

+ 1 - 1
std/haxe/CallStack.hx

@@ -112,7 +112,7 @@ abstract CallStack(Array<StackItem>) from Array<StackItem> {
 		return this[index];
 	}
 
-	inline function asArray():Array<StackItem> {
+	public inline function asArray():Array<StackItem> {
 		return this;
 	}
 

+ 2 - 1
std/haxe/Exception.hx

@@ -51,8 +51,9 @@ extern class Exception {
 	/**
 		The call stack at the moment of the exception creation.
 	**/
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	private function get_stack():CallStack;
+	private function set_stack(stack:CallStack):CallStack;
 
 	/**
 		Contains an exception, which was passed to `previous` constructor argument.

+ 9 - 1
std/hl/_std/haxe/Exception.hx

@@ -3,7 +3,7 @@ package haxe;
 @:coreApi
 class Exception {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -89,4 +89,12 @@ class Exception {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		__exceptionStack = stack;
+		__customStack = CallStack.toString(stack);
+		return stack;
+	}
+
+	@:noCompletion var __customStack:Null<String>;
 }

+ 5 - 1
std/js/_std/haxe/Exception.hx

@@ -5,7 +5,7 @@ import js.lib.Error;
 @:coreApi
 class Exception extends NativeException {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -106,6 +106,10 @@ class Exception extends NativeException {
 		}
 	}
 
+	function set_stack(stack:CallStack) {
+		return __exceptionStack = stack;
+	}
+
 	@:noCompletion
 	function setProperty(name:String, value:Any):Void {
 		try {

+ 16 - 1
std/jvm/_std/haxe/Exception.hx

@@ -10,7 +10,7 @@ import java.io.PrintWriter;
 @:coreApi
 class Exception extends NativeException {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -88,6 +88,21 @@ class Exception extends NativeException {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		var items = stack.asArray();
+		var a = [];
+		for (item in items) {
+			switch (item) {
+				case FilePos(Method(c, m), f, l):
+					a.push(new StackTraceElement(c, m, f, l));
+				case _:
+			}
+		}
+		var a = NativeArray.ofArray(a);
+		setStackTrace(a);
+		return __exceptionStack = stack;
+	}
 }
 
 @:dox(hide)

+ 5 - 1
std/lua/_std/haxe/Exception.hx

@@ -3,7 +3,7 @@ package haxe;
 @:coreApi
 class Exception {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -83,4 +83,8 @@ class Exception {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		return __exceptionStack = stack;
+	}
 }

+ 5 - 1
std/neko/_std/haxe/Exception.hx

@@ -3,7 +3,7 @@ package haxe;
 @:coreApi
 class Exception {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -91,4 +91,8 @@ class Exception {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		return __exceptionStack = stack;
+	}
 }

+ 5 - 1
std/php/_std/haxe/Exception.hx

@@ -7,7 +7,7 @@ import php.NativeIndexedArray;
 @:coreApi
 class Exception extends NativeException {
 	public var message(get,never):String;
-	public var stack(get,never):CallStack;
+	public var stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -80,6 +80,10 @@ class Exception extends NativeException {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		return __exceptionStack = stack;
+	}
 }
 
 @:dox(hide)

+ 5 - 1
std/python/_std/haxe/Exception.hx

@@ -10,7 +10,7 @@ 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 stack(get,set):CallStack;
 	public var previous(get,never):Null<Exception>;
 	public var native(get,never):Any;
 
@@ -91,4 +91,8 @@ class Exception extends PyException {
 			case s: s;
 		}
 	}
+
+	function set_stack(stack:CallStack) {
+		return __exceptionStack = stack;
+	}
 }

+ 22 - 0
tests/unit/src/unit/issues/Issue12213.hx

@@ -0,0 +1,22 @@
+package unit.issues;
+
+import haxe.CallStack;
+import haxe.exceptions.NotImplementedException;
+
+class Issue12213 extends Test {
+	function test() {
+		try {
+			throw new haxe.exceptions.NotImplementedException();
+		} catch (e:haxe.exceptions.NotImplementedException) {
+			var stack = [
+				CFunction,
+				Module("Foo"),
+				FilePos(null, "file", 1, 2),
+				Method("Class", "method"),
+				LocalFunction(0)
+			];
+			e.stack = stack;
+			utest.Assert.same(stack, e.stack);
+		}
+	}
+}