Skip to content

Commit 8d127c9

Browse files
koueregon
authored andcommitted
Use JRuby implementation for TruffleRuby (#149)
Fix GH-145 Rename `lib/fiddle/jruby.rb` to `lib/fiddle/ffi_backend.rb` as a generic ffi gem API based implementation. JRuby and TruffleRuby use `lib/fiddle/ffi_backend.rb`. --------- Co-authored-by: Benoit Daloze <eregontp@gmail.com>
1 parent bbd5b8d commit 8d127c9

File tree

13 files changed

+156
-109
lines changed

13 files changed

+156
-109
lines changed

ext/fiddle/extconf.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22
require 'mkmf'
33

4-
if RUBY_ENGINE == "jruby"
4+
unless RUBY_ENGINE == "ruby"
55
File.write('Makefile', dummy_makefile("").join)
66
return
77
end

ext/fiddle/fiddle.gemspec

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,10 @@ Gem::Specification.new do |spec|
3838
"lib/fiddle.rb",
3939
"lib/fiddle/closure.rb",
4040
"lib/fiddle/cparser.rb",
41+
"lib/fiddle/ffi_backend.rb",
4142
"lib/fiddle/function.rb",
4243
"lib/fiddle/import.rb",
43-
"lib/fiddle/jruby.rb",
4444
"lib/fiddle/pack.rb",
45-
"lib/fiddle/ruby.rb",
4645
"lib/fiddle/struct.rb",
4746
"lib/fiddle/types.rb",
4847
"lib/fiddle/value.rb",

ext/fiddle/lib/fiddle.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# frozen_string_literal: true
22

3-
require "fiddle/#{RUBY_ENGINE}"
3+
if RUBY_ENGINE == 'ruby'
4+
require 'fiddle.so'
5+
else
6+
require 'fiddle/ffi_backend'
7+
end
48
require 'fiddle/closure'
59
require 'fiddle/function'
610
require 'fiddle/version'

ext/fiddle/lib/fiddle/jruby.rb renamed to ext/fiddle/lib/fiddle/ffi_backend.rb

Lines changed: 79 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# This is part of JRuby's FFI-based fiddle implementation.
1+
# This is based on JRuby's FFI-based fiddle implementation.
22

33
require 'ffi'
44

@@ -74,7 +74,7 @@ module Types
7474

7575
WINDOWS = FFI::Platform.windows?
7676

77-
module JRuby
77+
module FFIBackend
7878
FFITypes = {
7979
'c' => FFI::Type::INT8,
8080
'h' => FFI::Type::INT16,
@@ -104,16 +104,16 @@ module JRuby
104104
Types::VARIADIC => FFI::Type::Builtin::VARARGS,
105105
}
106106

107-
def self.__ffi_type__(dl_type)
108-
if dl_type.is_a?(Symbol)
109-
dl_type = Types.const_get(dl_type.to_s.upcase)
107+
def self.to_ffi_type(fiddle_type)
108+
if fiddle_type.is_a?(Symbol)
109+
fiddle_type = Types.const_get(fiddle_type.to_s.upcase)
110110
end
111-
if !dl_type.is_a?(Integer) && dl_type.respond_to?(:to_int)
112-
dl_type = dl_type.to_int
111+
if !fiddle_type.is_a?(Integer) && fiddle_type.respond_to?(:to_int)
112+
fiddle_type = fiddle_type.to_int
113113
end
114-
ffi_type = FFITypes[dl_type]
115-
ffi_type = FFITypes[-dl_type] if ffi_type.nil? && dl_type.is_a?(Integer) && dl_type < 0
116-
raise TypeError.new("cannot convert #{dl_type} to ffi") unless ffi_type
114+
ffi_type = FFITypes[fiddle_type]
115+
ffi_type = FFITypes[-fiddle_type] if ffi_type.nil? && fiddle_type.is_a?(Integer) && fiddle_type < 0
116+
raise TypeError.new("cannot convert #{fiddle_type} to ffi") unless ffi_type
117117
ffi_type
118118
end
119119
end
@@ -133,8 +133,8 @@ def initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil)
133133
@ptr, @args, @return_type, @abi = ptr, args, return_type, abi
134134
raise TypeError.new "invalid argument types" unless args.is_a?(Array)
135135

