From d82b06ef8ed6976d3e2ca11c9923f154d91347c0 Mon Sep 17 00:00:00 2001
From: Sam Cao <sjtucaocao@gmail.com>
Date: Wed, 2 Oct 2024 10:35:41 +0800
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=B3=20chore:=20Add=20JavetBuddy=20v0.2?=
 =?UTF-8?q?.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .gitignore                                    |   5 +
 build.gradle.kts                              |  20 +-
 docs/release_notes.rst                        |   3 +-
 scripts/python/change_javet_version.py        |  90 ------
 scripts/python/requirements.txt               |   1 -
 scripts/ts/change-version.ts                  | 107 ++++++
 .../proxy/JavetReflectionObjectFactory.java   | 305 ------------------
 .../modules/javet/TestJavetModule.java        |   2 +-
 .../javenode/modules/javet/TutorialJavet.java |   2 +-
 9 files changed, 128 insertions(+), 407 deletions(-)
 delete mode 100644 scripts/python/change_javet_version.py
 delete mode 100644 scripts/python/requirements.txt
 create mode 100644 scripts/ts/change-version.ts
 delete mode 100644 src/main/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java

diff --git a/.gitignore b/.gitignore
index c6a0b6d..8c9d81c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,3 +77,8 @@ _build
 pom.xml.releaseBackup
 release.properties
 target
