From 6da7720c67404328c6726cfe4402a86eb92f82ec Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 21 Aug 2023 11:02:06 +0300 Subject: [PATCH] feat: add support for member class wrappers --- src/me/topchetoeu/jscript/engine/Engine.java | 3 +- src/me/topchetoeu/jscript/interop/Native.java | 2 +- .../jscript/interop/NativeTypeRegister.java | 38 +++++++++++++++++++ .../topchetoeu/jscript/interop/Overload.java | 11 +++++- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index b19cdc8..58f63b8 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -8,7 +8,6 @@ import me.topchetoeu.jscript.engine.CallContext.DataKey; import me.topchetoeu.jscript.engine.debug.DebugState; import me.topchetoeu.jscript.engine.modules.ModuleManager; import me.topchetoeu.jscript.engine.scope.GlobalScope; -import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; @@ -181,7 +180,7 @@ public class Engine { return msg.notifier; } - public CodeFunction compile(GlobalScope scope, String filename, String raw) throws InterruptedException { + public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException { return Parsing.compile(scope, filename, raw); } diff --git a/src/me/topchetoeu/jscript/interop/Native.java b/src/me/topchetoeu/jscript/interop/Native.java index 15742ba..f98765a 100644 --- a/src/me/topchetoeu/jscript/interop/Native.java +++ b/src/me/topchetoeu/jscript/interop/Native.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR }) +@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface Native { public String value() default ""; diff --git a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java index 429639b..c83e44a 100644 --- a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java +++ b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java @@ -71,15 +71,45 @@ public class NativeTypeRegister { } } } + private static void applyClasses(boolean member, ObjectValue target, Class clazz) { + for (var cl : clazz.getDeclaredClasses()) { + if (!Modifier.isStatic(cl.getModifiers()) != member) continue; + var nat = cl.getAnnotation(Native.class); + if (nat != null) { + var name = nat.value(); + if (name.equals("")) name = cl.getSimpleName(); + + var getter = new OverloadFunction("get " + name).add(Overload.getter(member ? clazz : null, (ctx, thisArg, args) -> { + return ctx.engine().typeRegister().getConstr(cl); + })); + + target.defineProperty(name, getter, null, true, false); + } + } + } + + /** + * Generates a prototype for the given class. + * The returned object will have appropriate wrappers for all instance members. + * All accessors and methods will expect the this argument to be a native wrapper of the given class type. + * @param clazz The class for which a prototype should be generated + */ public static ObjectValue makeProto(Class clazz) { var res = new ObjectValue(); applyMethods(true, res, clazz); applyFields(true, res, clazz); + applyClasses(true, res, clazz); return res; } + /** + * Generates a constructor for the given class. + * The returned function will have appropriate wrappers for all static members. + * When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible. + * @param clazz The class for which a constructor should be generated + */ public static FunctionValue makeConstructor(Class clazz) { FunctionValue func = new OverloadFunction(clazz.getName()); @@ -94,16 +124,24 @@ public class NativeTypeRegister { applyMethods(false, func, clazz); applyFields(false, func, clazz); + applyClasses(false, func, clazz); func.special = true; return func; } + /** + * Generates a namespace for the given class. + * The returned function will have appropriate wrappers for all static members. + * This method behaves almost like {@link NativeTypeRegister#makeConstructor}, but will return an object instead. + * @param clazz The class for which a constructor should be generated + */ public static ObjectValue makeNamespace(Class clazz) { ObjectValue res = new ObjectValue(); applyMethods(false, res, clazz); applyFields(false, res, clazz); + applyClasses(false, res, clazz); return res; } diff --git a/src/me/topchetoeu/jscript/interop/Overload.java b/src/me/topchetoeu/jscript/interop/Overload.java index 3131790..b37a894 100644 --- a/src/me/topchetoeu/jscript/interop/Overload.java +++ b/src/me/topchetoeu/jscript/interop/Overload.java @@ -15,7 +15,7 @@ public class Overload { IllegalArgumentException; } - public final Overload.OverloadRunner runner; + public final OverloadRunner runner; public final boolean variadic; public final Class thisArg; public final Class[] params; @@ -52,8 +52,15 @@ public class Overload { ); } + public static Overload getter(Class thisArg, OverloadRunner runner) { + return new Overload( + (ctx, th, args) -> runner.run(ctx, th, args), false, + thisArg, + new Class[0] + ); + } - public Overload(Overload.OverloadRunner runner, boolean variadic, Class thisArg, Class args[]) { + public Overload(OverloadRunner runner, boolean variadic, Class thisArg, Class args[]) { this.runner = runner; this.variadic = variadic; this.thisArg = thisArg;