136-
ffi_return_type = Fiddle::JRuby::__ffi_type__(@return_type)
137-
ffi_args = @args.map { |t| Fiddle::JRuby.__ffi_type__(t) }
136+
ffi_return_type = Fiddle::FFIBackend.to_ffi_type(@return_type)
137+
ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) }
138138
pointer = FFI::Pointer.new(ptr.to_i)
139139
options = {convention: @abi}
140140
if ffi_args.last == FFI::Type::Builtin::VARARGS
@@ -149,14 +149,25 @@ def initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil)
149149
end
150150
end
151151

152-
def call(*args, &block);
152+
def call(*args, &block)
153153
if @function.is_a?(FFI::VariadicInvoker)
154154
n_fixed_args = @args.size - 1
155155
n_fixed_args.step(args.size - 1, 2) do |i|
156156
if args[i] == :const_string || args[i] == Types::CONST_STRING
157157
args[i + 1] = String.try_convert(args[i + 1]) || args[i + 1]
158158
end
159-
args[i] = Fiddle::JRuby.__ffi_type__(args[i])
159+
args[i] = Fiddle::FFIBackend.to_ffi_type(args[i])
160+
end
161+
else
162+
args.map! do |arg|
163+
if arg.respond_to?(:to_ptr)
164+
begin
165+
arg = arg.to_ptr
166+
end until arg.is_a?(FFI::Pointer) || !arg.respond_to?(:to_ptr)
167+
arg
168+
else
169+
arg
170+
end
160171
end
161172
end
162173
result = @function.call(*args, &block)
@@ -170,19 +181,21 @@ def initialize(ret, args, abi = Function::DEFAULT)
170181
raise TypeError.new "invalid argument types" unless args.is_a?(Array)
171182

172183
@ctype, @args = ret, args
173-
ffi_args = @args.map { |t| Fiddle::JRuby.__ffi_type__(t) }
184+
ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) }
174185
if ffi_args.size == 1 && ffi_args[0] == FFI::Type::Builtin::VOID
175186
ffi_args = []
176187
end
177-
@function = FFI::Function.new(
178-
Fiddle::JRuby.__ffi_type__(@ctype),
179-
ffi_args,
180-
self,
181-
:convention => abi
182-
)
188+
return_type = Fiddle::FFIBackend.to_ffi_type(@ctype)
189+
raise "#{self.class} must implement #call" unless respond_to?(:call)
190+
callable = method(:call)
191+
@function = FFI::Function.new(return_type, ffi_args, callable, convention: abi)
183192
@freed = false
184193
end
185194

195+
def to_ptr
196+
@function
197+
end
198+
186199
def to_i
187200
@function.to_i
188201
end
@@ -550,51 +563,51 @@ def cleared?
550563
RUBY_FREE = Fiddle::Pointer::LibC::FREE.address
551564
NULL = Fiddle::Pointer.new(0)
552565

