44import java .lang .reflect .Member ;
55import java .lang .reflect .Method ;
66import java .lang .reflect .Modifier ;
7+ import java .util .ArrayList ;
78import java .util .Arrays ;
89import java .util .HashMap ;
910import java .util .HashSet ;
1011import java .util .stream .Collectors ;
12+ import java .util .stream .Stream ;
1113
1214import me .topchetoeu .jscript .common .Location ;
1315import 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