From 6ed57dbad1e3bb79e7cf0605f72cfd197a0468fb Mon Sep 17 00:00:00 2001 From: TimeBather Date: Fri, 3 Jan 2025 14:31:54 +0800 Subject: [PATCH] feat: implementation new JavaScript converter engine --- .../lib/core/addons/node/AssetReader.java | 2 +- .../frontend/common/event/DomEvent.java | 2 +- .../client/frontend/common/event/Event.java | 3 +- .../frontend/common/event/MessageEvent.java | 3 +- .../frontend/common/style/StyleList.java | 2 +- .../core/client/frontend/dom/DomContext.java | 2 +- .../client/frontend/dom/nodes/DomNode.java | 2 +- .../core/client/frontend/gui/GuiScreen.java | 4 +- .../client/frontend/gui/events/UIEvent.java | 10 + .../events/{ => mouse}/MouseClickEvent.java | 2 +- .../gui/events/{ => mouse}/MouseEvent.java | 9 +- .../{ => mouse}/MouseReleasedEvent.java | 2 +- .../frontend/gui/nodes/GuiCanvasNode.java | 3 +- .../client/frontend/gui/nodes/GuiDomNode.java | 4 +- .../core/javascript/CompoundTagWrapper.java | 3 +- .../engine/{ => annotations}/HostAccess.java | 2 +- .../engine/annotations/Optimization.java | 13 + .../engine/javet/JavetClassConverter.java | 8 +- .../javascript/engine/javet/JavetContext.java | 3 +- .../engine/javet/JavetKasugaConverter.java | 16 +- .../engine/javet/RequireFunction.java | 2 +- .../engine/javet/converter/ClassAccessor.java | 265 ++++++++++++++++++ .../converter/FastJavetClassConverter.java | 155 ++++++++++ .../javet/converter/INativeClassProvider.java | 7 + .../javet/converter/MethodOverrideMap.java | 28 ++ .../javet/converter/NativeProxyAccessor.java | 128 +++++++++ .../javascript/prebuilt/nbt/NBTModule.java | 2 +- .../prebuilt/process/ProcessModule.java | 2 +- .../registry/RegistryPrebuiltModule.java | 3 +- .../prebuilt/registry/RegistryProxy.java | 2 +- .../prebuilt/timer/TimerPrebuiltModule.java | 3 +- .../websocket/WebSocketPrebuiltModule.java | 2 +- .../prebuilt/websocket/WebsocketEvent.java | 2 +- .../websocket/WebsocketInterface.java | 2 +- .../core/menu/api/ChannelHandlerProxy.java | 3 +- .../lib/core/menu/api/ChannelProxy.java | 3 +- .../core/menu/api/GuiMenuOperateProxy.java | 2 +- .../menu/javascript/JavascriptMenuHandle.java | 2 +- .../block/gui/GuiExampleBlockApi.java | 3 +- .../lib/example_env/engine/Benchmarker.java | 132 +++++++++ .../example_env/engine/ClassBenchmarker.java | 38 +++ 41 files changed, 817 insertions(+), 64 deletions(-) create mode 100644 src/main/java/kasuga/lib/core/client/frontend/gui/events/UIEvent.java rename src/main/java/kasuga/lib/core/client/frontend/gui/events/{ => mouse}/MouseClickEvent.java (97%) rename src/main/java/kasuga/lib/core/client/frontend/gui/events/{ => mouse}/MouseEvent.java (89%) rename src/main/java/kasuga/lib/core/client/frontend/gui/events/{ => mouse}/MouseReleasedEvent.java (97%) rename src/main/java/kasuga/lib/core/javascript/engine/{ => annotations}/HostAccess.java (86%) create mode 100644 src/main/java/kasuga/lib/core/javascript/engine/annotations/Optimization.java create mode 100644 src/main/java/kasuga/lib/core/javascript/engine/javet/converter/ClassAccessor.java create mode 100644 src/main/java/kasuga/lib/core/javascript/engine/javet/converter/FastJavetClassConverter.java create mode 100644 src/main/java/kasuga/lib/core/javascript/engine/javet/converter/INativeClassProvider.java create mode 100644 src/main/java/kasuga/lib/core/javascript/engine/javet/converter/MethodOverrideMap.java create mode 100644 src/main/java/kasuga/lib/core/javascript/engine/javet/converter/NativeProxyAccessor.java create mode 100644 src/main/java/kasuga/lib/example_env/engine/Benchmarker.java create mode 100644 src/main/java/kasuga/lib/example_env/engine/ClassBenchmarker.java diff --git a/src/main/java/kasuga/lib/core/addons/node/AssetReader.java b/src/main/java/kasuga/lib/core/addons/node/AssetReader.java index 09d32dea..842755da 100644 --- a/src/main/java/kasuga/lib/core/addons/node/AssetReader.java +++ b/src/main/java/kasuga/lib/core/addons/node/AssetReader.java @@ -2,7 +2,7 @@ import kasuga.lib.core.addons.resource.ResourceProvider; import kasuga.lib.core.javascript.JavascriptContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/kasuga/lib/core/client/frontend/common/event/DomEvent.java b/src/main/java/kasuga/lib/core/client/frontend/common/event/DomEvent.java index 9c921b3c..84ed47ae 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/common/event/DomEvent.java +++ b/src/main/java/kasuga/lib/core/client/frontend/common/event/DomEvent.java @@ -1,7 +1,7 @@ package kasuga.lib.core.client.frontend.common.event; import kasuga.lib.core.client.frontend.dom.nodes.DomNode; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; public abstract class DomEvent extends Event{ protected final DomNode currentTarget; diff --git a/src/main/java/kasuga/lib/core/client/frontend/common/event/Event.java b/src/main/java/kasuga/lib/core/client/frontend/common/event/Event.java index c92c2041..84245b19 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/common/event/Event.java +++ b/src/main/java/kasuga/lib/core/client/frontend/common/event/Event.java @@ -1,7 +1,6 @@ package kasuga.lib.core.client.frontend.common.event; -import kasuga.lib.core.client.frontend.dom.nodes.DomNode; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; public abstract class Event { diff --git a/src/main/java/kasuga/lib/core/client/frontend/common/event/MessageEvent.java b/src/main/java/kasuga/lib/core/client/frontend/common/event/MessageEvent.java index 6a6f01c9..e2a70f45 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/common/event/MessageEvent.java +++ b/src/main/java/kasuga/lib/core/client/frontend/common/event/MessageEvent.java @@ -3,9 +3,8 @@ import com.caoccao.javet.annotations.V8Allow; import com.caoccao.javet.annotations.V8Convert; import com.caoccao.javet.enums.V8ConversionMode; -import com.caoccao.javet.enums.V8ProxyMode; import kasuga.lib.core.javascript.CompoundTagWrapper; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import net.minecraft.nbt.CompoundTag; @V8Convert(mode = V8ConversionMode.AllowOnly) diff --git a/src/main/java/kasuga/lib/core/client/frontend/common/style/StyleList.java b/src/main/java/kasuga/lib/core/client/frontend/common/style/StyleList.java index 983d5e56..0974fae4 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/common/style/StyleList.java +++ b/src/main/java/kasuga/lib/core/client/frontend/common/style/StyleList.java @@ -1,7 +1,7 @@ package kasuga.lib.core.client.frontend.common.style; import com.caoccao.javet.annotations.V8Convert; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.util.Callback; import java.util.*; diff --git a/src/main/java/kasuga/lib/core/client/frontend/dom/DomContext.java b/src/main/java/kasuga/lib/core/client/frontend/dom/DomContext.java index 312cca03..721ba790 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/dom/DomContext.java +++ b/src/main/java/kasuga/lib/core/client/frontend/dom/DomContext.java @@ -6,7 +6,7 @@ import kasuga.lib.core.client.frontend.dom.registration.DOMPriorityRegistry; import kasuga.lib.core.client.frontend.dom.registration.DOMRegistryItemDynamicProxy; import kasuga.lib.core.javascript.Tickable; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; import kasuga.lib.core.util.Callback; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/kasuga/lib/core/client/frontend/dom/nodes/DomNode.java b/src/main/java/kasuga/lib/core/client/frontend/dom/nodes/DomNode.java index ab03c4e7..da980e63 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/dom/nodes/DomNode.java +++ b/src/main/java/kasuga/lib/core/client/frontend/dom/nodes/DomNode.java @@ -5,7 +5,7 @@ import kasuga.lib.core.client.frontend.dom.event.EventEmitter; import kasuga.lib.core.client.frontend.rendering.RenderContext; import kasuga.lib.core.javascript.JavascriptContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; import java.util.ArrayList; diff --git a/src/main/java/kasuga/lib/core/client/frontend/gui/GuiScreen.java b/src/main/java/kasuga/lib/core/client/frontend/gui/GuiScreen.java index d732d4eb..c7d14453 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/gui/GuiScreen.java +++ b/src/main/java/kasuga/lib/core/client/frontend/gui/GuiScreen.java @@ -3,8 +3,8 @@ import com.mojang.blaze3d.vertex.PoseStack; import kasuga.lib.KasugaLib; import kasuga.lib.core.client.frontend.common.layouting.LayoutBox; -import kasuga.lib.core.client.frontend.gui.events.MouseClickEvent; -import kasuga.lib.core.client.frontend.gui.events.MouseReleasedEvent; +import kasuga.lib.core.client.frontend.gui.events.mouse.MouseClickEvent; +import kasuga.lib.core.client.frontend.gui.events.mouse.MouseReleasedEvent; import kasuga.lib.core.client.frontend.rendering.RenderContext; import kasuga.lib.core.util.data_type.Vec2i; import net.minecraft.client.gui.screens.Screen; diff --git a/src/main/java/kasuga/lib/core/client/frontend/gui/events/UIEvent.java b/src/main/java/kasuga/lib/core/client/frontend/gui/events/UIEvent.java new file mode 100644 index 00000000..5fb5f6ee --- /dev/null +++ b/src/main/java/kasuga/lib/core/client/frontend/gui/events/UIEvent.java @@ -0,0 +1,10 @@ +package kasuga.lib.core.client.frontend.gui.events; + +import kasuga.lib.core.client.frontend.common.event.DomEvent; +import kasuga.lib.core.client.frontend.dom.nodes.DomNode; + +public abstract class UIEvent extends DomEvent { + protected UIEvent(DomNode currentTarget, DomNode target, boolean trusted) { + super(currentTarget, target, trusted); + } +} diff --git a/src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseClickEvent.java b/src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseClickEvent.java similarity index 97% rename from src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseClickEvent.java rename to src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseClickEvent.java index 4fd748e6..c6edaf5e 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseClickEvent.java +++ b/src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseClickEvent.java @@ -1,4 +1,4 @@ -package kasuga.lib.core.client.frontend.gui.events; +package kasuga.lib.core.client.frontend.gui.events.mouse; import kasuga.lib.core.client.frontend.dom.nodes.DomNode; import kasuga.lib.core.client.frontend.gui.nodes.GuiDomNode; diff --git a/src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseEvent.java b/src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseEvent.java similarity index 89% rename from src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseEvent.java rename to src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseEvent.java index aca8dab0..d22bb64f 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseEvent.java +++ b/src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseEvent.java @@ -1,13 +1,12 @@ -package kasuga.lib.core.client.frontend.gui.events; +package kasuga.lib.core.client.frontend.gui.events.mouse; -import kasuga.lib.core.client.frontend.common.event.DomEvent; -import kasuga.lib.core.client.frontend.common.event.Event; import kasuga.lib.core.client.frontend.dom.nodes.DomNode; +import kasuga.lib.core.client.frontend.gui.events.UIEvent; import kasuga.lib.core.client.frontend.gui.nodes.GuiDomNode; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.util.data_type.Vec2i; -public class MouseEvent extends DomEvent { +public class MouseEvent extends UIEvent { protected final Vec2i screenPosition; protected final int button; diff --git a/src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseReleasedEvent.java b/src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseReleasedEvent.java similarity index 97% rename from src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseReleasedEvent.java rename to src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseReleasedEvent.java index 48ef08e4..2eaec333 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/gui/events/MouseReleasedEvent.java +++ b/src/main/java/kasuga/lib/core/client/frontend/gui/events/mouse/MouseReleasedEvent.java @@ -1,4 +1,4 @@ -package kasuga.lib.core.client.frontend.gui.events; +package kasuga.lib.core.client.frontend.gui.events.mouse; import kasuga.lib.core.client.frontend.dom.nodes.DomNode; import kasuga.lib.core.client.frontend.gui.nodes.GuiDomNode; diff --git a/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiCanvasNode.java b/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiCanvasNode.java index fd520f27..4cea6f63 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiCanvasNode.java +++ b/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiCanvasNode.java @@ -2,13 +2,12 @@ import kasuga.lib.KasugaLib; import kasuga.lib.core.client.frontend.common.layouting.LayoutBox; -import kasuga.lib.core.client.frontend.common.layouting.LayoutContext; import kasuga.lib.core.client.frontend.common.layouting.LayoutNode; import kasuga.lib.core.client.frontend.gui.GuiContext; import kasuga.lib.core.client.frontend.gui.canvas.CanvasRenderingContext2D; import kasuga.lib.core.client.frontend.gui.canvas.glfw.CanvasRenderer; import kasuga.lib.core.client.frontend.rendering.RenderContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import java.util.Map; diff --git a/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiDomNode.java b/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiDomNode.java index df892066..eef344db 100644 --- a/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiDomNode.java +++ b/src/main/java/kasuga/lib/core/client/frontend/gui/nodes/GuiDomNode.java @@ -11,11 +11,11 @@ import kasuga.lib.core.client.frontend.dom.nodes.DomNode; import kasuga.lib.core.client.frontend.font.ExtendableProperty; import kasuga.lib.core.client.frontend.gui.GuiContext; -import kasuga.lib.core.client.frontend.gui.events.MouseEvent; +import kasuga.lib.core.client.frontend.gui.events.mouse.MouseEvent; import kasuga.lib.core.client.frontend.gui.layout.EdgeSize2D; import kasuga.lib.core.client.frontend.rendering.BackgroundRenderer; import kasuga.lib.core.client.frontend.rendering.RenderContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraftforge.common.util.Lazy; diff --git a/src/main/java/kasuga/lib/core/javascript/CompoundTagWrapper.java b/src/main/java/kasuga/lib/core/javascript/CompoundTagWrapper.java index d30fc196..baa7db16 100644 --- a/src/main/java/kasuga/lib/core/javascript/CompoundTagWrapper.java +++ b/src/main/java/kasuga/lib/core/javascript/CompoundTagWrapper.java @@ -1,13 +1,12 @@ package kasuga.lib.core.javascript; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import java.util.Arrays; import java.util.Collection; -import java.util.List; public class CompoundTagWrapper { CompoundTag tag; diff --git a/src/main/java/kasuga/lib/core/javascript/engine/HostAccess.java b/src/main/java/kasuga/lib/core/javascript/engine/annotations/HostAccess.java similarity index 86% rename from src/main/java/kasuga/lib/core/javascript/engine/HostAccess.java rename to src/main/java/kasuga/lib/core/javascript/engine/annotations/HostAccess.java index 09bbf413..60c37dd2 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/HostAccess.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/annotations/HostAccess.java @@ -1,4 +1,4 @@ -package kasuga.lib.core.javascript.engine; +package kasuga.lib.core.javascript.engine.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/kasuga/lib/core/javascript/engine/annotations/Optimization.java b/src/main/java/kasuga/lib/core/javascript/engine/annotations/Optimization.java new file mode 100644 index 00000000..2898d71f --- /dev/null +++ b/src/main/java/kasuga/lib/core/javascript/engine/annotations/Optimization.java @@ -0,0 +1,13 @@ +package kasuga.lib.core.javascript.engine.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class Optimization { + + @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface DisableRuntimeAccessibilityCheck{} +} diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetClassConverter.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetClassConverter.java index db0431d8..1035a2c5 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetClassConverter.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetClassConverter.java @@ -5,13 +5,11 @@ 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.JavetProxyConverter; import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.reference.*; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; -import kasuga.lib.core.javascript.engine.javet.JavetJavascriptValue; -import kasuga.lib.core.javascript.engine.javet.JavetKasugaConverter; -import kasuga.lib.core.javascript.engine.javet.JavetValue; import kasuga.lib.core.util.data_type.Pair; import java.lang.reflect.*; @@ -272,6 +270,8 @@ public V8Value invoke(Object object, ArrayList alternativeMethods, V8Val for (Method sameNameMethod : alternativeMethods) { if(!sameNameMethod.canAccess(object)) continue; + if(sameNameMethod.getParameterCount() != argsList.size()) + continue; Pair callResult = tryInvoke(sameNameMethod, argsList, converted); if(callResult.getFirst()){ if(callResult.getSecond() instanceof V8ValueReference reference){ diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetContext.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetContext.java index e37ccf8f..ebe1d3ea 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetContext.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetContext.java @@ -9,6 +9,7 @@ import kasuga.lib.core.addons.node.NodePackage; import kasuga.lib.core.javascript.JavascriptContext; import kasuga.lib.core.javascript.engine.*; +import kasuga.lib.core.javascript.engine.javet.converter.FastJavetClassConverter; import java.io.*; import java.nio.charset.StandardCharsets; @@ -25,7 +26,7 @@ public class JavetContext implements JavascriptEngineContext { JavetStandardConsoleInterceptor consoleInterceptor = new JavetStandardConsoleInterceptor(runtime); consoleInterceptor.register(runtime.getGlobalObject()); moduleAPI = new JavetModuleAPI(runtime,this, context.getModuleLoader()); - runtime.setConverter(new JavetKasugaConverter(runtime)); + runtime.setConverter(new FastJavetClassConverter(runtime)); runtime.setPromiseRejectCallback((event, promise, value)->{ if(event.getCode() == 0){ System.err.println("Error" + event.getName()); diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetKasugaConverter.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetKasugaConverter.java index 4d020a96..9289fc08 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetKasugaConverter.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/JavetKasugaConverter.java @@ -1,29 +1,15 @@ package kasuga.lib.core.javascript.engine.javet; -import com.caoccao.javet.annotations.V8Convert; -import com.caoccao.javet.annotations.V8Function; import com.caoccao.javet.exceptions.JavetException; -import com.caoccao.javet.interfaces.IJavetEntityFunction; import com.caoccao.javet.interop.V8Runtime; -import com.caoccao.javet.interop.callback.JavetCallbackContext; import com.caoccao.javet.interop.converters.JavetObjectConverter; -import com.caoccao.javet.interop.converters.JavetProxyConverter; -import com.caoccao.javet.utils.receivers.JavetCallbackReceiver; import com.caoccao.javet.values.V8Value; -import com.caoccao.javet.values.reference.V8ValueFunction; import com.caoccao.javet.values.reference.V8ValueObject; -import com.caoccao.javet.values.reference.V8ValueReference; import com.caoccao.javet.values.reference.V8ValueSymbol; -import kasuga.lib.core.javascript.engine.HostAccess; -import kasuga.lib.core.javascript.engine.JavascriptValue; -import kasuga.lib.core.util.WeakCache; -import kasuga.lib.core.util.data_type.Pair; import java.lang.ref.WeakReference; -import java.lang.reflect.*; import java.math.BigInteger; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; /* THIS FILE WAS MODIFIED FROM https://www.caoccao.com/Javet/reference/converters/custom_converter.html @@ -36,7 +22,7 @@ public class JavetKasugaConverter extends JavetObjectConverter { private final V8ValueSymbol SYMBOL_NATIVE_OBJECT; HashMap> cachedObjects = new HashMap<>(); - JavetKasugaConverter(V8Runtime runtime){ + public JavetKasugaConverter(V8Runtime runtime){ this.runtime = runtime; try{ SYMBOL_NATIVE_OBJECT = runtime.createV8ValueSymbol("NATIVE OBJECT"); diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/RequireFunction.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/RequireFunction.java index 66789fd5..6d74435f 100644 --- a/src/main/java/kasuga/lib/core/javascript/engine/javet/RequireFunction.java +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/RequireFunction.java @@ -1,7 +1,7 @@ package kasuga.lib.core.javascript.engine.javet; import com.caoccao.javet.values.V8Value; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; public interface RequireFunction { @HostAccess.Export 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 new file mode 100644 index 00000000..35c1847d --- /dev/null +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/ClassAccessor.java @@ -0,0 +1,265 @@ +package kasuga.lib.core.javascript.engine.javet.converter; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.interop.callback.JavetCallbackContext; +import com.caoccao.javet.interop.converters.IJavetConverter; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.reference.V8ValueObject; +import kasuga.lib.core.javascript.engine.JavascriptValue; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; +import kasuga.lib.core.javascript.engine.javet.JavetJavascriptValue; + +import java.lang.reflect.*; +import java.util.*; +import java.util.function.Function; + +public class ClassAccessor { + private final V8Runtime runtime; + private final IJavetConverter provider; + private final Class classType; + Map> quickAccessors = new HashMap<>(); + Map methods = new HashMap<>(); + Map fields = new HashMap<>(); + + public ClassAccessor( + V8Runtime runtime, + IJavetConverter provider, + Class classType + ){ + this.runtime = runtime; + this.provider = provider; + this.classType = classType; + } + + + public Object invoke( + Object target, + String name, + V8Value ...value + ) throws InvocationTargetException, IllegalAccessException, JavetException { + System.gc(); + runtime.lowMemoryNotification(); + if(quickAccessors.containsKey(name)) { + return quickAccessors.get(name).apply(value); + } + + if(!methods.containsKey(name)) { + throw new RuntimeException("Illegal invocation"); + } + + MethodOverrideMap overrideMap = methods.get(name); + + int valueSize = value == null ? 0 : value.length; + BitSet convertMask = overrideMap.converterMask.get(valueSize); + List localMethods = overrideMap.methods.get(valueSize); + + if(convertMask == null || localMethods == null){ + throw new RuntimeException("Illegal invocation"); + } + + Object[] arrayParameters = new Object[valueSize]; + + for(int i = 0; i < valueSize ; i++) { + if(!convertMask.get(i)){ + arrayParameters[i] = value[i]; + continue; + } + Object nativeObject = provider.toObject(value[i]); + arrayParameters[i] = nativeObject == null ? + value[i] : nativeObject; + } + + JavascriptValue[] values = new JavascriptValue[valueSize]; + + for(int methodIndex = 0; methodIndex < localMethods.size(); methodIndex++){ + Method localMethod = localMethods.get(methodIndex); + Class[] parameterTypes = localMethod.getParameterTypes(); + Object[] parameters = new Object[valueSize]; + boolean isSignatureMatch = true; + for (int parameterIndex = parameterTypes.length - 1; parameterIndex >= 0; parameterIndex--) { + Class parameterType = parameterTypes[parameterIndex]; + if(parameterType == JavascriptValue.class){ + if(values[parameterIndex] == null){ + values[parameterIndex] = new JavetJavascriptValue(value[parameterIndex], runtime); + } + parameters[parameterIndex] = value[parameterIndex]; + continue; + } + + Class valueType = arrayParameters[parameterIndex].getClass(); + if( + parameterType == valueType || + parameterType.isAssignableFrom(valueType) + ) { + parameters[parameterIndex] = arrayParameters[parameterIndex]; + continue; + } + isSignatureMatch = false; + break; + } + + if(!isSignatureMatch){ + continue; + } + + return localMethod.invoke(target, parameters); + } + throw new RuntimeException("Illegal invocation"); + } + + public Object get(Object object, String name) throws IllegalAccessException { + return fields.get(name).get(object); + } + + public Object set( + Object object, + String name, + V8Value value + ) throws JavetException, IllegalAccessException { + Object nativeObject = provider.toObject(value); + if( + !fields.get(name).getType().isAssignableFrom(nativeObject.getClass()) && + !Modifier.isFinal(fields.get(name).getModifiers()) + ) { + throw new RuntimeException("Ileegal operation"); + } + fields.get(name).set(object, nativeObject); + return nativeObject; + } + + public void addMethod(String name, Method method){ + MethodOverrideMap overrideMap = methods.computeIfAbsent(name, (i)->new MethodOverrideMap()); + overrideMap.initIfAbsent(method.getParameterCount()); + overrideMap.methods.get(method.getParameterCount()).add(method); + Class[] parameterTypes = method.getParameterTypes(); + + for (int i = 0; i < parameterTypes.length; i++) { + if(parameterTypes[i] == JavascriptValue.class){ + overrideMap.converterMask.get(method.getParameterCount()).set(i, false); + } + } + + if(method.getReturnType() != Void.class){ + overrideMap.isVoidReturn = false; + } + + } + + public void addField(String name, Field field){ + this.fields.put(name, field); + } + + public static ClassAccessor collect( + V8Runtime runtime, + IJavetConverter converter, + Class classType + ){ + ClassAccessor accessor = new ClassAccessor(runtime, converter, classType); + Method[] methods = classType.getMethods(); + HashSet filteredMethods = new HashSet<>(); + for (Method method : methods) { + if(method.isAnnotationPresent(HostAccess.Export.class)){ + filteredMethods.add(method); + } + } + Class[] interfaces = classType.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if(interfaces[i].isAnnotationPresent(FunctionalInterface.class)){ + Method[] interfaceMethods = interfaces[i].getDeclaredMethods(); + for (Method interfaceMethod : interfaceMethods) { + for(Method method : methods){ + if( + Arrays.equals(method.getTypeParameters(), interfaceMethod.getTypeParameters()) && + method.getReturnType() == interfaceMethod.getReturnType() + ){ + filteredMethods.add(method); + } + } + } + } + } + + for (Method filteredMethod : filteredMethods) { + filteredMethod.setAccessible(true); + accessor.addMethod(filteredMethod.getName(), filteredMethod); + } + + Field[] fields = classType.getFields(); + + for (Field field : fields) { + if(field.isAnnotationPresent(HostAccess.Export.class)){ + accessor.addField(field.getName(), field); + } + } + + return accessor; + } + + public void bindPrototypeTo(V8Runtime runtime, FastJavetClassConverter converter, V8ValueObject value) throws JavetException { + for (Map.Entry entry : methods.entrySet()) { + String name = entry.getKey(); + MethodOverrideMap overrideMap = entry.getValue(); + bind(runtime, value, converter, name, overrideMap.isVoidReturn); + } + + for (Map.Entry fieldEntry : fields.entrySet()){ + String name = fieldEntry.getKey(); + Field field = fieldEntry.getValue(); + bindProp(runtime, value, converter, name, Modifier.isFinal(field.getModifiers())); + } + } + + protected void bind( + V8Runtime runtime, + V8ValueObject value, + FastJavetClassConverter converter, + String name, + boolean voidReturn + ) throws JavetException { + System.out.printf("BIND RUNTIME: %s \n", name); + JavetCallbackContext context = NativeProxyAccessor.getCallbackContext( + runtime, + converter, + this, + classType, + name, + voidReturn ? + NativeProxyAccessor.AccessorType.METHOD_VOID + : NativeProxyAccessor.AccessorType.METHOD + ); + value.bindFunction(context); + } + + + protected void bindProp( + V8Runtime runtime, + V8ValueObject value, + FastJavetClassConverter converter, + String name, + boolean isReadOnly + ) throws JavetException { + System.out.printf("BIND RUNTIME: %s \n", name); + JavetCallbackContext readContext = NativeProxyAccessor.getCallbackContext( + runtime, + converter, + this, + classType, + name, + NativeProxyAccessor.AccessorType.FIELD_READ + ); + if(isReadOnly){ + value.bindProperty(readContext); + return; + } + JavetCallbackContext writeContext = NativeProxyAccessor.getCallbackContext( + runtime, + converter, + this, + classType, + name, + NativeProxyAccessor.AccessorType.FIELD_WRITE + ); + value.bindProperty(readContext, writeContext); + } +} 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 new file mode 100644 index 00000000..7c776b99 --- /dev/null +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/FastJavetClassConverter.java @@ -0,0 +1,155 @@ +package kasuga.lib.core.javascript.engine.javet.converter; + +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.JavetObjectConverter; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.reference.V8ValueFunction; +import com.caoccao.javet.values.reference.V8ValueObject; +import com.caoccao.javet.values.reference.V8ValueSymbol; +import kasuga.lib.core.javascript.engine.javet.JavetJavascriptValue; +import kasuga.lib.core.javascript.engine.javet.JavetValue; + +import java.lang.ref.WeakReference; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.WeakHashMap; + +public class FastJavetClassConverter extends JavetObjectConverter { + private final V8Runtime v8Runtime; + private final V8ValueSymbol tracker; + private final V8ValueFunction setPrototypeOf; + + public FastJavetClassConverter(V8Runtime v8Runtime){ + this.v8Runtime = v8Runtime; + try { + tracker = v8Runtime.createV8ValueSymbol("GC Tracker"); + this.setPrototypeOf = ((V8ValueFunction) ((V8ValueObject)v8Runtime.getGlobalObject().getProperty("Object")).getProperty("setPrototypeOf")); + + } catch (JavetException e) { + throw new RuntimeException(e); + } + } + public HashMap classTypeCache = new HashMap<>(); + + + public V8ValueObject getClassPrototype(Class prototype) throws JavetException { + if(classTypeCache.containsKey(prototype)) + return classTypeCache.get(prototype); + V8ValueObject prototypeObject = v8Runtime.createV8ValueObject(); + + ClassAccessor accessor = ClassAccessor.collect(v8Runtime, this, prototype); + + accessor.bindPrototypeTo(v8Runtime, this, prototypeObject); + + classTypeCache.put(prototype, prototypeObject); + + return prototypeObject; + } + + + HashMap> cachedObjects = new HashMap<>(); + + HashMap trackingObjects = new HashMap<>(); + + @Override + protected T toV8Value(V8Runtime v8Runtime, Object object, int depth) throws JavetException { + if(object == null){ + return (T) v8Runtime.createV8ValueNull(); + } + if(object instanceof JavetJavascriptValue value){ + return (T) value.getValue().toClone(); + } + if( + object instanceof int[] || + object instanceof float[] || + object instanceof double[] || + object instanceof long[] || + object instanceof short[] || + object instanceof byte[] || + object instanceof String || + object instanceof Boolean || + object instanceof Byte || + object instanceof Short || + object instanceof Integer || + object instanceof Long || + object instanceof Float || + object instanceof Double || + object instanceof Character || + object instanceof BigInteger + ){ + // Directly convert to V8Value, no need to process + return (T) super.toV8Value(v8Runtime, object, depth); + } + T v8Value = super.toV8Value(v8Runtime, object, depth); + + if (v8Value != null && !(v8Value.isUndefined())) { + if(v8Value instanceof V8ValueObject v8ValueObject){ + int hashCode = System.identityHashCode(object); + + cachedObjects.put(hashCode, new WeakReference<>(object)); + + v8ValueObject.setPrivateProperty("KasugaLib#Address", hashCode); + + v8ValueObject.bindFunction(new JavetCallbackContext( + "__nGCTracker__", + JavetCallbackType.DirectCallThisAndResult, + (IJavetDirectCallable.NoThisAndResult) (V8Value ...value)->{ + return toV8Value(v8Runtime, object); + // Reference to object to prevent the GCs + } + )); + } + return v8Value; + } + + V8ValueObject proto = this.getClassPrototype(object.getClass()); + + + V8ValueObject childObject = v8Runtime.createV8ValueObject(); + int hashCode = System.identityHashCode(object); + cachedObjects.put(hashCode, new WeakReference<>(object)); + childObject.setPrivateProperty("KasugaLib#Address", hashCode); + childObject.bindFunction(new JavetCallbackContext( + "__nGCTracker__", + JavetCallbackType.DirectCallThisAndResult, + (IJavetDirectCallable.NoThisAndResult) (V8Value ...value)->{ + return toV8Value(v8Runtime, object); + } + )); + childObject.set("__proto__",proto); + + return (T) childObject; + } + + @Override + protected T toObject(V8Value v8Value, int depth) throws JavetException { + if(v8Value instanceof V8ValueObject object){ + if(object.hasPrivateProperty("KasugaLib#Address")){ + int address = object.getPrivatePropertyInteger("KasugaLib#Address"); + if(cachedObjects.containsKey(address)){ + // System.out.println("P "+ String.valueOf(address)); + Object nativeObject = cachedObjects.get(address).get(); + return (T) nativeObject; + } + } + + } + T parentConvertResult = super.toObject(v8Value, depth); + if(parentConvertResult instanceof V8Value){ + return (T) new JavetJavascriptValue(JavetValue.weakClone(v8Value), v8Value.getV8Runtime()); + } + return parentConvertResult; + } + + public Object getNativeObject(V8ValueObject object) throws JavetException { + Object idObj = object.getPrivatePropertyPrimitive("KasugaLib#Address"); + if(!(idObj instanceof Integer id)){ + throw new RuntimeException("Invalid Innvocation"); + } + return cachedObjects.get(id).get(); + } +} diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/INativeClassProvider.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/INativeClassProvider.java new file mode 100644 index 00000000..e8c55420 --- /dev/null +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/INativeClassProvider.java @@ -0,0 +1,7 @@ +package kasuga.lib.core.javascript.engine.javet.converter; + +import com.caoccao.javet.values.V8Value; + +public interface INativeClassProvider { + public Object getNativeObject(V8Value value); +} diff --git a/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/MethodOverrideMap.java b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/MethodOverrideMap.java new file mode 100644 index 00000000..02f6ebe7 --- /dev/null +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/MethodOverrideMap.java @@ -0,0 +1,28 @@ +package kasuga.lib.core.javascript.engine.javet.converter; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; +import java.util.List; + +public class MethodOverrideMap { + public HashMap converterMask = new HashMap<>(); + public HashMap> methods = new HashMap<>(); + + public boolean isVoidReturn = true; + + public MethodOverrideMap(){} + + public void initIfAbsent(int parameterCount){ + + converterMask + .computeIfAbsent(parameterCount, (i)->{ + BitSet bitSet = new BitSet(); + bitSet.set(0, i,true); + return bitSet; + }); + + methods.computeIfAbsent(parameterCount, (i)->new ArrayList<>()); + } +} 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 new file mode 100644 index 00000000..466614bb --- /dev/null +++ b/src/main/java/kasuga/lib/core/javascript/engine/javet/converter/NativeProxyAccessor.java @@ -0,0 +1,128 @@ +package kasuga.lib.core.javascript.engine.javet.converter; + +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.values.V8Value; +import com.caoccao.javet.values.reference.V8ValueObject; + +public class NativeProxyAccessor { + + public static enum AccessorType { + FIELD_READ, + FIELD_WRITE, + METHOD, + METHOD_VOID + } + public static JavetCallbackContext getCallbackContext( + V8Runtime runtime, + FastJavetClassConverter converter, + ClassAccessor accessor, + Class className, + String name, + AccessorType type + ){ + JavetCallbackContext context; + switch (type) { + case METHOD: + context = new JavetCallbackContext( + name, + JavetCallbackType.DirectCallThisAndResult, + (IJavetDirectCallable.ThisAndResult)(V8Value _this, V8Value ...args)-> { + if(!(_this instanceof V8ValueObject object)){ + throw new RuntimeException("Illegal invocation"); + } + + Object targetObject = converter.getNativeObject(object); + + if(targetObject == null){ + throw new RuntimeException("Illegal invocation"); + } + + if(!className.isAssignableFrom(targetObject.getClass())) { + throw new RuntimeException("Illegal invocation"); + } + + return converter.toV8Value(runtime ,accessor.invoke(targetObject, name, args)); + } + ); + break; + + case METHOD_VOID: + context = new JavetCallbackContext( + name, + JavetCallbackType.DirectCallThisAndNoResult, + (IJavetDirectCallable.ThisAndNoResult)(V8Value _this, V8Value ...args)-> { + if(!(_this instanceof V8ValueObject object)){ + throw new RuntimeException("Illegal invocation"); + } + + Object targetObject = converter.getNativeObject(object); + + if(targetObject == null){ + throw new RuntimeException("Illegal invocation"); + } + + if(!className.isAssignableFrom(targetObject.getClass())) { + throw new RuntimeException("Illegal invocation"); + } + + accessor.invoke(targetObject, name, args); + } + ); + break; + + case FIELD_READ: + context = new JavetCallbackContext( + name, + JavetCallbackType.DirectCallGetterAndThis, + (IJavetDirectCallable.GetterAndThis)(V8Value _this)-> { + if(!(_this instanceof V8ValueObject object)){ + throw new RuntimeException("Illegal invocation"); + } + + Object targetObject = converter.getNativeObject(object); + + if(targetObject == null){ + throw new RuntimeException("Illegal invocation"); + } + + if(!className.isAssignableFrom(targetObject.getClass())) { + throw new RuntimeException("Illegal invocation"); + } + + return converter.toV8Value(runtime, accessor.get(targetObject, name)); + } + ); + break; + case FIELD_WRITE: + context = new JavetCallbackContext( + name, + JavetCallbackType.DirectCallSetterAndThis, + (IJavetDirectCallable.SetterAndThis)(V8Value _this, V8Value value)-> { + if(!(_this instanceof V8ValueObject object)){ + throw new RuntimeException("Illegal invocation"); + } + + Object targetObject = converter.getNativeObject(object); + + if(targetObject == null){ + throw new RuntimeException("Illegal invocation"); + } + + if(!className.isAssignableFrom(targetObject.getClass())) { + throw new RuntimeException("Illegal invocation"); + } + + return converter.toV8Value(runtime, accessor.set(targetObject, name, value)); + } + ); + break; + default: + throw new UnsupportedOperationException(); + } + + return context; + } +} diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/nbt/NBTModule.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/nbt/NBTModule.java index 8afe6168..e9cb4b5d 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/nbt/NBTModule.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/nbt/NBTModule.java @@ -1,7 +1,7 @@ package kasuga.lib.core.javascript.prebuilt.nbt; import kasuga.lib.core.javascript.CompoundTagWrapper; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; public class NBTModule { private static final NBTModule INSTANCE = new NBTModule(); diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/process/ProcessModule.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/process/ProcessModule.java index 7f984a85..0e42c207 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/process/ProcessModule.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/process/ProcessModule.java @@ -1,6 +1,6 @@ package kasuga.lib.core.javascript.prebuilt.process; import kasuga.lib.core.javascript.JavascriptContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.prebuilt.PrebuiltModule; public class ProcessModule extends PrebuiltModule { diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryPrebuiltModule.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryPrebuiltModule.java index 70309de5..472525ea 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryPrebuiltModule.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryPrebuiltModule.java @@ -1,8 +1,7 @@ package kasuga.lib.core.javascript.prebuilt.registry; -import kasuga.lib.KasugaLib; import kasuga.lib.core.javascript.JavascriptContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; import kasuga.lib.core.javascript.ffi.ResourceLocationFFIHelper; import kasuga.lib.core.javascript.prebuilt.PrebuiltModule; diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryProxy.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryProxy.java index 165aaaf9..e38e96d9 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryProxy.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/registry/RegistryProxy.java @@ -1,7 +1,7 @@ package kasuga.lib.core.javascript.prebuilt.registry; import kasuga.lib.core.javascript.JavascriptContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; import kasuga.lib.core.javascript.ffi.ResourceLocationFFIHelper; import kasuga.lib.core.javascript.registration.JavascriptPriorityRegistry; 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 e35ec11d..1f7dbaeb 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 @@ -1,9 +1,8 @@ package kasuga.lib.core.javascript.prebuilt.timer; import kasuga.lib.core.javascript.JavascriptContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; -import kasuga.lib.core.javascript.engine.javet.JavetClassConverter; import kasuga.lib.core.javascript.prebuilt.PrebuiltModule; import java.util.HashMap; diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebSocketPrebuiltModule.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebSocketPrebuiltModule.java index 9ec86984..2337fcda 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebSocketPrebuiltModule.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebSocketPrebuiltModule.java @@ -1,7 +1,7 @@ package kasuga.lib.core.javascript.prebuilt.websocket; import kasuga.lib.core.javascript.JavascriptContext; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; import kasuga.lib.core.javascript.prebuilt.PrebuiltModule; import java.lang.ref.WeakReference; diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketEvent.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketEvent.java index a4af06ba..42ad3a5a 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketEvent.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketEvent.java @@ -1,7 +1,7 @@ package kasuga.lib.core.javascript.prebuilt.websocket; import io.netty.buffer.ByteBuf; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; public class WebsocketEvent { diff --git a/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketInterface.java b/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketInterface.java index 573398ef..fb85e0eb 100644 --- a/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketInterface.java +++ b/src/main/java/kasuga/lib/core/javascript/prebuilt/websocket/WebsocketInterface.java @@ -1,7 +1,7 @@ package kasuga.lib.core.javascript.prebuilt.websocket; import io.netty.buffer.ByteBuf; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; import kasuga.lib.core.util.data_type.Pair; diff --git a/src/main/java/kasuga/lib/core/menu/api/ChannelHandlerProxy.java b/src/main/java/kasuga/lib/core/menu/api/ChannelHandlerProxy.java index 39f17e63..7593ba22 100644 --- a/src/main/java/kasuga/lib/core/menu/api/ChannelHandlerProxy.java +++ b/src/main/java/kasuga/lib/core/menu/api/ChannelHandlerProxy.java @@ -1,9 +1,8 @@ package kasuga.lib.core.menu.api; -import kasuga.lib.core.channel.address.ConnectionInfo; import kasuga.lib.core.channel.peer.ChannelHandle; import kasuga.lib.core.javascript.CompoundTagWrapper; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import java.util.Objects; import java.util.WeakHashMap; diff --git a/src/main/java/kasuga/lib/core/menu/api/ChannelProxy.java b/src/main/java/kasuga/lib/core/menu/api/ChannelProxy.java index a95ad4a6..4e79ec6a 100644 --- a/src/main/java/kasuga/lib/core/menu/api/ChannelProxy.java +++ b/src/main/java/kasuga/lib/core/menu/api/ChannelProxy.java @@ -2,8 +2,7 @@ import kasuga.lib.core.channel.address.ConnectionInfo; import kasuga.lib.core.channel.peer.Channel; -import kasuga.lib.core.channel.peer.ChannelHandle; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.util.data_type.Pair; import java.util.Objects; diff --git a/src/main/java/kasuga/lib/core/menu/api/GuiMenuOperateProxy.java b/src/main/java/kasuga/lib/core/menu/api/GuiMenuOperateProxy.java index 42992e53..8d619757 100644 --- a/src/main/java/kasuga/lib/core/menu/api/GuiMenuOperateProxy.java +++ b/src/main/java/kasuga/lib/core/menu/api/GuiMenuOperateProxy.java @@ -1,7 +1,7 @@ package kasuga.lib.core.menu.api; import com.caoccao.javet.annotations.V8Convert; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.menu.base.GuiMenu; @V8Convert() diff --git a/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandle.java b/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandle.java index af47cf68..bf5a1e2a 100644 --- a/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandle.java +++ b/src/main/java/kasuga/lib/core/menu/javascript/JavascriptMenuHandle.java @@ -2,7 +2,7 @@ import kasuga.lib.core.client.frontend.dom.event.EventEmitter; import kasuga.lib.core.javascript.CompoundTagWrapper; -import kasuga.lib.core.javascript.engine.HostAccess; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; import kasuga.lib.core.javascript.engine.JavascriptValue; import java.util.HashMap; diff --git a/src/main/java/kasuga/lib/example_env/block/gui/GuiExampleBlockApi.java b/src/main/java/kasuga/lib/example_env/block/gui/GuiExampleBlockApi.java index 1aed62e5..f6561569 100644 --- a/src/main/java/kasuga/lib/example_env/block/gui/GuiExampleBlockApi.java +++ b/src/main/java/kasuga/lib/example_env/block/gui/GuiExampleBlockApi.java @@ -1,7 +1,6 @@ package kasuga.lib.example_env.block.gui; -import kasuga.lib.core.javascript.engine.HostAccess; -import net.minecraft.world.level.block.entity.BlockEntity; +import kasuga.lib.core.javascript.engine.annotations.HostAccess; public class GuiExampleBlockApi { diff --git a/src/main/java/kasuga/lib/example_env/engine/Benchmarker.java b/src/main/java/kasuga/lib/example_env/engine/Benchmarker.java new file mode 100644 index 00000000..c32d2c45 --- /dev/null +++ b/src/main/java/kasuga/lib/example_env/engine/Benchmarker.java @@ -0,0 +1,132 @@ +package kasuga.lib.example_env.engine; + + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interception.logging.JavetStandardConsoleInterceptor; +import com.caoccao.javet.interop.V8Host; +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.values.V8Value; +import com.caoccao.javet.values.reference.V8ValueObject; +import kasuga.lib.core.javascript.engine.javet.JavetKasugaConverter; +import kasuga.lib.core.javascript.engine.javet.converter.FastJavetClassConverter; + +public class Benchmarker { + public static String benchmarkCode = """ +measureTime(); +var i; +for(i=0;i<1000;i++){ + benchmarker.directCall(); + var a = benchmarker.directCallWithObjectReturnType(); + benchmarker.directCallWithObjectParameter(a); + benchmarker.directCallWithOverloading("Hello,World"); +} + +console.info("ΔT1=", measureTime() / 1000); + +measureTime(); +for(i=0;i<10000;i++){ + benchmarker.directCall(); +} +console.info("ΔTD=", measureTime() / 1000); + +measureTime(); +for(i=0;i<10000;i++){ + benchmarker.directCallWithOverloading("Hello,World"); +} +console.info("ΔTO=", measureTime() / 1000); + +var a = benchmarker.directCallWithObjectReturnType(); + +measureTime(); +for(i=0;i<10000;i++){ + benchmarker.directCallWithObjectParameter(a); +} +console.info("ΔTP=", measureTime() / 1000); +"""; + + + public static void benchmarkOriginal() throws JavetException { + var runtime = V8Host.getV8Instance().createV8Runtime(); + runtime.setConverter(new JavetKasugaConverter(runtime)); + JavetStandardConsoleInterceptor consoleInterceptor = new JavetStandardConsoleInterceptor(runtime); + consoleInterceptor.register(runtime.getGlobalObject()); + var ref = new Object() { + long initTime = System.nanoTime(); + }; + runtime.getGlobalObject().bindFunction(new JavetCallbackContext( + "measureTime", + JavetCallbackType.DirectCallNoThisAndResult, + (IJavetDirectCallable.NoThisAndResult)(V8Value...object)->{ + long oldInitTime = ref.initTime; + ref.initTime = System.nanoTime(); + return runtime.getConverter().toV8Value(runtime, ((int) (System.nanoTime() - oldInitTime))); + } + )); + + System.out.println("-----Benchmarking Result of JavetKasugaConverter-----"); + + long nanoTime = System.nanoTime(); + + runtime.getGlobalObject().set("benchmarker",new ClassBenchmarker()); + + System.out.printf("ΔT初 = %d\n", (System.nanoTime() - nanoTime)); + + int beforeRef = runtime.getReferenceCount(); + + int beforeCtx = runtime.getCallbackContextCount(); + + runtime.getExecutor(benchmarkCode).execute(); + + runtime.lowMemoryNotification(); + System.gc(); + + System.out.printf("ΔREF = %d\n" , beforeRef - runtime.getReferenceCount()); + System.out.printf("ΔCTX = %d\n" , beforeCtx - runtime.getCallbackContextCount()); + + runtime.close(); + } + + public static void benchmarkNeo() throws JavetException { + var runtime = V8Host.getV8Instance().createV8Runtime(); + runtime.setConverter(new FastJavetClassConverter(runtime)); + JavetStandardConsoleInterceptor consoleInterceptor = new JavetStandardConsoleInterceptor(runtime); + consoleInterceptor.register(runtime.getGlobalObject()); + var ref = new Object() { + long initTime = System.nanoTime(); + }; + runtime.getGlobalObject().bindFunction(new JavetCallbackContext( + "measureTime", + JavetCallbackType.DirectCallNoThisAndResult, + (IJavetDirectCallable.NoThisAndResult)(V8Value...object)->{ + long oldInitTime = ref.initTime; + ref.initTime = System.nanoTime(); + return runtime.getConverter().toV8Value(runtime, ((int) (System.nanoTime() - oldInitTime))); + } + )); + System.out.println("-----Benchmarking Result of FastJavetClassConverter-----"); + long nanoTime = System.nanoTime(); + runtime.getGlobalObject().set("benchmarker",new ClassBenchmarker()); + System.out.printf("ΔT初 = %d\n", (System.nanoTime() - nanoTime)); + + int beforeRef = runtime.getReferenceCount(); + + int beforeCtx = runtime.getCallbackContextCount(); + + runtime.getExecutor(benchmarkCode).execute(); + + runtime.lowMemoryNotification(); + System.gc(); + + System.out.printf("ΔREF = %d\n" , beforeRef - runtime.getReferenceCount()); + System.out.printf("ΔCTX = %d\n" , beforeCtx - runtime.getCallbackContextCount()); + + runtime.close(); + } + + public static void start() throws JavetException { + benchmarkOriginal(); + benchmarkNeo(); + } +} diff --git a/src/main/java/kasuga/lib/example_env/engine/ClassBenchmarker.java b/src/main/java/kasuga/lib/example_env/engine/ClassBenchmarker.java new file mode 100644 index 00000000..8b07f2a5 --- /dev/null +++ b/src/main/java/kasuga/lib/example_env/engine/ClassBenchmarker.java @@ -0,0 +1,38 @@ +package kasuga.lib.example_env.engine; + +import kasuga.lib.core.javascript.engine.annotations.HostAccess; + +public class ClassBenchmarker { + + public static int alive = 0; + ClassBenchmarker(){ + alive ++; + System.out.println("+ " + String.valueOf(System.identityHashCode(this))); + } + @HostAccess.Export + public void directCall(){} + + @HostAccess.Export + public void directCallWithOverloading(String a){} + @HostAccess.Export + public void directCallWithOverloading(String a, String b){} + @HostAccess.Export + public void directCallWithOverloading(String a, Boolean b){} + + @HostAccess.Export + public Object directCallWithObjectReturnType(){ + return new ClassBenchmarker(); + } + + @HostAccess.Export + public void directCallWithObjectParameter(ClassBenchmarker benchmarker){ + return; + } + + @Override + protected void finalize() throws Throwable { + alive--; + System.out.println("- " + String.valueOf(System.identityHashCode(this))); + super.finalize(); + } +}