553-
ALIGN_VOIDP = Fiddle::JRuby::FFITypes[Types::VOIDP].alignment
554-
ALIGN_CHAR = Fiddle::JRuby::FFITypes[Types::CHAR].alignment
555-
ALIGN_SHORT = Fiddle::JRuby::FFITypes[Types::SHORT].alignment
556-
ALIGN_INT = Fiddle::JRuby::FFITypes[Types::INT].alignment
557-
ALIGN_LONG = Fiddle::JRuby::FFITypes[Types::LONG].alignment
558-
ALIGN_LONG_LONG = Fiddle::JRuby::FFITypes[Types::LONG_LONG].alignment
559-
ALIGN_INT8_T = Fiddle::JRuby::FFITypes[Types::INT8_T].alignment
560-
ALIGN_INT16_T = Fiddle::JRuby::FFITypes[Types::INT16_T].alignment
561-
ALIGN_INT32_T = Fiddle::JRuby::FFITypes[Types::INT32_T].alignment
562-
ALIGN_INT64_T = Fiddle::JRuby::FFITypes[Types::INT64_T].alignment
563-
ALIGN_FLOAT = Fiddle::JRuby::FFITypes[Types::FLOAT].alignment
564-
ALIGN_DOUBLE = Fiddle::JRuby::FFITypes[Types::DOUBLE].alignment
565-
ALIGN_BOOL = Fiddle::JRuby::FFITypes[Types::BOOL].alignment
566-
ALIGN_SIZE_T = Fiddle::JRuby::FFITypes[Types::SIZE_T].alignment
566+
ALIGN_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].alignment
567+
ALIGN_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].alignment
568+
ALIGN_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].alignment
569+
ALIGN_INT = Fiddle::FFIBackend::FFITypes[Types::INT].alignment
570+
ALIGN_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].alignment
571+
ALIGN_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].alignment
572+
ALIGN_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].alignment
573+
ALIGN_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].alignment
574+
ALIGN_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].alignment
575+
ALIGN_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].alignment
576+
ALIGN_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].alignment
577+
ALIGN_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].alignment
578+
ALIGN_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].alignment
579+
ALIGN_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].alignment
567580
ALIGN_SSIZE_T = ALIGN_SIZE_T
568-
ALIGN_PTRDIFF_T = Fiddle::JRuby::FFITypes[Types::PTRDIFF_T].alignment
569-
ALIGN_INTPTR_T = Fiddle::JRuby::FFITypes[Types::INTPTR_T].alignment
570-
ALIGN_UINTPTR_T = Fiddle::JRuby::FFITypes[Types::UINTPTR_T].alignment
571-
572-
SIZEOF_VOIDP = Fiddle::JRuby::FFITypes[Types::VOIDP].size
573-
SIZEOF_CHAR = Fiddle::JRuby::FFITypes[Types::CHAR].size
574-
SIZEOF_UCHAR = Fiddle::JRuby::FFITypes[Types::UCHAR].size
575-
SIZEOF_SHORT = Fiddle::JRuby::FFITypes[Types::SHORT].size
576-
SIZEOF_USHORT = Fiddle::JRuby::FFITypes[Types::USHORT].size
577-
SIZEOF_INT = Fiddle::JRuby::FFITypes[Types::INT].size
578-
SIZEOF_UINT = Fiddle::JRuby::FFITypes[Types::UINT].size
579-
SIZEOF_LONG = Fiddle::JRuby::FFITypes[Types::LONG].size
580-
SIZEOF_ULONG = Fiddle::JRuby::FFITypes[Types::ULONG].size
581-
SIZEOF_LONG_LONG = Fiddle::JRuby::FFITypes[Types::LONG_LONG].size
582-
SIZEOF_ULONG_LONG = Fiddle::JRuby::FFITypes[Types::ULONG_LONG].size
583-
SIZEOF_INT8_T = Fiddle::JRuby::FFITypes[Types::INT8_T].size
584-
SIZEOF_UINT8_T = Fiddle::JRuby::FFITypes[Types::UINT8_T].size
585-
SIZEOF_INT16_T = Fiddle::JRuby::FFITypes[Types::INT16_T].size
586-
SIZEOF_UINT16_T = Fiddle::JRuby::FFITypes[Types::UINT16_T].size
587-
SIZEOF_INT32_T = Fiddle::JRuby::FFITypes[Types::INT32_T].size
588-
SIZEOF_UINT32_T = Fiddle::JRuby::FFITypes[Types::UINT32_T].size
589-
SIZEOF_INT64_T = Fiddle::JRuby::FFITypes[Types::INT64_T].size
590-
SIZEOF_UINT64_T = Fiddle::JRuby::FFITypes[Types::UINT64_T].size
591-
SIZEOF_FLOAT = Fiddle::JRuby::FFITypes[Types::FLOAT].size
592-
SIZEOF_DOUBLE = Fiddle::JRuby::FFITypes[Types::DOUBLE].size
593-
SIZEOF_BOOL = Fiddle::JRuby::FFITypes[Types::BOOL].size
594-
SIZEOF_SIZE_T = Fiddle::JRuby::FFITypes[Types::SIZE_T].size
581+
ALIGN_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].alignment
582+
ALIGN_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].alignment
583+
ALIGN_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].alignment
584+
585+
SIZEOF_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size
586+
SIZEOF_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].size
587+
SIZEOF_UCHAR = Fiddle::FFIBackend::FFITypes[Types::UCHAR].size
588+
SIZEOF_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].size
589+
SIZEOF_USHORT = Fiddle::FFIBackend::FFITypes[Types::USHORT].size
590+
SIZEOF_INT = Fiddle::FFIBackend::FFITypes[Types::INT].size
591+
SIZEOF_UINT = Fiddle::FFIBackend::FFITypes[Types::UINT].size
592+
SIZEOF_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].size
593+
SIZEOF_ULONG = Fiddle::FFIBackend::FFITypes[Types::ULONG].size
594+
SIZEOF_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].size
595+
SIZEOF_ULONG_LONG = Fiddle::FFIBackend::FFITypes[Types::ULONG_LONG].size
596+
SIZEOF_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].size
597+
SIZEOF_UINT8_T = Fiddle::FFIBackend::FFITypes[Types::UINT8_T].size
598+
SIZEOF_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].size
599+
SIZEOF_UINT16_T = Fiddle::FFIBackend::FFITypes[Types::UINT16_T].size
600+
SIZEOF_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].size
601+
SIZEOF_UINT32_T = Fiddle::FFIBackend::FFITypes[Types::UINT32_T].size
602+
SIZEOF_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].size
603+
SIZEOF_UINT64_T = Fiddle::FFIBackend::FFITypes[Types::UINT64_T].size
604+
SIZEOF_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].size
605+
SIZEOF_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].size
606+
SIZEOF_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].size
607+
SIZEOF_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].size
595608
SIZEOF_SSIZE_T = SIZEOF_SIZE_T
596-
SIZEOF_PTRDIFF_T = Fiddle::JRuby::FFITypes[Types::PTRDIFF_T].size
597-
SIZEOF_INTPTR_T = Fiddle::JRuby::FFITypes[Types::INTPTR_T].size
598-
SIZEOF_UINTPTR_T = Fiddle::JRuby::FFITypes[Types::UINTPTR_T].size
599-
SIZEOF_CONST_STRING = Fiddle::JRuby::FFITypes[Types::VOIDP].size
609+
SIZEOF_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].size
610+
SIZEOF_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].size
611+
SIZEOF_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].size
612+
SIZEOF_CONST_STRING = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size
600613
end

