/*
 * 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: 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(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 }