/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.jruby.ir.targets;
import com.headius.invokebinder.Signature;
import org.jcodings.Encoding;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.compiler.impl.SkinnyMethodAdapter;
import org.jruby.ir.IRManager;
import org.jruby.ir.IRScope;
import org.jruby.ir.instructions.CallBase;
import org.jruby.ir.instructions.ClosureAcceptingInstr;
import org.jruby.ir.instructions.EQQInstr;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.UndefinedValue;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CachingCallSite;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.callsite.MonomorphicCallSite;
import org.jruby.runtime.callsite.ProfilingCachingCallSite;
import org.jruby.runtime.callsite.RefinedCachingCallSite;
import org.jruby.runtime.callsite.VariableCachingCallSite;
import org.jruby.util.ByteList;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.RegexpOptions;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import static org.jruby.util.CodegenUtils.*;
Author: headius
/**
*
* @author headius
*/
public abstract class IRBytecodeAdapter {
public static final int MAX_ARGUMENTS = 250;
public IRBytecodeAdapter(SkinnyMethodAdapter adapter, Signature signature, ClassData classData) {
this.adapter = adapter;
this.signature = signature;
this.classData = classData;
}
Utility to lazily construct and cache a call site object.
Params: - method – the SkinnyMethodAdapter to that's generating the containing method body
- className – the name of the class in which the field will reside
- siteName – the unique name of the site, used for the field
- call – of we are making a callsite for.
/**
* Utility to lazily construct and cache a call site object.
*
* @param method the SkinnyMethodAdapter to that's generating the containing method body
* @param className the name of the class in which the field will reside
* @param siteName the unique name of the site, used for the field
* @param call of we are making a callsite for.
*/
public static void cacheCallSite(SkinnyMethodAdapter method, String className, String siteName, String scopeFieldName, CallBase call) {
CallType callType = call.getCallType();
boolean profileCandidate = call.hasLiteralClosure() && scopeFieldName != null && IRManager.IR_INLINER;
boolean profiled = false;
boolean refined = call.isPotentiallyRefined();
boolean specialSite = profiled || refined || profileCandidate;
if (!specialSite) {
// use indy to cache the site object
method.invokedynamic("callSite", sig(CachingCallSite.class), Bootstrap.CALLSITE, call.getId(), callType.ordinal());
return;
}
// site requires special handling (usually refined or profiled that need scope present)
Class<? extends CachingCallSite> siteClass;
String signature;
// call site object field
method.getClassVisitor().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, siteName, ci(CachingCallSite.class), null, null).visitEnd();
// lazily construct it
method.getstatic(className, siteName, ci(CachingCallSite.class));
method.dup();
Label doCall = new Label();
method.ifnonnull(doCall);
method.pop();
method.ldc(call.getId());
if (refined) {
siteClass = RefinedCachingCallSite.class;
signature = sig(siteClass, String.class, IRScope.class, String.class);
method.getstatic(className, scopeFieldName, ci(IRScope.class));
method.ldc(callType.name());
} else {
switch (callType) {
case NORMAL:
if (profileCandidate) {
profiled = true;
siteClass = ProfilingCachingCallSite.class;
} else {
siteClass = MonomorphicCallSite.class;
}
break;
case FUNCTIONAL:
if (profileCandidate) {
profiled = true;
siteClass = ProfilingCachingCallSite.class;
} else {
siteClass = FunctionalCachingCallSite.class;
}
break;
case VARIABLE:
siteClass = VariableCachingCallSite.class;
break;
default:
throw new RuntimeException("BUG: Unexpected call type " + callType + " in JVM6 invoke logic");
}
if (profiled) {
method.getstatic(className, scopeFieldName, ci(IRScope.class));
method.ldc(call.getCallSiteId());
signature = sig(CallType.class, siteClass, String.class, IRScope.class, long.class);
} else {
signature = sig(siteClass, String.class);
}
}
method.invokestatic(p(IRRuntimeHelpers.class), "new" + siteClass.getSimpleName(), signature);
method.dup();
method.putstatic(className, siteName, ci(CachingCallSite.class));
method.label(doCall);
}
public String getUniqueSiteName(String name) {
return "invokeOther" + getClassData().cacheFieldCount.getAndIncrement() + ":" + JavaNameMangler.mangleMethodName(name);
}
public ClassData getClassData() {
return classData;
}
public void startMethod() {
adapter.start();
}
public void endMethod() {
adapter.end(new Runnable() {
public void run() {
for (Map.Entry<Integer, Type> entry : variableTypes.entrySet()) {
int i = entry.getKey();
String name = variableNames.get(i);
adapter.local(i, name, entry.getValue());
}
}
});
}
public void loadLocal(int i) {
adapter.aload(i);
}
public void loadContext() {
adapter.aload(signature.argOffset("context"));
}
public void loadSelfBlock() {
int selfBlockOffset = signature.argOffset(JVMVisitor.SELF_BLOCK_NAME);
if (selfBlockOffset == -1) {
adapter.aconst_null();
} else {
adapter.aload(selfBlockOffset);
}
}
public void loadStaticScope() {
adapter.aload(signature.argOffset("scope"));
}
public void loadSelf() {
adapter.aload(signature.argOffset("self"));
}
public void loadArgs() {
adapter.aload(signature.argOffset("args"));
}
public void loadBlock() {
adapter.aload(signature.argOffset(JVMVisitor.BLOCK_ARG_NAME));
}
public void loadFrameClass() {
// when present, should be second-to-last element in signature
adapter.aload(signature.argCount() - 2);
}
public void loadFrameName() {
int superNameOffset = signature.argOffset(JVMVisitor.SUPER_NAME_NAME);
if (superNameOffset == -1) {
// load from self block
loadSelfBlock();
adapter.invokevirtual(p(Block.class), "getBinding", sig(Binding.class));
adapter.invokevirtual(p(Binding.class), "getMethod", sig(String.class));
} else {
adapter.aload(superNameOffset);
}
}
public void storeSelf() {
adapter.astore(signature.argOffset("self"));
}
public void storeArgs() {
adapter.astore(signature.argOffset("args"));
}
public void storeLocal(int i) {
adapter.astore(i);
}
public void invokeVirtual(Type type, Method method) {
adapter.invokevirtual(type.getInternalName(), method.getName(), method.getDescriptor());
}
public void invokeStatic(Type type, Method method) {
adapter.invokestatic(type.getInternalName(), method.getName(), method.getDescriptor());
}
public void invokeHelper(String name, String sig) {
adapter.invokestatic(p(Helpers.class), name, sig);
}
public void invokeHelper(String name, Class... x) {
adapter.invokestatic(p(Helpers.class), name, sig(x));
}
public void invokeIRHelper(String name, String sig) {
adapter.invokestatic(p(IRRuntimeHelpers.class), name, sig);
}
public void goTo(org.objectweb.asm.Label label) {
adapter.go_to(label);
}
public void branchIfTruthy(Label target) {
adapter.invokeinterface(p(IRubyObject.class), "isTrue", sig(boolean.class));
btrue(target);
}
Branch to label if value at top of stack is nil
stack: obj to check for nilness
/**
* Branch to label if value at top of stack is nil
*
* stack: obj to check for nilness
*/
public void branchIfNil(Label label) {
pushNil();
adapter.if_acmpeq(label);
}
public void bfalse(org.objectweb.asm.Label label) {
adapter.iffalse(label);
}
public void btrue(org.objectweb.asm.Label label) {
adapter.iftrue(label);
}
public void poll() {
loadContext();
adapter.invokevirtual(p(ThreadContext.class), "pollThreadEvents", sig(void.class));
}
public void pushObjectClass() {
loadContext();
invokeIRHelper("getObject", sig(RubyClass.class, ThreadContext.class));
}
public void pushUndefined() {
adapter.getstatic(p(UndefinedValue.class), "UNDEFINED", ci(UndefinedValue.class));
}
public void pushHandle(Handle handle) {
adapter.getMethodVisitor().visitLdcInsn(handle);
}
public void mark(org.objectweb.asm.Label label) {
adapter.label(label);
}
public void returnValue() {
adapter.areturn();
}
public int newLocal(String name, Type type) {
int index = variableCount++;
if (type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE) {
variableCount++;
}
variableTypes.put(index, type);
variableNames.put(index, name);
return index;
}
public org.objectweb.asm.Label newLabel() {
return new org.objectweb.asm.Label();
}
Stack required: none
Params: - l – long value to push as a Fixnum
/**
* Stack required: none
*
* @param l long value to push as a Fixnum
*/
public abstract void pushFixnum(long l);
Stack required: none
Params: - d – double value to push as a Float
/**
* Stack required: none
*
* @param d double value to push as a Float
*/
public abstract void pushFloat(double d);
Stack required: none
Params: - bl – ByteList for the String to push
/**
* Stack required: none
*
* @param bl ByteList for the String to push
*/
public abstract void pushString(ByteList bl, int cr);
Stack required: none
Params: - bl – ByteList for the String to push
/**
* Stack required: none
*
* @param bl ByteList for the String to push
*/
public abstract void pushFrozenString(ByteList bl, int cr, String path, int line);
Stack required: none
Params: - bl – ByteList to push
/**
* Stack required: none
*
* @param bl ByteList to push
*/
public abstract void pushByteList(ByteList bl);
Build and save a literal regular expression.
Stack required: none
Params: - options – options for the regexp
/**
* Build and save a literal regular expression.
*
* Stack required: none
*
* @param options options for the regexp
*/
public abstract void pushRegexp(ByteList source, int options);
Build a dynamic regexp.
No stack requirement. The callback must push onto this method's stack the ThreadContext and all arguments for
building the dregexp, matching the given arity.
Params: - options – options for the regexp
- arity – number of Strings passed in
/**
* Build a dynamic regexp.
*
* No stack requirement. The callback must push onto this method's stack the ThreadContext and all arguments for
* building the dregexp, matching the given arity.
*
* @param options options for the regexp
* @param arity number of Strings passed in
*/
public abstract void pushDRegexp(Runnable callback, RegexpOptions options, int arity);
Push a symbol on the stack.
Stack required: none
Params: - bytes – the ByteList for the symbol
/**
* Push a symbol on the stack.
*
* Stack required: none
*
* @param bytes the ByteList for the symbol
*/
public abstract void pushSymbol(ByteList bytes);
Push a Symbol.to_proc on the stack.
Stack required: none
Params: - bytes – the ByteList for the symbol
/**
* Push a Symbol.to_proc on the stack.
*
* Stack required: none
*
* @param bytes the ByteList for the symbol
*/
public abstract void pushSymbolProc(ByteList bytes);
Push the JRuby runtime on the stack.
Stack required: none
/**
* Push the JRuby runtime on the stack.
*
* Stack required: none
*/
public abstract void loadRuntime();
Push an encoding on the stack.
Stack required: none
Params: - encoding – the encoding to push
/**
* Push an encoding on the stack.
*
* Stack required: none
*
* @param encoding the encoding to push
*/
public abstract void pushEncoding(Encoding encoding);
Invoke a method on an object other than self.
Stack required: context, self, all arguments, optional block
Params: - call – the call to be invoked
/**
* Invoke a method on an object other than self.
*
* Stack required: context, self, all arguments, optional block
*
* @param call the call to be invoked
*/
public abstract void invokeOther(String file, int line, String scopeFieldName, CallBase call, int arity);
Invoke the array dereferencing method ([]) on an object other than self.
If this invokes against a Hash with a frozen string, it will follow an optimized path.
Stack required: context, self, target, arg0
Params: - file –
- line –
/**
* Invoke the array dereferencing method ([]) on an object other than self.
*
* If this invokes against a Hash with a frozen string, it will follow an optimized path.
*
* Stack required: context, self, target, arg0
* @param file
* @param line
*/
public abstract void invokeArrayDeref(String file, int line, String scopeFieldName, CallBase call);
Invoke the to_s method with AsString semantics (tainting, refinements, etc).
Stack required: context, self, target
Params: - file –
- line –
/**
* Invoke the to_s method with AsString semantics (tainting, refinements, etc).
*
* Stack required: context, self, target
* @param file
* @param line
*/
public abstract void invokeAsString(String file, int line, String scopeFieldName, CallBase call);
Invoke a fixnum-receiving method on an object other than self.
Stack required: context, self, receiver (fixnum will be handled separately)
/**
* Invoke a fixnum-receiving method on an object other than self.
*
* Stack required: context, self, receiver (fixnum will be handled separately)
*
*/
public abstract void invokeOtherOneFixnum(String file, int line, CallBase call, long fixnum);
Invoke a float-receiving method on an object other than self.
Stack required: context, self, receiver (float will be handled separately)
/**
* Invoke a float-receiving method on an object other than self.
*
* Stack required: context, self, receiver (float will be handled separately)
*
*/
public abstract void invokeOtherOneFloat(String file, int line, CallBase call, double flote);
public enum BlockPassType {
NONE(false, false),
GIVEN(true, false),
LITERAL(true, true);
private final boolean given;
private final boolean literal;
BlockPassType(boolean given, boolean literal) {
this.given = given;
this.literal = literal;
}
public boolean given() {
return given;
}
public boolean literal() {
return literal;
}
public static BlockPassType fromIR(ClosureAcceptingInstr callInstr) {
Operand closure = callInstr.getClosureArg();
return closure != null ? ( callInstr.hasLiteralClosure() ? BlockPassType.LITERAL : BlockPassType.GIVEN) : BlockPassType.NONE;
}
}
Invoke a method on self.
Stack required: context, caller, self, all arguments, optional block
Params: - file – the filename of the script making this call
- line – the line number where this call appears
- call – to be invoked on self
- arity – of the call.
/**
* Invoke a method on self.
*
* Stack required: context, caller, self, all arguments, optional block
*
* @param file the filename of the script making this call
* @param line the line number where this call appears
* @param call to be invoked on self
* @param arity of the call.
*/
public abstract void invokeSelf(String file, int line, String scopeFieldName, CallBase call, int arity);
Invoke a superclass method from an instance context.
Stack required: context, caller, self, start class, arguments[, block]
Params: - file – the filename of the script making this call
- line – the line number where this call appears
- name – name of the method to invoke
- arity – arity of the arguments on the stack
- hasClosure – whether a block is passed
- splatmap – a map of arguments to be splatted back into arg list
/**
* Invoke a superclass method from an instance context.
*
* Stack required: context, caller, self, start class, arguments[, block]
*
* @param file the filename of the script making this call
* @param line the line number where this call appears
* @param name name of the method to invoke
* @param arity arity of the arguments on the stack
* @param hasClosure whether a block is passed
* @param splatmap a map of arguments to be splatted back into arg list
*/
public abstract void invokeInstanceSuper(String file, int line, String name, int arity, boolean hasClosure, boolean[] splatmap);
Invoke a superclass method from a class context.
Stack required: context, caller, self, start class, arguments[, block]
Params: - file – the filename of the script making this call
- line – the line number where this call appears
- name – name of the method to invoke
- arity – arity of the arguments on the stack
- hasClosure – whether a block is passed
- splatmap – a map of arguments to be splatted back into arg list
/**
* Invoke a superclass method from a class context.
*
* Stack required: context, caller, self, start class, arguments[, block]
*
* @param file the filename of the script making this call
* @param line the line number where this call appears
* @param name name of the method to invoke
* @param arity arity of the arguments on the stack
* @param hasClosure whether a block is passed
* @param splatmap a map of arguments to be splatted back into arg list
*/
public abstract void invokeClassSuper(String file, int line, String name, int arity, boolean hasClosure, boolean[] splatmap);
Invoke a superclass method from an unresolved context.
Stack required: context, caller, self, arguments[, block]
Params: - file – the filename of the script making this call
- line – the line number where this call appears
- name – name of the method to invoke
- arity – arity of the arguments on the stack
- hasClosure – whether a block is passed
- splatmap – a map of arguments to be splatted back into arg list
/**
* Invoke a superclass method from an unresolved context.
*
* Stack required: context, caller, self, arguments[, block]
*
* @param file the filename of the script making this call
* @param line the line number where this call appears
* @param name name of the method to invoke
* @param arity arity of the arguments on the stack
* @param hasClosure whether a block is passed
* @param splatmap a map of arguments to be splatted back into arg list
*/
public abstract void invokeUnresolvedSuper(String file, int line, String name, int arity, boolean hasClosure, boolean[] splatmap);
Invoke a superclass method from a zsuper in a block.
Stack required: context, caller, self, arguments[, block]
Params: - file – the filename of the script making this call
- line – the line number where this call appears
- name – name of the method to invoke
- arity – arity of the arguments on the stack
- hasClosure – whether a block is passed
- splatmap – a map of arguments to be splatted back into arg list
/**
* Invoke a superclass method from a zsuper in a block.
*
* Stack required: context, caller, self, arguments[, block]
*
* @param file the filename of the script making this call
* @param line the line number where this call appears
* @param name name of the method to invoke
* @param arity arity of the arguments on the stack
* @param hasClosure whether a block is passed
* @param splatmap a map of arguments to be splatted back into arg list
*/
public abstract void invokeZSuper(String file, int line, String name, int arity, boolean hasClosure, boolean[] splatmap);
Lookup a constant from current context.
Stack required: context, static scope
Params: - name – name of the constant
- noPrivateConsts – whether to ignore private constants
/**
* Lookup a constant from current context.
*
* Stack required: context, static scope
*
* @param name name of the constant
* @param noPrivateConsts whether to ignore private constants
*/
public abstract void searchConst(String name, boolean noPrivateConsts);
Lookup a constant from current module.
Stack required: context, static scope
Params: - name – name of the constant
- noPrivateConsts – whether to ignore private constants
/**
* Lookup a constant from current module.
*
* Stack required: context, static scope
*
* @param name name of the constant
* @param noPrivateConsts whether to ignore private constants
*/
public abstract void searchModuleForConst(String name, boolean noPrivateConsts, boolean callConstMissing);
Lookup a constant from a given class or module.
Stack required: context, module
Params: - name – name of the constant
- noPrivateConsts – whether to ignore private constants
/**
* Lookup a constant from a given class or module.
*
* Stack required: context, module
*
* @param name name of the constant
* @param noPrivateConsts whether to ignore private constants
*/
public abstract void inheritanceSearchConst(String name, boolean noPrivateConsts);
Lookup a constant from a lexical scope.
Stack required: context, static scope
Params: - name – name of the constant
/**
* Lookup a constant from a lexical scope.
*
* Stack required: context, static scope
*
* @param name name of the constant
*/
public abstract void lexicalSearchConst(String name);
Load nil onto the stack.
Stack required: none
/**
* Load nil onto the stack.
*
* Stack required: none
*/
public abstract void pushNil();
Load a boolean onto the stack.
Stack required: none
Params: - b – the boolean to push
/**
* Load a boolean onto the stack.
*
* Stack required: none
*
* @param b the boolean to push
*/
public abstract void pushBoolean(boolean b);
Load a Bignum onto the stack.
Stack required: none
Params: - bigint – the value of the Bignum to push
/**
* Load a Bignum onto the stack.
*
* Stack required: none
*
* @param bigint the value of the Bignum to push
*/
public abstract void pushBignum(BigInteger bigint);
Store instance variable into self.
Stack required: self, value
Stack result: empty
Params: - name – name of variable to store
/**
* Store instance variable into self.
*
* Stack required: self, value
* Stack result: empty
*
* @param name name of variable to store
*/
public abstract void putField(String name);
Load instance variable from self.
Stack required: self
Stack result: value from self
Params: - name – name of variable to load
/**
* Load instance variable from self.
*
* Stack required: self
* Stack result: value from self
*
* @param name name of variable to load
*/
public abstract void getField(String name);
Construct an Array from elements on stack.
Stack required: all elements of array
Params: - length – number of elements
/**
* Construct an Array from elements on stack.
*
* Stack required: all elements of array
*
* @param length number of elements
*/
public abstract void array(int length);
Construct a Hash from elements on stack.
Stack required: context, all elements of hash
Params: - length – number of element pairs
/**
* Construct a Hash from elements on stack.
*
* Stack required: context, all elements of hash
*
* @param length number of element pairs
*/
public abstract void hash(int length);
Construct a Hash based on keyword arguments pasesd to this method, for use in zsuper
Stack required: context, kwargs hash to dup, remaining elements of hash
Params: - length – number of element pairs
/**
* Construct a Hash based on keyword arguments pasesd to this method, for use in zsuper
*
* Stack required: context, kwargs hash to dup, remaining elements of hash
*
* @param length number of element pairs
*/
public abstract void kwargsHash(int length);
Perform a thread event checkpoint.
Stack required: none
/**
* Perform a thread event checkpoint.
*
* Stack required: none
*/
public abstract void checkpoint();
Retrieve a global variable with the given name.
Stack required: none
/**
* Retrieve a global variable with the given name.
*
* Stack required: none
*/
public abstract void getGlobalVariable(String name, String file, int line);
Set the global variable with the given name to the value on stack.
Stack required: the new value
/**
* Set the global variable with the given name to the value on stack.
*
* Stack required: the new value
*/
public abstract void setGlobalVariable(String name, String file, int line);
Yield argument list to a block.
Stack required: context, block, argument
/**
* Yield argument list to a block.
*
* Stack required: context, block, argument
*/
public abstract void yield(boolean unwrap);
Yield to a block.
Stack required: context, block
/**
* Yield to a block.
*
* Stack required: context, block
*/
public abstract void yieldSpecific();
Yield a number of flat arguments to a block.
Stack required: context, block
/**
* Yield a number of flat arguments to a block.
*
* Stack required: context, block
*/
public abstract void yieldValues(int arity);
Prepare a block for a subsequent call.
Stack required: context, self, dynamicScope
/**
* Prepare a block for a subsequent call.
*
* Stack required: context, self, dynamicScope
*/
public abstract void prepareBlock(Handle handle, org.jruby.runtime.Signature signature, String className);
Perform a === call appropriate for a case/when statement.
Stack required: context, case value, when value
/**
* Perform a === call appropriate for a case/when statement.
*
* Stack required: context, case value, when value
*/
public abstract void callEqq(EQQInstr call);
public SkinnyMethodAdapter adapter;
private int variableCount = 0;
private Map<Integer, Type> variableTypes = new HashMap<Integer, Type>();
private Map<Integer, String> variableNames = new HashMap<Integer, String>();
protected final Signature signature;
private final ClassData classData;
public int ipc = 0; // counter for dumping instr index when in DEBUG
}