DataConverters.java

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

  2. import com.kenai.jffi.CallingConvention;
  3. import org.jruby.*;
  4. import org.jruby.ext.ffi.*;
  5. import org.jruby.runtime.ThreadContext;
  6. import org.jruby.runtime.builtin.IRubyObject;
  7. import org.jruby.runtime.callsite.CachingCallSite;
  8. import org.jruby.runtime.callsite.FunctionalCachingCallSite;
  9. import org.jruby.util.WeakIdentityHashMap;

  10. import java.util.Collections;
  11. import java.util.IdentityHashMap;
  12. import java.util.Map;


  13. /**
  14.  *
  15.  */
  16. public class DataConverters {
  17.     @SuppressWarnings("unchecked")
  18.     private static final Map<IRubyObject, NativeDataConverter> enumConverters = Collections.synchronizedMap(new WeakIdentityHashMap());

  19.     @Deprecated
  20.     static boolean isEnumConversionRequired(Type type, RubyHash enums) {
  21.         if (type instanceof Type.Builtin && enums != null && !enums.isEmpty()) {
  22.             switch (type.getNativeType()) {
  23.                 case CHAR:
  24.                 case UCHAR:
  25.                 case SHORT:
  26.                 case USHORT:
  27.                 case INT:
  28.                 case UINT:
  29.                 case LONG:
  30.                 case ULONG:
  31.                 case LONG_LONG:
  32.                 case ULONG_LONG:
  33.                     return true;
  34.                    
  35.                 default:
  36.                     return false;
  37.             }
  38.         }
  39.         return false;
  40.     }

  41.     static boolean isEnumConversionRequired(Type type, Enums enums) {
  42.         if (type instanceof Type.Builtin && enums != null && !enums.isEmpty()) {
  43.             switch (type.getNativeType()) {
  44.                 case CHAR:
  45.                 case UCHAR:
  46.                 case SHORT:
  47.                 case USHORT:
  48.                 case INT:
  49.                 case UINT:
  50.                 case LONG:
  51.                 case ULONG:
  52.                 case LONG_LONG:
  53.                 case ULONG_LONG:
  54.                     return true;
  55.                    
  56.                 default:
  57.                     return false;
  58.             }
  59.         }
  60.         return false;
  61.     }

  62.     static NativeDataConverter getResultConverter(Type type) {
  63.         if (type instanceof Type.Builtin) {
  64.             return null;
  65.        
  66.         } else if (type instanceof MappedType) {
  67.             return new MappedDataConverter((MappedType) type);
  68.        
  69.         } else if (type instanceof CallbackInfo) {
  70.             return new CallbackDataConverter((CallbackInfo) type);
  71.         }
  72.        
  73.         return null;
  74.     }


  75.     static NativeDataConverter getParameterConverter(Type type) {
  76.         if (type instanceof MappedType) {
  77.             return new MappedDataConverter((MappedType) type);

  78.         } else if (type instanceof CallbackInfo) {
  79.             return new CallbackDataConverter((CallbackInfo) type);
  80.         }

  81.         return null;
  82.     }

  83.     @Deprecated
  84.     static NativeDataConverter getParameterConverter(Type type, RubyHash enums) {
  85.         if (isEnumConversionRequired(type, enums)) {
  86.             NativeDataConverter converter = enumConverters.get(enums);
  87.             if (converter != null) {
  88.                 return converter;
  89.             }
  90.             enumConverters.put(enums, converter = new IntOrEnumConverter(NativeType.INT, enums));
  91.             return converter;
  92.        
  93.         } else {
  94.             return getParameterConverter(type);
  95.         }
  96.     }

  97.     static NativeDataConverter getParameterConverter(Type type, Enums enums) {
  98.         if (isEnumConversionRequired(type, enums)) {
  99.             NativeDataConverter converter = enumConverters.get(enums);
  100.             if (converter != null) {
  101.                 return converter;
  102.             }
  103.             enumConverters.put(enums, converter = new IntOrEnumConverter(NativeType.INT, enums));
  104.             return converter;
  105.        
  106.         } else {
  107.             return getParameterConverter(type);
  108.         }
  109.     }

  110.     public static final class IntOrEnumConverter extends NativeDataConverter {
  111.         private final NativeType nativeType;
  112.         private final IRubyObject enums;
  113.         private volatile IdentityHashMap<RubySymbol, RubyInteger> symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>();

  114.         public IntOrEnumConverter(NativeType nativeType, IRubyObject enums) {
  115.             this.nativeType = nativeType;
  116.             this.enums = enums;
  117.         }

  118.         @Override
  119.         public NativeType nativeType() {
  120.             return nativeType;
  121.         }

  122.         @Override
  123.         public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
  124.             throw new UnsupportedOperationException("Not supported yet.");
  125.         }

  126.         @Override
  127.         public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
  128.             // Fast path - handle fixnums quickly
  129.             if (obj instanceof RubyFixnum) {
  130.                 return obj;
  131.             }

  132.             return lookupOrConvert(context, obj);
  133.         }

  134.         IRubyObject lookupOrConvert(ThreadContext context, IRubyObject obj) {
  135.             if (obj instanceof RubySymbol) {
  136.                 IRubyObject value;
  137.                 if ((value = symbolToValue.get(obj)) != null) {
  138.                     return value;
  139.                 }

  140.                 return lookupAndCacheValue(context, obj);

  141.             } else {
  142.                 return obj.convertToInteger();
  143.             }
  144.         }

  145.         private synchronized IRubyObject lookupAndCacheValue(ThreadContext context, IRubyObject obj) {
  146.             IRubyObject value = enums instanceof Enums ? ((Enums)enums).mapSymbol(context, obj) : ((RubyHash)enums).fastARef(obj);
  147.             if (value.isNil() || !(value instanceof RubyInteger)) {
  148.                 throw obj.getRuntime().newArgumentError("invalid enum value, " + obj.inspect());
  149.             }

  150.             IdentityHashMap<RubySymbol, RubyInteger> s2v = new IdentityHashMap<RubySymbol, RubyInteger>(symbolToValue);
  151.             s2v.put((RubySymbol) obj, (RubyInteger) value);
  152.             this.symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>(s2v);

  153.             return value;
  154.         }
  155.     }
  156.    
  157.     public static final class MappedDataConverter extends NativeDataConverter {
  158.         private final MappedType converter;

  159.         public MappedDataConverter(MappedType converter) {
  160.             super(converter.isReferenceRequired(), converter.isPostInvokeRequired());
  161.             this.converter = converter;
  162.         }
  163.        
  164.         public NativeType nativeType() {
  165.             return converter.getRealType().getNativeType();
  166.         }

  167.         public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
  168.             return converter.fromNative(context, obj);
  169.         }

  170.         public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
  171.             return converter.toNative(context, obj);
  172.         }
  173.     }
  174.    
  175.     public static final class CallbackDataConverter extends NativeDataConverter {
  176.         private final CachingCallSite callSite = new FunctionalCachingCallSite("call");
  177.         private final NativeCallbackFactory callbackFactory;
  178.         private final NativeFunctionInfo functionInfo;

  179.         public CallbackDataConverter(CallbackInfo cbInfo) {
  180.             this.callbackFactory = CallbackManager.getInstance().getCallbackFactory(cbInfo.getRuntime(), cbInfo);
  181.             this.functionInfo = new NativeFunctionInfo(cbInfo.getRuntime(),
  182.                     cbInfo.getReturnType(), cbInfo.getParameterTypes(),
  183.                     cbInfo.isStdcall() ? CallingConvention.STDCALL : CallingConvention.DEFAULT);
  184.         }
  185.        
  186.         public NativeType nativeType() {
  187.             return NativeType.POINTER;
  188.         }

  189.         public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
  190.             if (!(obj instanceof Pointer)) {
  191.                 throw context.runtime.newTypeError("internal error: non-pointer");
  192.             }
  193.             Pointer ptr = (Pointer) obj;
  194.             if (ptr.getAddress() == 0) {
  195.                 return context.runtime.getNil();
  196.             }
  197.             return new org.jruby.ext.ffi.jffi.Function(context.runtime,
  198.                     context.runtime.getModule("FFI").getClass("Function"),
  199.                     new CodeMemoryIO(context.runtime, ptr), functionInfo, null);
  200.         }

  201.         public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
  202.             if (obj instanceof Pointer || obj.isNil()) {
  203.                 return obj;
  204.            
  205.             } else if (obj instanceof RubyObject) {
  206.                 return callbackFactory.getCallback((RubyObject) obj, callSite);

  207.             } else {
  208.                 throw context.runtime.newTypeError("wrong argument type.  Expected callable object");
  209.             }
  210.         }
  211.     }
  212.    
  213.     public static final class ChainedDataConverter extends NativeDataConverter {
  214.         private final NativeDataConverter upper;
  215.         private final NativeDataConverter lower;

  216.         public ChainedDataConverter(NativeDataConverter first, NativeDataConverter second) {
  217.             super(first.isReferenceRequired() || second.isReferenceRequired(), first.isPostInvokeRequired() || second.isPostInvokeRequired());
  218.             this.upper = first;
  219.             this.lower = second;
  220.         }
  221.        
  222.         public NativeType nativeType() {
  223.             return lower.nativeType();
  224.         }

  225.         public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
  226.             return upper.fromNative(context, lower.fromNative(context, obj));
  227.         }

  228.         public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
  229.             return lower.toNative(context, upper.toNative(context, obj));
  230.         }
  231.     }
  232. }