diff --git a/src/main/java/kasuga/lib/core/client/frontend/dom/event/EventEmitter.java b/src/main/java/kasuga/lib/core/client/frontend/dom/event/EventEmitter.java index 0ddbb26a..44939889 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/dom/event/EventEmitter.java +++ b/src/main/java/kasuga/lib/core/client/frontend/dom/event/EventEmitter.java @@ -26,6 +26,7 @@ public void unsubscribe(String eventName, Consumer consumer){ } public void subscribe(String eventName, JavascriptValue consumer){ + consumer = consumer.cloneValue(); consumer.pin(); functionalListeners.computeIfAbsent(eventName,(v)->new HashSet<>()) .add(consumer); diff --git a/src/main/java/kasuga/lib/core/client/frontend/dom/registration/DOMRegistryItem.java b/src/main/java/kasuga/lib/core/client/frontend/dom/registration/DOMRegistryItem.java index e31aaa7e..140d705a 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/dom/registration/DOMRegistryItem.java +++ b/src/main/java/kasuga/lib/core/client/frontend/dom/registration/DOMRegistryItem.java @@ -12,34 +12,37 @@ public abstract class DOMRegistryItem { public static DOMRegistryItem fromExecutable(JavascriptValue executable){ + JavascriptValue finalExecutable = executable.cloneValue(); return new DOMRegistryItem() { @Override JavascriptValue render(DomContext document) { - return executable.execute(document); + return finalExecutable.execute(document); } }; } public static DOMRegistryItem fromConfigurableObject(JavascriptValue object){ + JavascriptValue finalObject = object.cloneValue(); - if(!object.hasMember("render") || !object.getMember("render").canExecute()){ + if(!finalObject.hasMember("render") || !finalObject.getMember("render").canExecute()){ throw new IllegalArgumentException("Object must have a render method"); } DOMRegistryItem item = new DOMRegistryItem() { @Override JavascriptValue render(DomContext document) { - return object.invokeMember("render", document); + return finalObject.invokeMember("render", document); } }; - if(object.hasMember("renderEngine")){ + if(finalObject.hasMember("renderEngine")){ item.renderEngine = object.getMember("renderEngine").asString(); } - if(object.hasMember("lightLevel")){ + if(finalObject.hasMember("lightLevel")){ item.lightLevel = object.getMember("lightLevel").asString(); } + return item; } } diff --git a/src/main/java/kasuga/lib/core/javascript/engine/JavascriptValue.java b/src/main/java/kasuga/lib/core/javascript/engine/JavascriptValue.java index 1803f4ac..e4cded0a 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/JavascriptValue.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/JavascriptValue.java @@ -26,4 +26,6 @@ public interface JavascriptValue { boolean isNumber(); int asInt(); + + JavascriptValue cloneValue(); } diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetJavascriptValue.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetJavascriptValue.java index 7b554c5c..2d1c3bc6 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetJavascriptValue.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetJavascriptValue.java @@ -161,4 +161,13 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash(value, reciever); } + + @Override + public JavascriptValue cloneValue() { + try{ + return new JavetJavascriptValue(value.toClone(), runtime); + }catch (JavetException e){ + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/ClassAccessor.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/ClassAccessor.java index 35c1847d..d4673e7a 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/ClassAccessor.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/ClassAccessor.java @@ -2,7 +2,9 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.interop.callback.IJavetDirectCallable; import com.caoccao.javet.interop.callback.JavetCallbackContext; +import com.caoccao.javet.interop.callback.JavetCallbackType; import com.caoccao.javet.interop.converters.IJavetConverter; import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.reference.V8ValueObject; @@ -22,6 +24,9 @@ public class ClassAccessor { Map methods = new HashMap<>(); Map fields = new HashMap<>(); + boolean isFunctionalInterface = false; + String functionalMethodName; + public ClassAccessor( V8Runtime runtime, IJavetConverter provider, @@ -38,8 +43,6 @@ public Object invoke( String name, V8Value ...value ) throws InvocationTargetException, IllegalAccessException, JavetException { - System.gc(); - runtime.lowMemoryNotification(); if(quickAccessors.containsKey(name)) { return quickAccessors.get(name).apply(value); } @@ -62,7 +65,7 @@ public Object invoke( for(int i = 0; i < valueSize ; i++) { if(!convertMask.get(i)){ - arrayParameters[i] = value[i]; + arrayParameters[i] = new JavetJavascriptValue(value[i], runtime); continue; } Object nativeObject = provider.toObject(value[i]); @@ -83,7 +86,7 @@ public Object invoke( if(values[parameterIndex] == null){ values[parameterIndex] = new JavetJavascriptValue(value[parameterIndex], runtime); } - parameters[parameterIndex] = value[parameterIndex]; + parameters[parameterIndex] = values[parameterIndex]; continue; } @@ -98,11 +101,9 @@ public Object invoke( isSignatureMatch = false; break; } - if(!isSignatureMatch){ continue; } - return localMethod.invoke(target, parameters); } throw new RuntimeException("Illegal invocation"); @@ -174,6 +175,8 @@ public static ClassAccessor collect( method.getReturnType() == interfaceMethod.getReturnType() ){ filteredMethods.add(method); + accessor.isFunctionalInterface = true; + accessor.functionalMethodName = method.getName(); } } } @@ -262,4 +265,21 @@ protected void bindProp( ); value.bindProperty(readContext, writeContext); } + + public V8ValueObject createObject( + Object object, + IJavetConverter converter, + V8Runtime v8Runtime + ) throws JavetException { + if(isFunctionalInterface){ + return v8Runtime.createV8ValueFunction(new JavetCallbackContext( + "apply", + JavetCallbackType.DirectCallNoThisAndResult, + (IJavetDirectCallable.NoThisAndResult) (V8Value ...values)->{ + return converter.toV8Value(v8Runtime, invoke(object, functionalMethodName, values)); + } + )); + } + return v8Runtime.createV8ValueObject(); + } } diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/FastJavetClassConverter.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/FastJavetClassConverter.java index 7c776b99..6bb04486 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/FastJavetClassConverter.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/FastJavetClassConverter.java @@ -35,6 +35,8 @@ public FastJavetClassConverter(V8Runtime v8Runtime){ } public HashMap classTypeCache = new HashMap<>(); + public HashMap accessors = new HashMap<>(); + public V8ValueObject getClassPrototype(Class prototype) throws JavetException { if(classTypeCache.containsKey(prototype)) @@ -42,6 +44,7 @@ public V8ValueObject getClassPrototype(Class prototype) throws JavetException V8ValueObject prototypeObject = v8Runtime.createV8ValueObject(); ClassAccessor accessor = ClassAccessor.collect(v8Runtime, this, prototype); + accessors.put(prototype, accessor); accessor.bindPrototypeTo(v8Runtime, this, prototypeObject); @@ -63,6 +66,9 @@ protected T toV8Value(V8Runtime v8Runtime, Object object, in if(object instanceof JavetJavascriptValue value){ return (T) value.getValue().toClone(); } + if(object instanceof V8Value value){ + return (T) value.toClone(); + } if( object instanceof int[] || object instanceof float[] || @@ -102,14 +108,14 @@ protected T toV8Value(V8Runtime v8Runtime, Object object, in // Reference to object to prevent the GCs } )); + v8ValueObject.setWeak(); } return v8Value; } V8ValueObject proto = this.getClassPrototype(object.getClass()); - - V8ValueObject childObject = v8Runtime.createV8ValueObject(); + V8ValueObject childObject = this.accessors.get(object.getClass()).createObject(object, this, v8Runtime); int hashCode = System.identityHashCode(object); cachedObjects.put(hashCode, new WeakReference<>(object)); childObject.setPrivateProperty("KasugaLib#Address", hashCode); @@ -121,7 +127,7 @@ protected T toV8Value(V8Runtime v8Runtime, Object object, in } )); childObject.set("__proto__",proto); - + childObject.setWeak(); return (T) childObject; } diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/NativeProxyAccessor.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/NativeProxyAccessor.java index 466614bb..ac45668c 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/NativeProxyAccessor.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/NativeProxyAccessor.java @@ -41,7 +41,7 @@ public static JavetCallbackContext getCallbackContext( } if(!className.isAssignableFrom(targetObject.getClass())) { - throw new RuntimeException("Illegal invocation"); + throw new RuntimeException("Illegal invocation: "+className.getName() + "/" + targetObject.getClass().getName()); } return converter.toV8Value(runtime ,accessor.invoke(targetObject, name, args)); diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/timer/TimerPrebuiltModule.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/timer/TimerPrebuiltModule.java index 1f7dbaeb..407c3f3a 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/timer/TimerPrebuiltModule.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/timer/TimerPrebuiltModule.java @@ -26,20 +26,22 @@ public void tick() { } @HostAccess.Export public int requestTimeout(JavascriptValue callback, JavascriptValue interval){ - callback.pin(); + JavascriptValue finalCallback = callback.cloneValue(); + finalCallback.pin(); int[] cancelHandler = new int[1]; cancelHandler[0] = -1; return cancelHandler[0] = requestScheduled(KasugaTimer.TimerType.TIMEOUT,()->{ this.cancelHandler.remove(cancelHandler[0]); - callback.executeVoid(); - callback.unpin(); - }, ()->{callback.unpin();},interval); + finalCallback.executeVoid(); + finalCallback.unpin(); + }, ()->{finalCallback.unpin();},interval); } @HostAccess.Export public int requestInterval(JavascriptValue callback, JavascriptValue interval){ - callback.pin(); - return requestScheduled(KasugaTimer.TimerType.INTERVAL,()->callback.executeVoid(), ()->callback.unpin(), interval); + JavascriptValue finalCallback = callback.cloneValue(); + finalCallback.pin(); + return requestScheduled(KasugaTimer.TimerType.INTERVAL,()-> finalCallback.executeVoid(), ()->finalCallback.unpin(), interval); } diff --git a/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandler.java b/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandler.java index 163081e6..d9c88209 100644 --- a/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandler.java +++ b/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandler.java @@ -8,7 +8,7 @@ public class JavascriptMenuHandler { private JavascriptEngineContext context; private JavascriptValue handle; JavascriptMenuHandler(JavascriptEngineContext context,JavascriptValue handle){ - this.handle = handle; + this.handle = handle.cloneValue(); this.context = context; } public Runnable open(JavascriptMenuHandle handle){