Skip to content

Commit

Permalink
Showing 33 changed files with 472 additions and 304 deletions.
138 changes: 73 additions & 65 deletions core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
@@ -44,6 +44,7 @@
import org.jruby.compiler.Constantizable;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CallSite;
@@ -462,73 +463,10 @@ public IRubyObject encoding(ThreadContext context) {

@JRubyMethod
public IRubyObject to_proc(ThreadContext context) {
StaticScope scope = context.runtime.getStaticScopeFactory().getDummyScope();
final CallSite site = new FunctionalCachingCallSite(symbol);
BlockBody body = new ContextAwareBlockBody(scope, Signature.OPTIONAL) {
private IRubyObject yieldInner(ThreadContext context, RubyArray array, Block blockArg) {
if (array.isEmpty()) {
throw context.runtime.newArgumentError("no receiver given");
}

IRubyObject self = array.shift(context);

return site.call(context, self, self, array.toJavaArray(), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
RubyProc.prepareArgs(context, block.type, blockArg.getBody(), args);
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject value, Block blockArg) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), blockArg);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), Block.NULL_BLOCK);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), Block.NULL_BLOCK);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return site.call(context, arg0, arg0);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return site.call(context, arg0, arg0, arg1);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return site.call(context, arg0, arg0, arg1, arg2);
}

@Override
public String getFile() {
return symbol;
}

@Override
public int getLine() {
return -1;
}

@Override
public ArgumentDescriptor[] getArgumentDescriptors() {
return ArgumentDescriptor.ANON_REST;
}
};
BlockBody body = new SymbolProcBody(context.runtime, symbol);

return RubyProc.newProc(context.runtime,
new Block(body, context.currentBinding()),
new Block(body, Binding.DUMMY),
Block.Type.PROC);
}

@@ -1071,4 +1009,74 @@ public static String objectToSymbolString(IRubyObject object) {
return object.convertToString().getByteList().toString();
}
}

private static class SymbolProcBody extends ContextAwareBlockBody {
private final CallSite site;

public SymbolProcBody(Ruby runtime, String symbol) {
super(runtime.getStaticScopeFactory().getDummyScope(), Signature.OPTIONAL);
this.site = new FunctionalCachingCallSite(symbol);
}

private IRubyObject yieldInner(ThreadContext context, RubyArray array, Block blockArg) {
if (array.isEmpty()) {
throw context.runtime.newArgumentError("no receiver given");
}

IRubyObject self = array.shift(context);

return site.call(context, self, self, array.toJavaArray(), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
RubyProc.prepareArgs(context, block.type, blockArg.getBody(), args);
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject value, Block blockArg) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), blockArg);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), Block.NULL_BLOCK);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), Block.NULL_BLOCK);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return site.call(context, arg0, arg0);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return site.call(context, arg0, arg0, arg1);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return site.call(context, arg0, arg0, arg1, arg2);
}

@Override
public String getFile() {
return site.methodName;
}

@Override
public int getLine() {
return -1;
}

