2
0
Эх сурвалжийг харах

null cast/is/catch (#7532)

* add test

* [eval] conform to spec

* adapt `default_cast` to spec

* [php] `cast(null, T)` always returns `null`

* [tests] disable neko unicode test

* more special cases

* Update Issue7115.hx

* [lua] cast(null,T) just returns null

* add interface tests

see #7658

* Revert "revert Std.is changes for release"

This reverts commit a200541e35d86af1446d8a8241312c3d835be965.

* [tests] add tasks.json

* fix Java/C# logic

* try to get Java right

* disable failing flash tests for now
Simon Krajewski 6 жил өмнө
parent
commit
753b36e5d1

+ 4 - 1
src/codegen/codegen.ml

@@ -471,8 +471,11 @@ let default_cast ?(vtmp="$t") com e texpr t p =
 	let std = mk (TTypeExpr std) (mk_texpr std) p in
 	let std = mk (TTypeExpr std) (mk_texpr std) p in
 	let is = mk (TField (std,fis)) (tfun [t_dynamic;t_dynamic] api.tbool) p in
 	let is = mk (TField (std,fis)) (tfun [t_dynamic;t_dynamic] api.tbool) p in
 	let is = mk (TCall (is,[vexpr;texpr])) api.tbool p in
 	let is = mk (TCall (is,[vexpr;texpr])) api.tbool p in
+	let enull = Texpr.Builder.make_null vexpr.etype p in
+	let eop = Texpr.Builder.binop OpEq vexpr enull api.tbool p in
+	let echeck = Texpr.Builder.binop OpBoolOr is eop api.tbool p in
 	let exc = mk (TThrow (mk (TConst (TString "Class cast error")) api.tstring p)) t p in
 	let exc = mk (TThrow (mk (TConst (TString "Class cast error")) api.tstring p)) t p in
-	let check = mk (TIf (Texpr.Builder.mk_parent is,mk (TCast (vexpr,None)) t p,Some exc)) t p in
+	let check = mk (TIf (Texpr.Builder.mk_parent echeck,mk (TCast (vexpr,None)) t p,Some exc)) t p in
 	mk (TBlock [var;check;vexpr]) t p
 	mk (TBlock [var;check;vexpr]) t p
 
 
 module UnificationCallback = struct
 module UnificationCallback = struct

+ 1 - 1
src/generators/genjava.ml

@@ -299,7 +299,7 @@ struct
 						| TClassDecl{ cl_path = (["haxe"], "Int64") } ->
 						| TClassDecl{ cl_path = (["haxe"], "Int64") } ->
 							mk_is true obj i64_md
 							mk_is true obj i64_md
 						| TAbstractDecl{ a_path = ([], "Dynamic") }
 						| TAbstractDecl{ a_path = ([], "Dynamic") }
-						| TClassDecl{ cl_path = ([], "Dynamic") } ->
+						| TClassDecl{ cl_path = ([], "Dynamic") } when not (is_nullable obj.etype) ->
 							(match obj.eexpr with
 							(match obj.eexpr with
 								| TLocal _ | TConst _ -> { e with eexpr = TConst(TBool true) }
 								| TLocal _ | TConst _ -> { e with eexpr = TConst(TBool true) }
 								| _ -> { e with eexpr = TBlock([run obj; { e with eexpr = TConst(TBool true) }]) }
 								| _ -> { e with eexpr = TBlock([run obj; { e with eexpr = TConst(TBool true) }]) }

+ 4 - 3
src/macro/eval/evalEmitter.ml

@@ -190,7 +190,7 @@ let emit_try exec catches env =
 		eval.environment_offset <- environment_offset;
 		eval.environment_offset <- environment_offset;
 		let exec,_,varacc =
 		let exec,_,varacc =
 			try
 			try
-				List.find (fun (_,path,i) -> is v path) catches
+				List.find (fun (_,path,i) -> path = key_Dynamic || is v path) catches
 			with Not_found ->
 			with Not_found ->
 				raise_notrace exc
 				raise_notrace exc
 		in
 		in
@@ -220,8 +220,9 @@ let emit_throw exec p env = throw (exec env) p
 
 
 let emit_safe_cast exec t p env =
 let emit_safe_cast exec t p env =
 	let v1 = exec env in
 	let v1 = exec env in
-	if not (is v1 t) then throw_string "Class cast error" p;
-	v1
+	match vresolve v1 with
+	| VNull -> v1
+	| _ -> if not (is v1 t) then throw_string "Class cast error" p else v1
 
 
 (* Calls *)
 (* Calls *)
 
 

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

@@ -30,7 +30,9 @@ exception Return of value
 exception Sys_exit of int
 exception Sys_exit of int
 
 
 let is v path =
 let is v path =
-	path = key_Dynamic || match v with
+	if path = key_Dynamic then
+		v <> vnull
+	else match v with
 	| VInt32 _ -> path = key_Int || path = key_Float
 	| VInt32 _ -> path = key_Int || path = key_Float
 	| VFloat f -> path = key_Float || (path = key_Int && f = (float_of_int (int_of_float f)) && f <= 2147483647. && f >= -2147483648.)
 	| VFloat f -> path = key_Float || (path = key_Int && f = (float_of_int (int_of_float f)) && f <= 2147483647. && f >= -2147483648.)
 	| VTrue | VFalse -> path = key_Bool
 	| VTrue | VFalse -> path = key_Bool

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

@@ -27,7 +27,7 @@ import cs.internal.Exceptions;
 	public static function is( v : Dynamic, t : Dynamic ) : Bool
 	public static function is( v : Dynamic, t : Dynamic ) : Bool
 	{
 	{
 		if (v == null)
 		if (v == null)
-			return t == Dynamic;
+			return false;
 		if (t == null)
 		if (t == null)
 			return false;
 			return false;
 		var clt = cs.Lib.as(t, cs.system.Type);
 		var clt = cs.Lib.as(t, cs.system.Type);

+ 1 - 1
std/flash/Boot.hx

@@ -99,7 +99,7 @@ class Boot extends flash.display.MovieClip {
 	public static function __instanceof( v : Dynamic, t : Dynamic ) {
 	public static function __instanceof( v : Dynamic, t : Dynamic ) {
 		try {
 		try {
 			if( t == Dynamic )
 			if( t == Dynamic )
-				return true;
+				return v != null;
 			return untyped __is__(v,t);
 			return untyped __is__(v,t);
 		} catch( e : Dynamic ) {
 		} catch( e : Dynamic ) {
 		}
 		}

+ 1 - 1
std/hl/_std/Std.hx

@@ -45,7 +45,7 @@ class Std {
 		if( t == null ) return false;
 		if( t == null ) return false;
 		switch( t.__type__.kind ) {
 		switch( t.__type__.kind ) {
 		case HDyn:
 		case HDyn:
-			return true;
+			return v != null;
 		case HF64:
 		case HF64:
 			switch( hl.Type.getDynamic(v).kind ) {
 			switch( hl.Type.getDynamic(v).kind ) {
 			case HUI8, HUI16, HI32:
 			case HUI8, HUI16, HI32:

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

@@ -28,7 +28,7 @@ import java.internal.Exceptions;
 	public static function is( v : Dynamic, t : Dynamic ) : Bool
 	public static function is( v : Dynamic, t : Dynamic ) : Bool
 	{
 	{
 		if (v == null)
 		if (v == null)
-			return t == Dynamic;
+			return false;
 		if (t == null)
 		if (t == null)
 			return false;
 			return false;
 		var clt:java.lang.Class<Dynamic> = cast t;
 		var clt:java.lang.Class<Dynamic> = cast t;

+ 1 - 1
std/js/Boot.hx

@@ -186,7 +186,7 @@ class Boot {
 		case Array:
 		case Array:
 			return js.Syntax.instanceof(o, Array) && o.__enum__ == null;
 			return js.Syntax.instanceof(o, Array) && o.__enum__ == null;
 		case Dynamic:
 		case Dynamic:
-			return true;
+			return o != null;
 		default:
 		default:
 			if( o != null ) {
 			if( o != null ) {
 				// Check if o is an instance of a Haxe class or a native JS object
 				// Check if o is an instance of a Haxe class or a native JS object

+ 1 - 1
std/lua/Boot.hx

@@ -99,7 +99,7 @@ class Boot {
 			case Table:
 			case Table:
 				return Lua.type(o) == "table";
 				return Lua.type(o) == "table";
 			case Dynamic:
 			case Dynamic:
-				return true;
+				return o != null;
 			default: {
 			default: {
 				if ( o!= null &&  Lua.type(o)  == "table" && Lua.type(cl) == "table"){
 				if ( o!= null &&  Lua.type(o)  == "table" && Lua.type(cl) == "table"){
 					if (extendsOrImplements(getClass(o), cl)) return true;
 					if (extendsOrImplements(getClass(o), cl)) return true;

+ 1 - 1
std/neko/Boot.hx

@@ -70,7 +70,7 @@ class Boot {
 	private static function __instanceof(o:Dynamic, cl:Dynamic) {
 	private static function __instanceof(o:Dynamic, cl:Dynamic) {
 		untyped {
 		untyped {
 			if( cl == Dynamic )
 			if( cl == Dynamic )
-				return true;
+				return o != null;
 			switch( __dollar__typeof(o) ) {
 			switch( __dollar__typeof(o) ) {
 			case 1: return (cl == Int || cl == Float);
 			case 1: return (cl == Int || cl == Float);
 			case 2: return cl == Float || (cl == Int && __dollar__int(o) == o);
 			case 2: return cl == Float || (cl == Int && __dollar__int(o) == o);

+ 2 - 1
std/php/Boot.hx

@@ -292,6 +292,7 @@ class Boot {
 		@throws HxException if `value` cannot be casted to this type
 		@throws HxException 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) return null;
 		switch (hxClass.phpClassName) {
 		switch (hxClass.phpClassName) {
 			case 'Int':
 			case 'Int':
 				if (Boot.isNumber(value)) {
 				if (Boot.isNumber(value)) {
@@ -411,7 +412,7 @@ class Boot {
 		var phpType = type.phpClassName;
 		var phpType = type.phpClassName;
 		switch (phpType) {
 		switch (phpType) {
 			case 'Dynamic':
 			case 'Dynamic':
-				return true;
+				return value != null;
 			case 'Int':
 			case 'Int':
 				return (
 				return (
 						value.is_int()
 						value.is_int()

+ 1 - 1
std/python/_std/Std.hx

@@ -56,7 +56,7 @@ import python.Syntax;
 			return false;
 			return false;
 		}
 		}
 		if (isMetaType(t,Dynamic)) {
 		if (isMetaType(t,Dynamic)) {
-			return true;
+			return v != null;
 		}
 		}
 		var isBool = UBuiltins.isinstance(v, UBuiltins.bool);
 		var isBool = UBuiltins.isinstance(v, UBuiltins.bool);
 
 

+ 13 - 0
tests/unit/.vscode/tasks.json

@@ -0,0 +1,13 @@
+{
+	"version": "2.0.0",
+	"tasks": [
+		{
+			"type": "haxe",
+			"args": "active configuration",
+			"group": {
+				"kind": "build",
+				"isDefault": true
+			}
+		}
+	]
+}

+ 100 - 0
tests/unit/src/unit/issues/Issue7115.hx

@@ -0,0 +1,100 @@
+package unit.issues;
+
+private interface Interface { }
+
+class Issue7115 extends unit.Test {
+	function testIs() {
+		f(Std.is(getNull(), Int));
+		f(Std.is(getNull(), Float));
+		f(Std.is(getNull(), Bool));
+		f(Std.is(getNull(), String));
+		f(Std.is(getNull(), Issue7115));
+		f(Std.is(getNull(), haxe.ds.Option));
+		f(Std.is(getNull(), Dynamic));
+		f(Std.is(getNull(), null));
+		f(Std.is(getNull(), Interface));
+	}
+
+	function testCast() {
+		#if !flash // ???
+		eq(#if static 0 #else null #end, cast(getNull(), Int));
+		eq(#if static 0. #else null #end, cast(getNull(), Float));
+		eq(#if static false #else null #end, cast(getNull(), Bool));
+		eq(null, cast(getNull(), String));
+		#end
+		eq(null, cast(getNull(), Issue7115));
+		eq(null, cast(getNull(), haxe.ds.Option<Dynamic>));
+		eq(null, cast(getNull(), Interface));
+	}
+
+	function testCatch() {
+		var expected = #if static 1 #else 2 #end;
+
+		var i = try {
+			throw (getNull() : Int);
+		} catch(s:Int) {
+			1;
+		} catch(e:Dynamic) {
+			2;
+		}
+		eq(expected, i);
+
+		var i = try {
+			throw (getNull() : Float);
+		} catch(s:Float) {
+			1;
+		} catch(e:Dynamic) {
+			2;
+		}
+		eq(expected, i);
+
+		var i = try {
+			throw (getNull() : Bool);
+		} catch(s:Bool) {
+			1;
+		} catch(e:Dynamic) {
+			2;
+		}
+		eq(expected, i);
+
+		var i = try {
+			throw (getNull() : String);
+		} catch(s:String) {
+			1;
+		} catch(e:Dynamic) {
+			2;
+		}
+		eq(2, i);
+
+		var i = try {
+			throw (getNull() : Issue7115);
+		} catch(s:Issue7115) {
+			1;
+		} catch(e:Dynamic) {
+			2;
+		}
+		eq(2, i);
+
+		var i = try {
+			throw (getNull() : haxe.ds.Option<Dynamic>);
+		} catch(s:haxe.ds.Option<Dynamic>) {
+			1;
+		} catch(e:Dynamic) {
+			2;
+		}
+		eq(2, i);
+
+		var i = try {
+			throw (getNull() : Interface);
+		} catch(s:Interface) {
+			1;
+		} catch(e:Dynamic) {
+			2;
+		}
+		eq(2, i);
+	}
+
+	static function getNull<T>():T {
+		return null;
+	}
+}