ext/fiddle/lib/fiddle/ruby.rb

Lines changed: 0 additions & 1 deletion
This file was deleted.

test/fiddle/helper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ def teardown
183183
end
184184
end
185185

186+
def ffi_backend?
187+
RUBY_ENGINE != 'ruby'
188+
end
189+
186190
def under_gc_stress
187191
stress, GC.stress = GC.stress, true
188192
yield

test/fiddle/test_closure.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ def call thing
5858
end
5959

6060
def test_const_string
61-
if RUBY_ENGINE == "jruby"
61+
if ffi_backend?
6262
omit("Closure with :const_string works but " +
63-
"Function with :const_string doesn't work with JRuby")
63+
"Function with :const_string doesn't work with FFI backend")
6464
end
6565

6666
closure_class = Class.new(Closure) do
@@ -119,9 +119,14 @@ def test_memsize_ruby_dev_42480
119119
end
120120

121121
require 'objspace'
122+
closure_class = Class.new(Closure) do
123+
def call
124+
10
125+
end
126+
end
122127
n = 10000
123128
n.times do
124-
Closure.create(:int, [:void]) do |closure|
129+
closure_class.create(:int, [:void]) do |closure|
125130
ObjectSpace.memsize_of(closure)
126131
end
127132
end

test/fiddle/test_fiddle.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
class TestFiddle < Fiddle::TestCase
88
def test_nil_true_etc
9-
if RUBY_ENGINE == "jruby"
10-
omit("Fiddle::Q* aren't supported with JRuby")
9+
if ffi_backend?
10+
omit("Fiddle::Q* aren't supported with FFI backend")
1111
end
1212

