Skip to content

Commit

Permalink
Showing 10 changed files with 221 additions and 68 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;
}
}
}
6 changes: 5 additions & 1 deletion 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);
}
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();

/**
13 changes: 13 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import org.jruby.RubyFloat;
import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyProc;
import org.jruby.RubyRegexp;
import org.jruby.RubyString;
import org.jruby.RubySymbol;
@@ -261,6 +262,18 @@ public void run() {
});
}

public void pushSymbolProc(final String name, final Encoding encoding) {
cacheValuePermanently("symbolProc", RubyProc.class, null, new Runnable() {
@Override
public void run() {
loadContext();
adapter.ldc(name);
adapter.ldc(encoding.toString());
invokeIRHelper("newSymbolProc", sig(RubyProc.class, ThreadContext.class, String.class, String.class));
}
});
}

public void loadRuntime() {
loadContext();
adapter.getfield(p(ThreadContext.class), "runtime", ci(Ruby.class));
5 changes: 5 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -2306,6 +2306,11 @@ public void Symbol(Symbol symbol) {
jvmMethod().pushSymbol(symbol.getName(), symbol.getEncoding());
}

@Override
public void SymbolProc(SymbolProc symbolproc) {
jvmMethod().pushSymbolProc(symbolproc.getName(), symbolproc.getEncoding());
}

@Override
public void TemporaryVariable(TemporaryVariable temporaryvariable) {
jvmLoadLocal(temporaryvariable);
13 changes: 13 additions & 0 deletions core/src/main/java/org/jruby/runtime/Binding.java
Original file line number Diff line number Diff line change
@@ -33,15 +33,28 @@
package org.jruby.runtime;

import org.jruby.Ruby;
import org.jruby.RubyBasicObject;
import org.jruby.parser.StaticScopeFactory;
import org.jruby.runtime.backtrace.BacktraceElement;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.scope.ManyVarsDynamicScope;
import org.jruby.runtime.scope.NoVarsDynamicScope;

/**
* Internal live representation of a block ({...} or do ... end).
*/
public class Binding {

public static final Binding DUMMY =
new Binding(
RubyBasicObject.NEVER,
new Frame(),
Visibility.PUBLIC,
new NoVarsDynamicScope(StaticScopeFactory.newStaticScope(null, StaticScope.Type.BLOCK, null)),
"<dummy>",
"dummy",
-1);

/**
* frame of method which defined this block

0 comments on commit cca4a33

Please sign in to comment.