Skip to content

Commit 13f893d

Browse files
committed
No longer convert java.lang.String eagerly at the interop boundary
* Instead just treat them like any other foreign string.
1 parent e9c8ba1 commit 13f893d

24 files changed

+255
-440
lines changed

doc/contributor/interop.md

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ details.
1818
* [How to explicitly send messages from Ruby](#how-to-explicitly-send-messages-from-ruby)
1919
* [How to send messages using idiomatic Ruby](#how-to-send-messages-using-idiomatic-ruby)
2020
* [What messages are sent for Ruby syntax on foreign objects](#what-messages-are-sent-for-ruby-syntax-on-foreign-objects)
21-
* [String conversion](#string-conversion)
21+
* [Conversion of primitive values](#conversion-of-primitive-values)
2222
* [Import and export](#import-and-export)
2323
* [Interop eval](#interop-eval)
2424
* [Java interop](#java-interop)
@@ -119,39 +119,20 @@ runtime object instance.
119119
the foreign object, including allowing the special-forms listed above (see
120120
[notes on method resolution](#notes-on-method-resolution)).
121121

122-
## String conversion
123-
124-
Ruby strings and symbols are unboxable to Java strings.
122+
## Conversion of primitive values
125123

126124
A call from Ruby to a foreign language using `NEW`, `EXECUTE`, `INVOKE`, `READ`,
127-
`WRITE`, or `UNBOX`, that has Ruby strings or symbols as arguments, will convert
128-
each Ruby string or symbol argument to a Java string. You can avoid this
125+
`WRITE`, or `UNBOX`, that returns a `byte`, `short` or `float` will convert the
126+
returned value to respectively `int`, `int` or `double`. You can avoid this
129127
conversion for `EXECUTE` using `Truffle::Interop.execute_without_conversion`,
130128
for `READ` using `Truffle::Interop.read_without_conversion`, and for `UNBOX`
131129
using `Truffle::Interop.unbox_without_conversion`.
132130

131+
Import also converts `byte/short/float`, and has a `import_without_conversion` counterpart.
132+
133133
`Truffle::Interop.members` converts Java string member names to Ruby strings, so it
134134
also has a `Truffle::Interop.members_without_conversion` equivalent.
135-
136-
A call from Ruby to a foreign language using `NEW`, `EXECUTE`, `INVOKE`, `READ`,
137-
`WRITE`, or `UNBOX`, that returns a Java string will convert the returned string
138-
to a Ruby string.
139-
140-
A call from a foreign language to Ruby using `NEW`, `EXECUTE`, `INVOKE`, or
141-
`WRITE`, that has Java strings as arguments, will convert each Java string
142-
argument to a Ruby string.
143-
144-
A call from a foreign language to Ruby `NEW`, `EXECUTE`, `INVOKE` or `READ`
145-
that returns a Ruby string will not convert it to a Java string, as this would
146-
break our C extension support which uses these messages to get Ruby objects
147-
and expects to be able to mutate them and so on
148-
(compare with `Truffle::Interop.execute_without_conversion`).
149-
150-
Export and import also converts strings, and also has `_without_conversion`
151-
counterparts.
152-
153-
Boxed foreign strings (foreign objects that respond positively to `IS_BOXED` and
154-
`UNBOX` to a Java string) unbox on `to_s`, `to_str` and `inspect`.
135+
TODO remove?
155136

156137
## Import and export
157138

doc/contributor/interop_implicit_api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ Format: `Ruby code` sends `InteropLibrary message`
3131
- `foreign_object.method_name(*arguments, &block)` sends `invokeMember(foreign_object, method_name, *arguments, block)`
3232
- `foreign_object.new(*arguments)` sends `instantiate(foreign_object, *arguments)`
3333
- `foreign_object.inspect` returns a Ruby-style `#inspect` string showing members, array elements, etc
34-
- `foreign_object.to_s` sends `asString(foreign_object)` when `isString(foreign_object)` is true
34+
- `foreign_object.to_s` sends `asTruffleString(foreign_object)` when `isString(foreign_object)` is true
3535
- `foreign_object.to_s` sends `toDisplayString(foreign_object)` otherwise
36-
- `foreign_object.to_str` sends `asString(foreign_object)` when `isString(foreign_object)` is true
36+
- `foreign_object.to_str` sends `asTruffleString(foreign_object)` when `isString(foreign_object)` is true
3737
- `foreign_object.to_str` raises `NameError` otherwise
3838
- `foreign_object.to_a` converts to a Ruby `Array` with `Truffle::Interop.to_array(foreign_object)`
3939
- `foreign_object.to_ary` converts to a Ruby `Array` with `Truffle::Interop.to_array(foreign_object)`

doc/user/polyglot.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ The JVM configuration automatically has access to other languages.
2020
* [Using Ruby objects from a foreign language](#using-ruby-objects-from-a-foreign-language)
2121
* [Using foreign objects from Ruby](#using-foreign-objects-from-ruby)
2222
* [Accessing Java objects](#accessing-java-objects)
23-
* [Strings](#strings)
2423
* [Threading and interop](#threading-and-interop)
2524
* [Embedded configuration](#embedded-configuration)
2625

@@ -223,10 +222,6 @@ boolean isNull()
223222

224223
The [JRuby migration guide](jruby-migration.md) includes some more examples.
225224

226-
## Strings
227-
228-
Ruby strings and symbols are converted to Java strings when they are passed to foreign languages, and Java strings are converted to Ruby strings when they are passed into Ruby.
229-
230225
## Threading and Interop
231226

232227
Ruby is designed to be a multi-threaded language and much of the ecosystem expects threads to be available.

spec/truffle/interop/boxed_spec.rb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@
99
require_relative '../../ruby/spec_helper'
1010

1111
describe "Truffle::Interop.boxed?" do
12+
it "returns true for numbers" do
13+
Truffle::Interop.boxed?(1).should be_true
14+
Truffle::Interop.boxed?(1.2).should be_true
15+
Truffle::Interop.boxed?(Truffle::Debug.foreign_boxed_value(2)).should be_true
16+
Truffle::Interop.boxed?(Truffle::Debug.foreign_boxed_value(2.3)).should be_true
17+
end
1218

13-
it "returns true for strings" do
14-
Truffle::Interop.boxed?('test').should be_true
19+
it "returns false for strings" do
20+
Truffle::Interop.boxed?('test').should be_false
1521
end
1622

17-
it "returns true for symbols" do
18-
Truffle::Interop.boxed?(:test).should be_true
23+
it "returns false for symbols" do
24+
Truffle::Interop.boxed?(:test).should be_false
1925
end
2026

2127
it "returns false for other objects" do
2228
Truffle::Interop.boxed?(Object.new).should be_false
2329
end
24-
2530
end

spec/truffle/interop/export_spec.rb

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,59 +13,65 @@
1313
it "exports an object" do
1414
object = Object.new
1515
Truffle::Interop.export :exports_an_object, object
16-
Truffle::Interop.import(:exports_an_object).should == object
16+
Truffle::Interop.import(:exports_an_object).should.equal? object
1717
end
1818

1919
it "exports a primitive boolean" do
2020
Truffle::Interop.export :exports_a_primitive_number, true
21-
Truffle::Interop.import(:exports_a_primitive_number).should be_true
21+
Truffle::Interop.import(:exports_a_primitive_number).should.equal? true
2222
end
2323

2424
it "exports a primitive number" do
2525
Truffle::Interop.export :exports_a_primitive_number, 14
26-
Truffle::Interop.import(:exports_a_primitive_number).should == 14
26+
Truffle::Interop.import(:exports_a_primitive_number).should.equal? 14
2727
end
2828

2929
it "exports a string" do
30-
Truffle::Interop.export :exports_a_string, 'hello'
31-
(Truffle::Interop.import(:exports_a_string) == 'hello').should be_true
30+
ruby_string = 'hello'
31+
Truffle::Interop.export :exports_a_string, ruby_string
32+
Truffle::Interop.import(:exports_a_string).should.equal?(ruby_string)
3233
end
3334

34-
it "exports a symbol, getting back a string" do
35+
it "exports a symbol" do
3536
Truffle::Interop.export :exports_a_symbol, :hello
36-
(Truffle::Interop.import(:exports_a_symbol) == 'hello').should be_true
37+
Truffle::Interop.import(:exports_a_symbol).should.equal? :hello
3738
end
3839

3940
it "exports a foreign object" do
4041
foreign_object = Truffle::Debug.foreign_object
4142
Truffle::Interop.export :exports_a_foreign_object, foreign_object
42-
Truffle::Interop.import(:exports_a_foreign_object).equal?(foreign_object).should be_true
43+
Truffle::Interop.import(:exports_a_foreign_object).should.equal?(foreign_object)
4344
end
4445

4546
it "exports a Java string" do
46-
Truffle::Interop.export :exports_a_java_string, Truffle::Interop.to_java_string('hello')
47-
Truffle::Interop.import(:exports_a_java_string).should == 'hello'
47+
java_string = Truffle::Interop.to_java_string('hello')
48+
Truffle::Interop.export :exports_a_java_string, java_string
49+
Truffle::Interop.import(:exports_a_java_string).should.equal?(java_string)
50+
java_string.should == 'hello'
4851
end
4952

50-
it "converts to Java when exporting a string" do
51-
Truffle::Interop.export :exports_a_string_with_conversion, 'hello'
53+
it "does not convert to Java when exporting a Ruby string" do
54+
ruby_string = 'hello'
55+
Truffle::Interop.export :exports_a_string_with_conversion, ruby_string
5256
imported = Truffle::Interop.import_without_conversion(:exports_a_string_with_conversion)
53-
Truffle::Interop.java_string?(imported).should be_true
54-
Truffle::Interop.from_java_string(imported).should == 'hello'
57+
Truffle::Interop.should_not.java_string?(imported)
58+
imported.should.equal?(ruby_string)
5559
end
5660

5761
it "can export a string without conversion to Java" do
58-
Truffle::Interop.export_without_conversion :exports_a_string_without_conversion, 'hello'
62+
ruby_string = 'hello'
63+
Truffle::Interop.export_without_conversion :exports_a_string_without_conversion, ruby_string
5964
imported = Truffle::Interop.import_without_conversion(:exports_a_string_without_conversion)
60-
Truffle::Interop.java_string?(imported).should be_false
61-
imported.should == 'hello'
65+
Truffle::Interop.should_not.java_string?(imported)
66+
imported.should.equal?(ruby_string)
6267
end
6368

6469
it "can import a string without conversion from Java" do
65-
Truffle::Interop.export :imports_a_string_without_conversion, 'hello'
70+
ruby_string = 'hello'
71+
Truffle::Interop.export :imports_a_string_without_conversion, ruby_string
6672
imported = Truffle::Interop.import_without_conversion(:imports_a_string_without_conversion)
67-
Truffle::Interop.java_string?(imported).should be_true
68-
Truffle::Interop.from_java_string(imported).should == 'hello'
73+
Truffle::Interop.should_not.java_string?(imported)
74+
imported.should.equal?(ruby_string)
6975
end
7076

7177
it "can be used with a string name" do
@@ -85,7 +91,7 @@
8591

8692
it "returns the original exported value" do
8793
string = 'hello'
88-
Truffle::Interop.export(:returns_original_value, string).equal?(string).should be_true
94+
Truffle::Interop.export(:returns_original_value, string).should.equal?(string)
8995
end
9096
end
9197
end

spec/truffle/interop/foreign_string_spec.rb

Lines changed: 0 additions & 51 deletions
This file was deleted.

spec/truffle/interop/java_string_spec.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010

1111
describe "Java strings" do
1212

13-
it "is String" do
13+
it "is Interop.string?" do
1414
Truffle::Interop.string?(Truffle::Interop.to_java_string('test')).should be_true
1515
end
1616

17-
it "return the same object if attempted to be unboxed" do
18-
unboxed = Truffle::Interop.unbox_without_conversion(Truffle::Interop.to_java_string('test'))
19-
Truffle::Interop.java_string?(unboxed).should be_true
20-
Truffle::Interop.from_java_string(unboxed).should == 'test'
17+
it "is Interop.java_string?" do
18+
Truffle::Interop.java_string?(Truffle::Interop.to_java_string('test')).should be_true
2119
end
2220

2321
it "are converted to Ruby automatically on the LHS of string concatenation" do

spec/truffle/interop/node_library_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ def get_scopes()
7676

7777
scope["c"].should == :c
7878
scope["c"] = :d
79-
scope["c"].should == "d"
79+
scope["c"].should == :d
8080

8181
scope["a"].should == :a
8282
scope["a"] = :b
83-
scope["a"].should == "b"
83+
scope["a"].should == :b
8484

8585
-> { scope["missing"] = "missing" }.should raise_error(NameError)
8686
end

spec/truffle/interop/polyglot/class_spec.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,19 @@
5252
Truffle::Interop.to_java_array([1, 2, 3]).class.should == Polyglot::ForeignArray
5353
Truffle::Interop.to_java_list([1, 2, 3]).class.should == Polyglot::ForeignArray
5454
Java.type('java.lang.RuntimeException').new.class.should == Polyglot::ForeignException
55-
# ForeignExecutable
55+
Java.type('java.lang.Thread').currentThread.class.should == Polyglot::ForeignExecutable # Thread implements Runnable
5656
Java.type('java.math.BigInteger').class.should == Polyglot::ForeignClass
5757
Java.type('java.math.BigInteger')[:class].class.should == Polyglot::ForeignClass
5858
Java.type('int').class.should == Polyglot::ForeignMetaObject
59-
# ForeignIterable
59+
Java.type('java.util.ArrayDeque').new.class.should == Polyglot::ForeignIterable
6060
Truffle::Interop.to_java_list([1, 2, 3]).iterator.class.should == Polyglot::ForeignIterator
6161
Truffle::Debug.java_null.class.should == Polyglot::ForeignNull
6262
# ForeignNumber
6363
# ForeignPointer
64-
Java.type('java.lang.String').new("foo").class.should == String # Should be Polyglot::ForeignString
64+
Java.type('java.lang.String').new("foo").class.should == Polyglot::ForeignString
65+
Truffle::Interop.to_java_string("foo").class.should == Polyglot::ForeignString
66+
Truffle::Interop.as_string("foo").class.should == Polyglot::ForeignString
67+
Truffle::Interop.as_truffle_string("foo").class.should == Polyglot::ForeignString
6568
end
6669
end
6770
end

0 commit comments

Comments
 (0)