Skip to content

Commit 59fb511

Browse files
committed
[GR-13303] [GR-9815] [GR-9816] [GR-10821] [GR-8904] Support the full FFI.
PullRequest: truffleruby/801
2 parents a5ab7d3 + 3a62abe commit 59fb511

File tree

144 files changed

+15397
-127
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+15397
-127
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ src/main/c/truffleposix/truffleposix.o
4444
src/main/c/truffleposix/libtruffleposix.so
4545
src/main/c/truffleposix/libtruffleposix.dylib
4646

47+
spec/ffi/.bundle/config
48+
4749
spec/truffle/fixtures/libtestnfi.so
4850
spec/truffle/fixtures/libtestnfi.dylib
4951

.rubocop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ AllCops:
77
- 'lib/truffle/date/format.rb' # crashes Rubocop in VariableForce
88
- 'lib/truffle/date/delta/parser.rb' # generated code
99
- 'lib/truffle/socket/mri.rb' # taken from MRI
10-
- 'lib/truffle/ffi/library.rb' # taken from FFI gem
10+
- 'lib/truffle/ffi/**/*.rb' # taken from FFI gem
1111
- 'src/test/ruby/types.rb' # deliberately strange code for debugging purposes
1212
- 'src/test/ruby/lexical-context.rb' # deliberately strange code for debugging purposes
1313

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ New features:
1111

