Skip to content

Commit 80decc9

Browse files
committed
[Truffle] Kernel#caller_locations
1 parent d46c710 commit 80decc9

File tree

8 files changed

+182
-10
lines changed

8 files changed

+182
-10
lines changed

spec/truffle/tags/core/kernel/caller_locations_tags.txt

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

spec/truffle/tags/core/thread/backtrace/location/inspect_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/truffle/tags/core/thread/backtrace/location/to_s_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

truffle/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public void init() {
102102
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, TimeNodesFactory.getFactories());
103103
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, PosixNodesFactory.getFactories());
104104
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, RubiniusTypeNodesFactory.getFactories());
105+
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, ThreadBacktraceLocationNodesFactory.getFactories());
105106

106107
// Give the core library manager a chance to tweak some of those methods
107108

truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,51 @@ public Object caller(int omit) {
331331
}
332332
}
333333

334+
@CoreMethod(names = "caller_locations", isModuleFunction = true, optional = 2)
335+
public abstract static class CallerLocationsNode extends CoreMethodArrayArgumentsNode {
336+
337+
public CallerLocationsNode(RubyContext context, SourceSection sourceSection) {
338+
super(context, sourceSection);
339+
}
340+
341+
@Specialization
342+
public RubyArray callerLocations(UndefinedPlaceholder undefined1, UndefinedPlaceholder undefined2) {
343+
return callerLocations(1, -1);
344+
}
345+
346+
@Specialization
347+
public RubyArray callerLocations(int omit, UndefinedPlaceholder undefined) {
348+
return callerLocations(omit, -1);
349+
}
350+
351+
@TruffleBoundary
352+
@Specialization
353+
public RubyArray callerLocations(int omit, int length) {
354+
final RubyClass threadBacktraceLocationClass = getContext().getCoreLibrary().getThreadBacktraceLocationClass();
355+
356+
final Backtrace backtrace = RubyCallStack.getBacktrace(this, 1 + omit, true);
357+
358+
int locationsCount = backtrace.getActivations().size();
359+
360+
if (length != -1 && locationsCount > length) {
361+
locationsCount = length;
362+
}
363+
364+
final Object[] locations = new Object[locationsCount];
365+
366+
for (int n = 0; n < locationsCount; n++) {
367+
final RubyBasicObject location = threadBacktraceLocationClass.getAllocator().allocate(getContext(), threadBacktraceLocationClass, this);
368+
369+
// TODO CS 30-Apr-15 can't set set this in the allocator? How do we get it there?
370+
ThreadBacktraceLocationNodes.setActivation(location, backtrace.getActivations().get(n));
371+
372+
locations[n] = location;
373+
}
374+
375+
return new RubyArray(getContext().getCoreLibrary().getArrayClass(), locations, locations.length);
376+
}
377+
}
378+
334379
@CoreMethod(names = "class")
335380
public abstract static class KernelClassNode extends CoreMethodArrayArgumentsNode {
336381

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 1.0
7+
* GNU General Public License version 2
8+
* GNU Lesser General Public License version 2.1
9+
*/
10+
package org.jruby.truffle.nodes.core;
11+
12+
import com.oracle.truffle.api.CompilerDirectives;
13+
import com.oracle.truffle.api.dsl.Specialization;
14+
import com.oracle.truffle.api.nodes.Node;
15+
import com.oracle.truffle.api.object.*;
16+
import com.oracle.truffle.api.source.NullSourceSection;
17+
import com.oracle.truffle.api.source.SourceSection;
18+
import org.jruby.truffle.nodes.objects.Allocator;
19+
import org.jruby.truffle.runtime.RubyContext;
20+
import org.jruby.truffle.runtime.backtrace.Activation;
21+
import org.jruby.truffle.runtime.core.RubyBasicObject;
22+
import org.jruby.truffle.runtime.core.RubyClass;
23+
import org.jruby.truffle.runtime.core.RubyString;
24+
25+
import java.util.EnumSet;
26+
import java.util.concurrent.locks.ReentrantLock;
27+
28+
@CoreClass(name = "Thread::Backtrace::Location")
29+
public class ThreadBacktraceLocationNodes {
30+
31+
private static final HiddenKey ACTIVATION_IDENTIFIER = new HiddenKey("activation");
32+
private static final Property ACTIVATION_PROPERTY;
33+
34+
static {
35+
Shape.Allocator allocator = RubyBasicObject.LAYOUT.createAllocator();
36+
ACTIVATION_PROPERTY = Property.create(ACTIVATION_IDENTIFIER, allocator.locationForType(ReentrantLock.class, EnumSet.of(LocationModifier.NonNull)), 0);
37+
}
38+
39+
public static Allocator createThreadBacktraceLocationAllocator(Shape emptyShape) {
40+
final Shape shape = emptyShape.addProperty(ACTIVATION_PROPERTY);
41+
final DynamicObjectFactory factory = shape.createFactory();
42+
43+
return new Allocator() {
44+
@Override
45+
public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, Node currentNode) {
46+
return new RubyBasicObject(rubyClass, factory.newInstance(new ReentrantLock()));
47+
}
48+
};
49+
}
50+
51+
public static void setActivation(RubyBasicObject threadBacktraceLocation, Activation activation) {
52+
assert threadBacktraceLocation.getDynamicObject().getShape().hasProperty(ACTIVATION_IDENTIFIER);
53+
54+
try {
55+
ACTIVATION_PROPERTY.set(threadBacktraceLocation.getDynamicObject(), activation, threadBacktraceLocation.getDynamicObject().getShape());
56+
} catch (IncompatibleLocationException | FinalLocationException e) {
57+
throw new UnsupportedOperationException();
58+
}
59+
}
60+
61+
protected static Activation getActivation(RubyBasicObject threadBacktraceLocation) {
62+
assert threadBacktraceLocation.getDynamicObject().getShape().hasProperty(ACTIVATION_IDENTIFIER);
63+
return (Activation) ACTIVATION_PROPERTY.get(threadBacktraceLocation.getDynamicObject(), true);
64+
}
65+
66+
@CoreMethod(names = "absolute_path")
67+
public abstract static class AbsolutePathNode extends UnaryCoreMethodNode {
68+
69+
public AbsolutePathNode(RubyContext context, SourceSection sourceSection) {
70+
super(context, sourceSection);
71+
}
72+
73+
@CompilerDirectives.TruffleBoundary
74+
@Specialization
75+
public RubyString absolutePath(RubyBasicObject threadBacktraceLocation) {
76+
final Activation activation = getActivation(threadBacktraceLocation);
77+
78+
final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();
79+
80+
if (sourceSection instanceof NullSourceSection) {
81+
return getContext().makeString(sourceSection.getShortDescription());
82+
}
83+
84+
// TODO CS 30-Apr-15: not absolute - not sure how to solve that
85+
86+
return getContext().makeString(sourceSection.getSource().getPath());
87+
}
88+
89+
}
90+
91+
@CoreMethod(names = {"to_s", "inspect"})
92+
public abstract static class ToSNode extends UnaryCoreMethodNode {
93+
94+
public ToSNode(RubyContext context, SourceSection sourceSection) {
95+
super(context, sourceSection);
96+
}
97+
98+
@CompilerDirectives.TruffleBoundary
99+
@Specialization
100+
public RubyString toS(RubyBasicObject threadBacktraceLocation) {
101+
final Activation activation = getActivation(threadBacktraceLocation);
102+
103+
final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();
104+
105+
if (sourceSection instanceof NullSourceSection) {
106+
return getContext().makeString(sourceSection.getShortDescription());
107+
}
108+
109+
return getContext().makeString(String.format("%s:%d:in `%s'",
110+
sourceSection.getSource().getShortName(),
111+
sourceSection.getStartLine(),
112+
sourceSection.getIdentifier()));
113+
}
114+
115+
}
116+
117+
}