@Override
public ArgumentDescriptor[] getArgumentDescriptors() {
return ArgumentDescriptor.ANON_REST;
}
}
}
34 changes: 22 additions & 12 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -2338,7 +2338,11 @@ private Operand setupCallClosure(Node node) {
case ITERNODE:
return build(node);
case BLOCKPASSNODE:
return build(((BlockPassNode)node).getBodyNode());
Node bodyNode = ((BlockPassNode)node).getBodyNode();
if (bodyNode instanceof SymbolNode) {
return new SymbolProc(((SymbolNode)bodyNode).getName(), ((SymbolNode)bodyNode).getEncoding());
}
return build(bodyNode);
default:
throw new NotCompilableException("ERROR: Encountered a method with a non-block, non-blockpass iter node at: " + node);
}
@@ -2806,10 +2810,12 @@ public Operand buildOpAsgn(OpAsgnNode opAsgnNode) {
Label l;
Variable readerValue = createTemporaryVariable();
Variable writerValue = createTemporaryVariable();
Node receiver = opAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;

// get attr
Operand v1 = build(opAsgnNode.getReceiverNode());
addInstr(CallInstr.create(scope, readerValue, opAsgnNode.getVariableName(), v1, NO_ARGS, null));
Operand v1 = build(receiver);
addInstr(CallInstr.create(scope, callType, readerValue, opAsgnNode.getVariableName(), v1, NO_ARGS, null));

// Ex: e.val ||= n
// e.val &&= n
@@ -2820,7 +2826,7 @@ public Operand buildOpAsgn(OpAsgnNode opAsgnNode) {

// compute value and set it
Operand v2 = build(opAsgnNode.getValueNode());
addInstr(CallInstr.create(scope, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {v2}, null));
addInstr(CallInstr.create(scope, callType, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {v2}, null));
// It is readerValue = v2.
// readerValue = writerValue is incorrect because the assignment method
// might return something else other than the value being set!
@@ -2837,7 +2843,7 @@ public Operand buildOpAsgn(OpAsgnNode opAsgnNode) {
addInstr(CallInstr.create(scope, setValue, opAsgnNode.getOperatorName(), readerValue, new Operand[]{v2}, null));

// set attr
addInstr(CallInstr.create(scope, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {setValue}, null));
addInstr(CallInstr.create(scope, callType, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {setValue}, null));
// Returning writerValue is incorrect becuase the assignment method
// might return something else other than the value being set!
return setValue;
@@ -2910,16 +2916,18 @@ public Operand buildOpElementAsgn(OpElementAsgnNode node) {
}

private Operand buildOpElementAsgnWith(OpElementAsgnNode opElementAsgnNode, Boolean truthy) {
Operand array = buildWithOrder(opElementAsgnNode.getReceiverNode(), opElementAsgnNode.containsVariableAssignment());
Node receiver = opElementAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;
Operand array = buildWithOrder(receiver, opElementAsgnNode.containsVariableAssignment());
Label endLabel = getNewLabel();
Variable elt = createTemporaryVariable();
Operand[] argList = setupCallArgs(opElementAsgnNode.getArgsNode());
addInstr(CallInstr.create(scope, elt, "[]", array, argList, null));
addInstr(CallInstr.create(scope, callType, elt, "[]", array, argList, null));
addInstr(BEQInstr.create(elt, truthy, endLabel));
Operand value = build(opElementAsgnNode.getValueNode());

argList = addArg(argList, value);
addInstr(CallInstr.create(scope, elt, "[]=", array, argList, null));
addInstr(CallInstr.create(scope, callType, elt, "[]=", array, argList, null));
addInstr(new CopyInstr(elt, value));

addInstr(new LabelInstr(endLabel));
@@ -2928,18 +2936,20 @@ private Operand buildOpElementAsgnWith(OpElementAsgnNode opElementAsgnNode, Bool

// a[i] *= n, etc. anything that is not "a[i] &&= .. or a[i] ||= .."
public Operand buildOpElementAsgnWithMethod(OpElementAsgnNode opElementAsgnNode) {
Operand array = buildWithOrder(opElementAsgnNode.getReceiverNode(), opElementAsgnNode.containsVariableAssignment());
Node receiver = opElementAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;
Operand array = buildWithOrder(receiver, opElementAsgnNode.containsVariableAssignment());
Operand[] argList = setupCallArgs(opElementAsgnNode.getArgsNode());
Variable elt = createTemporaryVariable();
addInstr(CallInstr.create(scope, elt, "[]", array, argList, null)); // elt = a[args]
addInstr(CallInstr.create(scope, callType, elt, "[]", array, argList, null)); // elt = a[args]
Operand value = build(opElementAsgnNode.getValueNode()); // Load 'value'
String operation = opElementAsgnNode.getOperatorName();
addInstr(CallInstr.create(scope, elt, operation, elt, new Operand[] { value }, null)); // elt = elt.OPERATION(value)
addInstr(CallInstr.create(scope, callType, elt, operation, elt, new Operand[] { value }, null)); // elt = elt.OPERATION(value)
// SSS: do not load the call result into 'elt' to eliminate the RAW dependency on the call
// We already know what the result is going be .. we are just storing it back into the array
Variable tmp = createTemporaryVariable();
argList = addArg(argList, elt);
addInstr(CallInstr.create(scope, tmp, "[]=", array, argList, null)); // a[args] = elt
addInstr(CallInstr.create(scope, callType, tmp, "[]=", array, argList, null)); // a[args] = elt
return elt;
}

1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/IRVisitor.java
Original file line number Diff line number Diff line change
@@ -188,6 +188,7 @@ private void error(Object object) {
public void StringLiteral(StringLiteral stringliteral) { error(stringliteral); }
public void SValue(SValue svalue) { error(svalue); }
public void Symbol(Symbol symbol) { error(symbol); }
public void SymbolProc(SymbolProc symbolproc) { error(symbolproc); }
public void TemporaryVariable(TemporaryVariable temporaryvariable) { error(temporaryvariable); }
public void TemporaryLocalVariable(TemporaryLocalVariable temporarylocalvariable) { error(temporarylocalvariable); }
public void TemporaryFloatVariable(TemporaryFloatVariable temporaryfloatvariable) { error(temporaryfloatvariable); }
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/ir/operands/OperandType.java
Original file line number Diff line number Diff line change
@@ -43,7 +43,8 @@ public enum OperandType {
WRAPPED_IR_CLOSURE((byte) 'w'),
FROZEN_STRING((byte) 'z'),
NULL_BLOCK((byte) 'o'),
FILENAME((byte) 'm')
FILENAME((byte) 'm'),
SYMBOL_PROC((byte) 'P')
;

private final byte coded;
73 changes: 73 additions & 0 deletions core/src/main/java/org/jruby/ir/operands/SymbolProc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.jruby.ir.operands;

import org.jcodings.Encoding;
import org.jruby.ir.IRVisitor;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.ThreadContext;

/**
* A literal representing proc'ified symbols, as in &:foo.
*
* Used to cache a unique and constant proc at the use site to reduce allocation and improve caching.
*/
public class SymbolProc extends ImmutableLiteral {
private final String name;
private final Encoding encoding;

public SymbolProc(String name, Encoding encoding) {
super();
this.name = name;
this.encoding = encoding;
}

@Override
public OperandType getOperandType() {
return OperandType.SYMBOL_PROC;
}

@Override
public Object createCacheObject(ThreadContext context) {
return IRRuntimeHelpers.newSymbolProc(context, name, encoding);
}

@Override
public int hashCode() {
return 47 * 7 + (int) (this.name.hashCode() ^ (this.encoding.hashCode() >>> 32));
}

@Override
public boolean equals(Object other) {
return other instanceof SymbolProc && name.equals(((SymbolProc) other).name) && encoding.equals(((SymbolProc) other).encoding);
}

@Override
public void visit(IRVisitor visitor) {
visitor.SymbolProc(this);
}

public String getName() {
return name;
}

public Encoding getEncoding() {
return encoding;
}

@Override
public void encode(IRWriterEncoder e) {
super.encode(e);
e.encode(name);
e.encode(encoding);
}

public static SymbolProc decode(IRReaderDecoder d) {
return new SymbolProc(d.decodeString(), d.decodeEncoding());
}

@Override
public String toString() {
return "SymbolProc:" + name;
}
}
24 changes: 24 additions & 0 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1698,4 +1698,28 @@ public static IRubyObject useBindingSelf(Binding binding) {

return self;
}

/**
* Create a new Symbol.to_proc for the given symbol name and encoding.
*
* @param context
* @param symbol
* @return
*/
@Interp
public static RubyProc newSymbolProc(ThreadContext context, String symbol, Encoding encoding) {
return (RubyProc)context.runtime.newSymbol(symbol, encoding).to_proc(context);
}

/**
* Create a new Symbol.to_proc for the given symbol name and encoding.
*
* @param context
* @param symbol
* @return
*/
@JIT
public static RubyProc newSymbolProc(ThreadContext context, String symbol, String encoding) {
return newSymbolProc(context, symbol, retrieveJCodingsEncoding(context, encoding));
}
}
13 changes: 12 additions & 1 deletion core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter.java
Original file line number Diff line number Diff line change
@@ -351,14 +351,25 @@ public void pushBlockBody(Handle handle, org.jruby.runtime.Signature signature,
* Stack required: none
*
* @param sym the symbol's string identifier
* @param encoding the symbol's encoding
*/
public abstract void pushSymbol(String sym, Encoding encoding);

/**
* Push the JRuby runtime on the stack.
* Push a Symbol.to_proc on the stack.
*
* Stack required: none
*
* @param name the symbol's string identifier
* @param encoding the symbol's encoding
*/
public abstract void pushSymbolProc(String name, Encoding encoding);

/**
* Push the JRuby runtime on the stack.
*
* Stack required: none
*/
public abstract void loadRuntime();

/**
Loading

0 comments on commit 5c40a99

Please sign in to comment.