VariadicInvoker.java

  1. package org.jruby.ext.ffi.jffi;

  2. import com.kenai.jffi.CallingConvention;
  3. import com.kenai.jffi.Function;
  4. import com.kenai.jffi.HeapInvocationBuffer;
  5. import org.jruby.Ruby;
  6. import org.jruby.RubyArray;
  7. import org.jruby.RubyHash;
  8. import org.jruby.RubyClass;
  9. import org.jruby.RubyModule;
  10. import org.jruby.RubyObject;
  11. import org.jruby.anno.JRubyClass;
  12. import org.jruby.anno.JRubyMethod;
  13. import org.jruby.ext.ffi.Enums;
  14. import org.jruby.ext.ffi.NativeType;
  15. import org.jruby.ext.ffi.Pointer;
  16. import org.jruby.ext.ffi.Type;
  17. import org.jruby.runtime.Arity;
  18. import org.jruby.runtime.ObjectAllocator;
  19. import org.jruby.runtime.ThreadContext;
  20. import org.jruby.runtime.builtin.IRubyObject;
  21. import org.jruby.util.TypeConverter;

  22. @JRubyClass(name = "FFI::VariadicInvoker", parent = "Object")
  23. public class VariadicInvoker extends RubyObject {
  24.     private final CallingConvention convention;
  25.     private final Pointer address;
  26.     private final FunctionInvoker functionInvoker;
  27.     private final com.kenai.jffi.Type returnType;
  28.     private final IRubyObject enums;
  29.     private final boolean saveError;
  30.     private static final java.util.Locale LOCALE = java.util.Locale.ENGLISH;


  31.     public static RubyClass createVariadicInvokerClass(Ruby runtime, RubyModule module) {
  32.         RubyClass result = module.defineClassUnder("VariadicInvoker",
  33.                 runtime.getObject(),
  34.                 ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  35.         result.defineAnnotatedMethods(VariadicInvoker.class);
  36.         result.defineAnnotatedConstants(VariadicInvoker.class);

  37.         return result;
  38.     }

  39.     private VariadicInvoker(Ruby runtime, IRubyObject klazz, Pointer address,
  40.             FunctionInvoker functionInvoker, com.kenai.jffi.Type returnType,
  41.             CallingConvention convention, IRubyObject enums, boolean saveError) {
  42.         super(runtime, (RubyClass) klazz);
  43.         this.address = address;
  44.         this.functionInvoker = functionInvoker;
  45.         this.returnType = returnType;
  46.         this.convention = convention;
  47.         this.enums = enums;
  48.         this.saveError = saveError;
  49.     }

  50.     /**
  51.      * Returns the {@link org.jruby.runtime.Arity} of this function.
  52.      *
  53.      * @return The <tt>Arity</tt> of the native function.
  54.      */
  55.     public final Arity getArity() {
  56.         return Arity.OPTIONAL;
  57.     }

  58.     @JRubyMethod(name = { "new" }, meta = true, required = 3, optional = 1)
  59.     public static VariadicInvoker newInstance(ThreadContext context, IRubyObject klass, IRubyObject[] args) {
  60.         // Get the convention from the options hash
  61.         String convention = "default";
  62.         IRubyObject enums = null;
  63.         boolean saveError = true;
  64.         IRubyObject typeMap = null;

  65.         if (args.length == 4) {
  66.             RubyHash options = (RubyHash) args[3];
  67.             IRubyObject rbConvention = options.fastARef(context.runtime.newSymbol("convention"));
  68.             if (rbConvention != null && !rbConvention.isNil()) {
  69.                 convention = rbConvention.asJavaString();
  70.             }

  71.             IRubyObject rbSaveErrno = options.fastARef(context.runtime.newSymbol("save_errno"));
  72.             if (rbSaveErrno != null && !rbSaveErrno.isNil()) {
  73.                 saveError = rbSaveErrno.isTrue();
  74.             }

  75.             enums = options.fastARef(context.runtime.newSymbol("enums"));
  76.             if (enums != null && !enums.isNil() && !(enums instanceof RubyHash || enums instanceof Enums)) {
  77.                 throw context.runtime.newTypeError("wrong type for options[:enum] "
  78.                     + enums.getMetaClass().getName() + " (expected Hash or Enums)");

  79.             }
  80.             typeMap = options.fastARef(context.runtime.newSymbol("type_map"));
  81.             if (typeMap != null && !typeMap.isNil() && !(typeMap instanceof RubyHash)) {
  82.                 throw context.runtime.newTypeError("wrong type for options[:type_map] "
  83.                         + typeMap.getMetaClass().getName() + " (expected Hash)");

  84.             }
  85.         }

  86.         final Type returnType = org.jruby.ext.ffi.Util.findType(context, args[0], typeMap);

  87.         if (!(args[1] instanceof RubyArray)) {
  88.             throw context.runtime.newTypeError("Invalid parameter array "
  89.                     + args[1].getMetaClass().getName() + " (expected Array)");
  90.         }

  91.         if (!(args[2] instanceof Pointer)) {
  92.             throw context.runtime.newTypeError(args[2], context.runtime.getFFI().pointerClass);
  93.         }
  94.         final Pointer address = (Pointer) args[2];

  95.         CallingConvention callConvention = "stdcall".equals(convention)
  96.                         ? CallingConvention.STDCALL : CallingConvention.DEFAULT;

  97.         RubyArray paramTypes = (RubyArray) args[1];
  98.         RubyArray fixed = RubyArray.newArray(context.runtime);
  99.         for (int i = 0; i < paramTypes.getLength(); ++i) {
  100.             Type type = (Type)paramTypes.entry(i);
  101.             if (type.getNativeType() != org.jruby.ext.ffi.NativeType.VARARGS)
  102.                 fixed.append(type);
  103.         }

  104.         FunctionInvoker functionInvoker = DefaultMethodFactory.getFunctionInvoker(returnType);

  105.         VariadicInvoker varInvoker = new VariadicInvoker(context.runtime, klass, address, functionInvoker, FFIUtil.getFFIType(returnType), callConvention, enums, saveError);

  106.         varInvoker.setInstanceVariable("@fixed", fixed);
  107.         varInvoker.setInstanceVariable("@type_map", typeMap);

  108.         return varInvoker;
  109.     }

  110.     @JRubyMethod(name = { "invoke" })
  111.     public IRubyObject invoke(ThreadContext context, IRubyObject typesArg, IRubyObject paramsArg) {
  112.         IRubyObject[] types = ((RubyArray) typesArg).toJavaArrayMaybeUnsafe();
  113.         IRubyObject[] params = ((RubyArray) paramsArg).toJavaArrayMaybeUnsafe();
  114.         com.kenai.jffi.Type[] ffiParamTypes = new com.kenai.jffi.Type[types.length];
  115.         ParameterMarshaller[] marshallers = new ParameterMarshaller[types.length];
  116.         RubyClass builtinClass = Type.getTypeClass(context.getRuntime()).getClass("Builtin");

  117.         for (int i = 0; i < types.length; ++i) {
  118.             Type type = (Type) types[i];
  119.             switch (NativeType.valueOf(type)) {
  120.                 case CHAR:
  121.                 case SHORT:
  122.                 case INT:
  123.                     ffiParamTypes[i] = com.kenai.jffi.Type.SINT32;
  124.                     marshallers[i] = DefaultMethodFactory.getMarshaller((Type)builtinClass.getConstant(NativeType.INT.name().toUpperCase(LOCALE)), convention, enums);
  125.                     break;
  126.                 case UCHAR:
  127.                 case USHORT:
  128.                 case UINT:
  129.                     ffiParamTypes[i] = com.kenai.jffi.Type.UINT32;
  130.                     marshallers[i] = DefaultMethodFactory.getMarshaller((Type)builtinClass.getConstant(NativeType.UINT.name().toUpperCase(LOCALE)), convention, enums);
  131.                     break;
  132.                 case FLOAT:
  133.                 case DOUBLE:
  134.                     ffiParamTypes[i] = com.kenai.jffi.Type.DOUBLE;
  135.                     marshallers[i] = DefaultMethodFactory.getMarshaller((Type)builtinClass.getConstant(NativeType.DOUBLE.name().toUpperCase(LOCALE)), convention, enums);
  136.                     break;
  137.                 default:
  138.                     ffiParamTypes[i] = FFIUtil.getFFIType(type);
  139.                     marshallers[i] = DefaultMethodFactory.getMarshaller((Type) types[i], convention, enums);
  140.                     break;
  141.             }
  142.         }

  143.         Invocation invocation = new Invocation(context);
  144.         Function function = new Function(address.getAddress(), returnType, ffiParamTypes, convention, saveError);
  145.         try {
  146.             HeapInvocationBuffer args = new HeapInvocationBuffer(function);
  147.             for (int i = 0; i < marshallers.length; ++i) {
  148.                 marshallers[i].marshal(invocation, args, params[i]);
  149.             }

  150.             return functionInvoker.invoke(context, function, args);
  151.         } finally {
  152.             invocation.finish();
  153.         }
  154.     }
  155. }