Skip to content

Commit 08836cd

Browse files
committed
[GR-19220] Better foreign exception handling (#2706)
PullRequest: truffleruby/3461
2 parents fee6dd9 + 78ff87e commit 08836cd

File tree

6 files changed

+72
-2
lines changed

6 files changed

+72
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Bug fixes:
1313
* Update `Process` methods to use `module_function` (@bjfish).
1414
* Fix `File::Stat`'s `#executable?` and `#executable_real?` predicates that unconditionally returned `true` for a superuser (#2690, @andrykonchin).
1515
* The `strip` option `--keep-section=.llvmbc` is not supported on macOS (#2697, @eregon).
16+
* Disallow the marshaling of polyglot exceptions since we can't properly reconstruct them (@nirvdrum).
1617

1718
Compatibility:
1819

@@ -23,6 +24,7 @@ Compatibility:
2324
* Fix `IO.pipe` - allow overriding `IO.new` that is used to create new pipes (#2692, @andykonchin).
2425
* Fix exception message when there are missing or extra keyword arguments - it contains all the missing/extra keywords now (#1522, @andrykonchin).
2526
* Always terminate native strings with enough `\0` bytes (#2704, @eregon).
27+
* Support `#dup` and `#clone` on foreign strings (@eregon).
2628

2729
Performance:
2830

spec/truffle/interop/polyglot/foreign_exception_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@
9090
end
9191
end
9292

93+
it 'cannot be marshaled' do
94+
-> {
95+
Marshal.dump(@foreign)
96+
}.should raise_error(TypeError)
97+
end
98+
9399
describe "when reaching the top-level" do
94100
it "is printed like a Ruby exception" do
95101
out = ruby_exe('raise Truffle::Debug.foreign_exception "main"', args: "2>&1", exit_status: 1, escape: false)

spec/truffle/interop/polyglot/foreign_string_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
@java_string = Truffle::Interop.as_string("abc")
1515
@truffle_string = Truffle::Interop.as_truffle_string("abc")
1616
@foreign_string = Truffle::Debug.foreign_string("abc")
17+
@all = [@java_character, @java_string, @truffle_string, @foreign_string]
1718
end
1819

1920
it "are of the expected Java class" do
@@ -99,6 +100,33 @@
99100
@foreign_string.should == "abc"
100101
end
101102

103+
it "supports #+@ and then mutation" do
104+
@all.each do |foreign_string|
105+
copy = +foreign_string
106+
copy << "."
107+
copy.should == "#{foreign_string}."
108+
end
109+
end
110+
111+
it "supports #dup and then mutation" do
112+
@all.each do |foreign_string|
113+
copy = foreign_string.dup
114+
copy << "."
115+
copy.should == "#{foreign_string}."
116+
end
117+
end
118+
119+
it "supports #clone and then mutation" do
120+
@all.each do |foreign_string|
121+
foreign_string.clone.should.frozen?
122+
foreign_string.clone(freeze: true).should.frozen?
123+
124+
copy = foreign_string.clone(freeze: false)
125+
copy << "."
126+
copy.should == "#{foreign_string}."
127+
end
128+
end
129+
102130
it "has all Ruby String methods" do
103131
@java_character.should == "C"
104132
@java_string.capitalize.should == "Abc"

src/main/ruby/truffleruby/core/truffle/polyglot.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,14 @@ def backtrace_locations
312312
def backtrace
313313
backtrace_locations&.map(&:to_s)
314314
end
315+
316+
def marshal_dump
317+
raise TypeError, "Foreign exception cannot be dumped: #{inspect}"
318+
end
319+
320+
def marshal_load(array)
321+
raise TypeError, 'Foreign exception cannot be restored'
322+
end
315323
end
316324

317325
module ExecutableTrait
@@ -525,11 +533,27 @@ def delete(member)
525533

526534
class ForeignObject < Object
527535
include ObjectTrait
536+
537+
class << self
538+
undef_method :new
539+
end
540+
541+
def self.__allocate__
542+
raise TypeError, "allocator undefined for #{self}"
543+
end
528544
end
529545

530546
class ForeignException < Exception # rubocop:disable Lint/InheritException
531547
include ObjectTrait
532548
include ExceptionTrait
549+
550+
class << self
551+
undef_method :new
552+
end
553+
554+
def self.__allocate__
555+
raise TypeError, "allocator undefined for #{self}"
556+
end
533557
end
534558
# endregion
535559
end

src/main/ruby/truffleruby/core/truffle/polyglot_methods.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ def clear(...)
128128
to_s.clear(...)
129129
end
130130

131+
def clone(...)
132+
to_s.clone(...)
133+
end
134+
131135
def codepoints(...)
132136
to_s.codepoints(...)
133137
end
@@ -180,6 +184,10 @@ def dump(...)
180184
to_s.dump(...)
181185
end
182186

187+
def dup(...)
188+
to_s.dup(...)
189+
end
190+
183191
def each_byte(...)
184192
to_s.each_byte(...)
185193
end

tool/generate-polyglot-methods.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ module Polyglot
1717
module StringTrait
1818
RUBY
1919

20-
string_methods = String.public_instance_methods(false).sort
20+
string_methods = String.public_instance_methods(false)
2121
# Already implemented
2222
string_methods -= %i[to_s to_str freeze frozen?]
23+
# Kernel methods which we want to handle by converting to a Ruby String first
24+
string_methods += %i[clone dup]
2325

24-
string_methods.each do |name|
26+
string_methods.sort.each do |name|
2527
code << <<-RUBY
2628
def #{name}(...)
2729
to_s.#{name}(...)

0 commit comments

Comments
 (0)