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

Commit fc6ddf7

Browse files
committed
feat: allow interface proxy wrappers
1 parent 7f27509 commit fc6ddf7

2 files changed

Lines changed: 81 additions & 46 deletions

File tree

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
project_group = me.topchetoeu
22
project_name = jscript
3-
project_version = 0.9.38-beta
3+
project_version = 0.9.40-beta
44
main_class = me.topchetoeu.jscript.utils.JScriptRepl

src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java

Lines changed: 80 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import java.lang.reflect.Member;
55
import java.lang.reflect.Method;
66
import java.lang.reflect.Modifier;
7+
import java.util.ArrayList;
78
import java.util.Arrays;
89
import java.util.HashMap;
910
import java.util.HashSet;
1011
import java.util.stream.Collectors;
12+
import java.util.stream.Stream;
1113

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

3538
private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) {
@@ -83,10 +86,16 @@ private static void checkSignature(Method method, boolean forceStatic, Class<?>
8386
String.join(", ", Arrays.stream(actual).map(v -> v.getName()).collect(Collectors.toList()))
8487
));
8588
}
86-
private static String getName(Class<?> clazz) {
87-
var classNat = clazz.getAnnotation(WrapperName.class);
88-
if (classNat != null && !classNat.value().trim().equals("")) return classNat.value().trim();
89-
else return clazz.getSimpleName();
89+
private static String getName(Class<?> ...classes) {
90+
String last = null;
91+
92+
for (var clazz : classes) {
93+
var classNat = clazz.getAnnotation(WrapperName.class);
94+
if (classNat != null && !classNat.value().trim().equals("")) return classNat.value().trim();
95+
else if (last != null) last = clazz.getSimpleName();
96+
}
97+
98+
return last;
9099
}
91100

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

121130
for (var method : clazz.getDeclaredMethods()) {
122131
for (var annotation : method.getAnnotationsByType(Expose.class)) {
132+
any = true;
123133
if (!annotation.target().shouldApply(target)) continue;
124134

125135
checkUnderscore(method);
126136
var name = getName(method, annotation.value());
127137
var key = getKey(name);
128138
var repeat = false;
129-
any = true;
130139

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

167177
checkUnderscore(method);
168178
var name = getName(method, annotation.value());
169179
var key = getKey(name);
170180
var repeat = false;
171-
any = true;
172181

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

196206
checkUnderscore(field);
197207
var name = getName(field, annotation.value());
198208
var key = getKey(name);
199209
var repeat = false;
200-
any = true;
201210

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

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

241-
private static Method getConstructor(Class<?> clazz) {
242-
for (var method : clazz.getDeclaredMethods()) {
243-
if (!method.isAnnotationPresent(ExposeConstructor.class)) continue;
244-
checkSignature(method, true, Arguments.class);
245-
return method;
252+
for (var i = appliers.length - 1; i >= 0; i--) {
253+
res |= apply(obj, target, appliers[i]);
254+
}
255+
256+
return res;
257+
}
258+
259+
private static Method getConstructor(Class<?> ...appliers) {
260+
for (var clazz : appliers) {
261+
for (var method : clazz.getDeclaredMethods()) {
262+
if (!method.isAnnotationPresent(ExposeConstructor.class)) continue;
263+
checkSignature(method, true, Arguments.class);
264+
return method;
265+
}
246266
}
247267

248268
return null;
@@ -254,10 +274,10 @@ private static Method getConstructor(Class<?> clazz) {
254274
* All accessors and methods will expect the this argument to be a native wrapper of the given class type.
255275
* @param clazz The class for which a prototype should be generated
256276
*/
257-
public static ObjectValue makeProto(Class<?> clazz) {
277+
public static ObjectValue makeProto(Class<?> ...appliers) {
258278
var res = new ObjectValue();
259-
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
260-
if (!apply(res, ExposeTarget.PROTOTYPE, clazz)) return null;
279+
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(appliers));
280+
if (!apply(res, ExposeTarget.PROTOTYPE, appliers)) return null;
261281
return res;
262282
}
263283
/**
@@ -266,14 +286,14 @@ public static ObjectValue makeProto(Class<?> clazz) {
266286
* When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible.
267287
* @param clazz The class for which a constructor should be generated
268288
*/
269-
public static FunctionValue makeConstructor(Class<?> clazz) {
270-
var constr = getConstructor(clazz);
289+
public static FunctionValue makeConstructor(Class<?> ...appliers) {
290+
var constr = getConstructor(appliers);
271291

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

276-
apply(res, ExposeTarget.CONSTRUCTOR, clazz);
296+
if (constr == null && !apply(res, ExposeTarget.CONSTRUCTOR, appliers)) return null;
277297

278298
return res;
279299
}
@@ -283,13 +303,27 @@ public static FunctionValue makeConstructor(Class<?> clazz) {
283303
* This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead.
284304
* @param clazz The class for which a constructor should be generated
285305
*/
286-
public static ObjectValue makeNamespace(Class<?> clazz) {
306+
public static ObjectValue makeNamespace(Class<?> ...appliers) {
287307
var res = new ObjectValue();
288-
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
289-
if (!apply(res, ExposeTarget.NAMESPACE, clazz)) return null;
308+
309+
if (!apply(res, ExposeTarget.NAMESPACE, appliers)) return null;
310+
290311
return res;
291312
}
292313

314+
private Class<?>[] getAppliers(Class<?> clazz) {
315+
var res = new ArrayList<Class<?>>();
316+
317+
res.add(clazz);
318+
319+
if (classToProxy.containsKey(clazz)) res.add(classToProxy.get(clazz));
320+
for (var intf : interfaceToProxy.keySet()) {
321+
if (intf.isAssignableFrom(clazz)) res.add(interfaceToProxy.get(intf));
322+
}
323+
324+
return res.toArray(Class<?>[]::new);
325+
}
326+
293327
private void updateProtoChain(Class<?> clazz, ObjectValue proto, FunctionValue constr) {
294328
var parent = clazz;
295329

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

329-
if (constr == null) constr = makeConstructor(clazz);
330-
if (proto == null) proto = makeProto(clazz);
363+
var appliers = getAppliers(clazz);
364+
365+
if (constr == null) constr = makeConstructor(appliers);
366+
if (proto == null) proto = makeProto(appliers);
331367

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

@@ -386,30 +422,29 @@ public NativeWrapperProvider copy() {
386422

387423
public void set(Class<?> clazz, Class<?> wrapper) {
388424
if (clazz == null) return;
389-
if (wrapper == null) wrapper = clazz;
390-
if (classToProxy.get(clazz) == wrapper) return;
391-
392-
classToProxy.remove(wrapper);
393-
proxyToClass.remove(clazz);
394-
395-
classToProxy.put(clazz, wrapper);
396-
proxyToClass.put(wrapper, clazz);
397425

398-
ignore.remove(clazz);
399-
400-
var proto = makeProto(wrapper);
401-
var constr = makeConstructor(wrapper);
426+
if (clazz.isInterface()) {
427+
if (wrapper == null || wrapper == clazz) interfaceToProxy.remove(clazz);
428+
else interfaceToProxy.put(clazz, wrapper);
429+
}
430+
else {
431+
if (wrapper == null || wrapper == clazz) classToProxy.remove(clazz);
432+
else classToProxy.put(clazz, wrapper);
433+
}
402434

403-
prototypes.put(clazz, proto);
404-
constructors.put(clazz, constr);
435+
var classes = Stream.concat(
436+
Stream.of(clazz),
437+
prototypes.keySet().stream().filter(clazz::isAssignableFrom)
438+
).toArray(Class<?>[]::new);
405439

406-
proto.defineProperty(null, "constructor", constr, true, false, false);
407-
constr.defineProperty(null, "prototype", proto, true, false, false);
440+
for (var el : classes) {
441+
prototypes.remove(el);
442+
constructors.remove(el);
443+
namespaces.remove(el);
444+
}
408445

409-
for (var el : prototypes.keySet()) {
410-
if (clazz.isAssignableFrom(el)) {
411-
updateProtoChain(el, getProto(el), getConstr(el));
412-
}
446+
for (var el : classes) {
447+
initType(el, null, null);
413448
}
414449
}
415450

0 commit comments

Comments
 (0)