Skip to content
This repository has been archived by the owner on Dec 31, 2024. It is now read-only.

Commit

Permalink
feat: allow interface proxy wrappers
Browse files Browse the repository at this point in the history
  • Loading branch information
TopchetoEU committed Apr 12, 2024
1 parent 7f27509 commit fc6ddf7
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 46 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project_group = me.topchetoeu
project_name = jscript
project_version = 0.9.38-beta
project_version = 0.9.40-beta
main_class = me.topchetoeu.jscript.utils.JScriptRepl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.runtime.Context;
Expand All @@ -30,6 +32,7 @@ public class NativeWrapperProvider implements Copyable {
private final HashMap<Class<?>, ObjectValue> namespaces = new HashMap<>();
private final HashMap<Class<?>, Class<?>> classToProxy = new HashMap<>();
private final HashMap<Class<?>, Class<?>> proxyToClass = new HashMap<>();
private final HashMap<Class<?>, Class<?>> interfaceToProxy = new HashMap<>();
private final HashSet<Class<?>> ignore = new HashSet<>();

private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) {
Expand Down Expand Up @@ -83,10 +86,16 @@ private static void checkSignature(Method method, boolean forceStatic, Class<?>
String.join(", ", Arrays.stream(actual).map(v -> v.getName()).collect(Collectors.toList()))
));
}
private static String getName(Class<?> clazz) {
var classNat = clazz.getAnnotation(WrapperName.class);
if (classNat != null && !classNat.value().trim().equals("")) return classNat.value().trim();
else return clazz.getSimpleName();
private static String getName(Class<?> ...classes) {
String last = null;

for (var clazz : classes) {
var classNat = clazz.getAnnotation(WrapperName.class);
if (classNat != null && !classNat.value().trim().equals("")) return classNat.value().trim();
else if (last != null) last = clazz.getSimpleName();
}

return last;
}

private static void checkUnderscore(Member member) {
Expand Down Expand Up @@ -120,13 +129,13 @@ private static boolean apply(ObjectValue obj, ExposeTarget target, Class<?> claz

for (var method : clazz.getDeclaredMethods()) {
for (var annotation : method.getAnnotationsByType(Expose.class)) {
any = true;
if (!annotation.target().shouldApply(target)) continue;

checkUnderscore(method);
var name = getName(method, annotation.value());
var key = getKey(name);
var repeat = false;
any = true;

switch (annotation.type()) {
case METHOD:
Expand Down Expand Up @@ -162,13 +171,13 @@ private static boolean apply(ObjectValue obj, ExposeTarget target, Class<?> claz
));
}
for (var annotation : method.getAnnotationsByType(ExposeField.class)) {
any = true;
if (!annotation.target().shouldApply(target)) continue;

checkUnderscore(method);
var name = getName(method, annotation.value());
var key = getKey(name);
var repeat = false;
any = true;

if (props.contains(key) || nonProps.contains(key)) repeat = true;
else {
Expand All @@ -191,13 +200,13 @@ private static boolean apply(ObjectValue obj, ExposeTarget target, Class<?> claz
}
for (var field : clazz.getDeclaredFields()) {
for (var annotation : field.getAnnotationsByType(ExposeField.class)) {
any = true;
if (!annotation.target().shouldApply(target)) continue;

checkUnderscore(field);
var name = getName(field, annotation.value());
var key = getKey(name);
var repeat = false;
any = true;

if (props.contains(key) || nonProps.contains(key)) repeat = true;
else {
Expand Down Expand Up @@ -237,12 +246,23 @@ private static boolean apply(ObjectValue obj, ExposeTarget target, Class<?> claz

return any;
}
private static boolean apply(ObjectValue obj, ExposeTarget target, Class<?> ...appliers) {
var res = false;

private static Method getConstructor(Class<?> clazz) {
for (var method : clazz.getDeclaredMethods()) {
if (!method.isAnnotationPresent(ExposeConstructor.class)) continue;
checkSignature(method, true, Arguments.class);
return method;
for (var i = appliers.length - 1; i >= 0; i--) {
res |= apply(obj, target, appliers[i]);
}

return res;
}

private static Method getConstructor(Class<?> ...appliers) {
for (var clazz : appliers) {
for (var method : clazz.getDeclaredMethods()) {
if (!method.isAnnotationPresent(ExposeConstructor.class)) continue;
checkSignature(method, true, Arguments.class);
return method;
}
}

return null;
Expand All @@ -254,10 +274,10 @@ private static Method getConstructor(Class<?> clazz) {
* 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) {
public static ObjectValue makeProto(Class<?> ...appliers) {
var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
if (!apply(res, ExposeTarget.PROTOTYPE, clazz)) return null;
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(appliers));
if (!apply(res, ExposeTarget.PROTOTYPE, appliers)) return null;
return res;
}
/**
Expand All @@ -266,14 +286,14 @@ public static ObjectValue makeProto(Class<?> clazz) {
* 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) {
var constr = getConstructor(clazz);
public static FunctionValue makeConstructor(Class<?> ...appliers) {
var constr = getConstructor(appliers);

FunctionValue res = constr == null ?
new NativeFunction(getName(clazz), args -> { throw EngineException.ofError("This constructor is not invokable."); }) :
create(getName(clazz), constr);
new NativeFunction(getName(appliers), args -> { throw EngineException.ofError("This constructor is not invokable."); }) :
create(getName(appliers), constr);

apply(res, ExposeTarget.CONSTRUCTOR, clazz);
if (constr == null && !apply(res, ExposeTarget.CONSTRUCTOR, appliers)) return null;

return res;
}
Expand All @@ -283,13 +303,27 @@ public static FunctionValue makeConstructor(Class<?> clazz) {
* This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead.
* @param clazz The class for which a constructor should be generated
*/
public static ObjectValue makeNamespace(Class<?> clazz) {
public static ObjectValue makeNamespace(Class<?> ...appliers) {
var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
if (!apply(res, ExposeTarget.NAMESPACE, clazz)) return null;

if (!apply(res, ExposeTarget.NAMESPACE, appliers)) return null;

return res;
}

private Class<?>[] getAppliers(Class<?> clazz) {
var res = new ArrayList<Class<?>>();

res.add(clazz);

if (classToProxy.containsKey(clazz)) res.add(classToProxy.get(clazz));
for (var intf : interfaceToProxy.keySet()) {
if (intf.isAssignableFrom(clazz)) res.add(interfaceToProxy.get(intf));
}

return res.toArray(Class<?>[]::new);
}

private void updateProtoChain(Class<?> clazz, ObjectValue proto, FunctionValue constr) {
var parent = clazz;

Expand Down Expand Up @@ -326,8 +360,10 @@ private void initType(Class<?> clazz, FunctionValue constr, ObjectValue proto) {
clazz.isSynthetic()
) return;

if (constr == null) constr = makeConstructor(clazz);
if (proto == null) proto = makeProto(clazz);
var appliers = getAppliers(clazz);

if (constr == null) constr = makeConstructor(appliers);
if (proto == null) proto = makeProto(appliers);

if (constr == null || proto == null) return;

Expand Down Expand Up @@ -386,30 +422,29 @@ public NativeWrapperProvider copy() {

public void set(Class<?> clazz, Class<?> wrapper) {
if (clazz == null) return;
if (wrapper == null) wrapper = clazz;
if (classToProxy.get(clazz) == wrapper) return;

classToProxy.remove(wrapper);
proxyToClass.remove(clazz);

classToProxy.put(clazz, wrapper);
proxyToClass.put(wrapper, clazz);

ignore.remove(clazz);

var proto = makeProto(wrapper);
var constr = makeConstructor(wrapper);
if (clazz.isInterface()) {
if (wrapper == null || wrapper == clazz) interfaceToProxy.remove(clazz);
else interfaceToProxy.put(clazz, wrapper);
}
else {
if (wrapper == null || wrapper == clazz) classToProxy.remove(clazz);
else classToProxy.put(clazz, wrapper);
}

prototypes.put(clazz, proto);
constructors.put(clazz, constr);
var classes = Stream.concat(
Stream.of(clazz),
prototypes.keySet().stream().filter(clazz::isAssignableFrom)
).toArray(Class<?>[]::new);

proto.defineProperty(null, "constructor", constr, true, false, false);
constr.defineProperty(null, "prototype", proto, true, false, false);
for (var el : classes) {
prototypes.remove(el);
constructors.remove(el);
namespaces.remove(el);
}

for (var el : prototypes.keySet()) {
if (clazz.isAssignableFrom(el)) {
updateProtoChain(el, getProto(el), getConstr(el));
}
for (var el : classes) {
initType(el, null, null);
}
}

Expand Down

0 comments on commit fc6ddf7

Please sign in to comment.