From 0b7442a3d8bd08321cadeb17167ddbcecb8a383f Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:50:03 +0300 Subject: [PATCH] fix: remove raw native funcs, use this arg passing instead --- src/me/topchetoeu/jscript/interop/Native.java | 2 +- .../jscript/interop/NativeConstructor.java | 2 +- .../jscript/interop/NativeGetter.java | 2 +- .../jscript/interop/NativeSetter.java | 2 +- .../jscript/interop/NativeTypeRegister.java | 30 ++++----- .../topchetoeu/jscript/interop/Overload.java | 34 ++++------ .../jscript/interop/OverloadFunction.java | 65 +++++++++---------- .../jscript/polyfills/FunctionPolyfill.java | 39 ++++------- .../jscript/polyfills/ObjectPolyfill.java | 8 +-- .../jscript/polyfills/PromisePolyfill.java | 25 +++---- 10 files changed, 84 insertions(+), 125 deletions(-) diff --git a/src/me/topchetoeu/jscript/interop/Native.java b/src/me/topchetoeu/jscript/interop/Native.java index c71992c..b0465a6 100644 --- a/src/me/topchetoeu/jscript/interop/Native.java +++ b/src/me/topchetoeu/jscript/interop/Native.java @@ -9,5 +9,5 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) public @interface Native { public String value() default ""; - public boolean raw() default false; + public boolean thisArg() default false; } diff --git a/src/me/topchetoeu/jscript/interop/NativeConstructor.java b/src/me/topchetoeu/jscript/interop/NativeConstructor.java index 5603a77..e9aa462 100644 --- a/src/me/topchetoeu/jscript/interop/NativeConstructor.java +++ b/src/me/topchetoeu/jscript/interop/NativeConstructor.java @@ -8,6 +8,6 @@ import java.lang.annotation.Target; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface NativeConstructor { - public boolean raw() default false; + public boolean thisArg() default false; } diff --git a/src/me/topchetoeu/jscript/interop/NativeGetter.java b/src/me/topchetoeu/jscript/interop/NativeGetter.java index 5e94a5e..62c9061 100644 --- a/src/me/topchetoeu/jscript/interop/NativeGetter.java +++ b/src/me/topchetoeu/jscript/interop/NativeGetter.java @@ -9,5 +9,5 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) public @interface NativeGetter { public String value(); - public boolean raw() default false; + public boolean thisArg() default false; } diff --git a/src/me/topchetoeu/jscript/interop/NativeSetter.java b/src/me/topchetoeu/jscript/interop/NativeSetter.java index b460831..33b7d73 100644 --- a/src/me/topchetoeu/jscript/interop/NativeSetter.java +++ b/src/me/topchetoeu/jscript/interop/NativeSetter.java @@ -9,5 +9,5 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) public @interface NativeSetter { public String value(); - public boolean raw() default false; + public boolean thisArg() default false; } diff --git a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java index f927415..3e90161 100644 --- a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java +++ b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java @@ -13,23 +13,15 @@ public class NativeTypeRegister implements WrappersProvider { private final HashMap, FunctionValue> constructors = new HashMap<>(); private final HashMap, ObjectValue> prototypes = new HashMap<>(); - private static boolean isMember(Class[] args) { - return args.length == 3; - } - private static void applyMethods(boolean member, ObjectValue target, Class clazz) { for (var method : clazz.getDeclaredMethods()) { var nat = method.getAnnotation(Native.class); var get = method.getAnnotation(NativeGetter.class); var set = method.getAnnotation(NativeSetter.class); - var params = method.getParameterTypes(); var memberMismatch = !Modifier.isStatic(method.getModifiers()) != member; if (nat != null) { - if (nat.raw()) { - if (isMember(params) != member) continue; - } - else if (memberMismatch) continue; + if (nat.thisArg() != member && memberMismatch) continue; var name = nat.value(); var val = target.values.get(name); @@ -37,11 +29,12 @@ public class NativeTypeRegister implements WrappersProvider { if (name.equals("")) name = method.getName(); if (!(val instanceof OverloadFunction)) target.defineProperty(null, name, val = new OverloadFunction(name)); - ((OverloadFunction)val).overloads.add(Overload.fromMethod(method, nat.raw())); + ((OverloadFunction)val).overloads.add(Overload.fromMethod(method, nat.thisArg())); } else { if (get != null) { - if (get.raw() && isMember(params) != member || memberMismatch) continue; + if (get.thisArg() != member && memberMismatch) continue; + var name = get.value(); var prop = target.properties.get(name); OverloadFunction getter = null; @@ -50,11 +43,12 @@ public class NativeTypeRegister implements WrappersProvider { if (prop != null && prop.getter instanceof OverloadFunction) getter = (OverloadFunction)prop.getter; else getter = new OverloadFunction("get " + name); - getter.overloads.add(Overload.fromMethod(method, get.raw())); + getter.overloads.add(Overload.fromMethod(method, get.thisArg())); target.defineProperty(null, name, getter, setter, true, true); } if (set != null) { - if (set.raw() && isMember(params) != member || memberMismatch) continue; + if (set.thisArg() != member && memberMismatch) continue; + var name = set.value(); var prop = target.properties.get(name); var getter = prop == null ? null : prop.getter; @@ -63,7 +57,7 @@ public class NativeTypeRegister implements WrappersProvider { if (prop != null && prop.setter instanceof OverloadFunction) setter = (OverloadFunction)prop.setter; else setter = new OverloadFunction("set " + name); - setter.overloads.add(Overload.fromMethod(method, set.raw())); + setter.overloads.add(Overload.fromMethod(method, set.thisArg())); target.defineProperty(null, name, getter, setter, true, true); } } @@ -77,8 +71,8 @@ public class NativeTypeRegister implements WrappersProvider { if (nat != null) { var name = nat.value(); if (name.equals("")) name = field.getName(); - var getter = new OverloadFunction("get " + name).add(Overload.getterFromField(field, nat.raw())); - var setter = new OverloadFunction("set " + name).add(Overload.setterFromField(field, nat.raw())); + var getter = new OverloadFunction("get " + name).add(Overload.getterFromField(field)); + var setter = new OverloadFunction("set " + name).add(Overload.setterFromField(field)); target.defineProperty(null, name, getter, setter, true, false); } } @@ -126,12 +120,12 @@ public class NativeTypeRegister implements WrappersProvider { for (var overload : clazz.getConstructors()) { var nat = overload.getAnnotation(Native.class); if (nat == null) continue; - ((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.raw())); + ((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.thisArg())); } for (var overload : clazz.getMethods()) { var constr = overload.getAnnotation(NativeConstructor.class); if (constr == null) continue; - ((OverloadFunction)func).add(Overload.fromMethod(overload, constr.raw())); + ((OverloadFunction)func).add(Overload.fromMethod(overload, constr.thisArg())); } if (((OverloadFunction)func).overloads.size() == 0) { diff --git a/src/me/topchetoeu/jscript/interop/Overload.java b/src/me/topchetoeu/jscript/interop/Overload.java index 740fe99..b325aeb 100644 --- a/src/me/topchetoeu/jscript/interop/Overload.java +++ b/src/me/topchetoeu/jscript/interop/Overload.java @@ -17,63 +17,55 @@ public class Overload { public final OverloadRunner runner; public final boolean variadic; - public final boolean raw; + public final boolean passThis; public final Class thisArg; public final Class[] params; - public static Overload fromMethod(Method method, boolean raw) { + public static Overload fromMethod(Method method, boolean passThis) { return new Overload( (ctx, th, args) -> method.invoke(th, args), - method.isVarArgs(), raw, + method.isVarArgs(), passThis, Modifier.isStatic(method.getModifiers()) ? null : method.getDeclaringClass(), method.getParameterTypes() ); } - public static Overload fromConstructor(Constructor method, boolean raw) { + public static Overload fromConstructor(Constructor method, boolean passThis) { return new Overload( (ctx, th, args) -> method.newInstance(args), - method.isVarArgs(), raw, + method.isVarArgs(), passThis, Modifier.isStatic(method.getModifiers()) ? null : method.getDeclaringClass(), method.getParameterTypes() ); } - public static Overload getterFromField(Field field, boolean raw) { + public static Overload getterFromField(Field field) { return new Overload( - (ctx, th, args) -> field.get(th), false, raw, + (ctx, th, args) -> field.get(th), false, false, Modifier.isStatic(field.getModifiers()) ? null : field.getDeclaringClass(), new Class[0] ); } - public static Overload setterFromField(Field field, boolean raw) { + public static Overload setterFromField(Field field) { if (Modifier.isFinal(field.getModifiers())) return null; return new Overload( - (ctx, th, args) -> { field.set(th, args[0]); return null; }, false, raw, + (ctx, th, args) -> { field.set(th, args[0]); return null; }, false, false, Modifier.isStatic(field.getModifiers()) ? null : field.getDeclaringClass(), new Class[0] ); } - public static Overload getter(Class thisArg, OverloadRunner runner, boolean raw) { + public static Overload getter(Class thisArg, OverloadRunner runner, boolean passThis) { return new Overload( - (ctx, th, args) -> runner.run(ctx, th, args), false, raw, + (ctx, th, args) -> runner.run(ctx, th, args), false, passThis, thisArg, new Class[0] ); } - public Overload(OverloadRunner runner, boolean variadic, boolean raw, Class thisArg, Class args[]) { + public Overload(OverloadRunner runner, boolean variadic, boolean passThis, Class thisArg, Class args[]) { this.runner = runner; this.variadic = variadic; - this.raw = raw; + this.passThis = passThis; this.thisArg = thisArg; this.params = args; - - if (raw) { - if (!( - thisArg == null && ( - args.length == 3 && args[0] == Context.class && args[1] == Object.class && args[2] == Object[].class || - args.length == 2 && args[0] == Context.class && args[1] == Object[].class - ))) throw new IllegalArgumentException("Invalid signature for raw method."); - } } } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/interop/OverloadFunction.java b/src/me/topchetoeu/jscript/interop/OverloadFunction.java index 89a66d9..0954302 100644 --- a/src/me/topchetoeu/jscript/interop/OverloadFunction.java +++ b/src/me/topchetoeu/jscript/interop/OverloadFunction.java @@ -18,53 +18,48 @@ public class OverloadFunction extends FunctionValue { loop: for (var overload : overloads) { Object[] newArgs = new Object[overload.params.length]; - if (overload.raw) { - newArgs[0] = ctx; - newArgs[1] = thisArg; - newArgs[2] = args; + boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class; + int start = (consumesEngine ? 1 : 0) + (overload.passThis ? 1 : 0); + int end = overload.params.length - (overload.variadic ? 1 : 0); + + for (var i = start; i < end; i++) { + Object val; + + if (i - start >= args.length) val = null; + else val = args[i - start]; + + try { + newArgs[i] = Values.convert(ctx, val, overload.params[i]); + } + catch (ConvertException e) { + if (overloads.size() > 1) continue loop; + else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target)); + } } - else { - boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class; - int start = consumesEngine ? 1 : 0; - int end = overload.params.length - (overload.variadic ? 1 : 0); - for (var i = start; i < end; i++) { - Object val; - - if (i - start >= args.length) val = null; - else val = args[i - start]; + if (overload.variadic) { + var type = overload.params[overload.params.length - 1].getComponentType(); + var n = Math.max(args.length - end + start, 0); + Object varArg = Array.newInstance(type, n); + for (var i = 0; i < n; i++) { try { - newArgs[i] = Values.convert(ctx, val, overload.params[i]); + Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type)); } catch (ConvertException e) { if (overloads.size() > 1) continue loop; - else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target)); + else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target)); } } - if (overload.variadic) { - var type = overload.params[overload.params.length - 1].getComponentType(); - var n = Math.max(args.length - end + start, 0); - Object varArg = Array.newInstance(type, n); - - for (var i = 0; i < n; i++) { - try { - Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type)); - } - catch (ConvertException e) { - if (overloads.size() > 1) continue loop; - else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target)); - } - } - - newArgs[newArgs.length - 1] = varArg; - } - - if (consumesEngine) newArgs[0] = ctx; + newArgs[newArgs.length - 1] = varArg; } - Object _this = overload.thisArg == null ? null : Values.convert(ctx, thisArg, overload.thisArg); + var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg; + Object _this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType); + + if (consumesEngine) newArgs[0] = ctx; + if (overload.passThis) newArgs[consumesEngine ? 1 : 0] = _this; try { return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs)); diff --git a/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java b/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java index 89253d0..cec1917 100644 --- a/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java @@ -8,43 +8,26 @@ import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.interop.Native; public class FunctionPolyfill { - @Native(raw=true) public static Object apply(Context ctx, Object func, Object[] args) throws InterruptedException { - var thisArg = args.length > 0 ? args[0] : null; - var _args = args.length > 1 ? args[1] : null; - - if (!(_args instanceof ArrayValue)) throw EngineException.ofError("Expected arguments to be an array."); - if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function."); - - return ((FunctionValue)func).call(ctx, thisArg, ((ArrayValue)_args).toArray()); + @Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException { + return func.call(ctx, thisArg, args.toArray()); } - @Native(raw=true) public static Object call(Context ctx, Object func, Object[] args) throws InterruptedException { - var thisArg = args.length > 0 ? args[0] : null; - var _args = new Object[args.length > 1 ? args.length - 1 : 0]; - + @Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException { if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function."); - if (_args.length != 0) System.arraycopy(args, 1, _args, 0, _args.length); - - return ((FunctionValue)func).call(ctx, thisArg, _args); + return func.call(ctx, thisArg, args); } - @Native(raw=true) public static Object bind(Context ctx, Object func, Object[] args) { - var thisArg = args.length > 0 ? args[0] : null; - var _args = new Object[args.length > 1 ? args.length - 1 : 0]; - FunctionValue _func = (FunctionValue)func; - + @Native(thisArg = true) public static Object bind(Context ctx, FunctionValue func, Object thisArg, Object... args) { if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function."); - if (_args.length != 0) System.arraycopy(args, 1, _args, 0, _args.length); + return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> { + var resArgs = new Object[args.length + callArgs.length]; + System.arraycopy(args, 0, resArgs, 0, args.length); + System.arraycopy(callArgs, 0, resArgs, args.length, callArgs.length); - return new NativeFunction(_func.name + " (bound)", (callCtx, _0, callArgs) -> { - var resArgs = new Object[_args.length + callArgs.length]; - System.arraycopy(_args, 0, resArgs, 0, _args.length); - System.arraycopy(callArgs, 0, resArgs, _args.length, resArgs.length - _args.length); - - return _func.call(ctx, thisArg, resArgs); + return func.call(ctx, thisArg, resArgs); }); } - @Native(raw=true) public static String toString(Context ctx, Object func, Object[] args) { + @Native(thisArg = true) public static String toString(Context ctx, Object func) { return "function (...) { ... }"; } diff --git a/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java b/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java index 10226a4..0c15f92 100644 --- a/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java @@ -187,18 +187,18 @@ public class ObjectPolyfill { return true; } - @Native(raw = true) public static Object valueOf(Context ctx, Object thisArg, Object[] args) { + @Native(thisArg = true) public static Object valueOf(Context ctx, Object thisArg) { return thisArg; } - @Native(raw = true) public static String toString(Context ctx, Object thisArg, Object[] args) throws InterruptedException { + @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException { var name = Values.getMember(ctx, thisArg, ctx.env.symbol("Symbol.typeName")); if (name == null) name = "Unknown"; else name = Values.toString(ctx, name); return "[object " + name + "]"; } - @Native(raw = true) public static boolean hasOwnProperty(Context ctx, Object thisArg, Object[] args) throws InterruptedException { - return ObjectPolyfill.hasOwn(ctx, thisArg, Values.convert(ctx, args.length == 0 ? null : args[0], String.class)); + @Native(thisArg = true) public static boolean hasOwnProperty(Context ctx, Object thisArg, Object key) throws InterruptedException { + return ObjectPolyfill.hasOwn(ctx, thisArg, Values.convert(ctx, key, String.class)); } @NativeConstructor public static Object constructor(Context ctx, Object arg) throws InterruptedException { diff --git a/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java b/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java index a640572..dfffc22 100644 --- a/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java @@ -152,12 +152,9 @@ public class PromisePolyfill { * Thread safe - you can call this from anywhere * HOWEVER, it's strongly recommended to use this only in javascript */ - @Native(raw = true) public static Object then(Context ctx, Object thisArg, Object ...args) throws InterruptedException { - var onFulfill = args.length > 0 ? args[0] : null; - var onReject = args.length > 1 ? args[1]: null; - - if (!(onFulfill instanceof FunctionValue)) onFulfill = null; - if (!(onReject instanceof FunctionValue)) onReject = null; + @Native(thisArg=true) public static Object then(Context ctx, Object thisArg, Object _onFulfill, Object _onReject) throws InterruptedException { + var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null; + var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null; var res = new PromisePolyfill(); @@ -206,24 +203,22 @@ public class PromisePolyfill { * Thread safe - you can call this from anywhere * HOWEVER, it's strongly recommended to use this only in javascript */ - @Native(value = "catch", raw = true) public static Object _catch(Context ctx, Object thisArg, Object ...args) throws InterruptedException { - return then(ctx, thisArg, null, args.length > 0 ? args[0] : null); + @Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) throws InterruptedException { + return then(ctx, thisArg, null, _onReject); } /** * Thread safe - you can call this from anywhere * HOWEVER, it's strongly recommended to use this only in javascript */ - @Native(value = "finally", raw = true) public static Object _finally(Context ctx, Object thisArg, Object ...args) throws InterruptedException { - var handleFunc = args.length > 0 ? args[0] : null; - + @Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) throws InterruptedException { return then(ctx, thisArg, new NativeFunction(null, (e, th, _args) -> { - if (handleFunc instanceof FunctionValue) ((FunctionValue)handleFunc).call(ctx); - return args[0]; + if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx); + return _args.length > 0 ? _args[0] : null; }), new NativeFunction(null, (e, th, _args) -> { - if (handleFunc instanceof FunctionValue) ((FunctionValue)handleFunc).call(ctx); - throw new EngineException(_args[0]); + if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx); + throw new EngineException(_args.length > 0 ? _args[0] : null); }) ); }