2
2
3
3
import java .lang .reflect .Constructor ;
4
4
import java .lang .reflect .Method ;
5
+ import java .lang .reflect .Modifier ;
5
6
import java .util .Arrays ;
7
+
6
8
import org .jruby .Ruby ;
7
9
import org .jruby .RubyArray ;
10
+ import org .jruby .RubyBoolean ;
8
11
import org .jruby .RubyClass ;
9
12
import org .jruby .RubyInstanceConfig ;
10
13
import org .jruby .RubyModule ;
11
14
import org .jruby .anno .JRubyMethod ;
12
15
import org .jruby .exceptions .RaiseException ;
13
16
import org .jruby .internal .runtime .methods .DynamicMethod ;
17
+ import org .jruby .internal .runtime .methods .JavaMethod ;
14
18
import org .jruby .internal .runtime .methods .JavaMethod .JavaMethodN ;
15
19
import org .jruby .internal .runtime .methods .JavaMethod .JavaMethodOne ;
16
20
import org .jruby .internal .runtime .methods .JavaMethod .JavaMethodOneBlock ;
@@ -258,6 +262,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
258
262
public static void addRealImplClassNew (final RubyClass clazz ) {
259
263
clazz .setAllocator (new ObjectAllocator () {
260
264
private Constructor proxyConstructor ;
265
+
261
266
public IRubyObject allocate (Ruby runtime , RubyClass klazz ) {
262
267
// if we haven't been here before, reify the class
263
268
Class reifiedClass = klazz .getReifiedClass ();
@@ -342,33 +347,68 @@ public static IRubyObject op_aref(ThreadContext context, IRubyObject self, IRuby
342
347
return JavaProxy .op_aref (context , self , args );
343
348
}
344
349
345
- @ JRubyMethod (name = "impl" , rest = true )
350
+ @ JRubyMethod (name = "impl" , rest = true ) // impl(methods = true)
346
351
public static IRubyObject impl (ThreadContext context , IRubyObject self , IRubyObject [] args , final Block implBlock ) {
347
352
final Ruby runtime = context .runtime ;
348
353
349
354
if ( ! implBlock .isGiven () ) {
350
355
throw runtime .newArgumentError ("block required to call #impl on a Java interface" );
351
356
}
352
357
358
+ boolean allMethods = true ;
353
359
final IRubyObject [] methodNames ;
354
360
if ( args .length == 0 ) methodNames = null ;
361
+ else if ( args .length == 1 && args [0 ] instanceof RubyBoolean ) {
362
+ allMethods = args [0 ].isTrue (); // impl(false) ... allMethods = false
363
+ methodNames = null ;
364
+ }
355
365
else {
356
366
methodNames = args .clone ();
357
367
Arrays .sort (methodNames ); // binarySearch needs a sorted array
358
368
// RubySymbol implements a Java compareTo thus will always work
359
369
}
360
370
361
- RubyClass implClass = RubyClass .newClass (runtime , runtime .getObject ());
362
- implClass .include (context , self );
363
-
364
- final IRubyObject implObject = implClass .callMethod (context , "new" );
365
-
366
- implClass .addMethod ("method_missing" , new BlockInterfaceImpl (implClass , implBlock , methodNames ));
371
+ RubyClass implClass = RubyClass .newClass (runtime , runtime .getObject ()); // ImplClass = Class.new
372
+ implClass .include (context , self ); // ImplClass.include Interface
373
+
374
+ final BlockInterfaceImpl ifaceImpl = new BlockInterfaceImpl (implClass , implBlock , methodNames );
375
+ implClass .addMethod ("method_missing" , ifaceImpl ); // def ImplClass.method_missing ...
376
+
377
+ final Class <?> ifaceClass = JavaClass .getJavaClass (context , ((RubyModule ) self ));
378
+ if ( methodNames == null ) {
379
+ final BlockInterfaceImpl .ConcreteMethod implMethod = ifaceImpl .getConcreteMethod ();
380
+ for ( Method method : ifaceClass .getMethods () ) {
381
+ if ( method .isBridge () || method .isSynthetic () ) continue ;
382
+ if ( Modifier .isStatic ( method .getModifiers () ) ) continue ;
383
+ // override default methods (by default) - users should pass down method names or impl(false) { ... }
384
+ if ( ! allMethods && ! Modifier .isAbstract ( method .getModifiers () ) ) continue ;
385
+ implClass .addMethodInternal (method .getName (), implMethod ); // might add twice - its fine
386
+ }
387
+ }
388
+ else {
389
+ final BlockInterfaceImpl .ConcreteMethod implMethod = ifaceImpl .getConcreteMethod ();
390
+ final Method [] decMethods = ifaceClass .getDeclaredMethods ();
391
+ loop : for ( IRubyObject methodName : methodNames ) {
392
+ final String name = methodName .toString ();
393
+ for ( int i = 0 ; i < decMethods .length ; i ++ ) {
394
+ final Method method = decMethods [i ];
395
+ if ( method .isBridge () || method .isSynthetic () ) continue ;
396
+ if ( Modifier .isStatic ( method .getModifiers () ) ) continue ;
397
+ // add if its a declared method of the interface or its super-interfaces
398
+ if ( name .equals (decMethods [i ].getName ()) ) {
399
+ implClass .addMethodInternal (name , implMethod );
400
+ continue loop ;
401
+ }
402
+ }
403
+ // did not continue (main) loop - passed method name not found in interface
404
+ runtime .getWarnings ().warn ("`" + name + "' is not a declared method in interface " + ifaceClass .getName ());
405
+ }
406
+ }
367
407
368
- return implObject ;
408
+ return implClass . callMethod ( context , "new" ); // ImplClass.new
369
409
}
370
410
371
- private static final class BlockInterfaceImpl extends org . jruby . internal . runtime . methods . JavaMethod {
411
+ private static final class BlockInterfaceImpl extends JavaMethod {
372
412
373
413
private final IRubyObject [] methodNames ; // RubySymbol[]
374
414
private final Block implBlock ;
@@ -398,6 +438,11 @@ else if ( Arrays.binarySearch(methodNames, args[0]) >= 0 ) {
398
438
return clazz .getSuperClass ().callMethod (context , "method_missing" , args , block );
399
439
}
400
440
441
+ @ Override
442
+ public final IRubyObject call (ThreadContext context , IRubyObject self , RubyModule klazz , String name , Block block ) {
443
+ return callImpl (context , klazz , block ); // avoids checkArgumentCount
444
+ }
445
+
401
446
@ Override
402
447
public final IRubyObject call (ThreadContext context , IRubyObject self , RubyModule klazz , String name , IRubyObject arg0 , Block block ) {
403
448
return callImpl (context , klazz , block , arg0 ); // avoids checkArgumentCount
@@ -413,7 +458,60 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul
413
458
return callImpl (context , klazz , block , arg0 , arg1 , arg2 ); // avoids checkArgumentCount
414
459
}
415
460
416
- //public DynamicMethod dup() { return this; }
461
+ public DynamicMethod dup () { return this ; }
462
+
463
+ final ConcreteMethod getConcreteMethod () { return new ConcreteMethod (); }
464
+
465
+ private final class ConcreteMethod extends JavaMethod {
466
+
467
+ ConcreteMethod () {
468
+ super (BlockInterfaceImpl .this .implementationClass , Visibility .PUBLIC );
469
+ }
470
+
471
+ @ Override
472
+ public IRubyObject call (ThreadContext context , IRubyObject self , RubyModule klazz , String name , Block block ) {
473
+ final IRubyObject [] nargs = new IRubyObject [] { context .runtime .newSymbol (name ) };
474
+ return BlockInterfaceImpl .this .callImpl (context , klazz , block , nargs );
475
+ }
476
+
477
+ @ Override
478
+ public IRubyObject call (ThreadContext context , IRubyObject self , RubyModule klazz , String name , IRubyObject arg0 , Block block ) {
479
+ final IRubyObject [] nargs = new IRubyObject [] { context .runtime .newSymbol (name ), arg0 };
480
+ return BlockInterfaceImpl .this .callImpl (context , klazz , block , nargs );
481
+ }
482
+
483
+ @ Override
484
+ public IRubyObject call (ThreadContext context , IRubyObject self , RubyModule klazz , String name , IRubyObject arg0 , IRubyObject arg1 , Block block ) {
485
+ final IRubyObject [] nargs = new IRubyObject [] { context .runtime .newSymbol (name ), arg0 , arg1 };
486
+ return BlockInterfaceImpl .this .callImpl (context , klazz , block , nargs );
487
+ }
488
+
489
+ @ Override
490
+ public IRubyObject call (ThreadContext context , IRubyObject self , RubyModule klazz , String name , IRubyObject arg0 , IRubyObject arg1 , IRubyObject arg2 , Block block ) {
491
+ final IRubyObject [] nargs = new IRubyObject [] { context .runtime .newSymbol (name ), arg0 , arg1 , arg2 };
492
+ return BlockInterfaceImpl .this .callImpl (context , klazz , block , nargs );
493
+ }
494
+
495
+ @ Override
496
+ public IRubyObject call (ThreadContext context , IRubyObject self , RubyModule klazz , String name , IRubyObject [] args , Block block ) {
497
+ switch (args .length ) {
498
+ case 0 :
499
+ return call (context , self , klazz , name , block );
500
+ case 1 :
501
+ return call (context , self , klazz , name , args [0 ], block );
502
+ case 2 :
503
+ return call (context , self , klazz , name , args [0 ], args [1 ], block );
504
+ case 3 :
505
+ return call (context , self , klazz , name , args [0 ], args [1 ], args [2 ], block );
506
+ default :
507
+ final IRubyObject [] nargs = new IRubyObject [args .length + 1 ];
508
+ nargs [0 ] = context .runtime .newSymbol (name );
509
+ System .arraycopy (args , 0 , nargs , 1 , args .length );
510
+ return BlockInterfaceImpl .this .callImpl (context , klazz , block , nargs );
511
+ }
512
+ }
513
+
514
+ }
417
515
418
516
}
419
517
0 commit comments