From 2e3470acdb75d122b9254b6a4d3f375cf33c77c3 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Mon, 30 Sep 2024 11:33:40 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20Move=20JavetReflecti?= =?UTF-8?q?onObjectFactory=20to=20JavetBuddy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 6 + docs/reference/converters/proxy_converter.rst | 29 +- .../jvm/TestJavetJVMInterceptor.java | 2 +- .../converters/TestJavetProxyConverter.java | 2 +- .../proxy/JavetReflectionObjectFactory.java | 306 ------------------ .../javet/tutorial/TestAccessTheWholeJVM.java | 2 +- 6 files changed, 13 insertions(+), 334 deletions(-) delete mode 100644 src/test/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java diff --git a/build.gradle.kts b/build.gradle.kts index 2075f5d00..7c5061aa1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,6 +51,9 @@ object Config { // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind const val JACKSON_DATABIND = "com.fasterxml.jackson.core:jackson-databind:${Versions.JACKSON_DATABIND}" + // https://mvnrepository.com/artifact/com.caoccao.javet.buddy/javet-buddy + const val JAVET_BUDDY = "com.caoccao.javet.buddy:javet-buddy:${Versions.JAVET_BUDDY}" + // https://mvnrepository.com/artifact/org.eclipse.jetty.websocket/javax-websocket-server-impl const val JETTY_JAVAX_WEBSOCKET_SERVER_IMPL = "org.eclipse.jetty.websocket:javax-websocket-server-impl:${Versions.JETTY_WEBSOCKET}" @@ -73,6 +76,7 @@ object Config { const val JACKSON_DATABIND = "2.16.0" const val JAVA_VERSION = "1.8" const val JAVET = "3.1.8" + const val JAVET_BUDDY = "0.1.0" const val JETTY_WEBSOCKET = "9.4.53.v20231009" const val JUNIT = "5.10.1" } @@ -103,10 +107,12 @@ java { dependencies { testImplementation(Config.Projects.BYTE_BUDDY) testImplementation(Config.Projects.JACKSON_DATABIND) + testImplementation(Config.Projects.JAVET_BUDDY) testImplementation(Config.Projects.JETTY_JAVAX_WEBSOCKET_SERVER_IMPL) testImplementation(Config.Projects.JETTY_WEBSOCKET_SERVER) testImplementation(Config.Projects.JUNIT_JUPITER_API) testImplementation(Config.Projects.JUNIT_JUPITER_PARAMS) +// testImplementation(files("../JavetBuddy/build/libs/javet-buddy-${Config.Versions.JAVET_BUDDY}.jar")) testRuntimeOnly(Config.Projects.JUNIT_JUPITER_ENGINE) } diff --git a/docs/reference/converters/proxy_converter.rst b/docs/reference/converters/proxy_converter.rst index d6cadff91..36d7b6617 100644 --- a/docs/reference/converters/proxy_converter.rst +++ b/docs/reference/converters/proxy_converter.rst @@ -352,30 +352,9 @@ Dynamic: Anonymous Object for Class This feature is similar to the dynamic anonymous object for interface, but it allows implementing all methods exposed by a non-final Java class. -1. Add ``ByteBuddy`` to the dependency. You may skip this step if your project has already referenced ``ByteBuddy``. +1. Add ``ByteBuddy`` and ``JavetBuddy`` to the dependency. Please refer to `JavetBuddy `_ for detail. -.. code-block:: xml - - - - net.bytebuddy - byte-buddy - 1.14.10 - - -.. code-block:: kotlin - - // Gradle Kotlin DSL - implementation("net.bytebuddy:byte-buddy:1.14.10") - -.. code-block:: groovy - - // Gradle Groovy DSL - implementation 'net.bytebuddy:byte-buddy:1.14.10' - -2. Copy :extsource3:`JavetReflectionObjectFactory.java <../../../src/test/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java>` to your project. As Javet doesn't reference ``ByteBuddy`` directly, ``JavetReflectionObjectFactory`` has to stay at the test project. - -3. Define a simple class ``DynamicClass`` for adding two integers. +2. Define a simple class ``DynamicClass`` for adding two integers. .. code-block:: java @@ -385,7 +364,7 @@ This feature is similar to the dynamic anonymous object for interface, but it al } } -4. Create an instance of a class which takes an instance of the ``DynamicClass``. +3. Create an instance of a class which takes an instance of the ``DynamicClass``. .. code-block:: java @@ -397,7 +376,7 @@ This feature is similar to the dynamic anonymous object for interface, but it al } }; -5. Inject the implementation from JavaScript. Please note that dynamic object support is disabled by default and ``JavetReflectionObjectFactory`` needs to be set to the converter config for ``JavetProxyConverter`` to enable this feature. +4. Inject the implementation from JavaScript. Please note that dynamic object support is disabled by default and ``JavetReflectionObjectFactory`` needs to be set to the converter config for ``JavetProxyConverter`` to enable this feature. .. code-block:: java diff --git a/src/test/java/com/caoccao/javet/interception/jvm/TestJavetJVMInterceptor.java b/src/test/java/com/caoccao/javet/interception/jvm/TestJavetJVMInterceptor.java index c0af4d3dc..d5615674c 100644 --- a/src/test/java/com/caoccao/javet/interception/jvm/TestJavetJVMInterceptor.java +++ b/src/test/java/com/caoccao/javet/interception/jvm/TestJavetJVMInterceptor.java @@ -17,9 +17,9 @@ package com.caoccao.javet.interception.jvm; import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.buddy.interop.proxy.JavetReflectionObjectFactory; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.interop.converters.JavetProxyConverter; -import com.caoccao.javet.interop.proxy.JavetReflectionObjectFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/caoccao/javet/interop/converters/TestJavetProxyConverter.java b/src/test/java/com/caoccao/javet/interop/converters/TestJavetProxyConverter.java index 60af555b0..77b5be0b2 100644 --- a/src/test/java/com/caoccao/javet/interop/converters/TestJavetProxyConverter.java +++ b/src/test/java/com/caoccao/javet/interop/converters/TestJavetProxyConverter.java @@ -18,6 +18,7 @@ import com.caoccao.javet.BaseTestJavetRuntime; import com.caoccao.javet.annotations.*; +import com.caoccao.javet.buddy.interop.proxy.JavetReflectionObjectFactory; import com.caoccao.javet.enums.JavetErrorType; import com.caoccao.javet.enums.V8ConversionMode; import com.caoccao.javet.enums.V8ProxyMode; @@ -31,7 +32,6 @@ import com.caoccao.javet.interfaces.IJavetUniFunction; import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.interop.proxy.IJavetDirectProxyHandler; -import com.caoccao.javet.interop.proxy.JavetReflectionObjectFactory; import com.caoccao.javet.mock.MockCallbackReceiver; import com.caoccao.javet.mock.MockDirectProxyFunctionHandler; import com.caoccao.javet.mock.MockDirectProxyListHandler; diff --git a/src/test/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java b/src/test/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java deleted file mode 100644 index f6e60e982..000000000 --- a/src/test/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2022. caoccao.com Sam Cao - * - * 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 com.caoccao.javet.interop.proxy; - -import com.caoccao.javet.interfaces.IJavetLogger; -import com.caoccao.javet.utils.JavetDefaultLogger; -import com.caoccao.javet.utils.JavetResourceUtils; -import com.caoccao.javet.utils.StringUtils; -import com.caoccao.javet.values.V8Value; -import com.caoccao.javet.values.reference.V8ValueFunction; -import com.caoccao.javet.values.reference.V8ValueObject; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; -import net.bytebuddy.implementation.MethodDelegation; -import net.bytebuddy.implementation.bind.annotation.AllArguments; -import net.bytebuddy.implementation.bind.annotation.Origin; -import net.bytebuddy.implementation.bind.annotation.RuntimeType; -import net.bytebuddy.implementation.bind.annotation.SuperCall; -import net.bytebuddy.matcher.ElementMatchers; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Locale; -import java.util.concurrent.Callable; - -/** - * The type Javet dynamic object factory. - * - * @since 2.0.1 - */ -public final class JavetReflectionObjectFactory implements IJavetReflectionObjectFactory { - private static final JavetReflectionObjectFactory instance = new JavetReflectionObjectFactory(); - private final IJavetLogger logger; - - private JavetReflectionObjectFactory() { - logger = new JavetDefaultLogger(getClass().getName()); - } - - /** - * Gets instance. - * - * @return the instance - * @since 2.0.1 - */ - public static JavetReflectionObjectFactory getInstance() { - return instance; - } - - @Override - public Object toObject(Class type, V8Value v8Value) { - if (v8Value instanceof V8ValueObject) { - V8ValueObject v8ValueObject = null; - try { - DynamicObjectAutoCloseableInvocationHandler invocationHandler; - v8ValueObject = v8Value.toClone(); - if (AutoCloseable.class.isAssignableFrom(type)) { - invocationHandler = new DynamicObjectAutoCloseableInvocationHandler(type, v8ValueObject); - } else { - invocationHandler = new DynamicObjectForceCloseableInvocationHandler(type, v8ValueObject); - } - invocationHandler.initialize(); - return invocationHandler.getDynamicObject(); - } catch (Throwable t) { - logger.logError(t, "Failed to create dynamic object for {0}.", type.getName()); - JavetResourceUtils.safeClose(v8ValueObject); - } - } - return null; - } - - /** - * The type Dynamic object auto closeable invocation handler. - * - * @since 2.0.1 - */ - public static class DynamicObjectAutoCloseableInvocationHandler { - /** - * The constant CONSTRUCTOR_STRATEGY. - * - * @since 2.0.1 - */ - protected static final ConstructorStrategy CONSTRUCTOR_STRATEGY = - new ConstructorStrategy.ForDefaultConstructor(); - /** - * The constant METHOD_CLOSE. - * - * @since 2.0.1 - */ - protected static final String METHOD_CLOSE = "close"; - /** - * The Dynamic object. - * - * @since 2.0.1 - */ - protected Object dynamicObject; - /** - * The Type. - * - * @since 2.0.1 - */ - protected Class type; - /** - * The V8 value object. - * - * @since 2.0.1 - */ - protected V8ValueObject v8ValueObject; - - /** - * Instantiates a new Dynamic object auto closeable invocation handler. - * - * @param type the type - * @param v8ValueObject the V8 value object - * @since 2.0.1 - */ - public DynamicObjectAutoCloseableInvocationHandler(Class type, V8ValueObject v8ValueObject) { - this.type = type; - this.v8ValueObject = v8ValueObject; - } - - /** - * Close. - * - * @throws Exception the exception - * @since 2.0.1 - */ - public void close() throws Exception { - JavetResourceUtils.safeClose(v8ValueObject); - dynamicObject = null; - v8ValueObject = null; - } - - @Override - protected void finalize() throws Throwable { - close(); - } - - /** - * Gets dynamic object. - * - * @return the dynamic object - * @since 2.0.1 - */ - public Object getDynamicObject() { - return dynamicObject; - } - - /** - * Initialize. - * - * @throws IOException the io exception - * @throws NoSuchMethodException the no such method exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @since 2.0.1 - */ - public void initialize() - throws IOException, NoSuchMethodException, InvocationTargetException, - InstantiationException, IllegalAccessException { - Class objectClass; - try (DynamicType.Unloaded unloadedType = new ByteBuddy() - .subclass(type, CONSTRUCTOR_STRATEGY) - .method(ElementMatchers.isPublic()) - .intercept(MethodDelegation.to(this)) - .make()) { - objectClass = unloadedType.load(getClass().getClassLoader()).getLoaded(); - } - dynamicObject = objectClass.getConstructor().newInstance(); - } - - /** - * Intercept object. - * - * @param method the method - * @param arguments the arguments - * @param callable the callable - * @return the object - * @throws Exception the exception - * @since 2.0.1 - */ - @RuntimeType - public Object intercept( - @Origin Method method, - @AllArguments Object[] arguments, - @SuperCall Callable callable) throws Exception { - if (v8ValueObject != null) { - String methodName = method.getName(); - final int argumentLength = arguments.length; - if (METHOD_CLOSE.equals(methodName) && argumentLength == 0) { - close(); - return callable.call(); - } else if (v8ValueObject.has(methodName)) { - // Function or Property - try (V8Value v8ValueProperty = v8ValueObject.get(methodName)) { - if (v8ValueProperty instanceof V8ValueFunction) { - // Function - V8ValueFunction v8ValueFunction = (V8ValueFunction) v8ValueProperty; - return v8ValueFunction.callObject(null, arguments); - } else if (argumentLength == 0) { - // Property - return v8ValueObject.getV8Runtime().toObject(v8ValueProperty); - } - } - } else if (argumentLength == 0) { - // Getter - String propertyName = null; - if (methodName.startsWith(V8ValueObject.METHOD_PREFIX_IS)) { - propertyName = methodName.substring(V8ValueObject.METHOD_PREFIX_IS.length()); - } else if (methodName.startsWith(V8ValueObject.METHOD_PREFIX_GET)) { - propertyName = methodName.substring(V8ValueObject.METHOD_PREFIX_GET.length()); - } - if (StringUtils.isNotEmpty(propertyName)) { - propertyName = propertyName.substring(0, 1).toLowerCase(Locale.ROOT) - + propertyName.substring(1); - if (v8ValueObject.has(propertyName)) { - return v8ValueObject.getObject(propertyName); - } - } - } else if (argumentLength == 1) { - // Setter - String propertyName = null; - if (methodName.startsWith(V8ValueObject.METHOD_PREFIX_SET)) { - propertyName = methodName.substring(V8ValueObject.METHOD_PREFIX_SET.length()); - } - if (StringUtils.isNotEmpty(propertyName)) { - propertyName = propertyName.substring(0, 1).toLowerCase(Locale.ROOT) - + propertyName.substring(1); - if (v8ValueObject.has(propertyName)) { - return v8ValueObject.set(propertyName, arguments[0]); - } - } - } - } - return callable.call(); - } - } - - /** - * The type Dynamic object force closeable invocation handler. - * - * @since 2.0.1 - */ - public static class DynamicObjectForceCloseableInvocationHandler - extends DynamicObjectAutoCloseableInvocationHandler - implements AutoCloseable { - - /** - * Instantiates a new Dynamic object force closeable invocation handler. - * - * @param type the type - * @param v8ValueObject the V8 value object - * @since 2.0.1 - */ - public DynamicObjectForceCloseableInvocationHandler(Class type, V8ValueObject v8ValueObject) { - super(type, v8ValueObject); - } - - @RuntimeType - @Override - public void close() throws Exception { - super.close(); - } - - @Override - public void initialize() - throws IOException, NoSuchMethodException, InvocationTargetException, - InstantiationException, IllegalAccessException { - Class objectClass; - try (DynamicType.Unloaded unloadedType = new ByteBuddy() - .subclass(type, CONSTRUCTOR_STRATEGY) - .implement(AutoCloseable.class) - .method(ElementMatchers.isPublic()) - .intercept(MethodDelegation.to(this)) - .make()) { - objectClass = unloadedType.load(getClass().getClassLoader()).getLoaded(); - } - dynamicObject = objectClass.getConstructor().newInstance(); - } - - @RuntimeType - @Override - public Object intercept( - @Origin Method method, - @AllArguments Object[] arguments, - @SuperCall Callable callable) throws Exception { - return super.intercept(method, arguments, callable); - } - } -} diff --git a/src/test/java/com/caoccao/javet/tutorial/TestAccessTheWholeJVM.java b/src/test/java/com/caoccao/javet/tutorial/TestAccessTheWholeJVM.java index ee74cfbd4..552540069 100644 --- a/src/test/java/com/caoccao/javet/tutorial/TestAccessTheWholeJVM.java +++ b/src/test/java/com/caoccao/javet/tutorial/TestAccessTheWholeJVM.java @@ -17,13 +17,13 @@ package com.caoccao.javet.tutorial; import com.caoccao.javet.annotations.V8Function; +import com.caoccao.javet.buddy.interop.proxy.JavetReflectionObjectFactory; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.interception.jvm.JavetJVMInterceptor; import com.caoccao.javet.interfaces.IJavetAnonymous; import com.caoccao.javet.interop.V8Host; import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.interop.converters.JavetProxyConverter; -import com.caoccao.javet.interop.proxy.JavetReflectionObjectFactory; public class TestAccessTheWholeJVM {