1313
assert_equal Fiddle::Qtrue, Fiddle.dlwrap(true)
@@ -30,6 +30,10 @@ def test_dlopen_linker_script_input_linux
3030
if Dir.glob("/usr/lib/*/libncurses.so").empty?
3131
omit("libncurses.so is needed")
3232
end
33+
if ffi_backend?
34+
omit("Fiddle::Handle#file_name doesn't exist in FFI backend")
35+
end
36+
3337
# libncurses.so uses INPUT() on Debian GNU/Linux
3438
# $ cat /usr/lib/x86_64-linux-gnu/libncurses.so
3539
# INPUT(libncurses.so.6 -ltinfo)
@@ -44,6 +48,10 @@ def test_dlopen_linker_script_input_linux
4448

4549
def test_dlopen_linker_script_group_linux
4650
omit("This is only for Linux") unless RUBY_PLATFORM.match?("linux")
51+
if ffi_backend?
52+
omit("Fiddle::Handle#file_name doesn't exist in FFI backend")
53+
end
54+
4755
# libc.so uses GROUP() on Debian GNU/Linux
4856
# $ cat /usr/lib/x86_64-linux-gnu/libc.so
4957
# /* GNU ld script

test/fiddle/test_func.rb

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ def test_strtod
6464
end
6565

6666
def test_qsort1
67+
if RUBY_ENGINE == "jruby"
68+
omit("The untouched sanity check is broken on JRuby: https://github.com/jruby/jruby/issues/8365")
69+
end
70+
6771
closure_class = Class.new(Closure) do
6872
def call(x, y)
6973
Pointer.new(x)[0] <=> Pointer.new(y)[0]
@@ -74,27 +78,32 @@ def call(x, y)
7478
qsort = Function.new(@libc['qsort'],
7579
[TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP],
7680
TYPE_VOID)
77-
buff = "9341"
81+
untouched = "9341"
82+
buff = +"9341"
7883
qsort.call(buff, buff.size, 1, callback)
7984
assert_equal("1349", buff)
8085

8186
bug4929 = '[ruby-core:37395]'
82-
buff = "9341"
87+
buff = +"9341"
8388
under_gc_stress do
8489
qsort.call(buff, buff.size, 1, callback)
8590
end
8691
assert_equal("1349", buff, bug4929)
92+
93+
# Ensure the test didn't mutate String literals
94+
assert_equal("93" + "41", untouched)
8795
end
8896
ensure
8997
# We can't use ObjectSpace with JRuby.
90-
return if RUBY_ENGINE == "jruby"
91-
# Ensure freeing all closures.
92-
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
93-
not_freed_closures = []
94-
ObjectSpace.each_object(Fiddle::Closure) do |closure|
95-
not_freed_closures << closure unless closure.freed?
98+
unless RUBY_ENGINE == "jruby"
99+
# Ensure freeing all closures.
100+
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
101+
not_freed_closures = []
102+
ObjectSpace.each_object(Fiddle::Closure) do |closure|
103+
not_freed_closures << closure unless closure.freed?
104+
end
105+
assert_equal([], not_freed_closures)
96106
end
97-
assert_equal([], not_freed_closures)
98107
end
99108

100109
def test_snprintf

test/fiddle/test_function.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def test_need_gvl?
4141
if RUBY_ENGINE == "jruby"
4242
omit("rb_str_dup() doesn't exist in JRuby")
4343
end
44+
if RUBY_ENGINE == "truffleruby"
45+
omit("rb_str_dup() doesn't work with TruffleRuby")
46+
end
4447

4548
libruby = Fiddle.dlopen(nil)
4649
rb_str_dup = Function.new(libruby['rb_str_dup'],
@@ -109,8 +112,8 @@ def call one
109112
end
110113

111114
def test_last_error
112-
if RUBY_ENGINE == "jruby"
113-
omit("Fiddle.last_error doesn't work with JRuby")
115+
if ffi_backend?
116+
omit("Fiddle.last_error doesn't work with FFI backend")
114117
end
115118

116119
func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
@@ -219,6 +222,9 @@ def test_no_memory_leak
219222
if RUBY_ENGINE == "jruby"
220223
omit("rb_obj_frozen_p() doesn't exist in JRuby")
221224
end
225+
if RUBY_ENGINE == "truffleruby"
226+
omit("memory leak detection is fragile with TruffleRuby")
227+
end
222228

223229
if respond_to?(:assert_nothing_leaked_memory)
224230
rb_obj_frozen_p_symbol = Fiddle.dlopen(nil)["rb_obj_frozen_p"]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy