Pointer.java

  1. package org.jruby.ext.ffi;

  2. import java.nio.ByteOrder;

  3. import org.jruby.*;
  4. import org.jruby.anno.JRubyClass;
  5. import org.jruby.anno.JRubyMethod;
  6. import org.jruby.internal.runtime.methods.CallConfiguration;
  7. import org.jruby.internal.runtime.methods.DynamicMethod;
  8. import org.jruby.runtime.*;
  9. import org.jruby.runtime.builtin.IRubyObject;

  10. import static org.jruby.runtime.Visibility.*;

  11. /**
  12.  * C memory pointer operations.
  13.  * <p>
  14.  * This is an abstract class that defines Pointer operations
  15.  * </p>
  16.  */
  17. @JRubyClass(name="FFI::Pointer", parent=AbstractMemory.ABSTRACT_MEMORY_RUBY_CLASS)
  18. public class Pointer extends AbstractMemory {
  19.     public static RubyClass createPointerClass(Ruby runtime, RubyModule module) {
  20.         RubyClass pointerClass = module.defineClassUnder("Pointer",
  21.                 module.getClass(AbstractMemory.ABSTRACT_MEMORY_RUBY_CLASS),
  22.                 RubyInstanceConfig.REIFY_RUBY_CLASSES ? new ReifyingAllocator(Pointer.class) : PointerAllocator.INSTANCE);

  23.         pointerClass.defineAnnotatedMethods(Pointer.class);
  24.         pointerClass.defineAnnotatedConstants(Pointer.class);
  25.         pointerClass.setReifiedClass(Pointer.class);
  26.         pointerClass.kindOf = new RubyModule.KindOf() {
  27.             @Override
  28.             public boolean isKindOf(IRubyObject obj, RubyModule type) {
  29.                 return obj instanceof Pointer && super.isKindOf(obj, type);
  30.             }
  31.         };

  32.         module.defineClassUnder("NullPointerError", runtime.getRuntimeError(),
  33.                 runtime.getRuntimeError().getAllocator());

  34.         // Add Pointer::NULL as a constant
  35.         Pointer nullPointer = new Pointer(runtime, pointerClass, new NullMemoryIO(runtime));
  36.         pointerClass.setConstant("NULL", nullPointer);
  37.        
  38.         runtime.getNilClass().addMethod("to_ptr", new NilToPointerMethod(runtime.getNilClass(), nullPointer));

  39.         return pointerClass;
  40.     }

  41.     private static final class PointerAllocator implements ObjectAllocator {
  42.         static final ObjectAllocator INSTANCE = new PointerAllocator();

  43.         public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
  44.             return new Pointer(runtime, klazz);
  45.         }
  46.     }

  47.     public static final Pointer getNull(Ruby runtime) {
  48.         return runtime.getFFI().nullPointer;
  49.     }

  50.     public Pointer(Ruby runtime, RubyClass klazz) {
  51.         super(runtime, klazz, runtime.getFFI().getNullMemoryIO(), 0);
  52.     }

  53.     public Pointer(Ruby runtime, MemoryIO io) {
  54.         this(runtime, getPointerClass(runtime), io);
  55.     }
  56.     public Pointer(Ruby runtime, MemoryIO io, long size, int typeSize) {
  57.         this(runtime, getPointerClass(runtime), io, size, typeSize);
  58.     }
  59.     protected Pointer(Ruby runtime, RubyClass klass, MemoryIO io) {
  60.         super(runtime, klass, io, Long.MAX_VALUE);
  61.     }
  62.     protected Pointer(Ruby runtime, RubyClass klass, MemoryIO io, long size) {
  63.         super(runtime, klass, io, size);
  64.     }
  65.     protected Pointer(Ruby runtime, RubyClass klass, MemoryIO io, long size, int typeSize) {
  66.         super(runtime, klass, io, size, typeSize);
  67.     }

  68.     public static final RubyClass getPointerClass(Ruby runtime) {
  69.         return runtime.getFFI().pointerClass;
  70.     }

  71.     public final AbstractMemory order(Ruby runtime, ByteOrder order) {
  72.         return new Pointer(runtime,
  73.                 order.equals(getMemoryIO().order()) ? getMemoryIO() : new SwappedMemoryIO(runtime, getMemoryIO()),
  74.                 size, typeSize);
  75.     }
  76.    
  77.     @JRubyMethod(name = "size", meta = true, visibility = PUBLIC)
  78.     public static IRubyObject size(ThreadContext context, IRubyObject recv) {
  79.         return RubyFixnum.newFixnum(context.getRuntime(), Factory.getInstance().sizeOf(NativeType.POINTER));
  80.     }

  81.     @JRubyMethod(name = { "initialize" }, visibility = PRIVATE)
  82.     public IRubyObject initialize(ThreadContext context, IRubyObject address) {
  83.         setMemoryIO(address instanceof Pointer
  84.                 ? ((Pointer) address).getMemoryIO()
  85.                 : Factory.getInstance().wrapDirectMemory(context.runtime, RubyFixnum.num2long(address)));
  86.         size = Long.MAX_VALUE;
  87.         typeSize = 1;

  88.         return this;
  89.     }

  90.     @JRubyMethod(name = { "initialize" }, visibility = PRIVATE)
  91.     public IRubyObject initialize(ThreadContext context, IRubyObject type, IRubyObject address) {
  92.         setMemoryIO(address instanceof Pointer
  93.                 ? ((Pointer) address).getMemoryIO()
  94.                 : Factory.getInstance().wrapDirectMemory(context.runtime, RubyFixnum.num2long(address)));
  95.         size = Long.MAX_VALUE;
  96.         typeSize = calculateTypeSize(context, type);

  97.         return this;
  98.     }
  99.    
  100.     /**
  101.      *
  102.      */
  103.     @JRubyMethod(required = 1, visibility=PRIVATE)
  104.     public IRubyObject initialize_copy(ThreadContext context, IRubyObject other) {
  105.         if (this == other) {
  106.             return this;
  107.         }
  108.         Pointer orig = (Pointer) other;
  109.         this.typeSize = orig.typeSize;
  110.         this.size = orig.size;

  111.         setMemoryIO(orig.getMemoryIO().dup());

  112.         return this;
  113.     }


  114.     /**
  115.      * Tests if this <tt>Pointer</tt> represents the C <tt>NULL</tt> value.
  116.      *
  117.      * @return true if the address is NULL.
  118.      */
  119.     @JRubyMethod(name = "null?")
  120.     public IRubyObject null_p(ThreadContext context) {
  121.         return context.runtime.newBoolean(getMemoryIO().isNull());
  122.     }


  123.     @Override
  124.     @JRubyMethod(name = { "to_s", "inspect" }, optional = 1)
  125.     public IRubyObject to_s(ThreadContext context, IRubyObject[] args) {
  126.         String s = size != Long.MAX_VALUE
  127.                 ? String.format("#<%s address=0x%x size=%s>", getMetaClass().getName(), getAddress(), size)
  128.                 : String.format("#<%s address=0x%x>", getMetaClass().getName(), getAddress());

  129.         return RubyString.newString(context.runtime, s);
  130.     }

  131.     @JRubyMethod(name = { "address", "to_i" })
  132.     public IRubyObject address(ThreadContext context) {
  133.         return context.runtime.newFixnum(getAddress());
  134.     }

  135.     /**
  136.      * Gets the native memory address of this pointer.
  137.      *
  138.      * @return A long containing the native memory address.
  139.      */
  140.     public final long getAddress() {
  141.         return getMemoryIO().address();
  142.     }

  143.     @JRubyMethod(name = "==", required = 1)
  144.     public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
  145.         return context.runtime.newBoolean(this == obj
  146.                 || getAddress() == 0L && obj.isNil()
  147.                 || (obj instanceof Pointer && ((Pointer) obj).getAddress() == getAddress()));
  148.     }
  149.    
  150.     @Override
  151.     protected AbstractMemory slice(Ruby runtime, long offset) {
  152.         return new Pointer(runtime, getPointerClass(runtime),
  153.                 getMemoryIO().slice(offset),
  154.                 size == Long.MAX_VALUE ? Long.MAX_VALUE : size - offset, typeSize);
  155.     }

  156.     @Override
  157.     protected AbstractMemory slice(Ruby runtime, long offset, long size) {
  158.         return new Pointer(runtime, getPointerClass(runtime),
  159.                 getMemoryIO().slice(offset, size), size, typeSize);
  160.     }

  161.     protected Pointer getPointer(Ruby runtime, long offset) {
  162.         return new Pointer(runtime, getPointerClass(runtime), getMemoryIO().getMemoryIO(offset), Long.MAX_VALUE);
  163.     }

  164.     private static final class NilToPointerMethod extends DynamicMethod {
  165.         private static final Arity ARITY = Arity.NO_ARGUMENTS;
  166.         private final Pointer nullPointer;

  167.         private NilToPointerMethod(RubyModule implementationClass, Pointer nullPointer) {
  168.             super(implementationClass, Visibility.PUBLIC, CallConfiguration.FrameNoneScopeNone);
  169.             this.nullPointer = nullPointer;
  170.         }

  171.         @Override
  172.         public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  173.             ARITY.checkArity(context.runtime, args);
  174.             return nullPointer;
  175.         }

  176.         @Override
  177.         public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name) {
  178.             return nullPointer;
  179.         }

  180.         @Override
  181.         public DynamicMethod dup() {
  182.             return this;
  183.         }
  184.     }
  185. }