Skip to content

Commit

Permalink
[Truffle] Kernel#caller_locations
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed May 1, 2015
1 parent d46c710 commit 80decc9
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 10 deletions.
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/kernel/caller_locations_tags.txt

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public void init() {
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, TimeNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, PosixNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, RubiniusTypeNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, ThreadBacktraceLocationNodesFactory.getFactories());

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,51 @@ public Object caller(int omit) {
}
}

@CoreMethod(names = "caller_locations", isModuleFunction = true, optional = 2)
public abstract static class CallerLocationsNode extends CoreMethodArrayArgumentsNode {

public CallerLocationsNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization
public RubyArray callerLocations(UndefinedPlaceholder undefined1, UndefinedPlaceholder undefined2) {
return callerLocations(1, -1);
}

@Specialization
public RubyArray callerLocations(int omit, UndefinedPlaceholder undefined) {
return callerLocations(omit, -1);
}

@TruffleBoundary
@Specialization
public RubyArray callerLocations(int omit, int length) {
final RubyClass threadBacktraceLocationClass = getContext().getCoreLibrary().getThreadBacktraceLocationClass();

final Backtrace backtrace = RubyCallStack.getBacktrace(this, 1 + omit, true);

int locationsCount = backtrace.getActivations().size();

if (length != -1 && locationsCount > length) {
locationsCount = length;
}

final Object[] locations = new Object[locationsCount];

for (int n = 0; n < locationsCount; n++) {
final RubyBasicObject location = threadBacktraceLocationClass.getAllocator().allocate(getContext(), threadBacktraceLocationClass, this);

// TODO CS 30-Apr-15 can't set set this in the allocator? How do we get it there?
ThreadBacktraceLocationNodes.setActivation(location, backtrace.getActivations().get(n));

locations[n] = location;
}

return new RubyArray(getContext().getCoreLibrary().getArrayClass(), locations, locations.length);
}
}

@CoreMethod(names = "class")
public abstract static class KernelClassNode extends CoreMethodArrayArgumentsNode {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2015 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 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.core;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.*;
import com.oracle.truffle.api.source.NullSourceSection;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.backtrace.Activation;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyClass;
import org.jruby.truffle.runtime.core.RubyString;

import java.util.EnumSet;
import java.util.concurrent.locks.ReentrantLock;

@CoreClass(name = "Thread::Backtrace::Location")
public class ThreadBacktraceLocationNodes {

private static final HiddenKey ACTIVATION_IDENTIFIER = new HiddenKey("activation");
private static final Property ACTIVATION_PROPERTY;

static {
Shape.Allocator allocator = RubyBasicObject.LAYOUT.createAllocator();
ACTIVATION_PROPERTY = Property.create(ACTIVATION_IDENTIFIER, allocator.locationForType(ReentrantLock.class, EnumSet.of(LocationModifier.NonNull)), 0);
}

public static Allocator createThreadBacktraceLocationAllocator(Shape emptyShape) {
final Shape shape = emptyShape.addProperty(ACTIVATION_PROPERTY);
final DynamicObjectFactory factory = shape.createFactory();

return new Allocator() {
@Override
public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, Node currentNode) {
return new RubyBasicObject(rubyClass, factory.newInstance(new ReentrantLock()));
}
};
}

public static void setActivation(RubyBasicObject threadBacktraceLocation, Activation activation) {
assert threadBacktraceLocation.getDynamicObject().getShape().hasProperty(ACTIVATION_IDENTIFIER);

try {
ACTIVATION_PROPERTY.set(threadBacktraceLocation.getDynamicObject(), activation, threadBacktraceLocation.getDynamicObject().getShape());
} catch (IncompatibleLocationException | FinalLocationException e) {
throw new UnsupportedOperationException();
}
}

protected static Activation getActivation(RubyBasicObject threadBacktraceLocation) {
assert threadBacktraceLocation.getDynamicObject().getShape().hasProperty(ACTIVATION_IDENTIFIER);
return (Activation) ACTIVATION_PROPERTY.get(threadBacktraceLocation.getDynamicObject(), true);
}

@CoreMethod(names = "absolute_path")
public abstract static class AbsolutePathNode extends UnaryCoreMethodNode {

public AbsolutePathNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public RubyString absolutePath(RubyBasicObject threadBacktraceLocation) {
final Activation activation = getActivation(threadBacktraceLocation);

final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();

if (sourceSection instanceof NullSourceSection) {
return getContext().makeString(sourceSection.getShortDescription());
}

// TODO CS 30-Apr-15: not absolute - not sure how to solve that

return getContext().makeString(sourceSection.getSource().getPath());
}

}

@CoreMethod(names = {"to_s", "inspect"})
public abstract static class ToSNode extends UnaryCoreMethodNode {

public ToSNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public RubyString toS(RubyBasicObject threadBacktraceLocation) {
final Activation activation = getActivation(threadBacktraceLocation);

final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();

if (sourceSection instanceof NullSourceSection) {
return getContext().makeString(sourceSection.getShortDescription());
}

return getContext().makeString(String.format("%s:%d:in `%s'",
sourceSection.getSource().getShortName(),
sourceSection.getStartLine(),
sourceSection.getIdentifier()));
}

}

}
13 changes: 10 additions & 3 deletions truffle/src/main/java/org/jruby/truffle/runtime/RubyCallStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.NullSourceSection;
import org.jruby.truffle.nodes.CoreSourceSection;
import org.jruby.truffle.runtime.backtrace.Activation;
import org.jruby.truffle.runtime.backtrace.Backtrace;
Expand Down Expand Up @@ -59,7 +60,11 @@ public static Backtrace getBacktrace(Node currentNode) {
return getBacktrace(currentNode, 0);
}

