mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
jso: improve support of instanceof and cast against JS wrapper types
Fix #808
This commit is contained in:
parent
e069bc3a44
commit
a6fb67817c
|
@ -125,6 +125,13 @@ let $rt_throwCCE = () => teavm_javaConstructorExists("java.lang.ClassCastExcepti
|
||||||
? $rt_throw(teavm_javaConstructor("java.lang.ClassCastException", "()V")())
|
? $rt_throw(teavm_javaConstructor("java.lang.ClassCastException", "()V")())
|
||||||
: $rt_throw($rt_createException($rt_str("")));
|
: $rt_throw($rt_createException($rt_str("")));
|
||||||
|
|
||||||
|
let $rt_throwCCEIfFalse = (value, o) => {
|
||||||
|
if (!value) {
|
||||||
|
$rt_throwCCE();
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
let $rt_createStackElement = (className, methodName, fileName, lineNumber) => {
|
let $rt_createStackElement = (className, methodName, fileName, lineNumber) => {
|
||||||
if (teavm_javaConstructorExists("java.lang.StackTraceElement",
|
if (teavm_javaConstructorExists("java.lang.StackTraceElement",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V")) {
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V")) {
|
||||||
|
|
|
@ -18,7 +18,9 @@ package org.teavm.jso.core;
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSPrimitiveType;
|
||||||
|
|
||||||
|
@JSPrimitiveType("boolean")
|
||||||
public abstract class JSBoolean implements JSObject {
|
public abstract class JSBoolean implements JSObject {
|
||||||
private JSBoolean() {
|
private JSBoolean() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,19 @@
|
||||||
package org.teavm.jso.core;
|
package org.teavm.jso.core;
|
||||||
|
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSClass;
|
||||||
import org.teavm.jso.JSFunctor;
|
import org.teavm.jso.JSFunctor;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
import org.teavm.jso.JSProperty;
|
import org.teavm.jso.JSProperty;
|
||||||
|
|
||||||
public abstract class JSError implements JSObject {
|
@JSClass(name = "Error")
|
||||||
|
public class JSError implements JSObject {
|
||||||
|
public JSError() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSError(String message) {
|
||||||
|
}
|
||||||
|
|
||||||
@JSBody(params = { "tryClause", "catchClause" }, script = ""
|
@JSBody(params = { "tryClause", "catchClause" }, script = ""
|
||||||
+ "try {"
|
+ "try {"
|
||||||
+ "return tryClause();"
|
+ "return tryClause();"
|
||||||
|
@ -29,17 +37,19 @@ public abstract class JSError implements JSObject {
|
||||||
+ "}")
|
+ "}")
|
||||||
public static native <T> T catchNative(TryClause<T> tryClause, CatchClause<T> catchClause);
|
public static native <T> T catchNative(TryClause<T> tryClause, CatchClause<T> catchClause);
|
||||||
|
|
||||||
@JSBody(params = "object", script = "return object instanceof Error;")
|
@Deprecated
|
||||||
public static native boolean isError(JSObject object);
|
public static boolean isError(JSObject object) {
|
||||||
|
return object instanceof JSError;
|
||||||
|
}
|
||||||
|
|
||||||
@JSProperty
|
@JSProperty
|
||||||
public abstract String getStack();
|
public native String getStack();
|
||||||
|
|
||||||
@JSProperty
|
@JSProperty
|
||||||
public abstract String getMessage();
|
public native String getMessage();
|
||||||
|
|
||||||
@JSProperty
|
@JSProperty
|
||||||
public abstract String getName();
|
public native String getName();
|
||||||
|
|
||||||
@JSFunctor
|
@JSFunctor
|
||||||
public interface TryClause<T> extends JSObject {
|
public interface TryClause<T> extends JSObject {
|
||||||
|
|
|
@ -17,8 +17,10 @@ package org.teavm.jso.core;
|
||||||
|
|
||||||
import org.teavm.jso.JSByRef;
|
import org.teavm.jso.JSByRef;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSPrimitiveType;
|
||||||
import org.teavm.jso.JSProperty;
|
import org.teavm.jso.JSProperty;
|
||||||
|
|
||||||
|
@JSPrimitiveType("function")
|
||||||
public abstract class JSFunction implements JSObject {
|
public abstract class JSFunction implements JSObject {
|
||||||
@JSProperty
|
@JSProperty
|
||||||
public abstract int getLength();
|
public abstract int getLength();
|
||||||
|
|
|
@ -18,7 +18,9 @@ package org.teavm.jso.core;
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSPrimitiveType;
|
||||||
|
|
||||||
|
@JSPrimitiveType("number")
|
||||||
public abstract class JSNumber implements JSObject {
|
public abstract class JSNumber implements JSObject {
|
||||||
private JSNumber() {
|
private JSNumber() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,13 +43,14 @@ public final class JSObjects {
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
public static native <T extends JSObject> T createWithoutProto();
|
public static native <T extends JSObject> T createWithoutProto();
|
||||||
|
|
||||||
@JSBody(params = "object", script = "return typeof object === 'undefined';")
|
public static boolean isUndefined(Object object) {
|
||||||
@NoSideEffects
|
return object instanceof JSUndefined;
|
||||||
public static native boolean isUndefined(Object object);
|
}
|
||||||
|
|
||||||
@JSBody(script = "return void 0;")
|
@Deprecated
|
||||||
@NoSideEffects
|
public static JSObject undefined() {
|
||||||
public static native JSObject undefined();
|
return JSUndefined.instance();
|
||||||
|
}
|
||||||
|
|
||||||
@JSBody(params = "object", script = "return typeof object;")
|
@JSBody(params = "object", script = "return typeof object;")
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
|
|
|
@ -18,8 +18,10 @@ package org.teavm.jso.core;
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSPrimitiveType;
|
||||||
import org.teavm.jso.JSProperty;
|
import org.teavm.jso.JSProperty;
|
||||||
|
|
||||||
|
@JSPrimitiveType("string")
|
||||||
public abstract class JSString implements JSObject {
|
public abstract class JSString implements JSObject {
|
||||||
private JSString() {
|
private JSString() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,11 @@ package org.teavm.jso.core;
|
||||||
|
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSIndexer;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSPrimitiveType;
|
||||||
|
|
||||||
|
@JSPrimitiveType("symbol")
|
||||||
public class JSSymbol<T> implements JSObject {
|
public class JSSymbol<T> implements JSObject {
|
||||||
private JSSymbol() {
|
private JSSymbol() {
|
||||||
}
|
}
|
||||||
|
@ -26,10 +29,10 @@ public class JSSymbol<T> implements JSObject {
|
||||||
@JSBody(params = "name", script = "return Symbol(name);")
|
@JSBody(params = "name", script = "return Symbol(name);")
|
||||||
public static native <T> JSSymbol<T> create(String name);
|
public static native <T> JSSymbol<T> create(String name);
|
||||||
|
|
||||||
@JSBody(params = "obj", script = "return obj[this];")
|
@JSIndexer
|
||||||
public native T get(Object obj);
|
public native T get(Object obj);
|
||||||
|
|
||||||
@JSBody(params = { "obj", "value" }, script = "obj[this] = value;")
|
@JSIndexer
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
public native void set(Object obj, T value);
|
public native void set(Object obj, T value);
|
||||||
}
|
}
|
||||||
|
|
33
jso/apis/src/main/java/org/teavm/jso/core/JSUndefined.java
Normal file
33
jso/apis/src/main/java/org/teavm/jso/core/JSUndefined.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.jso.core;
|
||||||
|
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSClass;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSPrimitiveType;
|
||||||
|
|
||||||
|
@JSClass
|
||||||
|
@JSPrimitiveType("undefined")
|
||||||
|
public class JSUndefined implements JSObject {
|
||||||
|
private JSUndefined() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(script = "return void 0;")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSUndefined instance();
|
||||||
|
}
|
|
@ -24,4 +24,6 @@ import java.lang.annotation.Target;
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
public @interface JSClass {
|
public @interface JSClass {
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
|
boolean transparent() default false;
|
||||||
}
|
}
|
||||||
|
|
27
jso/core/src/main/java/org/teavm/jso/JSPrimitiveType.java
Normal file
27
jso/core/src/main/java/org/teavm/jso/JSPrimitiveType.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.jso;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface JSPrimitiveType {
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -704,4 +704,16 @@ final class JS {
|
||||||
@InjectedBy(JSNativeInjector.class)
|
@InjectedBy(JSNativeInjector.class)
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
public static native JSObject importModule(String name);
|
public static native JSObject importModule(String name);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native boolean instanceOf(JSObject obj, JSObject cls);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native boolean isPrimitive(JSObject obj, JSObject primitive);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject throwCCEIfFalse(boolean value, JSObject o);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,10 @@ import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.jso.JSByRef;
|
import org.teavm.jso.JSByRef;
|
||||||
|
import org.teavm.jso.JSClass;
|
||||||
import org.teavm.jso.JSFunctor;
|
import org.teavm.jso.JSFunctor;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSPrimitiveType;
|
||||||
import org.teavm.model.AnnotationContainerReader;
|
import org.teavm.model.AnnotationContainerReader;
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.AnnotationHolder;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
|
@ -86,6 +88,10 @@ class JSClassProcessor {
|
||||||
Object.class, JSObject.class);
|
Object.class, JSObject.class);
|
||||||
private static final MethodReference IS_JS = new MethodReference(JSWrapper.class, "isJs",
|
private static final MethodReference IS_JS = new MethodReference(JSWrapper.class, "isJs",
|
||||||
Object.class, boolean.class);
|
Object.class, boolean.class);
|
||||||
|
private static final MethodReference IS_PRIMITIVE = new MethodReference(JSWrapper.class, "isPrimitive",
|
||||||
|
Object.class, JSObject.class, boolean.class);
|
||||||
|
private static final MethodReference INSTANCE_OF = new MethodReference(JSWrapper.class, "instanceOf",
|
||||||
|
Object.class, JSObject.class, boolean.class);
|
||||||
private final ClassReaderSource classSource;
|
private final ClassReaderSource classSource;
|
||||||
private final JSBodyRepository repository;
|
private final JSBodyRepository repository;
|
||||||
private final JavaInvocationProcessor javaInvocationProcessor;
|
private final JavaInvocationProcessor javaInvocationProcessor;
|
||||||
|
@ -470,11 +476,23 @@ class JSClassProcessor {
|
||||||
|
|
||||||
ClassReader targetClass = classSource.get(targetClassName);
|
ClassReader targetClass = classSource.get(targetClassName);
|
||||||
if (targetClass.getAnnotations().get(JSFunctor.class.getName()) == null) {
|
if (targetClass.getAnnotations().get(JSFunctor.class.getName()) == null) {
|
||||||
AssignInstruction assign = new AssignInstruction();
|
if (isTransparent(targetClassName)) {
|
||||||
assign.setLocation(location.getSourceLocation());
|
var assign = new AssignInstruction();
|
||||||
assign.setAssignee(cast.getValue());
|
assign.setLocation(location.getSourceLocation());
|
||||||
assign.setReceiver(cast.getReceiver());
|
assign.setAssignee(cast.getValue());
|
||||||
replacement.add(assign);
|
assign.setReceiver(cast.getReceiver());
|
||||||
|
replacement.add(assign);
|
||||||
|
} else {
|
||||||
|
var instanceOfResult = program.createVariable();
|
||||||
|
processIsInstanceUnwrapped(cast.getLocation(), cast.getValue(), targetClassName, instanceOfResult);
|
||||||
|
|
||||||
|
var invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setMethod(JSMethods.THROW_CCE_IF_FALSE);
|
||||||
|
invoke.setArguments(instanceOfResult, cast.getValue());
|
||||||
|
invoke.setReceiver(cast.getReceiver());
|
||||||
|
replacement.add(invoke);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,23 +517,107 @@ class JSClassProcessor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = types.typeOf(isInstance.getValue());
|
replacement.clear();
|
||||||
if (type == JSType.JS) {
|
processIsInstance(isInstance.getLocation(), types.typeOf(isInstance.getValue()), isInstance.getValue(),
|
||||||
var replacement = new IntegerConstantInstruction();
|
targetClassName, isInstance.getReceiver());
|
||||||
replacement.setConstant(1);
|
isInstance.insertPreviousAll(replacement);
|
||||||
replacement.setReceiver(isInstance.getReceiver());
|
isInstance.delete();
|
||||||
replacement.setLocation(isInstance.getLocation());
|
replacement.clear();
|
||||||
isInstance.replace(replacement);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var replacement = new InvokeInstruction();
|
private void processIsInstance(TextLocation location, JSType type, Variable value, String targetClassName,
|
||||||
replacement.setType(InvocationType.SPECIAL);
|
Variable receiver) {
|
||||||
replacement.setMethod(IS_JS);
|
if (type == JSType.JS) {
|
||||||
replacement.setArguments(isInstance.getValue());
|
if (isTransparent(targetClassName)) {
|
||||||
replacement.setReceiver(isInstance.getReceiver());
|
var cst = new IntegerConstantInstruction();
|
||||||
replacement.setLocation(isInstance.getLocation());
|
cst.setConstant(1);
|
||||||
isInstance.replace(replacement);
|
cst.setReceiver(receiver);
|
||||||
|
cst.setLocation(location);
|
||||||
|
replacement.add(cst);
|
||||||
|
} else {
|
||||||
|
var primitiveType = getPrimitiveType(targetClassName);
|
||||||
|
var invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setMethod(primitiveType != null ? JSMethods.IS_PRIMITIVE : JSMethods.INSTANCE_OF);
|
||||||
|
var secondArg = primitiveType != null
|
||||||
|
? marshaller.addJsString(primitiveType, location)
|
||||||
|
: marshaller.classRef(targetClassName, location);
|
||||||
|
invoke.setArguments(value, secondArg);
|
||||||
|
invoke.setReceiver(receiver);
|
||||||
|
invoke.setLocation(location);
|
||||||
|
replacement.add(invoke);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isTransparent(targetClassName)) {
|
||||||
|
var invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setMethod(IS_JS);
|
||||||
|
invoke.setArguments(value);
|
||||||
|
invoke.setReceiver(receiver);
|
||||||
|
invoke.setLocation(location);
|
||||||
|
replacement.add(invoke);
|
||||||
|
} else {
|
||||||
|
var primitiveType = getPrimitiveType(targetClassName);
|
||||||
|
var invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setMethod(primitiveType != null ? IS_PRIMITIVE : INSTANCE_OF);
|
||||||
|
var secondArg = primitiveType != null
|
||||||
|
? marshaller.addJsString(primitiveType, location)
|
||||||
|
: marshaller.classRef(targetClassName, location);
|
||||||
|
invoke.setArguments(value, secondArg);
|
||||||
|
invoke.setReceiver(receiver);
|
||||||
|
invoke.setLocation(location);
|
||||||
|
replacement.add(invoke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processIsInstanceUnwrapped(TextLocation location, Variable value, String targetClassName,
|
||||||
|
Variable receiver) {
|
||||||
|
var primitiveType = getPrimitiveType(targetClassName);
|
||||||
|
var invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setMethod(primitiveType != null ? JSMethods.IS_PRIMITIVE : JSMethods.INSTANCE_OF);
|
||||||
|
var secondArg = primitiveType != null
|
||||||
|
? marshaller.addJsString(primitiveType, location)
|
||||||
|
: marshaller.classRef(targetClassName, location);
|
||||||
|
invoke.setArguments(value, secondArg);
|
||||||
|
invoke.setReceiver(receiver);
|
||||||
|
invoke.setLocation(location);
|
||||||
|
replacement.add(invoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTransparent(String className) {
|
||||||
|
var cls = classSource.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (cls.hasModifier(ElementModifier.INTERFACE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var clsAnnot = cls.getAnnotations().get(JSClass.class.getName());
|
||||||
|
if (clsAnnot != null) {
|
||||||
|
var transparent = clsAnnot.getValue("transparent");
|
||||||
|
if (transparent != null) {
|
||||||
|
return transparent.getBoolean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPrimitiveType(String className) {
|
||||||
|
var cls = classSource.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var clsAnnot = cls.getAnnotations().get(JSPrimitiveType.class.getName());
|
||||||
|
if (clsAnnot != null) {
|
||||||
|
var value = clsAnnot.getValue("value");
|
||||||
|
if (value != null) {
|
||||||
|
return value.getString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Variable wrapJsAsJava(Instruction instruction, Variable var, ValueType type) {
|
private Variable wrapJsAsJava(Instruction instruction, Variable var, ValueType type) {
|
||||||
|
|
|
@ -125,6 +125,13 @@ final class JSMethods {
|
||||||
public static final MethodReference IMPORT_MODULE = new MethodReference(JS.class, "importModule",
|
public static final MethodReference IMPORT_MODULE = new MethodReference(JS.class, "importModule",
|
||||||
String.class, JSObject.class);
|
String.class, JSObject.class);
|
||||||
|
|
||||||
|
public static final MethodReference INSTANCE_OF = new MethodReference(JS.class, "instanceOf", JSObject.class,
|
||||||
|
JSObject.class, boolean.class);
|
||||||
|
public static final MethodReference IS_PRIMITIVE = new MethodReference(JS.class, "isPrimitive", JSObject.class,
|
||||||
|
JSObject.class, boolean.class);
|
||||||
|
public static final MethodReference THROW_CCE_IF_FALSE = new MethodReference(JS.class, "throwCCEIfFalse",
|
||||||
|
boolean.class, JSObject.class, JSObject.class);
|
||||||
|
|
||||||
public static final ValueType JS_OBJECT = ValueType.object(JSObject.class.getName());
|
public static final ValueType JS_OBJECT = ValueType.object(JSObject.class.getName());
|
||||||
public static final ValueType OBJECT = ValueType.object("java.lang.Object");
|
public static final ValueType OBJECT = ValueType.object("java.lang.Object");
|
||||||
public static final ValueType JS_ARRAY = ValueType.object(JSArray.class.getName());
|
public static final ValueType JS_ARRAY = ValueType.object(JSArray.class.getName());
|
||||||
|
|
|
@ -190,6 +190,41 @@ public class JSNativeInjector implements Injector, DependencyPlugin {
|
||||||
writer.appendFunction(context.importModule(name));
|
writer.appendFunction(context.importModule(name));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "instanceOf": {
|
||||||
|
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||||
|
writer.append("(");
|
||||||
|
}
|
||||||
|
context.writeExpr(context.getArgument(0), Precedence.COMPARISON.next());
|
||||||
|
writer.append(" instanceof ");
|
||||||
|
context.writeExpr(context.getArgument(1), Precedence.COMPARISON.next());
|
||||||
|
writer.ws().append("?").ws().append("1").ws().append(":").ws().append("0");
|
||||||
|
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||||
|
writer.append(")");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "isPrimitive": {
|
||||||
|
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||||
|
writer.append("(");
|
||||||
|
}
|
||||||
|
writer.append("typeof ");
|
||||||
|
context.writeExpr(context.getArgument(0), Precedence.UNARY.next());
|
||||||
|
writer.ws().append("===").ws();
|
||||||
|
context.writeExpr(context.getArgument(1), Precedence.COMPARISON.next());
|
||||||
|
writer.ws().append("?").ws().append("1").ws().append(":").ws().append("0");
|
||||||
|
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||||
|
writer.append(")");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "throwCCEIfFalse": {
|
||||||
|
writer.appendFunction("$rt_throwCCEIfFalse").append("(");
|
||||||
|
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||||
|
writer.append(",").ws();
|
||||||
|
context.writeExpr(context.getArgument(1), Precedence.min());
|
||||||
|
writer.append(")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (methodRef.getName().startsWith("unwrap")) {
|
if (methodRef.getName().startsWith("unwrap")) {
|
||||||
|
|
|
@ -550,7 +550,7 @@ class JSValueMarshaller {
|
||||||
|
|
||||||
Variable addString(String str, TextLocation location) {
|
Variable addString(String str, TextLocation location) {
|
||||||
Variable var = program.createVariable();
|
Variable var = program.createVariable();
|
||||||
StringConstantInstruction nameInsn = new StringConstantInstruction();
|
var nameInsn = new StringConstantInstruction();
|
||||||
nameInsn.setReceiver(var);
|
nameInsn.setReceiver(var);
|
||||||
nameInsn.setConstant(str);
|
nameInsn.setConstant(str);
|
||||||
nameInsn.setLocation(location);
|
nameInsn.setLocation(location);
|
||||||
|
@ -558,6 +558,10 @@ class JSValueMarshaller {
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variable addJsString(String str, TextLocation location) {
|
||||||
|
return addStringWrap(addString(str, location), location);
|
||||||
|
}
|
||||||
|
|
||||||
Variable classRef(String className, TextLocation location) {
|
Variable classRef(String className, TextLocation location) {
|
||||||
String name = null;
|
String name = null;
|
||||||
String module = null;
|
String module = null;
|
||||||
|
@ -586,16 +590,10 @@ class JSValueMarshaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
Variable globalRef(String name, TextLocation location) {
|
Variable globalRef(String name, TextLocation location) {
|
||||||
var nameInsn = new StringConstantInstruction();
|
|
||||||
nameInsn.setReceiver(program.createVariable());
|
|
||||||
nameInsn.setConstant(name);
|
|
||||||
nameInsn.setLocation(location);
|
|
||||||
replacement.add(nameInsn);
|
|
||||||
|
|
||||||
var invoke = new InvokeInstruction();
|
var invoke = new InvokeInstruction();
|
||||||
invoke.setType(InvocationType.SPECIAL);
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
invoke.setMethod(JSMethods.GLOBAL);
|
invoke.setMethod(JSMethods.GLOBAL);
|
||||||
invoke.setArguments(nameInsn.getReceiver());
|
invoke.setArguments(addString(name, location));
|
||||||
invoke.setReceiver(program.createVariable());
|
invoke.setReceiver(program.createVariable());
|
||||||
invoke.setLocation(location);
|
invoke.setLocation(location);
|
||||||
replacement.add(invoke);
|
replacement.add(invoke);
|
||||||
|
@ -618,26 +616,11 @@ class JSValueMarshaller {
|
||||||
invoke.setLocation(location);
|
invoke.setLocation(location);
|
||||||
replacement.add(invoke);
|
replacement.add(invoke);
|
||||||
|
|
||||||
var nameInsn = new StringConstantInstruction();
|
|
||||||
nameInsn.setReceiver(program.createVariable());
|
|
||||||
nameInsn.setConstant(name);
|
|
||||||
nameInsn.setLocation(location);
|
|
||||||
replacement.add(nameInsn);
|
|
||||||
|
|
||||||
var wrapName = new InvokeInstruction();
|
|
||||||
wrapName.setType(InvocationType.SPECIAL);
|
|
||||||
wrapName.setMethod(referenceCache.getCached(new MethodReference(JS.class, "wrap",
|
|
||||||
String.class, JSObject.class)));
|
|
||||||
wrapName.setReceiver(program.createVariable());
|
|
||||||
wrapName.setArguments(nameInsn.getReceiver());
|
|
||||||
wrapName.setLocation(location);
|
|
||||||
replacement.add(wrapName);
|
|
||||||
|
|
||||||
var get = new InvokeInstruction();
|
var get = new InvokeInstruction();
|
||||||
get.setType(InvocationType.SPECIAL);
|
get.setType(InvocationType.SPECIAL);
|
||||||
get.setMethod(JSMethods.GET_PURE);
|
get.setMethod(JSMethods.GET_PURE);
|
||||||
get.setReceiver(program.createVariable());
|
get.setReceiver(program.createVariable());
|
||||||
get.setArguments(invoke.getReceiver(), wrapName.getReceiver());
|
get.setArguments(invoke.getReceiver(), addJsString(name, location));
|
||||||
get.setLocation(location);
|
get.setLocation(location);
|
||||||
replacement.add(get);
|
replacement.add(get);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.jso.impl;
|
||||||
|
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSClass;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
import org.teavm.jso.core.JSBoolean;
|
import org.teavm.jso.core.JSBoolean;
|
||||||
import org.teavm.jso.core.JSFinalizationRegistry;
|
import org.teavm.jso.core.JSFinalizationRegistry;
|
||||||
|
@ -24,11 +25,12 @@ import org.teavm.jso.core.JSMap;
|
||||||
import org.teavm.jso.core.JSNumber;
|
import org.teavm.jso.core.JSNumber;
|
||||||
import org.teavm.jso.core.JSObjects;
|
import org.teavm.jso.core.JSObjects;
|
||||||
import org.teavm.jso.core.JSString;
|
import org.teavm.jso.core.JSString;
|
||||||
|
import org.teavm.jso.core.JSUndefined;
|
||||||
import org.teavm.jso.core.JSWeakMap;
|
import org.teavm.jso.core.JSWeakMap;
|
||||||
import org.teavm.jso.core.JSWeakRef;
|
import org.teavm.jso.core.JSWeakRef;
|
||||||
|
|
||||||
public final class JSWrapper {
|
public final class JSWrapper {
|
||||||
private static final JSWeakMap<JSObject, JSNumber> hashCodes = new JSWeakMap<>();
|
private static final JSWeakMap<JSObject, JSTransparentInt> hashCodes = new JSWeakMap<>();
|
||||||
private static final JSWeakMap<JSObject, JSWeakRef<JSObject>> wrappers = JSWeakRef.isSupported()
|
private static final JSWeakMap<JSObject, JSWeakRef<JSObject>> wrappers = JSWeakRef.isSupported()
|
||||||
? new JSWeakMap<>() : null;
|
? new JSWeakMap<>() : null;
|
||||||
private static final JSMap<JSString, JSWeakRef<JSObject>> stringWrappers = JSWeakRef.isSupported()
|
private static final JSMap<JSString, JSWeakRef<JSObject>> stringWrappers = JSWeakRef.isSupported()
|
||||||
|
@ -68,8 +70,8 @@ public final class JSWrapper {
|
||||||
if (wrappers != null) {
|
if (wrappers != null) {
|
||||||
if (isObject) {
|
if (isObject) {
|
||||||
var existingRef = get(wrappers, js);
|
var existingRef = get(wrappers, js);
|
||||||
var existing = !JSObjects.isUndefined(existingRef) ? deref(existingRef) : JSObjects.undefined();
|
var existing = !isUndefined(existingRef) ? deref(existingRef) : JSUndefined.instance();
|
||||||
if (JSObjects.isUndefined(existing)) {
|
if (isUndefined(existing)) {
|
||||||
var wrapper = new JSWrapper(js);
|
var wrapper = new JSWrapper(js);
|
||||||
set(wrappers, js, createWeakRef(wrapperToJs(wrapper)));
|
set(wrappers, js, createWeakRef(wrapperToJs(wrapper)));
|
||||||
return wrapper;
|
return wrapper;
|
||||||
|
@ -79,8 +81,8 @@ public final class JSWrapper {
|
||||||
} else if (type.equals("string")) {
|
} else if (type.equals("string")) {
|
||||||
var jsString = (JSString) js;
|
var jsString = (JSString) js;
|
||||||
var existingRef = get(stringWrappers, jsString);
|
var existingRef = get(stringWrappers, jsString);
|
||||||
var existing = !JSObjects.isUndefined(existingRef) ? deref(existingRef) : JSObjects.undefined();
|
var existing = !isUndefined(existingRef) ? deref(existingRef) : JSUndefined.instance();
|
||||||
if (JSObjects.isUndefined(existing)) {
|
if (isUndefined(existing)) {
|
||||||
var wrapper = new JSWrapper(js);
|
var wrapper = new JSWrapper(js);
|
||||||
var wrapperAsJs = wrapperToJs(wrapper);
|
var wrapperAsJs = wrapperToJs(wrapper);
|
||||||
set(stringWrappers, jsString, createWeakRef(wrapperAsJs));
|
set(stringWrappers, jsString, createWeakRef(wrapperAsJs));
|
||||||
|
@ -92,8 +94,8 @@ public final class JSWrapper {
|
||||||
} else if (type.equals("number")) {
|
} else if (type.equals("number")) {
|
||||||
var jsNumber = (JSNumber) js;
|
var jsNumber = (JSNumber) js;
|
||||||
var existingRef = get(numberWrappers, jsNumber);
|
var existingRef = get(numberWrappers, jsNumber);
|
||||||
var existing = !JSObjects.isUndefined(existingRef) ? deref(existingRef) : JSObjects.undefined();
|
var existing = !isUndefined(existingRef) ? deref(existingRef) : JSUndefined.instance();
|
||||||
if (JSObjects.isUndefined(existing)) {
|
if (isUndefined(existing)) {
|
||||||
var wrapper = new JSWrapper(js);
|
var wrapper = new JSWrapper(js);
|
||||||
var wrapperAsJs = wrapperToJs(wrapper);
|
var wrapperAsJs = wrapperToJs(wrapper);
|
||||||
set(numberWrappers, jsNumber, createWeakRef(wrapperAsJs));
|
set(numberWrappers, jsNumber, createWeakRef(wrapperAsJs));
|
||||||
|
@ -104,8 +106,8 @@ public final class JSWrapper {
|
||||||
}
|
}
|
||||||
} else if (type.equals("undefined")) {
|
} else if (type.equals("undefined")) {
|
||||||
var existingRef = undefinedWrapper;
|
var existingRef = undefinedWrapper;
|
||||||
var existing = existingRef != null ? deref(existingRef) : JSObjects.undefined();
|
var existing = existingRef != null ? deref(existingRef) : JSUndefined.instance();
|
||||||
if (JSObjects.isUndefined(existing)) {
|
if (isUndefined(existing)) {
|
||||||
var wrapper = new JSWrapper(js);
|
var wrapper = new JSWrapper(js);
|
||||||
var wrapperAsJs = wrapperToJs(wrapper);
|
var wrapperAsJs = wrapperToJs(wrapper);
|
||||||
undefinedWrapper = createWeakRef(wrapperAsJs);
|
undefinedWrapper = createWeakRef(wrapperAsJs);
|
||||||
|
@ -215,13 +217,21 @@ public final class JSWrapper {
|
||||||
return !isJava(o) || o instanceof JSWrapper;
|
return !isJava(o) || o instanceof JSWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPrimitive(Object o, JSObject primitive) {
|
||||||
|
return isJs(o) && JS.isPrimitive(maybeUnwrap(o), primitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean instanceOf(Object o, JSObject type) {
|
||||||
|
return isJs(o) && JS.instanceOf(maybeUnwrap(o), type);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
var type = JSObjects.typeOf(js);
|
var type = JSObjects.typeOf(js);
|
||||||
if (type.equals("object") || type.equals("symbol") || type.equals("function")) {
|
if (type.equals("object") || type.equals("symbol") || type.equals("function")) {
|
||||||
var code = hashCodes.get(js);
|
var code = hashCodes.get(js);
|
||||||
if (JSObjects.isUndefined(code)) {
|
if (isUndefined(code)) {
|
||||||
code = JSNumber.valueOf(++hashCodeGen);
|
code = JSTransparentInt.valueOf(++hashCodeGen);
|
||||||
hashCodes.set(js, code);
|
hashCodes.set(js, code);
|
||||||
}
|
}
|
||||||
return code.intValue();
|
return code.intValue();
|
||||||
|
@ -263,6 +273,18 @@ public final class JSWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return JSObjects.isUndefined(js) ? "undefined" : JSObjects.toString(js);
|
return isUndefined(js) ? "undefined" : JSObjects.toString(js);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JSClass(transparent = true)
|
||||||
|
static abstract class JSTransparentInt implements JSObject {
|
||||||
|
@JSBody(script = "return this;")
|
||||||
|
native int intValue();
|
||||||
|
|
||||||
|
@JSBody(params = "value", script = "return value;")
|
||||||
|
static native JSTransparentInt valueOf(int value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(params = "obj", script = "return typeof obj == 'undefined'")
|
||||||
|
private static native boolean isUndefined(JSObject obj);
|
||||||
}
|
}
|
||||||
|
|
118
tests/src/test/java/org/teavm/jso/test/InstanceOfTest.java
Normal file
118
tests/src/test/java/org/teavm/jso/test/InstanceOfTest.java
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.jso.test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSClass;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSProperty;
|
||||||
|
import org.teavm.jso.core.JSNumber;
|
||||||
|
import org.teavm.junit.AttachJavaScript;
|
||||||
|
import org.teavm.junit.EachTestCompiledSeparately;
|
||||||
|
import org.teavm.junit.OnlyPlatform;
|
||||||
|
import org.teavm.junit.SkipJVM;
|
||||||
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
import org.teavm.junit.TestPlatform;
|
||||||
|
|
||||||
|
@RunWith(TeaVMTestRunner.class)
|
||||||
|
@SkipJVM
|
||||||
|
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||||
|
@EachTestCompiledSeparately
|
||||||
|
public class InstanceOfTest {
|
||||||
|
@Test
|
||||||
|
@AttachJavaScript("org/teavm/jso/test/classWithConstructor.js")
|
||||||
|
public void instanceOf() {
|
||||||
|
var a = createClassWithConstructor();
|
||||||
|
assertTrue(a instanceof ClassWithConstructor);
|
||||||
|
assertFalse(a instanceof JSNumber);
|
||||||
|
assertTrue(a instanceof I);
|
||||||
|
assertTrue(a instanceof C);
|
||||||
|
|
||||||
|
var b = callCreateClassWithConstructor();
|
||||||
|
assertTrue(b instanceof ClassWithConstructor);
|
||||||
|
assertFalse(b instanceof JSNumber);
|
||||||
|
assertTrue(b instanceof I);
|
||||||
|
assertTrue(b instanceof C);
|
||||||
|
|
||||||
|
var c = createNumber();
|
||||||
|
assertFalse(c instanceof ClassWithConstructor);
|
||||||
|
assertTrue(c instanceof JSNumber);
|
||||||
|
assertTrue(c instanceof I);
|
||||||
|
assertTrue(c instanceof C);
|
||||||
|
|
||||||
|
var d = callCreateNumber();
|
||||||
|
assertFalse(d instanceof ClassWithConstructor);
|
||||||
|
assertTrue(d instanceof JSNumber);
|
||||||
|
assertTrue(d instanceof I);
|
||||||
|
assertTrue(d instanceof C);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@AttachJavaScript("org/teavm/jso/test/classWithConstructor.js")
|
||||||
|
public void cast() {
|
||||||
|
var a = createClassWithConstructor();
|
||||||
|
assertEquals(99, ((ClassWithConstructor) a).getFoo());
|
||||||
|
|
||||||
|
try {
|
||||||
|
assertEquals(99, ((JSNumber) a).intValue());
|
||||||
|
fail("CCE not thrown");
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
assertEquals(99, ((I) a).getFoo());
|
||||||
|
assertEquals(99, ((C) a).getFoo());
|
||||||
|
|
||||||
|
var c = createNumber();
|
||||||
|
assertEquals(23, ((JSNumber) c).intValue());
|
||||||
|
try {
|
||||||
|
assertEquals(99, ((ClassWithConstructor) c).getFoo());
|
||||||
|
fail("CCE not thrown");
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object callCreateClassWithConstructor() {
|
||||||
|
return createClassWithConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(script = "return new ClassWithConstructor();")
|
||||||
|
private static native JSObject createClassWithConstructor();
|
||||||
|
|
||||||
|
private Object callCreateNumber() {
|
||||||
|
return createNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(script = "return 23;")
|
||||||
|
private static native JSObject createNumber();
|
||||||
|
|
||||||
|
interface I extends JSObject {
|
||||||
|
@JSProperty
|
||||||
|
int getFoo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSClass(transparent = true)
|
||||||
|
abstract class C implements JSObject {
|
||||||
|
@JSProperty
|
||||||
|
public abstract int getFoo();
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import org.teavm.jso.JSObject;
|
||||||
import org.teavm.jso.core.JSNumber;
|
import org.teavm.jso.core.JSNumber;
|
||||||
import org.teavm.jso.core.JSObjects;
|
import org.teavm.jso.core.JSObjects;
|
||||||
import org.teavm.jso.core.JSString;
|
import org.teavm.jso.core.JSString;
|
||||||
|
import org.teavm.jso.core.JSUndefined;
|
||||||
import org.teavm.junit.EachTestCompiledSeparately;
|
import org.teavm.junit.EachTestCompiledSeparately;
|
||||||
import org.teavm.junit.OnlyPlatform;
|
import org.teavm.junit.OnlyPlatform;
|
||||||
import org.teavm.junit.SkipJVM;
|
import org.teavm.junit.SkipJVM;
|
||||||
|
@ -313,10 +314,10 @@ public class JSWrapperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void wrapUndefined() {
|
public void wrapUndefined() {
|
||||||
field1 = JSObjects.undefined();
|
field1 = JSUndefined.instance();
|
||||||
assertEquals("undefined", field1.toString());
|
assertEquals("undefined", field1.toString());
|
||||||
assertEquals(JSObjects.undefined(), field1);
|
assertEquals(JSUndefined.instance(), field1);
|
||||||
assertSame(JSObjects.undefined(), field1);
|
assertSame(JSUndefined.instance(), field1);
|
||||||
assertTrue(field1 instanceof JSObject);
|
assertTrue(field1 instanceof JSObject);
|
||||||
assertTrue(JSObjects.isUndefined(field1));
|
assertTrue(JSObjects.isUndefined(field1));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user