Переглянути джерело

[jvm] don't NPE on null string switches

see #4481
Simon Krajewski 6 роки тому
батько
коміт
6a75183713

+ 15 - 4
src/generators/genjvm.ml

@@ -826,7 +826,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 			in
 			self#texpr rvalue_any e1;
 			jm#cast TInt;
-			jm#int_switch is_exhaustive cases def
+			ignore(jm#int_switch is_exhaustive cases def);
 		end else if List.for_all is_const_string_pattern cases then begin
 			let cases = List.map (fun (el,e) ->
 				let il = List.map (fun e -> match e.eexpr with
@@ -841,8 +841,19 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 			in
 			self#texpr rvalue_any e1;
 			jm#cast string_sig;
+			let r = ref 0 in
+			(* all strings can be null and we're not supposed to cause NPEs here... *)
+			code#dup;
+			jm#if_then
+				(fun () -> jm#get_code#if_nonnull_ref string_sig)
+				(fun () ->
+					code#pop;
+					r := code#get_fp;
+					code#goto r
+				);
 			jm#invokevirtual string_path "hashCode" (method_sig [] (Some TInt));
-			jm#int_switch is_exhaustive cases def
+			let r_default = jm#int_switch is_exhaustive cases def in
+			r := r_default - !r;
 		end else begin
 			(* TODO: rewriting this is stupid *)
 			let pop_scope = jm#push_scope in
@@ -2146,7 +2157,7 @@ let generate_dynamic_access gctx (jc : JvmClass.builder) fields is_anon =
 			load();
 			jm#invokespecial jc#get_super_path "_hx_getField" jsig;
 		) in
-		jm#int_switch false cases (Some def);
+		ignore(jm#int_switch false cases (Some def));
 		jm#return
 	end;
 	let fields = List.filter (fun (_,_,kind) -> match kind with
@@ -2190,7 +2201,7 @@ let generate_dynamic_access gctx (jc : JvmClass.builder) fields is_anon =
 				end;
 			)
 		) fields in
-		jm#int_switch false cases (Some def);
+		ignore(jm#int_switch false cases (Some def));
 		jm#return
 	end
 

+ 3 - 2
src/generators/jvm/jvmMethod.ml

@@ -573,6 +573,7 @@ class builder jc name jsig = object(self)
 			code#lookupswitch offset_def a;
 		end;
 		let restore = self#start_branch in
+		let offset_exit = ref code#get_fp in
 		let def_term,r_def = match def with
 			| None ->
 				true,ref 0
@@ -584,7 +585,6 @@ class builder jc name jsig = object(self)
 				pop_scope();
 				self#is_terminated,self#maybe_make_jump
 		in
-
 		let rec loop acc cases = match cases with
 		| (rl,f) :: cases ->
 			restore();
@@ -599,7 +599,8 @@ class builder jc name jsig = object(self)
 			List.rev acc
 		in
 		let rl = loop [] cases in
-		self#close_jumps (def <> None) ((def_term,if def = None then offset_def else r_def) :: rl)
+		self#close_jumps (def <> None) ((def_term,if def = None then offset_def else r_def) :: rl);
+		if def = None then code#get_fp else !offset_exit
 
 	(** Adds a local with a given [name], signature [jsig] and an [init_state].
 	    This function returns a tuple consisting of:

+ 6 - 2
tests/unit/src/unit/issues/Issue4481.hx

@@ -2,12 +2,16 @@ package unit.issues;
 
 class Issue4481 extends unit.Test {
 	static var s:String = null;
-#if !jvm
+
 	function test() {
 		switch s {
 			case 'a': assert();
 			case _: eq(null, s);
 		}
+
+		eq(1, switch s {
+			case 'a': 0;
+			case _: 1;
+		});
 	}
-#end
 }