public static Backtrace getBacktrace(Node currentNode, final int omit) {
public static Backtrace getBacktrace(Node currentNode, int omit) {
return getBacktrace(currentNode, omit, false);
}

public static Backtrace getBacktrace(Node currentNode, final int omit, final boolean filterNullSourceSection) {
CompilerAsserts.neverPartOfCompilation();

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

if (frameInstance.getCallNode() != null && depth >= omit) {
activations.add(new Activation(frameInstance.getCallNode(),
frameInstance.getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize()));
if (!(frameInstance.getCallNode().getEncapsulatingSourceSection() instanceof NullSourceSection)) {
activations.add(new Activation(frameInstance.getCallNode(),
frameInstance.getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize()));
}
}
depth++;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.jruby.truffle.nodes.core.ArrayNodes;
import org.jruby.truffle.nodes.core.MutexNodes;
import org.jruby.truffle.nodes.core.ProcessNodes;
import org.jruby.truffle.nodes.core.ThreadBacktraceLocationNodes;
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.nodes.rubinius.NativeFunctionPrimitiveNodes;
import org.jruby.truffle.runtime.RubyCallStack;
Expand Down Expand Up @@ -100,6 +101,8 @@ public class CoreLibrary {
private final RubyClass syntaxErrorClass;
private final RubyClass systemCallErrorClass;
private final RubyClass threadClass;
private final RubyClass threadBacktraceClass;
private final RubyClass threadBacktraceLocationClass;
private final RubyClass timeClass;
private final RubyClass transcodingClass;
private final RubyClass trueClass;
Expand Down Expand Up @@ -271,6 +274,8 @@ public CoreLibrary(RubyContext context) {
stringClass = defineClass("String", new RubyString.StringAllocator());
symbolClass = defineClass("Symbol");
threadClass = defineClass("Thread", new RubyThread.ThreadAllocator());
threadBacktraceClass = defineClass(threadClass, objectClass, "Backtrace");
threadBacktraceLocationClass = defineClass(threadBacktraceClass, objectClass, "Location", ThreadBacktraceLocationNodes.createThreadBacktraceLocationAllocator(context.getEmptyShape()));
timeClass = defineClass("Time", new RubyTime.TimeAllocator());
trueClass = defineClass("TrueClass");
unboundMethodClass = defineClass("UnboundMethod");
Expand Down Expand Up @@ -1243,4 +1248,8 @@ public RubyClass getSymbolClass() {
return symbolClass;
}

public RubyClass getThreadBacktraceLocationClass() {
return threadBacktraceLocationClass;
}

}

0 comments on commit 80decc9

Please sign in to comment.