truffle/src/main/java/org/jruby/truffle/runtime/RubyCallStack.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
1616
import com.oracle.truffle.api.frame.VirtualFrame;
1717
import com.oracle.truffle.api.nodes.Node;
18+
import com.oracle.truffle.api.source.NullSourceSection;
1819
import org.jruby.truffle.nodes.CoreSourceSection;
1920
import org.jruby.truffle.runtime.backtrace.Activation;
2021
import org.jruby.truffle.runtime.backtrace.Backtrace;
@@ -59,7 +60,11 @@ public static Backtrace getBacktrace(Node currentNode) {
5960
return getBacktrace(currentNode, 0);
6061
}
6162

62-
public static Backtrace getBacktrace(Node currentNode, final int omit) {
63+
public static Backtrace getBacktrace(Node currentNode, int omit) {
64+
return getBacktrace(currentNode, omit, false);
65+
}
66+
67+
public static Backtrace getBacktrace(Node currentNode, final int omit, final boolean filterNullSourceSection) {
6368
CompilerAsserts.neverPartOfCompilation();
6469

6570
final ArrayList<Activation> activations = new ArrayList<>();
@@ -83,8 +88,10 @@ public InternalMethod visitFrame(FrameInstance frameInstance) {
8388
// Multiple top level methods (require) introduce null call nodes - ignore them
8489

8590
if (frameInstance.getCallNode() != null && depth >= omit) {
86-
activations.add(new Activation(frameInstance.getCallNode(),
87-
frameInstance.getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize()));
91+
if (!(frameInstance.getCallNode().getEncapsulatingSourceSection() instanceof NullSourceSection)) {
92+
activations.add(new Activation(frameInstance.getCallNode(),
93+
frameInstance.getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize()));
94+
}
8895
}
8996
depth++;
9097

truffle/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.jruby.truffle.nodes.core.ArrayNodes;
2525
import org.jruby.truffle.nodes.core.MutexNodes;
2626
import org.jruby.truffle.nodes.core.ProcessNodes;
27+
import org.jruby.truffle.nodes.core.ThreadBacktraceLocationNodes;
2728
import org.jruby.truffle.nodes.objects.Allocator;
2829
import org.jruby.truffle.nodes.rubinius.NativeFunctionPrimitiveNodes;
2930
import org.jruby.truffle.runtime.RubyCallStack;
@@ -100,6 +101,8 @@ public class CoreLibrary {
100101
private final RubyClass syntaxErrorClass;
101102
private final RubyClass systemCallErrorClass;
102103
private final RubyClass threadClass;
104+
private final RubyClass threadBacktraceClass;
105+
private final RubyClass threadBacktraceLocationClass;
103106
private final RubyClass timeClass;
104107
private final RubyClass transcodingClass;
105108
private final RubyClass trueClass;
@@ -271,6 +274,8 @@ public CoreLibrary(RubyContext context) {
271274
stringClass = defineClass("String", new RubyString.StringAllocator());
272275
symbolClass = defineClass("Symbol");
273276
threadClass = defineClass("Thread", new RubyThread.ThreadAllocator());
277+
threadBacktraceClass = defineClass(threadClass, objectClass, "Backtrace");
278+
threadBacktraceLocationClass = defineClass(threadBacktraceClass, objectClass, "Location", ThreadBacktraceLocationNodes.createThreadBacktraceLocationAllocator(context.getEmptyShape()));
274279
timeClass = defineClass("Time", new RubyTime.TimeAllocator());
275280
trueClass = defineClass("TrueClass");
276281
unboundMethodClass = defineClass("UnboundMethod");
@@ -1243,4 +1248,8 @@ public RubyClass getSymbolClass() {
12431248
return symbolClass;
12441249
}
12451250

1251+
public RubyClass getThreadBacktraceLocationClass() {
1252+
return threadBacktraceLocationClass;
1253+
}
1254+
12461255
}

0 commit comments

Comments
 (0)