Skip to content

Commit 6484582

Browse files
eregonandrykonchin
authored andcommitted
Implement toDisplayString/hasMetaObject/getMetaObject for RubyIntRange and RubyLongRange
* And add tests.
1 parent 1937933 commit 6484582

File tree

11 files changed

+143
-12
lines changed

11 files changed

+143
-12
lines changed

doc/contributor/interop_details.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,28 @@
7878

7979
# Behavior of interop messages for Ruby objects
8080

81+
## toDisplayString()
82+
83+
When interop message `toDisplayString` is sent
84+
- to
85+
it returns a String representing the object.
86+
- otherwise
87+
it returns the same as `subject.inspect`.
88+
89+
## MetaObject related messages
90+
91+
When interop message `hasMetaObject` is sent
92+
- to **`nil`**, **`:symbol`**, **a `String`**, **a `BigDecimal`**, **an `Object`**, **a frozen `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **an `Exception`**, **an `Exception` with a cause**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot members**, **polyglot array** or **polyglot hash**
93+
it returns true.
94+
- otherwise
95+
it returns false.
96+
97+
When interop message `getMetaObject` is sent
98+
- to
99+
it returns the Ruby exception's class.
100+
- otherwise
101+
it returns the same Ruby class as `subject.class`.
102+
81103
## `null` related messages
82104

83105
When interop message `isNull` is sent
@@ -329,7 +351,7 @@ When interop message `writeMember` is sent
329351
it writes the given value under the given name.
330352
- to **a `StructWithValue`**
331353
it writes the value to the given struct member.
332-
- to **a `BigDecimal`**, **`nil`**, **`:symbol`** or **a frozen `Object`**
354+
- to **`nil`**, **`false`**, **`true`**, **`:symbol`**, **an `Integer`**, **a `Float`**, **a `BigDecimal`** or **a frozen `Object`**
333355
it fails with `UnsupportedMessageError` when the receiver is frozen.
334356
- otherwise
335357
it fails with `UnsupportedMessageError`.

spec/truffle/interop/matrix_spec.rb

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,23 @@ def spec_it(subject)
231231
strange_symbol: Subject.(:"strange -=@\0x2397"),
232232
empty_string: Subject.() { "" },
233233
string: Subject.(name: AN_INSTANCE, doc: true) { "string" },
234+
frozen_string: Subject.() { "frozen".freeze },
234235

235236
zero: Subject.(0),
236237
small_integer: Subject.(1, name: AN_INSTANCE, doc: true),
238+
long_integer: Subject.(1 << 60),
239+
big_integer: Subject.(1 << 80),
237240
zero_float: Subject.(0.0),
238241
small_float: Subject.(1.0, name: AN_INSTANCE, doc: true),
239242
big_decimal: Subject.(BigDecimal('1e99'), name: AN_INSTANCE, doc: true),
240243

244+
# Other immutables
245+
range: Subject.(1..),
246+
int_range: Subject.(1..2),
247+
long_range: Subject.((1 << 60)..(1 << 61)),
248+
encoding: Subject.(Encoding::UTF_8),
249+
regexp: Subject.(/regexp/),
250+
241251
object: Subject.(name: AN_INSTANCE, doc: true) { Object.new },
242252
frozen_object: Subject.(name: "a frozen `Object`", doc: true) { Object.new.freeze },
243253
struct: Subject.(name: AN_INSTANCE, doc: true, explanation: "a `Struct` with one property named `value`") { StructWithValue.new DEFAULT },
@@ -312,9 +322,9 @@ def spec_it(subject)
312322
interop_library_reference) { TruffleInteropSpecs::PolyglotHash.new }
313323
}.each { |key, subject| subject.key = key }
314324

315-
immediate_subjects = [:false, :true, :zero, :small_integer, :zero_float, :small_float]
325+
immediate_subjects = [:false, :true, :zero, :small_integer, :long_integer, :zero_float, :small_float]
316326
non_immediate_subjects = SUBJECTS.keys - immediate_subjects
317-
frozen_subjects = [:big_decimal, :nil, :symbol, :strange_symbol, :frozen_object]
327+
frozen_subjects = SUBJECTS.each_pair.select { |key, subject| key != :raise_exception && subject.value.frozen? }.map(&:first)
318328
exception_subjects = [:exception, :exception_with_cause, :raise_exception]
319329

320330
# not part of the standard matrix, not considered in last rest case
@@ -352,6 +362,28 @@ def array_element_predicate(message, predicate, insert_on_true_case)
352362
end
353363

354364
MESSAGES = [
365+
Delimiter["toDisplayString()"],
366+
Message[:toDisplayString,
367+
Test.new("returns a String representing the object", :strange_symbol, :raise_exception) do |subject|
368+
Truffle::Interop.should.string? Truffle::Interop.to_display_string(subject)
369+
end,
370+
Test.new("returns the same as `subject.inspect`") do |subject|
371+
Truffle::Interop.to_display_string(subject).should == subject.inspect
372+
end],
373+
374+
Delimiter["MetaObject related messages"],
375+
Message[:hasMetaObject,
376+
Test.new("returns true", *non_immediate_subjects, &predicate(:has_meta_object?, true)),
377+
Test.new("returns false", &predicate(:has_meta_object?, false))],
378+
Message[:getMetaObject,
379+
Test.new("returns the Ruby exception's class", :raise_exception) do |subject|
380+
Truffle::Interop.meta_object(subject).should == RuntimeError
381+
end,
382+
Test.new("returns the same Ruby class as `subject.class`") do |subject|
383+
# It works for immediate_subjects too because of Truffle::Interop.meta_object handling that
384+
Truffle::Interop.meta_object(subject).should == subject.class
385+
end],
386+
355387
Delimiter["`null` related messages"],
356388
Message[:isNull,
357389
Test.new("returns true", :nil, &predicate(:null?, true)),

spec/truffle/interop/meta_object_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
Truffle::Interop.meta_object([1, 2, 3]).should == Array
3030
end
3131

32+
it "returns Range class for a range" do
33+
Truffle::Interop.should.has_meta_object?(1..)
34+
Truffle::Interop.should.has_meta_object?(1..2)
35+
Truffle::Interop.should.has_meta_object?((1<<60)..(1<<61))
36+
37+
Truffle::Interop.meta_object(1..).should == Range
38+
Truffle::Interop.meta_object(1..2).should == Range
39+
Truffle::Interop.meta_object((1<<60)..(1<<61)).should == Range
40+
end
41+
3242
it "returns a Ruby class implementing all meta objects methods" do
3343
meta = Truffle::Interop.meta_object("string")
3444
Truffle::Interop.meta_simple_name(meta).should == 'String'

spec/truffle/parsing/fixtures/ranges/with_integer_boundaries.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ ast: |
77
ObjectLiteralNode
88
attributes:
99
flags = 1
10-
object = org.truffleruby.core.range.RubyIntRange@...(begin = 1, end = 2, excludedEnd = false)
10+
object = 1..2

spec/truffle/parsing/fixtures/ranges/with_long_integer_boundaries.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ ast: |
77
ObjectLiteralNode
88
attributes:
99
flags = 1
10-
object = org.truffleruby.core.range.RubyLongRange@...(begin = 4294967296, end = 4294967296, excludedEnd = false)
10+
object = 4294967296..4294967296

spec/truffle/parsing/fixtures/rescue/backtrace_optimization/disabled/when_range.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ ast: |
2727
ObjectLiteralNode
2828
attributes:
2929
flags = 1
30-
object = org.truffleruby.core.range.RubyIntRange@...(begin = 0, end = 42, excludedEnd = false)
30+
object = 0..42
3131
]
3232
tryPart =
3333
StringLiteralNode

spec/truffle/parsing/fixtures/rescue/modifier/backtrace_optimization/disabled/when_range.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ ast: |
2323
ObjectLiteralNode
2424
attributes:
2525
flags = 0
26-
object = org.truffleruby.core.range.RubyIntRange@...(begin = 0, end = 42, excludedEnd = false)
26+
object = 0..42
2727
]
2828
tryPart =
2929
StringLiteralNode

src/main/java/org/truffleruby/core/range/RubyIntOrLongRange.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public RubyIntOrLongRange(boolean excludedEnd) {
2929
this.excludedEnd = excludedEnd;
3030
}
3131

32+
// region Iterator messages
3233
@ExportMessage
3334
public boolean hasIterator() {
3435
return true;
@@ -41,5 +42,6 @@ public Object getIterator(
4142
final RubyContext context = RubyContext.get(node);
4243
return dispatchNode.call(context.getCoreLibrary().truffleInteropOperationsModule, "get_iterator", this);
4344
}
45+
// endregion
4446

4547
}

src/main/java/org/truffleruby/core/range/RubyIntRange.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@
99
*/
1010
package org.truffleruby.core.range;
1111

12+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
13+
import com.oracle.truffle.api.interop.InteropLibrary;
14+
import com.oracle.truffle.api.library.CachedLibrary;
15+
import com.oracle.truffle.api.library.ExportLibrary;
16+
import com.oracle.truffle.api.library.ExportMessage;
17+
import org.truffleruby.RubyContext;
18+
import org.truffleruby.core.klass.RubyClass;
1219
import org.truffleruby.core.string.StringUtils;
1320

21+
@ExportLibrary(InteropLibrary.class)
1422
public final class RubyIntRange extends RubyIntOrLongRange {
1523

1624
public final int begin;
@@ -22,10 +30,33 @@ public RubyIntRange(boolean excludedEnd, int begin, int end) {
2230
this.end = end;
2331
}
2432

33+
@TruffleBoundary
2534
@Override
2635
public String toString() {
27-
String suffix = StringUtils.format("(begin = %s, end = %s, excludedEnd = %s)", begin, end, excludedEnd);
28-
return super.toString() + suffix;
36+
if (excludedEnd) {
37+
return StringUtils.format("%d...%d", begin, end);
38+
} else {
39+
return StringUtils.format("%d..%d", begin, end);
40+
}
2941
}
3042

43+
// region InteropLibrary messages
44+
@Override
45+
@ExportMessage
46+
public String toDisplayString(boolean allowSideEffects) {
47+
return toString();
48+
}
49+
50+
@ExportMessage
51+
public boolean hasMetaObject() {
52+
return true;
53+
}
54+
55+
@ExportMessage
56+
public RubyClass getMetaObject(
57+
@CachedLibrary("this") InteropLibrary node) {
58+
return RubyContext.get(node).getCoreLibrary().rangeClass;
59+
}
60+
// endregion
61+
3162
}

src/main/java/org/truffleruby/core/range/RubyLongRange.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@
99
*/
1010
package org.truffleruby.core.range;
1111

12+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
13+
import com.oracle.truffle.api.interop.InteropLibrary;
14+
import com.oracle.truffle.api.library.CachedLibrary;
15+
import com.oracle.truffle.api.library.ExportLibrary;
16+
import com.oracle.truffle.api.library.ExportMessage;
17+
import org.truffleruby.RubyContext;
18+
import org.truffleruby.core.klass.RubyClass;
1219
import org.truffleruby.core.string.StringUtils;
1320

21+
@ExportLibrary(InteropLibrary.class)
1422
public final class RubyLongRange extends RubyIntOrLongRange {
1523

1624
public final long begin;
@@ -22,10 +30,33 @@ public RubyLongRange(boolean excludedEnd, long begin, long end) {
2230
this.end = end;
2331
}
2432

33+
@TruffleBoundary
2534
@Override
2635
public String toString() {
27-
String suffix = StringUtils.format("(begin = %s, end = %s, excludedEnd = %s)", begin, end, excludedEnd);
28-
return super.toString() + suffix;
36+
if (excludedEnd) {
37+
return StringUtils.format("%d...%d", begin, end);
38+
} else {
39+
return StringUtils.format("%d..%d", begin, end);
40+
}
2941
}
3042

43+
// region InteropLibrary messages
44+
@Override
45+
@ExportMessage
46+
public String toDisplayString(boolean allowSideEffects) {
47+
return toString();
48+
}
49+
50+
@ExportMessage
51+
public boolean hasMetaObject() {
52+
return true;
53+
}
54+
55+
@ExportMessage
56+
public RubyClass getMetaObject(
57+
@CachedLibrary("this") InteropLibrary node) {
58+
return RubyContext.get(node).getCoreLibrary().rangeClass;
59+
}
60+
// endregion
61+
3162
}

0 commit comments

Comments
 (0)