Core library reprogramming #5

Merged
TopchetoEU merged 23 commits from TopchetoEU/corelib-reprogramming into master 2023-10-04 05:50:26 +00:00
10 changed files with 84 additions and 125 deletions
Showing only changes of commit 0b7442a3d8 - Show all commits

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -13,23 +13,15 @@ public class NativeTypeRegister implements WrappersProvider {
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
private final HashMap<Class<?>, 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) {

View File

@ -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.");
}
}
}

View File

@ -18,14 +18,8 @@ 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;
}
else {
boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class;
int start = consumesEngine ? 1 : 0;
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++) {
@ -61,10 +55,11 @@ public class OverloadFunction extends FunctionValue {
newArgs[newArgs.length - 1] = varArg;
}
if (consumesEngine) newArgs[0] = ctx;
}
var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg;
Object _this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
Object _this = overload.thisArg == null ? null : Values.convert(ctx, thisArg, overload.thisArg);
if (consumesEngine) newArgs[0] = ctx;
if (overload.passThis) newArgs[consumesEngine ? 1 : 0] = _this;
try {
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));

View File

@ -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 (...) { ... }";
}

View File

@ -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 {

View File

@ -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);
})
);
}