Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 603b8edfc249
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ca8c2107b1c5
Choose a head ref
  • 3 commits
  • 38 files changed
  • 2 contributors

Commits on Dec 4, 2014

  1. typed_enums

    tduehr committed Dec 4, 2014
    Copy the full SHA
    2c637dc View commit details
  2. fix specs so they run

    tduehr committed Dec 4, 2014
    Copy the full SHA
    4f1d66a View commit details
  3. Merge pull request #2274 from tduehr/typed_enums

    Typed enums
    enebo committed Dec 4, 2014
    Copy the full SHA
    ca8c210 View commit details
56 changes: 41 additions & 15 deletions core/src/main/java/org/jruby/ext/ffi/Enum.java
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@

import java.util.IdentityHashMap;
import java.util.Map;
import org.jcodings.util.IntHash;
import java.util.concurrent.ConcurrentHashMap;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.Ruby;
@@ -52,12 +52,12 @@
*/
@JRubyClass(name="FFI::Enum", parent="Object")
public final class Enum extends RubyObject {
private final IRubyObject nativeType;
private IRubyObject nativeType;
private final RubyHash kv_map;
private volatile IRubyObject tag;

private volatile Map<RubySymbol, RubyInteger> symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>();
private volatile IntHash<RubySymbol> valueToSymbol = new IntHash<RubySymbol>();
private volatile ConcurrentHashMap<Long, RubySymbol> valueToSymbol = new ConcurrentHashMap<Long, RubySymbol>();

public static RubyClass createEnumClass(Ruby runtime, RubyModule ffiModule) {
RubyClass enumClass = ffiModule.defineClassUnder("Enum", runtime.getObject(),
@@ -79,27 +79,53 @@ public final IRubyObject allocate(Ruby runtime, RubyClass klass) {

private Enum(Ruby runtime, RubyClass klass) {
super(runtime, klass);
nativeType = runtime.getModule("FFI").getClass("Type").getConstant("INT");
kv_map = RubyHash.newHash(runtime);
tag = runtime.getNil();
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public final IRubyObject initialize(ThreadContext context, IRubyObject values, IRubyObject tag) {
this.tag = tag;
return initialize(context, values);
public final IRubyObject initialize(ThreadContext context, IRubyObject arg) {
return initialize(context, null, null, arg);
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public final IRubyObject initialize(ThreadContext context, IRubyObject values) {
public final IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
if (arg0 instanceof org.jruby.ext.ffi.Type)
return initialize(context, arg0, arg1, null);

if (arg1.isNil())
return initialize(context, null, arg0, null);

// Handles bad args and tag, values case.
return initialize(context, null, arg0, arg1);
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public final IRubyObject initialize(ThreadContext context, IRubyObject type, IRubyObject values, IRubyObject tag) {
int offset = 0;
if (type instanceof org.jruby.ext.ffi.Type) {
nativeType = type;
} else {
if (!(type == null || type.isNil()))
throw context.runtime.newTypeError(type, context.runtime.getModule("FFI").getClass("Type"));

nativeType = context.runtime.getModule("FFI").getClass("Type").getConstant("INT");
}

if (!(tag == null || tag.isNil() || tag instanceof RubySymbol))
throw context.runtime.newTypeError(tag, context.runtime.getSymbol());

this.tag = tag;

if (!(values instanceof RubyArray)) {
throw context.runtime.newTypeError(values, context.runtime.getArray());
}

RubyArray ary = (RubyArray) values;

Map<RubySymbol, RubyInteger> s2v = new IdentityHashMap<RubySymbol, RubyInteger>();
IRubyObject prevConstant = null;
int nextValue = 0;
long nextValue = 0;

for (int i = 0; i < ary.size(); i++) {
IRubyObject v = ary.entry(i);
@@ -109,24 +135,24 @@ public final IRubyObject initialize(ThreadContext context, IRubyObject values) {
prevConstant = v;
nextValue++;

} else if (v instanceof RubyFixnum) {
} else if (v instanceof RubyInteger) {
if (prevConstant == null) {
throw context.runtime.newArgumentError("invalid enum sequence - no symbol for value "
+ v);
}
s2v.put((RubySymbol) prevConstant, (RubyFixnum) v);
nextValue = (int) ((RubyInteger) v).getLongValue() + 1;
nextValue = ((RubyInteger) v).getLongValue() + 1;

} else {
throw context.runtime.newTypeError(v, context.runtime.getSymbol());
}
}

symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>(s2v);
valueToSymbol = new IntHash<RubySymbol>(symbolToValue.size());
valueToSymbol = new ConcurrentHashMap<Long, RubySymbol>(symbolToValue.size());
for (Map.Entry<RubySymbol, RubyInteger> e : symbolToValue.entrySet()) {
kv_map.fastASet(e.getKey(), e.getValue());
valueToSymbol.put((int) e.getValue().getLongValue(), e.getKey());
valueToSymbol.put(e.getValue().getLongValue(), e.getKey());
}

return this;
@@ -139,7 +165,7 @@ public final IRubyObject find(ThreadContext context, IRubyObject query) {
return value != null ? value : context.runtime.getNil();

} else if (query instanceof RubyInteger) {
RubySymbol symbol = valueToSymbol.get((int) ((RubyInteger) query).getLongValue());
RubySymbol symbol = valueToSymbol.get((Long)((RubyInteger) query).getLongValue());
return symbol != null ? symbol : context.runtime.getNil();

} else {
@@ -194,7 +220,7 @@ public final IRubyObject from_native(ThreadContext context, IRubyObject value, I

RubySymbol sym;

if (value instanceof RubyInteger && (sym = valueToSymbol.get((int) ((RubyInteger) value).getLongValue())) != null) {
if (value instanceof RubyInteger && (sym = valueToSymbol.get((Long)((RubyInteger) value).getLongValue())) != null) {
return sym;
}

4 changes: 3 additions & 1 deletion core/src/main/java/org/jruby/ext/ffi/Enums.java
Original file line number Diff line number Diff line change
@@ -93,7 +93,9 @@ public IRubyObject append(final ThreadContext context, IRubyObject item){
}
allEnums.append(item);
if (!(item == null || item == context.nil)){
taggedEnums.fastASet(((Enum)item).tag(context), item);
IRubyObject tag = ((Enum)item).tag(context);
if (tag != null && !tag.isNil())
taggedEnums.fastASet(tag, item);
}
symbolMap.merge_bang(context, ((Enum)item).symbol_map(context), Block.NULL_BLOCK);
return item;
24 changes: 22 additions & 2 deletions lib/ruby/stdlib/ffi/library.rb
Original file line number Diff line number Diff line change
@@ -424,18 +424,38 @@ def typedef(old, add, info=nil)
# @example
# enum [:zero, :one, :two] # unnamed enum, equivalent to above example
# @param [Array] values values for enum
# @overload enum(native_type, name, values)
# Create a named enum and specify the native type.
# @example
# enum FFI::Type::UINT64, :foo, [:zero, :one, :two] # named enum
# @param [FFI::Type] native_type native type for new enum
# @param [Symbol] name name for new enum
# @param [Array] values values for enum
# @overload enum(native_type, *args)
# Create an unnamed enum and specify the native type.
# @example
# enum FFI::Type::UINT64, :zero, :one, :two # unnamed enum
# @param [FFI::Type] native_type native type for new enum
# @param args values for enum
# @overload enum(native_type, values)
# Create an unnamed enum and specify the native type.
# @example
# enum Type::UINT64, [:zero, :one, :two] # unnamed enum, equivalent to above example
# @param [FFI::Type] native_type native type for new enum
# @param [Array] values values for enum
# @return [FFI::Enum]
# Create a new {FFI::Enum}.
def enum(*args)
native_type = args.first.kind_of?(FFI::Type) ? args.shift : nil
name, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array)
[ args[0], args[1] ]
elsif args[0].kind_of?(Array)
[ nil, args[0] ]
else
[ nil, args ]
end
@ffi_enums ||= FFI::Enums.new
@ffi_enums << (e = FFI::Enum.new(values, name))
@ffi_enums = FFI::Enums.new unless defined?(@ffi_enums)
@ffi_enums << (e = native_type ? FFI::Enum.new(native_type, values, name) : FFI::Enum.new(values, name))

# If called as enum :foo, [ :zero, :one, :two ], add a typedef alias
typedef(e, name) if name
11 changes: 5 additions & 6 deletions spec/ffi/async_callback_spec.rb
Original file line number Diff line number Diff line change
@@ -3,8 +3,7 @@
# For licensing, see LICENSE.SPECS
#

require 'ffi'
require_relative 'spec_helper'
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))

describe "async callback" do
module LibTest
@@ -21,16 +20,16 @@ module LibTest
called = false
cb = Proc.new {|i| v = i; called = true }
LibTest.testAsyncCallback(cb, 0x7fffffff)
called.should be_true
v.should == 0x7fffffff
expect(called).to be true
expect(v).to eq(0x7fffffff)
end

it "called a second time" do
v = 0xdeadbeef
called = false
cb = Proc.new {|i| v = i; called = true }
LibTest.testAsyncCallback(cb, 0x7fffffff)
called.should be_true
v.should == 0x7fffffff
expect(called).to be true
expect(v).to eq(0x7fffffff)
end
end
19 changes: 10 additions & 9 deletions spec/ffi/bool_spec.rb
Original file line number Diff line number Diff line change
@@ -3,8 +3,7 @@
# For licensing, see LICENSE.SPECS
#

require 'ffi'
require_relative 'spec_helper'
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))

describe "Function with primitive boolean arguments and return values" do
module LibTest
@@ -15,17 +14,19 @@ module LibTest
attach_function :bool_return_val, [ :bool ], :bool
attach_function :bool_reverse_val, [ :bool ], :bool
end

it "bools" do
LibTest.bool_return_true.should == true
LibTest.bool_return_false.should == false
expect(LibTest.bool_return_true).to be true
expect(LibTest.bool_return_false).to be false

LibTest.bool_return_val(true).should == true
LibTest.bool_return_val(false).should == false
expect(LibTest.bool_return_val(true)).to be true
expect(LibTest.bool_return_val(false)).to be false

LibTest.bool_reverse_val(true).should == false
LibTest.bool_reverse_val(false).should == true
expect(LibTest.bool_reverse_val(true)).to be false
expect(LibTest.bool_reverse_val(false)).to be true
end

it "raise error on invalid types" do
lambda { LibTest.bool_return_val(nil) }.should raise_error(::TypeError)
expect { LibTest.bool_return_val(nil) }.to raise_error(::TypeError)
end
end
Loading