jso: when casting to JS objects, don't fail cast when cast object is null

This commit is contained in:
Alexey Andreev 2024-04-16 20:03:19 +02:00
parent e94b171fe9
commit a1d711d069
6 changed files with 24 additions and 2 deletions

View File

@ -44,3 +44,4 @@ let $rt_castToClass = (obj, cls) => {
} }
return obj; return obj;
} }
let $rt_instanceOfOrNull = (obj, cls) => obj === null || obj instanceof cls;

View File

@ -709,6 +709,10 @@ final class JS {
@NoSideEffects @NoSideEffects
public static native boolean instanceOf(JSObject obj, JSObject cls); public static native boolean instanceOf(JSObject obj, JSObject cls);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
public static native boolean instanceOfOrNull(JSObject obj, JSObject cls);
@InjectedBy(JSNativeInjector.class) @InjectedBy(JSNativeInjector.class)
@NoSideEffects @NoSideEffects
public static native boolean isPrimitive(JSObject obj, JSObject primitive); public static native boolean isPrimitive(JSObject obj, JSObject primitive);

View File

@ -578,7 +578,7 @@ class JSClassProcessor {
var primitiveType = getPrimitiveType(targetClassName); var primitiveType = getPrimitiveType(targetClassName);
var invoke = new InvokeInstruction(); var invoke = new InvokeInstruction();
invoke.setType(InvocationType.SPECIAL); invoke.setType(InvocationType.SPECIAL);
invoke.setMethod(primitiveType != null ? JSMethods.IS_PRIMITIVE : JSMethods.INSTANCE_OF); invoke.setMethod(primitiveType != null ? JSMethods.IS_PRIMITIVE : JSMethods.INSTANCE_OF_OR_NULL);
var secondArg = primitiveType != null var secondArg = primitiveType != null
? marshaller.addJsString(primitiveType, location) ? marshaller.addJsString(primitiveType, location)
: marshaller.classRef(targetClassName, location); : marshaller.classRef(targetClassName, location);

View File

@ -127,6 +127,8 @@ final class JSMethods {
public static final MethodReference INSTANCE_OF = new MethodReference(JS.class, "instanceOf", JSObject.class, public static final MethodReference INSTANCE_OF = new MethodReference(JS.class, "instanceOf", JSObject.class,
JSObject.class, boolean.class); JSObject.class, boolean.class);
public static final MethodReference INSTANCE_OF_OR_NULL = new MethodReference(JS.class, "instanceOfOrNull",
JSObject.class, JSObject.class, boolean.class);
public static final MethodReference IS_PRIMITIVE = new MethodReference(JS.class, "isPrimitive", JSObject.class, public static final MethodReference IS_PRIMITIVE = new MethodReference(JS.class, "isPrimitive", JSObject.class,
JSObject.class, boolean.class); JSObject.class, boolean.class);
public static final MethodReference THROW_CCE_IF_FALSE = new MethodReference(JS.class, "throwCCEIfFalse", public static final MethodReference THROW_CCE_IF_FALSE = new MethodReference(JS.class, "throwCCEIfFalse",

View File

@ -215,6 +215,14 @@ public class JSNativeInjector implements Injector, DependencyPlugin {
} }
break; break;
} }
case "instanceOfOrNull": {
writer.appendFunction("$rt_instanceOfOrNull").append("(");
context.writeExpr(context.getArgument(0), Precedence.min());
writer.append(",").ws();
context.writeExpr(context.getArgument(1), Precedence.min());
writer.append(")");
break;
}
case "isPrimitive": { case "isPrimitive": {
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) { if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
writer.append("("); writer.append("(");

View File

@ -17,6 +17,7 @@ package org.teavm.jso.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import org.junit.Test; import org.junit.Test;
@ -89,6 +90,9 @@ public class InstanceOfTest {
} catch (ClassCastException e) { } catch (ClassCastException e) {
// expected // expected
} }
var d = (ClassWithConstructor) returnNull();
assertNull(d);
} }
private Object callCreateClassWithConstructor() { private Object callCreateClassWithConstructor() {
@ -98,6 +102,9 @@ public class InstanceOfTest {
@JSBody(script = "return new ClassWithConstructor();") @JSBody(script = "return new ClassWithConstructor();")
private static native JSObject createClassWithConstructor(); private static native JSObject createClassWithConstructor();
@JSBody(script = "return null;")
private static native JSObject returnNull();
private Object callCreateNumber() { private Object callCreateNumber() {
return createNumber(); return createNumber();
} }
@ -111,7 +118,7 @@ public class InstanceOfTest {
} }
@JSClass(transparent = true) @JSClass(transparent = true)
abstract class C implements JSObject { static abstract class C implements JSObject {
@JSProperty @JSProperty
public abstract int getFoo(); public abstract int getFoo();
} }