Skip to content

Commit 1d1953c

Browse files
committed
Don't use ReadCallerFrameNode in BasicObjectNodes.InstanceEvalNode class
1 parent c7a8533 commit 1d1953c

File tree

4 files changed

+120
-80
lines changed

4 files changed

+120
-80
lines changed

spec/ruby/core/basicobject/instance_eval_spec.rb

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@
2020
a.instance_eval('self').equal?(a).should be_true
2121
end
2222

23-
it "expects a block with no arguments" do
24-
-> { "hola".instance_eval }.should raise_error(ArgumentError)
23+
it "raises an ArgumentError when no arguments and no block are given" do
24+
-> { "hola".instance_eval }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)")
2525
end
2626

27-
it "takes no arguments with a block" do
28-
-> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError)
27+
it "raises an ArgumentError when a block and normal arguments are given" do
28+
-> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)")
29+
end
30+
31+
it "raises an ArgumentError when more than 3 arguments are given" do
32+
-> {
33+
"hola".instance_eval("1 + 1", "some file", 0, "bogus")
34+
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
2935
end
3036

3137
it "yields the object to the block" do
@@ -185,4 +191,58 @@ def meth(arg); arg; end
185191

186192
x.should == :value
187193
end
194+
195+
it "converts string argument with #to_str method" do
196+
source_code = Object.new
197+
def source_code.to_str() "1" end
198+
199+
a = BasicObject.new
200+
a.instance_eval(source_code).should == 1
201+
end
202+
203+
it "raises ArgumentError if returned value is not String" do
204+
source_code = Object.new
205+
def source_code.to_str() :symbol end
206+
207+
a = BasicObject.new
208+
-> { a.instance_eval(source_code) }.should raise_error(TypeError, /can't convert Object to String/)
209+
end
210+
211+
it "converts filename argument with #to_str method" do
212+
filename = Object.new
213+
def filename.to_str() "file.rb" end
214+
215+
err = begin
216+
Object.new.instance_eval("raise", filename)
217+
rescue => e
218+
e
219+
end
220+
err.backtrace.first.split(":")[0].should == "file.rb"
221+
end
222+
223+
it "raises ArgumentError if returned value is not String" do
224+
filename = Object.new
225+
def filename.to_str() :symbol end
226+
227+
-> { Object.new.instance_eval("raise", filename) }.should raise_error(TypeError, /can't convert Object to String/)
228+
end
229+
230+
it "converts lineno argument with #to_int method" do
231+
lineno = Object.new
232+
def lineno.to_int() 15 end
233+
234+
err = begin
235+
Object.new.instance_eval("raise", "file.rb", lineno)
236+
rescue => e
237+
e
238+
end
239+
err.backtrace.first.split(":")[1].should == "15"
240+
end
241+
242+
it "raises ArgumentError if returned value is not Integer" do
243+
lineno = Object.new
244+
def lineno.to_int() :symbol end
245+
246+
-> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_error(TypeError, /can't convert Object to Integer/)
247+
end
188248
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
fails:BasicObject#instance_eval gets constants in the receiver if a string given
2+
fails:BasicObject#instance_eval raises an ArgumentError when more than 3 arguments are given

src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java

Lines changed: 50 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.oracle.truffle.api.interop.InteropLibrary;
1414
import com.oracle.truffle.api.interop.UnsupportedMessageException;
1515
import com.oracle.truffle.api.object.Shape;
16+
import com.oracle.truffle.api.profiles.BranchProfile;
1617
import com.oracle.truffle.api.strings.AbstractTruffleString;
1718
import org.truffleruby.Layouts;
1819
import org.truffleruby.builtins.CoreMethod;
@@ -23,6 +24,8 @@
2324
import org.truffleruby.core.basicobject.BasicObjectNodesFactory.ReferenceEqualNodeFactory;
2425
import org.truffleruby.core.cast.BooleanCastNode;
2526
import org.truffleruby.core.cast.NameToJavaStringNode;
27+
import org.truffleruby.core.cast.ToIntNode;
28+
import org.truffleruby.core.cast.ToStrNode;
2629
import org.truffleruby.core.encoding.RubyEncoding;
2730
import org.truffleruby.core.exception.ExceptionOperations.ExceptionFormatter;
2831
import org.truffleruby.core.exception.RubyException;
@@ -44,7 +47,6 @@
4447
import org.truffleruby.language.RubyNode;
4548
import org.truffleruby.language.RubySourceNode;
4649
import org.truffleruby.language.Visibility;
47-
import org.truffleruby.language.arguments.ReadCallerFrameNode;
4850
import org.truffleruby.language.arguments.RubyArguments;
4951
import org.truffleruby.language.control.RaiseException;
5052
import org.truffleruby.language.dispatch.DispatchNode;
@@ -316,94 +318,66 @@ protected Object initialize(Frame callerFrame, Object self, Object[] rubyArgs, R
316318
}
317319
}
318320

319-
@CoreMethod(names = "instance_eval", needsBlock = true, optional = 3, lowerFixnum = 3)
320-
public abstract static class InstanceEvalNode extends CoreMethodArrayArgumentsNode {
321-
322-
@Specialization(guards = { "strings.isRubyString(string)", "stringsFileName.isRubyString(fileName)" },
323-
limit = "1")
324-
protected Object instanceEval(
325-
VirtualFrame frame, Object receiver, Object string, Object fileName, int line, Nil block,
326-
@Cached RubyStringLibrary strings,
327-
@Cached RubyStringLibrary stringsFileName,
328-
@Cached ToJavaStringNode toJavaStringNode,
329-
@Cached ReadCallerFrameNode callerFrameNode,
330-
@Cached IndirectCallNode callNode) {
331-
final MaterializedFrame callerFrame = callerFrameNode.execute(frame);
321+
@GenerateUncached
322+
@CoreMethod(names = "instance_eval", needsBlock = true, optional = 3, alwaysInlined = true)
323+
public abstract static class InstanceEvalNode extends AlwaysInlinedMethodNode {
324+
325+
@Specialization(guards = "isBlockProvided(rubyArgs)")
326+
protected Object evalWithBlock(Frame callerFrame, Object self, Object[] rubyArgs, RootCallTarget target,
327+
@Cached(allowUncached = true) InstanceExecNode instanceExecNode,
328+
@Cached BranchProfile errorProfile) {
329+
final int count = RubyArguments.getPositionalArgumentsCount(rubyArgs, false);
330+
331+
if (count > 0) {
332+
errorProfile.enter();
333+
throw new RaiseException(getContext(), coreExceptions().argumentError(count, 0, this));
334+
}
332335

333-
return instanceEvalHelper(
334-
callerFrame,
335-
receiver,
336-
strings.getTString(string),
337-
strings.getEncoding(string),
338-
toJavaStringNode.executeToJavaString(fileName),
339-
line,
340-
callNode);
336+
final Object block = RubyArguments.getBlock(rubyArgs);
337+
return instanceExecNode.executeInstanceExec(callerFrame.materialize(), self, new Object[]{ self },
338+
(RubyProc) block);
341339
}
342340

343-
@Specialization(guards = { "strings.isRubyString(string)", "stringsFileName.isRubyString(fileName)" },
344-
limit = "1")
345-
protected Object instanceEval(
346-
VirtualFrame frame, Object receiver, Object string, Object fileName, NotProvided line, Nil block,
341+
@Specialization(guards = "!isBlockProvided(rubyArgs)")
342+
protected Object evalWithString(Frame callerFrame, Object self, Object[] rubyArgs, RootCallTarget target,
343+
@Cached BranchProfile errorProfile,
347344
@Cached RubyStringLibrary strings,
348-
@Cached RubyStringLibrary stringsFileName,
349345
@Cached ToJavaStringNode toJavaStringNode,
350-
@Cached ReadCallerFrameNode callerFrameNode,
346+
@Cached ToStrNode toStrNode,
347+
@Cached ToIntNode toIntNode,
351348
@Cached IndirectCallNode callNode) {
352-
final MaterializedFrame callerFrame = callerFrameNode.execute(frame);
349+
final Object sourceCode;
350+
String fileName = coreStrings().EVAL_FILENAME_STRING.toString();
351+
int line = 1;
352+
int count = RubyArguments.getPositionalArgumentsCount(rubyArgs, false);
353+
354+
if (count == 0) {
355+
errorProfile.enter();
356+
throw new RaiseException(getContext(), coreExceptions().argumentError(0, 1, 2, this));
357+
}
353358

354-
return instanceEvalHelper(
355-
callerFrame,
356-
receiver,
357-
strings.getTString(string),
358-
strings.getEncoding(string),
359-
toJavaStringNode.executeToJavaString(fileName),
360-
1,
361-
callNode);
362-
}
359+
sourceCode = toStrNode.execute(RubyArguments.getArgument(rubyArgs, 0));
363360

364-
@Specialization(guards = "strings.isRubyString(string)", limit = "1")
365-
protected Object instanceEval(
366-
VirtualFrame frame, Object receiver, Object string, NotProvided fileName, NotProvided line, Nil block,
367-
@Cached RubyStringLibrary strings,
368-
@Cached ReadCallerFrameNode callerFrameNode,
369-
@Cached IndirectCallNode callNode) {
370-
final MaterializedFrame callerFrame = callerFrameNode.execute(frame);
361+
if (count >= 2) {
362+
fileName = toJavaStringNode
363+
.executeToJavaString(toStrNode.execute(RubyArguments.getArgument(rubyArgs, 1)));
364+
}
365+
366+
if (count >= 3) {
367+
line = toIntNode.execute(RubyArguments.getArgument(rubyArgs, 2));
368+
}
371369

370+
needCallerFrame(callerFrame, target);
372371
return instanceEvalHelper(
373-
callerFrame,
374-
receiver,
375-
strings.getTString(string),
376-
strings.getEncoding(string),
377-
coreStrings().EVAL_FILENAME_STRING.toString(),
378-
1,
372+
callerFrame.materialize(),
373+
self,
374+
strings.getTString(sourceCode),
375+
strings.getEncoding(sourceCode),
376+
fileName,
377+
line,
379378
callNode);
380379
}
381380

382-
@Specialization
383-
protected Object instanceEval(
384-
VirtualFrame frame,
385-
Object receiver,
386-
NotProvided string,
387-
NotProvided fileName,
388-
NotProvided line,
389-
RubyProc block,
390-
@Cached InstanceExecNode instanceExecNode) {
391-
return instanceExecNode.executeInstanceExec(frame, receiver, new Object[]{ receiver }, block);
392-
}
393-
394-
@Specialization
395-
protected Object noArgsNoBlock(
396-
Object receiver, NotProvided string, NotProvided fileName, NotProvided line, Nil block) {
397-
throw new RaiseException(getContext(), coreExceptions().argumentError(0, 1, 2, this));
398-
}
399-
400-
@Specialization(guards = "wasProvided(string)")
401-
protected Object argsAndBlock(
402-
Object receiver, Object string, Object maybeFileName, Object maybeLine, RubyProc block) {
403-
final int passed = RubyGuards.wasProvided(maybeLine) ? 3 : RubyGuards.wasProvided(maybeFileName) ? 2 : 1;
404-
throw new RaiseException(getContext(), coreExceptions().argumentError(passed, 0, this));
405-
}
406-
407381
@TruffleBoundary
408382
private Object instanceEvalHelper(MaterializedFrame callerFrame, Object receiver, AbstractTruffleString code,
409383
RubyEncoding encoding,

src/main/java/org/truffleruby/core/inlined/AlwaysInlinedMethodNode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.oracle.truffle.api.frame.Frame;
1717
import org.truffleruby.language.CallStackManager;
1818
import org.truffleruby.language.RubyBaseNode;
19+
import org.truffleruby.language.arguments.RubyArguments;
1920
import org.truffleruby.language.control.RaiseException;
2021

2122
/** A core method that should always be executed inline, without going through a CallTarget. That enables accessing the
@@ -66,4 +67,8 @@ private RaiseException buildException(String method) {
6667
getNode()));
6768
}
6869

70+
public static boolean isBlockProvided(Object[] rubyArgs) {
71+
return RubyArguments.getBlock(rubyArgs) != nil;
72+
}
73+
6974
}

0 commit comments

Comments
 (0)