+
+# ------------------------------------------------------------------------------
+# Deno
+
+deno.lock
diff --git a/build.gradle.kts b/build.gradle.kts
index e835c3c..758144d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -47,13 +47,13 @@ object Config {
     }
 
     object Projects {
-        // https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy
-        const val BYTE_BUDDY = "net.bytebuddy:byte-buddy:${Versions.BYTE_BUDDY}"
-
         const val JAVET = "com.caoccao.javet:javet:${Versions.JAVET}"
         const val JAVET_LINUX_ARM64 = "com.caoccao.javet:javet-linux-arm64:${Versions.JAVET}"
         const val JAVET_MACOS = "com.caoccao.javet:javet-macos:${Versions.JAVET}"
 
+        // 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.junit.jupiter/junit-jupiter-api
         const val JUNIT_JUPITER_API = "org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT}"
 
@@ -67,7 +67,8 @@ object Config {
     object Versions {
         const val BYTE_BUDDY = "1.14.10"
         const val JAVA_VERSION = "1.8"
-        const val JAVET = "3.1.7"
+        const val JAVET = "3.1.8"
+        const val JAVET_BUDDY = "0.2.0"
         const val JAVENODE = "0.8.0"
         const val JUNIT = "5.10.1"
         const val VERTX = "4.4.6"
@@ -104,13 +105,16 @@ dependencies {
     val os = OperatingSystem.current()
     val cpuArch = System.getProperty("os.arch")
     if (os.isMacOsX) {
-        implementation(Config.Projects.JAVET_MACOS)
+        compileOnly(Config.Projects.JAVET_MACOS)
+        testImplementation(Config.Projects.JAVET_MACOS)
     } else if (os.isLinux && (cpuArch == "aarch64" || cpuArch == "arm64")) {
-        implementation(Config.Projects.JAVET_LINUX_ARM64)
+        compileOnly(Config.Projects.JAVET_LINUX_ARM64)
+        testImplementation(Config.Projects.JAVET_LINUX_ARM64)
     } else {
-        implementation(Config.Projects.JAVET)
+        compileOnly(Config.Projects.JAVET)
+        testImplementation(Config.Projects.JAVET)
     }
-    implementation(Config.Projects.BYTE_BUDDY)
+    implementation(Config.Projects.JAVET_BUDDY)
     implementation(Config.Projects.VERTX)
     testImplementation(Config.Projects.JUNIT_JUPITER_API)
     testRuntimeOnly(Config.Projects.JUNIT_JUPITER_ENGINE)
diff --git a/docs/release_notes.rst b/docs/release_notes.rst
index f312d08..836f336 100644
--- a/docs/release_notes.rst
+++ b/docs/release_notes.rst
@@ -5,7 +5,8 @@ Release Notes
 0.8.0
 -----
 
-* Upgraded Javet to v3.1.4
+* Upgraded Javet to v3.1.8
+* Added JavetBuddy v0.2.0
 
 0.7.0
 -----
diff --git a/scripts/python/change_javet_version.py b/scripts/python/change_javet_version.py
deleted file mode 100644
index 8574869..0000000
--- a/scripts/python/change_javet_version.py
+++ /dev/null
@@ -1,90 +0,0 @@
-'''
-  Copyright (c) 2021-2024 caoccao.com Sam Cao
-  All rights reserved.
-
-  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.
-'''
-
-import importlib
-import logging
-import pathlib
-import re
-import sys
-
-if importlib.util.find_spec('coloredlogs'):
-  import coloredlogs
-  coloredlogs.install(level=logging.DEBUG, fmt='%(asctime)-15s %(name)s %(levelname)s: %(message)s')
-
-class ChangeVersion(object):
-
-  def __init__(self, version) -> None:
-    self._root_path = (pathlib.Path(__file__) / '../../../').resolve().absolute()
-    self._version = version
-
-  def update_javenode(self):
-    self._update(
-      'README.rst', '\n',
-      re.compile(r'^        <version>(?P<version>\d+\.\d+\.\d+)</version>$'),
-      re.compile(r'javenode[\-\w]*:(?P<version>\d+\.\d+\.\d+)["\']{1}'))
-    self._update(
-      'build.gradle.kts', '\n',
-      re.compile(r'JAVENODE = "(?P<version>\d+\.\d+\.\d+)"$'))
-    self._update(
-      'docs/conf.py', '\n',
-      re.compile(r'release\s*=\s*\'(?P<version>\d+\.\d+\.\d+)\'$'))
-
-  def update_javet(self):
-    self._update(
-      'build.gradle.kts', '\n',
-      re.compile(r'JAVET = "(?P<version>\d+\.\d+\.\d+)"$'))
-
-  def _update(self, relative_file_path: str, line_separator: str, *patterns: list):
-    file_path = (self._root_path / relative_file_path).resolve().absolute()
-    logging.info('Updating %s.', str(file_path))
-    lines, line_number = [], 1
-    original_buffer = file_path.read_bytes()
-    for line in original_buffer.decode('utf-8').split(line_separator):
-      for pattern in patterns:
-        match_object = pattern.search(line)
-        if match_object is not None:
-          version = self._version
-          if ',' in match_object.group('version'):
-            version = version.replace('.', ',')
-          logging.info(
-            '  %d: %s -> %s',
-            line_number,
-            match_object.group('version'),
-            version)
-          line = '{prefix}{version}{suffix}'.format(
-            prefix=line[:match_object.start('version')],
-            version=version,
-            suffix=line[match_object.end('version'):])
-          break
-      lines.append(line)
-      line_number += 1
-    new_buffer = line_separator.join(lines).encode('utf-8')
-    if original_buffer == new_buffer:
-      logging.warning('  Skipped.')
-    else:
-      file_path.write_bytes(new_buffer)
-      logging.info('  Updated.')
-
-def main():
-  change_javenode_version = ChangeVersion('0.8.0')
-  change_javenode_version.update_javenode()
-  change_javet_version = ChangeVersion('3.1.4')
-  change_javet_version.update_javet()
-  return 0
-
-if __name__ == '__main__':
-  sys.exit(int(main() or 0))
diff --git a/scripts/python/requirements.txt b/scripts/python/requirements.txt
deleted file mode 100644
index d895475..0000000
--- a/scripts/python/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-coloredlogs==14.0
diff --git a/scripts/ts/change-version.ts b/scripts/ts/change-version.ts
new file mode 100644
index 0000000..4110c6e
--- /dev/null
+++ b/scripts/ts/change-version.ts
@@ -0,0 +1,107 @@
+/*
+* Copyright (c) 2024. caoccao.com Sam Cao
+* All rights reserved.
+
+* 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.
+*/
+
+import * as fs from "https://deno.land/std/fs/mod.ts";
+import * as path from "https://deno.land/std/path/mod.ts";
+
+class ChangeVersion {
+  private productionVersion: string;
+  private rootDirPath: string;
+  private snapshotVersion: string;
+
+  constructor(productionVersion: string, snapshotVersion: string) {
+    this.productionVersion = productionVersion;
+    this.snapshotVersion = snapshotVersion;
+    this.rootDirPath = path.join(
+      path.dirname(path.fromFileUrl(import.meta.url)),
+      "../../"
+    );
+  }
+
+  private _change(
+    filePath: string,
+    patterns: Array<RegExp>,
+    isSnapshot = true
+  ) {
+    const sourceFilePath = path.join(this.rootDirPath, filePath);
+    if (fs.existsSync(sourceFilePath)) {
+      console.info(`Processing ${sourceFilePath}.`);
+    } else {
+      console.error(`%c${sourceFilePath} is not found.`, "color: red");
+      return;
+    }
+    const newVersion = isSnapshot
+      ? this.snapshotVersion
+      : this.productionVersion;
+    const positionGroups: Array<{ start: number; end: number }> = [];
+    const currentContent = Deno.readTextFileSync(sourceFilePath);
+    patterns.map((pattern) => {
+      [...currentContent.matchAll(pattern)].map((match) => {
+        const matchedString = match[0];
+        if (match.groups) {
+          const currentVersion = match.groups["version"];
+          if (newVersion === currentVersion) {
+            console.warn(`%c  Ignored ${matchedString}`, "color: yellow");
+          } else {
+            console.info(`  ${matchedString} => ${newVersion}`);
+            const start = match.index + matchedString.indexOf(currentVersion);
+            const end = start + currentVersion.length;
+            positionGroups.push({ start: start, end: end });
+          }
+        }
+      });
+    });
+    if (positionGroups.length > 0) {
+      let newContent = "";
+      let lastEnd = 0;
+      positionGroups.sort((pg1, pg2) => pg1.start - pg2.start);
+      positionGroups.map((positionGroup) => {
+        if (positionGroup.start > lastEnd) {
+          newContent += currentContent.substring(lastEnd, positionGroup.start);
+        }
+        newContent += newVersion;
+        lastEnd = positionGroup.end;
+      });
+      if (lastEnd < currentContent.length) {
+        newContent += currentContent.substring(lastEnd, currentContent.length);
+      }
+      if (newContent !== currentContent) {
+        Deno.writeTextFileSync(sourceFilePath, newContent);
+      }
+    }
+  }
+
+  change() {
+    this._change(
+      "README.rst",
+      [
+        /^\s*<version>(?<version>\d+\.\d+\.\d+)<\/version>/gim,
+        /javenode:(?<version>\d+\.\d+\.\d+)"/gim,
+      ],
+      false
+    );
+    this._change("build.gradle.kts", [
+      /^\s*const val JAVENODE = "(?<version>\d+\.\d+\.\d+)"/gim,
+    ]);
+    this._change("docs/conf.py", [
+      /^release = "(?<version>\d+\.\d+\.\d+)"/gim,
+    ]);
+  }
+}
+
+const changeVersion = new ChangeVersion("0.8.0", "0.8.0");
+changeVersion.change();
diff --git a/src/main/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java b/src/main/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java
deleted file mode 100644
index a493134..0000000
--- a/src/main/java/com/caoccao/javet/interop/proxy/JavetReflectionObjectFactory.java
+++ /dev/null
@@ -1,305 +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.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<Object> 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 (propertyName != null && propertyName.length() > 0) {
-                        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 (propertyName != null && propertyName.length() > 0) {
-                        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<Object> callable) throws Exception {
-            return super.intercept(method, arguments, callable);
-        }
-    }
-}
diff --git a/src/test/java/com/caoccao/javet/javenode/modules/javet/TestJavetModule.java b/src/test/java/com/caoccao/javet/javenode/modules/javet/TestJavetModule.java
index bea20ab..c7efed6 100644
--- a/src/test/java/com/caoccao/javet/javenode/modules/javet/TestJavetModule.java
+++ b/src/test/java/com/caoccao/javet/javenode/modules/javet/TestJavetModule.java
@@ -17,10 +17,10 @@
 package com.caoccao.javet.javenode.modules.javet;
 
 import com.caoccao.javet.annotations.V8Function;
+import com.caoccao.javet.buddy.interop.proxy.JavetReflectionObjectFactory;
 import com.caoccao.javet.exceptions.JavetException;
 import com.caoccao.javet.interfaces.IJavetAnonymous;
 import com.caoccao.javet.interop.converters.JavetProxyConverter;
-import com.caoccao.javet.interop.proxy.JavetReflectionObjectFactory;
 import com.caoccao.javet.javenode.BaseJNTestSuite;
 import com.caoccao.javet.javenode.enums.JNModuleType;
 import org.junit.jupiter.api.Test;
diff --git a/src/test/java/com/caoccao/javet/javenode/modules/javet/TutorialJavet.java b/src/test/java/com/caoccao/javet/javenode/modules/javet/TutorialJavet.java
index 9a0bd88..572e39e 100644
--- a/src/test/java/com/caoccao/javet/javenode/modules/javet/TutorialJavet.java
+++ b/src/test/java/com/caoccao/javet/javenode/modules/javet/TutorialJavet.java
@@ -16,11 +16,11 @@
 
 package com.caoccao.javet.javenode.modules.javet;
 
+import com.caoccao.javet.buddy.interop.proxy.JavetReflectionObjectFactory;
 import com.caoccao.javet.exceptions.JavetException;
 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;
 import com.caoccao.javet.javenode.JNEventLoop;
 import com.caoccao.javet.javenode.enums.JNModuleType;