1212
* Rounding modes have been implemented or improved for `Float`, `Rational`, `BigDecimal` (#1509).
1313
* Support Homebrew installed in other prefixes than `/usr/local` (#1583).
14+
* Added a pure-Ruby implementation of FFI which passes almost all Ruby FFI specs (#1529, #1524).
1415

1516
Changes:
1617

ci.jsonnet

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ local part_definitions = {
379379
["mx", "unittest", "org.truffleruby"],
380380
["mx", "tck"],
381381
] + jt(["test", "specs"] + self["$.run.specs"].test_spec_options),
382-
# + jt(["test", "specs", ":next"]) disabled as it's currently empty and MSpec doesn't support empty sets of files
382+
# + jt(["test", "specs", ":next"]) disabled as it's currently empty and MSpec doesn't support empty sets of files
383383
},
384384

385385
test_fast: {
@@ -403,6 +403,7 @@ local part_definitions = {
403403
test_gems: { run+: jt(["test", "gems"]) },
404404
test_ecosystem: { run+: jt(["test", "ecosystem"]) },
405405
test_compiler: { run+: jt(["test", "compiler"]) },
406+
test_ffi: { run+: [["test/truffle/ffi.sh"]] },
406407

407408
test_cexts: {
408409
is_after+:: ["$.use.common"],
@@ -522,7 +523,7 @@ local composition_environment = utils.add_inclusion_tracking(part_definitions, "
522523
{
523524
local linux_gate = $.platform.linux + $.cap.gate + $.jdk.labsjdk8 + $.use.common + $.use.build + { timelimit: "01:00:00" },
524525

525-
"ruby-test-specs-linux": linux_gate + $.run.test_unit_tck_specs + $.run.test_basictest + { timelimit: "35:00" },
526+
"ruby-test-specs-linux": linux_gate + $.run.test_unit_tck_specs + $.run.test_basictest + $.run.test_ffi + { timelimit: "35:00" },
526527
"ruby-test-fast-linux": linux_gate + $.run.test_fast + { timelimit: "30:00" }, # To catch missing slow tags
527528
"ruby-test-mri-linux": linux_gate + $.run.test_mri + { timelimit: "30:00" },
528529
"ruby-test-integration": linux_gate + $.run.test_integration,
@@ -539,7 +540,7 @@ local composition_environment = utils.add_inclusion_tracking(part_definitions, "
539540
{
540541
local darwin_gate = $.platform.darwin + $.cap.gate + $.jdk.labsjdk8 + $.use.common + $.use.build + { timelimit: "01:00:00" },
541542

542-
"ruby-test-specs-darwin": darwin_gate + $.run.test_unit_tck_specs + $.run.test_basictest + { timelimit: "45:00" },
543+
"ruby-test-specs-darwin": darwin_gate + $.run.test_unit_tck_specs + $.run.test_basictest + $.run.test_ffi + { timelimit: "45:00" },
543544
"ruby-test-mri-darwin": darwin_gate + $.run.test_mri,
544545
"ruby-test-cexts-darwin": darwin_gate + $.use.gem_test_pack + $.run.test_cexts,
545546
} +

doc/legal/legal.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ licenses. We've added the licenses to the individual files.
174174
We do not distribute MSpec or the Ruby Specs, but they are both copyright 2008
175175
Engine Yard and are released under an MIT licence (see `mit.txt`).
176176

177+
## FFI Specs
178+
179+
We do not distribute the FFI Specs, but they are copyright 2008-2014
180+
Ruby-FFI contributors and are released under an MIT licence (see `mit.txt`).
181+
177182
## Jay
178183

179184
TruffleRuby uses the Jay parser generator, modified from

doc/user/compatibility.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ The following standard libraries are unsupported.
7474
but not enough to run anything serious.
7575

7676
We provide our own included implementation of the interface of the `ffi` gem,
77-
like JRuby and Rubinius, but the implemention of this is limited at the
78-
moment.
77+
like JRuby and Rubinius. The implementation should be fairly complete and passes
78+
all the specs of the `ffi` gem except for some rarely-used corner cases.
7979

8080
#### Safe levels
8181

lib/truffle/ffi.rb

Lines changed: 29 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -6,126 +6,42 @@
66
# GNU General Public License version 2, or
77
# GNU Lesser General Public License version 2.1.
88

9-
require_relative 'truffle/ffi/ffi'
10-
require_relative 'truffle/ffi/ffi_struct'
9+
require 'rbconfig'
1110

12-
# Minimal support needed to run ffi/library
1311
module FFI
14-
Platform = Truffle::FFI::Platform
12+
AbstractMemory = ::Truffle::FFI::AbstractMemory
13+
Pointer = ::Truffle::FFI::Pointer
14+
MemoryPointer = ::Truffle::FFI::MemoryPointer
15+
NullPointerError = ::Truffle::FFI::NullPointerError
1516

16-
class DynamicLibrary
17-
RTLD_LAZY = Truffle::Config['platform.dlopen.RTLD_LAZY']
18-
RTLD_NOW = Truffle::Config['platform.dlopen.RTLD_NOW']
19-
RTLD_GLOBAL = Truffle::Config['platform.dlopen.RTLD_GLOBAL']
20-
RTLD_LOCAL = Truffle::Config['platform.dlopen.RTLD_LOCAL']
21-
22-
attr_reader :name
23-
24-
def self.open(libname, flags)
25-
code = libname ? "load '#{libname}'" : 'default'
26-
handle = Truffle::Interop.eval('application/x-native', code)
27-
DynamicLibrary.new(libname, handle)
28-
end
29-
30-
def initialize(name, handle)
31-
@name = name
32-
@handle = handle
33-
end
34-
35-
def find_symbol(name)
36-
@handle[name]
37-
end
38-
39-
def inspect
40-
"\#<#{self.class} @name=#{@name.inspect}>"
17+
# Redefine Pointer.find_type_size to consider FFI types
18+
class Pointer
19+
def self.find_type_size(type)
20+
::FFI.type_size(::FFI.find_type(type))
4121
end
4222
end
4323

44-
def self.find_type(*args)
45-
Truffle::FFI.find_type(*args)
24+
module Platform
25+
CPU = RbConfig::CONFIG['host_cpu']
26+
# eregon: I would like to use rbconfig/sizeof here, but the linker requires
27+
# ffi, and the linker is needed to build the rbconfig/sizeof C extension,
28+
# so we need to break the cycle.
29+
ADDRESS_SIZE = 64
30+
LONG_SIZE = 64
4631
end
4732

48-
def self.type_size(*args)
49-
Truffle::FFI.type_size(*args)
50-
end
33+
TypeDefs = {}
5134
end
5235

53-
require_relative 'ffi/library'
54-
55-
module FFI
56-
module Library
57-
LIBC = Truffle::LIBC
58-
59-
# Indicies are based on the NativeTypes enum
60-
TO_NFI_TYPE = [
61-
'SINT8', # char
62-
'UINT8', # uchar
63-
'UINT8', # bool
64-
'SINT16', # short
65-
'UINT16', # ushort
66-
'SINT32', # int
67-
'UINT32', # uint
68-
'SINT64', # long
69-
'UINT64', # ulong
70-
'SINT64', # ll
71-
'UINT64', # ull
72-
'FLOAT', # float
73-
'DOUBLE', # double
74-
'POINTER',# ptr
75-
'VOID', # void
76-
'STRING', # string
77-
# strptr
78-
# chararr
79-
# enum
80-
# varargs
81-
]
82-
83-
private def to_nfi_type(type)
84-
idx = FFI.find_type(type)
85-
TO_NFI_TYPE.fetch(idx)
86-
end
87-
88-
def attach_function(method_name, native_name, args_types, return_type = nil, options = {})
89-
unless return_type && (String === native_name || Symbol === native_name)
90-
native_name, args_types, return_type, options = method_name, native_name, args_types, return_type
91-
options ||= {}
92-
end
93-
94-
warn "options #{options.inspect} ignored for attach_function :#{method_name}" unless options.empty?
95-
96-
nfi_args_types = args_types.map { |type| to_nfi_type(type) }
97-
nfi_return_type = to_nfi_type(return_type)
98-
99-
function = @ffi_libs.each do |library|
100-
break library.find_symbol(native_name)
101-
end
102-
signature = "(#{nfi_args_types.join(',')}):#{nfi_return_type}"
103-
function = function.bind(Truffle::Interop.to_java_string(signature))
104-
105-
define_singleton_method(method_name) do |*args|
106-
result = function.call(*args)
107-
if return_type == :pointer
108-
FFI::Pointer.new(Truffle::Interop.unbox(result))
109-
else
110-
result
111-
end
112-
end
113-
end
114-
115-
def find_type(t)
116-
FFI.find_type(t)
117-
end
118-
end
119-
120-
Pointer = Truffle::FFI::Pointer
121-
MemoryPointer = Truffle::FFI::MemoryPointer
122-
Struct = Truffle::FFI::Struct
123-
Union = Truffle::FFI::Union
124-
125-
class Struct
126-
def self.ptr
127-
warn "validation for #{self} parameter not yet implemented"
128-
:pointer
129-
end
130-
end
131-
end
36+
# Require the pure-Ruby Truffle NFI backend
37+
require_relative 'truffle/ffi_backend/last_error'
38+
require_relative 'truffle/ffi_backend/data_converter'
39+
require_relative 'truffle/ffi_backend/type'
40+
require_relative 'truffle/ffi_backend/struct_layout'
41+
require_relative 'truffle/ffi_backend/struct'
42+
require_relative 'truffle/ffi_backend/buffer'
43+
require_relative 'truffle/ffi_backend/function'
44+
require_relative 'truffle/ffi_backend/dynamic_library'
45+
46+
# Require the FFI gem Ruby files
47+
require_relative 'ffi/ffi'

0 commit comments

Comments
 (0)