DataConverters.java
- package org.jruby.ext.ffi.jffi;
- import com.kenai.jffi.CallingConvention;
- import org.jruby.*;
- import org.jruby.ext.ffi.*;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.callsite.CachingCallSite;
- import org.jruby.runtime.callsite.FunctionalCachingCallSite;
- import org.jruby.util.WeakIdentityHashMap;
- import java.util.Collections;
- import java.util.IdentityHashMap;
- import java.util.Map;
- /**
- *
- */
- public class DataConverters {
- @SuppressWarnings("unchecked")
- private static final Map<IRubyObject, NativeDataConverter> enumConverters = Collections.synchronizedMap(new WeakIdentityHashMap());
- @Deprecated
- static boolean isEnumConversionRequired(Type type, RubyHash enums) {
- if (type instanceof Type.Builtin && enums != null && !enums.isEmpty()) {
- switch (type.getNativeType()) {
- case CHAR:
- case UCHAR:
- case SHORT:
- case USHORT:
- case INT:
- case UINT:
- case LONG:
- case ULONG:
- case LONG_LONG:
- case ULONG_LONG:
- return true;
-
- default:
- return false;
- }
- }
- return false;
- }
- static boolean isEnumConversionRequired(Type type, Enums enums) {
- if (type instanceof Type.Builtin && enums != null && !enums.isEmpty()) {
- switch (type.getNativeType()) {
- case CHAR:
- case UCHAR:
- case SHORT:
- case USHORT:
- case INT:
- case UINT:
- case LONG:
- case ULONG:
- case LONG_LONG:
- case ULONG_LONG:
- return true;
-
- default:
- return false;
- }
- }
- return false;
- }
- static NativeDataConverter getResultConverter(Type type) {
- if (type instanceof Type.Builtin) {
- return null;
-
- } else if (type instanceof MappedType) {
- return new MappedDataConverter((MappedType) type);
-
- } else if (type instanceof CallbackInfo) {
- return new CallbackDataConverter((CallbackInfo) type);
- }
-
- return null;
- }
- static NativeDataConverter getParameterConverter(Type type) {
- if (type instanceof MappedType) {
- return new MappedDataConverter((MappedType) type);
- } else if (type instanceof CallbackInfo) {
- return new CallbackDataConverter((CallbackInfo) type);
- }
- return null;
- }
- @Deprecated
- static NativeDataConverter getParameterConverter(Type type, RubyHash enums) {
- if (isEnumConversionRequired(type, enums)) {
- NativeDataConverter converter = enumConverters.get(enums);
- if (converter != null) {
- return converter;
- }
- enumConverters.put(enums, converter = new IntOrEnumConverter(NativeType.INT, enums));
- return converter;
-
- } else {
- return getParameterConverter(type);
- }
- }
- static NativeDataConverter getParameterConverter(Type type, Enums enums) {
- if (isEnumConversionRequired(type, enums)) {
- NativeDataConverter converter = enumConverters.get(enums);
- if (converter != null) {
- return converter;
- }
- enumConverters.put(enums, converter = new IntOrEnumConverter(NativeType.INT, enums));
- return converter;
-
- } else {
- return getParameterConverter(type);
- }
- }
- public static final class IntOrEnumConverter extends NativeDataConverter {
- private final NativeType nativeType;
- private final IRubyObject enums;
- private volatile IdentityHashMap<RubySymbol, RubyInteger> symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>();
- public IntOrEnumConverter(NativeType nativeType, IRubyObject enums) {
- this.nativeType = nativeType;
- this.enums = enums;
- }
- @Override
- public NativeType nativeType() {
- return nativeType;
- }
- @Override
- public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
- throw new UnsupportedOperationException("Not supported yet.");
- }
- @Override
- public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
- // Fast path - handle fixnums quickly
- if (obj instanceof RubyFixnum) {
- return obj;
- }
- return lookupOrConvert(context, obj);
- }
- IRubyObject lookupOrConvert(ThreadContext context, IRubyObject obj) {
- if (obj instanceof RubySymbol) {
- IRubyObject value;
- if ((value = symbolToValue.get(obj)) != null) {
- return value;
- }
- return lookupAndCacheValue(context, obj);
- } else {
- return obj.convertToInteger();
- }
- }
- private synchronized IRubyObject lookupAndCacheValue(ThreadContext context, IRubyObject obj) {
- IRubyObject value = enums instanceof Enums ? ((Enums)enums).mapSymbol(context, obj) : ((RubyHash)enums).fastARef(obj);
- if (value.isNil() || !(value instanceof RubyInteger)) {
- throw obj.getRuntime().newArgumentError("invalid enum value, " + obj.inspect());
- }
- IdentityHashMap<RubySymbol, RubyInteger> s2v = new IdentityHashMap<RubySymbol, RubyInteger>(symbolToValue);
- s2v.put((RubySymbol) obj, (RubyInteger) value);
- this.symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>(s2v);
- return value;
- }
- }
-
- public static final class MappedDataConverter extends NativeDataConverter {
- private final MappedType converter;
- public MappedDataConverter(MappedType converter) {
- super(converter.isReferenceRequired(), converter.isPostInvokeRequired());
- this.converter = converter;
- }
-
- public NativeType nativeType() {
- return converter.getRealType().getNativeType();
- }
- public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
- return converter.fromNative(context, obj);
- }
- public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
- return converter.toNative(context, obj);
- }
- }
-
- public static final class CallbackDataConverter extends NativeDataConverter {
- private final CachingCallSite callSite = new FunctionalCachingCallSite("call");
- private final NativeCallbackFactory callbackFactory;
- private final NativeFunctionInfo functionInfo;
- public CallbackDataConverter(CallbackInfo cbInfo) {
- this.callbackFactory = CallbackManager.getInstance().getCallbackFactory(cbInfo.getRuntime(), cbInfo);
- this.functionInfo = new NativeFunctionInfo(cbInfo.getRuntime(),
- cbInfo.getReturnType(), cbInfo.getParameterTypes(),
- cbInfo.isStdcall() ? CallingConvention.STDCALL : CallingConvention.DEFAULT);
- }
-
- public NativeType nativeType() {
- return NativeType.POINTER;
- }
- public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
- if (!(obj instanceof Pointer)) {
- throw context.runtime.newTypeError("internal error: non-pointer");
- }
- Pointer ptr = (Pointer) obj;
- if (ptr.getAddress() == 0) {
- return context.runtime.getNil();
- }
- return new org.jruby.ext.ffi.jffi.Function(context.runtime,
- context.runtime.getModule("FFI").getClass("Function"),
- new CodeMemoryIO(context.runtime, ptr), functionInfo, null);
- }
- public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
- if (obj instanceof Pointer || obj.isNil()) {
- return obj;
-
- } else if (obj instanceof RubyObject) {
- return callbackFactory.getCallback((RubyObject) obj, callSite);
- } else {
- throw context.runtime.newTypeError("wrong argument type. Expected callable object");
- }
- }
- }
-
- public static final class ChainedDataConverter extends NativeDataConverter {
- private final NativeDataConverter upper;
- private final NativeDataConverter lower;
- public ChainedDataConverter(NativeDataConverter first, NativeDataConverter second) {
- super(first.isReferenceRequired() || second.isReferenceRequired(), first.isPostInvokeRequired() || second.isPostInvokeRequired());
- this.upper = first;
- this.lower = second;
- }
-
- public NativeType nativeType() {
- return lower.nativeType();
- }
- public IRubyObject fromNative(ThreadContext context, IRubyObject obj) {
- return upper.fromNative(context, lower.fromNative(context, obj));
- }
- public IRubyObject toNative(ThreadContext context, IRubyObject obj) {
- return lower.toNative(context, upper.toNative(context, obj));
- }
- }
- }