Skip to content

Commit

Permalink
[GR-26849] Raise a RuntimeError when calling a method needing caller …
Browse files Browse the repository at this point in the history
…data directly from a foreign language

* Instead of an internal error later on.
  • Loading branch information
eregon committed Jun 10, 2021
1 parent a50c8e5 commit 9c0f7ce
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 4 deletions.
34 changes: 34 additions & 0 deletions spec/truffle/capi/ext/truffleruby_foreign_caller_spec.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 2.0, or
* GNU General Public License version 2, or
* GNU Lesser General Public License version 2.1.
*/
#include "ruby.h"
#include "ruby/thread.h"
#include "rubyspec.h"

#ifdef __cplusplus
extern "C" {
#endif

static VALUE call_binding(VALUE self) {
return rb_tr_wrap(polyglot_invoke(rb_tr_unwrap(self), "binding"));
}

static VALUE call_binding_rb_funcall(VALUE self) {
return rb_funcall(self, rb_intern("binding"), 0);
}

void Init_truffleruby_foreign_caller_spec(void) {
VALUE cls = rb_define_class("CApiTruffleRubyForeignCallerSpecs", rb_cObject);
rb_define_method(cls, "call_binding", call_binding, 0);
rb_define_method(cls, "call_binding_rb_funcall", call_binding_rb_funcall, 0);
}

#ifdef __cplusplus
}
#endif
35 changes: 35 additions & 0 deletions spec/truffle/capi/foreign_caller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 2.0, or
# GNU General Public License version 2, or
# GNU Lesser General Public License version 2.1.

require_relative '../../ruby/optional/capi/spec_helper'

load_extension("truffleruby_foreign_caller")

describe "Calling a method needing the caller frame" do
before :each do
@s = CApiTruffleRubyForeignCallerSpecs.new
end

it "directly from C code raises a RuntimeError" do
-> {
@s.call_binding
}.should raise_error(RuntimeError, 'Cannot call Ruby method which needs caller data directly in a foreign language')
end

it "using rb_funcall() yields the Binding of rb_funcall()" do
caller_variable = nil
binding = @s.call_binding_rb_funcall
binding.should be_kind_of(Binding)

# On CRuby it would instead return the Binding of the caller Ruby frame
binding.local_variables.should.include?(:args)
binding.local_variables.should_not.include?(:caller_variable)

caller_variable.should == nil
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public R visitFrame(FrameInstance frameInstance) {
}
}

private boolean isRubyFrame(Frame frame) {
public static boolean isRubyFrame(Frame frame) {
return tryGetMethod(frame) != null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
Expand All @@ -19,9 +20,11 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;

import org.truffleruby.language.CallStackManager;
import org.truffleruby.language.FrameAndVariablesSendingNode;
import org.truffleruby.language.NotOptimizedWarningNode;
import org.truffleruby.language.RubyContextNode;
import org.truffleruby.language.control.RaiseException;

public abstract class ReadCallerDataNode extends RubyContextNode {

Expand All @@ -47,10 +50,20 @@ private Object getCallerData() {
// we don't want to deoptimize this CallTarget on every call.
getNotOptimizedNode().warn("Unoptimized reading of caller data.");
}
MaterializedFrame callerFrame = getContext()
.getCallStack()
.getCallerFrame(FrameAccess.MATERIALIZE)

final MaterializedFrame callerFrame = Truffle
.getRuntime()
.getCallerFrame()
.getFrame(FrameAccess.MATERIALIZE)
.materialize();
if (!CallStackManager.isRubyFrame(callerFrame)) {
throw new RaiseException(
getContext(),
coreExceptions().runtimeError(
"Cannot call Ruby method which needs caller data directly in a foreign language",
this));
}

return getDataFromFrame(callerFrame);
}

Expand Down

0 comments on commit 9c0f7ce

Please sign in to comment.