Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Truffle] Compiled Ruby snippets in Java.
Browse files Browse the repository at this point in the history
chrisseaton committed Apr 15, 2016
1 parent 44af462 commit 94dd7c3
Showing 7 changed files with 178 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -710,7 +710,7 @@ protected RootNodeWrapper compileSource(VirtualFrame frame, DynamicObject source

final TranslatorDriver translator = new TranslatorDriver(getContext());

return new RootNodeWrapper(translator.parse(getContext(), source, encoding, ParserContext.EVAL, null, parentFrame, true, this));
return new RootNodeWrapper(translator.parse(getContext(), source, encoding, ParserContext.EVAL, null, null, parentFrame, true, this));
}

protected boolean parseDependsOnDeclarationFrame(RootNodeWrapper rootNode) {
Original file line number Diff line number Diff line change
@@ -82,6 +82,7 @@
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.SnippetNode;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;
@@ -126,8 +127,8 @@ public DynamicObject allocate(DynamicObject rubyClass) {

@CoreMethod(names = "+", required = 1)
@NodeChildren({
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "other")
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "other")
})
public abstract static class AddNode extends CoreMethodNode {

@@ -406,9 +407,18 @@ public Object concatString(DynamicObject string, DynamicObject other) {
}

@Specialization(guards = "!isRubyString(other)")
public Object concat(DynamicObject string, Object other) {
return ruby("string.concat_internal(other)", "string", string, "other", other);
public Object concat(
VirtualFrame frame,
DynamicObject string,
Object other,
@Cached("createSnippet()") SnippetNode snippet) {
return snippet.execute(frame, string, other);
}

protected SnippetNode createSnippet() {
return new SnippetNode("string.concat_internal(other)", "string", "other");
}

}

@CoreMethod(names = {"[]", "slice"}, required = 1, optional = 1, lowerFixnumParameters = {0, 1}, taintFromSelf = true)
@@ -656,8 +666,8 @@ public int byteSize(DynamicObject string) {

@CoreMethod(names = "casecmp", required = 1)
@NodeChildren({
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "other")
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "other")
})
public abstract static class CaseCmpNode extends CoreMethodNode {

@@ -1270,9 +1280,9 @@ public Object initializeCopy(DynamicObject self, DynamicObject from) {

@CoreMethod(names = "insert", required = 2, lowerFixnumParameters = 0, raiseIfFrozenSelf = true)
@NodeChildren({
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "index"),
@NodeChild(type = RubyNode.class, value = "otherString")
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "index"),
@NodeChild(type = RubyNode.class, value = "otherString")
})
public abstract static class InsertNode extends CoreMethodNode {

@@ -1541,8 +1551,8 @@ private int codePoint(Encoding encoding, byte[] bytes, int p, int end) {

@CoreMethod(names = "replace", required = 1, raiseIfFrozenSelf = true, taintFromParameter = 0)
@NodeChildren({
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "other")
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "other")
})
public abstract static class ReplaceNode extends CoreMethodNode {

@@ -1750,9 +1760,9 @@ private ByteList dumpCommon(DynamicObject string) {

@CoreMethod(names = "setbyte", required = 2, raiseIfFrozenSelf = true)
@NodeChildren({
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "index"),
@NodeChild(type = RubyNode.class, value = "value")
@NodeChild(type = RubyNode.class, value = "string"),
@NodeChild(type = RubyNode.class, value = "index"),
@NodeChild(type = RubyNode.class, value = "value")
})
public abstract static class SetByteNode extends CoreMethodNode {

@@ -2167,9 +2177,9 @@ public static boolean reverseIsEqualToSelf(DynamicObject string) {

@CoreMethod(names = "tr!", required = 2, raiseIfFrozenSelf = true)
@NodeChildren({
@NodeChild(type = RubyNode.class, value = "self"),
@NodeChild(type = RubyNode.class, value = "fromStr"),
@NodeChild(type = RubyNode.class, value = "toStrNode")
@NodeChild(type = RubyNode.class, value = "self"),
@NodeChild(type = RubyNode.class, value = "fromStr"),
@NodeChild(type = RubyNode.class, value = "toStrNode")
})
@ImportStatic(StringGuards.class)
public abstract static class TrBangNode extends CoreMethodNode {
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ public Object execute(VirtualFrame frame) {
final TranslatorDriver translator = new TranslatorDriver(context);

final RubyRootNode rootNode = translator.parse(context, source, UTF8Encoding.INSTANCE,
ParserContext.TOP_LEVEL, argumentNames, null, true, null);
ParserContext.TOP_LEVEL, argumentNames, null, null, true, null);

final CallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);

109 changes: 109 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/language/SnippetNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2016 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.language;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.Source;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.arguments.RubyArguments;
import org.jruby.truffle.language.methods.DeclarationContext;
import org.jruby.truffle.language.parser.ParserContext;

public class SnippetNode extends RubyBaseNode {

private final String expression;
private final String[] parameters;

@CompilationFinal private FrameDescriptor frameDescriptor;
@CompilationFinal private FrameSlot[] parameterFrameSlots;

@Child private DirectCallNode directCallNode;

public SnippetNode(String expression, String... parameters) {
this.expression = expression;
this.parameters = parameters;
}

@ExplodeLoop
public Object execute(VirtualFrame frame, Object... arguments) {
if (directCallNode == null) {
CompilerDirectives.transferToInterpreter();

frameDescriptor = new FrameDescriptor(nil());
parameterFrameSlots = new FrameSlot[parameters.length];

for (int n = 0; n < parameters.length; n++) {
parameterFrameSlots[n] = frameDescriptor.findOrAddFrameSlot(parameters[n]);
}

final Source source = Source.fromText(StringOperations.createByteList(expression), "(snippet)");

final RubyRootNode rootNode = getContext().getCodeLoader().parse(
source,
UTF8Encoding.INSTANCE,
ParserContext.INLINE,
frameDescriptor,
null,
true,
this);

directCallNode = insert(Truffle.getRuntime().createDirectCallNode(Truffle.getRuntime().createCallTarget(rootNode)));

if (directCallNode.isInlinable()) {
directCallNode.forceInlining();
}
}

final Object[] parentFrameArguments = RubyArguments.pack(
null,
null,
RubyArguments.getMethod(frame),
DeclarationContext.INSTANCE_EVAL,
null,
RubyArguments.getSelf(frame),
null,
new Object[]{});

final MaterializedFrame parentFrame = Truffle.getRuntime().createMaterializedFrame(
parentFrameArguments,
frameDescriptor);

if (arguments.length != parameterFrameSlots.length) {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException("number of arguments doesn't match number of parameters");
}

for (int n = 0; n < parameters.length; n++) {
parentFrame.setObject(parameterFrameSlots[n], arguments[n]);
}

final Object[] callArguments = RubyArguments.pack(
parentFrame,
null,
RubyArguments.getMethod(frame),
DeclarationContext.INSTANCE_EVAL,
null,
RubyArguments.getSelf(frame),
null,
new Object[]{});

return directCallNode.call(frame, callArguments);
}

}
Original file line number Diff line number Diff line change
@@ -45,15 +45,25 @@ public CodeLoader(RubyContext context) {
public RubyRootNode parse(Source source,
Encoding defaultEncoding,
ParserContext parserContext,
FrameDescriptor frameDescriptor,
MaterializedFrame parentFrame,
boolean ownScopeForAssignments,
Node currentNode) {
final TranslatorDriver translator = new TranslatorDriver(context);

return translator.parse(context, source, defaultEncoding, parserContext, null, parentFrame,
return translator.parse(context, source, defaultEncoding, parserContext, null, frameDescriptor, parentFrame,
ownScopeForAssignments, currentNode);
}

@TruffleBoundary
public RubyRootNode parse(Source source,
Encoding defaultEncoding,
ParserContext parserContext,
MaterializedFrame parentFrame,
boolean ownScopeForAssignments,
Node currentNode) {
return parse(source, defaultEncoding, parserContext, null, parentFrame, ownScopeForAssignments, currentNode);
}

@TruffleBoundary
public DeferredCall prepareExecute(ParserContext parserContext,
DeclarationContext declarationContext,
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
*/
package org.jruby.truffle.language.parser;

import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.Source;
@@ -23,6 +24,7 @@ RubyRootNode parse(RubyContext context,
Encoding defaultEncoding,
ParserContext parserContext,
String[] argumentNames,
FrameDescriptor frameDescriptor,
MaterializedFrame parentFrame,
boolean ownScopeForAssignments,
Node currentNode);
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
package org.jruby.truffle.language.parser.jruby;

import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -57,7 +58,7 @@ public TranslatorDriver(RubyContext context) {
}

@Override
public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEncoding, ParserContext parserContext, String[] argumentNames, MaterializedFrame parentFrame, boolean ownScopeForAssignments, Node currentNode) {
public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEncoding, ParserContext parserContext, String[] argumentNames, FrameDescriptor frameDescriptor, MaterializedFrame parentFrame, boolean ownScopeForAssignments, Node currentNode) {
// Set up the JRuby parser

final org.jruby.parser.Parser parser = new org.jruby.parser.Parser(context.getJRubyRuntime());
@@ -70,8 +71,18 @@ public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEn
* parent scope.
*/

if (parentFrame != null) {
final TranslatorEnvironment parentEnvironment;

if (frameDescriptor != null) {
for (FrameSlot slot : frameDescriptor.getSlots()) {
if (slot.getIdentifier() instanceof String) {
final String name = (String) slot.getIdentifier();
staticScope.addVariableThisScope(name.intern()); // StaticScope expects interned var names
}
}

parentEnvironment = environmentForFrameDescriptor(context, frameDescriptor);
} else if (parentFrame != null) {
MaterializedFrame frame = parentFrame;

while (frame != null) {
@@ -84,6 +95,10 @@ public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEn

frame = RubyArguments.getDeclarationFrame(frame);
}

parentEnvironment = environmentForFrame(context, parentFrame);
} else {
parentEnvironment = environmentForFrame(context, null);
}

if (argumentNames != null) {
@@ -138,7 +153,7 @@ public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEn
// TODO (10 Feb. 2015): name should be "<top (required)> for the require-d/load-ed files.
final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, parseEnvironment.getLexicalScope(), Arity.NO_ARGUMENTS, "<main>", false, null, false, false, false);

final TranslatorEnvironment environment = new TranslatorEnvironment(context, environmentForFrame(context, parentFrame),
final TranslatorEnvironment environment = new TranslatorEnvironment(context, parentEnvironment,
parseEnvironment, parseEnvironment.allocateReturnID(), ownScopeForAssignments, false, sharedMethodInfo, sharedMethodInfo.getName(), 0, null);

// Declare arguments as local variables in the top-level environment - we'll put the values there in a prelude
@@ -219,6 +234,14 @@ public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEn
return new RubyRootNode(context, truffleNode.getSourceSection(), environment.getFrameDescriptor(), sharedMethodInfo, truffleNode, environment.needsDeclarationFrame());
}

private TranslatorEnvironment environmentForFrameDescriptor(RubyContext context, FrameDescriptor frameDescriptor) {
SourceSection sourceSection = SourceSection.createUnavailable("Unknown source section", "(unknown)");
final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, context.getRootLexicalScope(), Arity.NO_ARGUMENTS, "(unknown)", false, null, false, false, false);
// TODO(CS): how do we know if the frame is a block or not?
return new TranslatorEnvironment(context, null, parseEnvironment,
parseEnvironment.allocateReturnID(), true, true, sharedMethodInfo, sharedMethodInfo.getName(), 0, null, frameDescriptor);
}

private TranslatorEnvironment environmentForFrame(RubyContext context, MaterializedFrame frame) {
if (frame == null) {
return null;

0 comments on commit 94dd7c3

Please sign in to comment.