예제 #1
0
파일: Bootstrap.java 프로젝트: godfat/jruby
  public static IRubyObject lexicalSearchConst(
      ThreadContext context,
      StaticScope scope,
      MutableCallSite site,
      String constName,
      boolean noPrivateConsts)
      throws Throwable {
    Ruby runtime = context.runtime;

    IRubyObject constant = scope.getConstantInner(constName);

    if (constant == null) {
      constant = UndefinedValue.UNDEFINED;
    }

    SwitchPoint switchPoint = (SwitchPoint) runtime.getConstantInvalidator(constName).getData();

    // bind constant until invalidated
    MethodHandle target = Binder.from(site.type()).drop(0, 2).constant(constant);
    MethodHandle fallback =
        Binder.from(site.type())
            .append(site, constName)
            .append(noPrivateConsts)
            .invokeStatic(LOOKUP, Bootstrap.class, "lexicalSearchConst");

    site.setTarget(switchPoint.guardWithTest(target, fallback));

    return constant;
  }
예제 #2
0
 public GuardBuilder filter(int index, Filter filter) throws Exception {
   MethodHandle methodHandle =
       filter.methodHandle(
           methodType(binder.type().parameterType(index), binder.type().parameterType(index)));
   binder = binder.filter(index, methodHandle);
   return this;
 }
예제 #3
0
파일: Bootstrap.java 프로젝트: godfat/jruby
 public static CallSite contextValueString(
     Lookup lookup, String name, MethodType type, String str) {
   MutableCallSite site = new MutableCallSite(type);
   site.setTarget(
       Binder.from(type).append(site, str).invokeStaticQuiet(lookup, Bootstrap.class, name));
   return site;
 }
예제 #4
0
파일: Bootstrap.java 프로젝트: godfat/jruby
  public static IRubyObject inheritanceSearchConst(
      ThreadContext context,
      IRubyObject cmVal,
      MutableCallSite site,
      String constName,
      boolean noPrivateConsts)
      throws Throwable {
    Ruby runtime = context.runtime;
    RubyModule module;

    if (cmVal instanceof RubyModule) {
      module = (RubyModule) cmVal;
    } else {
      throw runtime.newTypeError(cmVal + " is not a type/class");
    }

    IRubyObject constant =
        noPrivateConsts
            ? module.getConstantFromNoConstMissing(constName, false)
            : module.getConstantNoConstMissing(constName);

    if (constant == null) {
      constant = UndefinedValue.UNDEFINED;
    }

    SwitchPoint switchPoint = (SwitchPoint) runtime.getConstantInvalidator(constName).getData();

    // bind constant until invalidated
    MethodHandle target = Binder.from(site.type()).drop(0, 2).constant(constant);
    MethodHandle fallback =
        Binder.from(site.type())
            .append(site, constName)
            .append(noPrivateConsts)
            .invokeStatic(LOOKUP, Bootstrap.class, "inheritanceSearchConst");

    // test that module is same as before
    MethodHandle test =
        Binder.from(site.type().changeReturnType(boolean.class))
            .drop(0, 1)
            .insert(1, module.id)
            .invokeStaticQuiet(LOOKUP, Bootstrap.class, "testArg0ModuleMatch");
    target = guardWithTest(test, target, fallback);
    site.setTarget(switchPoint.guardWithTest(target, fallback));

    return constant;
  }
예제 #5
0
파일: Bootstrap.java 프로젝트: godfat/jruby
 public static CallSite kwargsHash(Lookup lookup, String name, MethodType type) {
   MethodHandle handle =
       Binder.from(lookup, type)
           .collect(2, IRubyObject[].class)
           .invokeStaticQuiet(LOOKUP, Bootstrap.class, "kwargsHash");
   CallSite site = new ConstantCallSite(handle);
   return site;
 }
예제 #6
0
파일: Bootstrap.java 프로젝트: godfat/jruby
  public static IRubyObject searchConst(
      ThreadContext context,
      StaticScope staticScope,
      MutableCallSite site,
      String constName,
      boolean noPrivateConsts)
      throws Throwable {

    // Lexical lookup
    Ruby runtime = context.getRuntime();
    RubyModule object = runtime.getObject();
    IRubyObject constant =
        (staticScope == null)
            ? object.getConstant(constName)
            : staticScope.getConstantInner(constName);

    // Inheritance lookup
    RubyModule module = null;
    if (constant == null) {
      // SSS FIXME: Is this null check case correct?
      module = staticScope == null ? object : staticScope.getModule();
      constant =
          noPrivateConsts
              ? module.getConstantFromNoConstMissing(constName, false)
              : module.getConstantNoConstMissing(constName);
    }

    // Call const_missing or cache
    if (constant == null) {
      return module.callMethod(context, "const_missing", context.runtime.fastNewSymbol(constName));
    }

    SwitchPoint switchPoint = (SwitchPoint) runtime.getConstantInvalidator(constName).getData();

    // bind constant until invalidated
    MethodHandle target = Binder.from(site.type()).drop(0, 2).constant(constant);
    MethodHandle fallback =
        Binder.from(site.type())
            .append(site, constName)
            .append(noPrivateConsts)
            .invokeStatic(LOOKUP, Bootstrap.class, "searchConst");

    site.setTarget(switchPoint.guardWithTest(target, fallback));

    return constant;
  }
예제 #7
0
  public MethodHandle getTestBlockBody() {
    if (testBlockBody != null) return testBlockBody;

    return testBlockBody =
        Binder.from(boolean.class, ThreadContext.class, Block.class)
            .drop(0)
            .append(this)
            .invoke(TEST_BLOCK_BODY);
  }
예제 #8
0
파일: Bootstrap.java 프로젝트: godfat/jruby
  public static RubyString frozenString(MutableCallSite site, ByteList value, ThreadContext context)
      throws Throwable {
    RubyString frozen =
        context.runtime.freezeAndDedupString(RubyString.newStringShared(context.runtime, value));
    MethodHandle handle =
        Binder.from(RubyString.class, ThreadContext.class).dropAll().constant(frozen);

    site.setTarget(handle);

    return frozen;
  }
예제 #9
0
파일: Bootstrap.java 프로젝트: godfat/jruby
  public static CallSite string(
      Lookup lookup, String name, MethodType type, String value, String encodingName) {
    Encoding encoding;
    EncodingDB.Entry entry = EncodingDB.getEncodings().get(encodingName.getBytes());
    if (entry == null) entry = EncodingDB.getAliases().get(encodingName.getBytes());
    if (entry == null) throw new RuntimeException("could not find encoding: " + encodingName);
    encoding = entry.getEncoding();
    ByteList byteList = new ByteList(value.getBytes(RubyEncoding.ISO), encoding);
    MutableCallSite site = new MutableCallSite(type);
    Binder binder =
        Binder.from(RubyString.class, ThreadContext.class)
            .insert(0, arrayOf(MutableCallSite.class, ByteList.class), site, byteList);
    if (name.equals("frozen")) {
      site.setTarget(binder.invokeStaticQuiet(lookup, Bootstrap.class, "frozenString"));
    } else {
      site.setTarget(binder.invokeStaticQuiet(lookup, Bootstrap.class, "string"));
    }

    return site;
  }
 @Override
 public StrategicLink linkGetMethod(
     StrategyChain chain, Object receiver, String methodName, Binder binder, Binder guardBinder)
     throws NoSuchMethodException, IllegalAccessException {
   StrategicLink link = super.linkGetMethod(chain, receiver, methodName, binder, guardBinder);
   if (link != null) {
     MethodHandle target = link.getTarget();
     return new StrategicLink(
         Binder.from(target.type()).filterReturn(FILTER_HANDLE).invoke(target), link.getGuard());
   }
   return null;
 }
예제 #11
0
파일: Bootstrap.java 프로젝트: godfat/jruby
  public static CallSite searchConst(
      Lookup lookup, String name, MethodType type, int noPrivateConsts) {
    MutableCallSite site = new MutableCallSite(type);
    String[] bits = name.split(":");
    String constName = bits[1];

    MethodHandle handle =
        Binder.from(lookup, type)
            .append(site, constName.intern())
            .append(noPrivateConsts == 0 ? false : true)
            .invokeStaticQuiet(LOOKUP, Bootstrap.class, bits[0]);

    site.setTarget(handle);

    return site;
  }
 @Override
 public StrategicLink linkCall(
     StrategyChain chain,
     Object receiver,
     Object self,
     Object[] args,
     Binder binder,
     Binder guardBinder)
     throws NoSuchMethodException, IllegalAccessException {
   // TODO Auto-generated method stub
   StrategicLink link = super.linkCall(chain, receiver, self, args, binder, guardBinder);
   if (link != null) {
     MethodHandle target = link.getTarget();
     return new StrategicLink(
         Binder.from(target.type()).filterReturn(FILTER_HANDLE).invoke(target), link.getGuard());
   }
   return null;
 }
예제 #13
0
 public GuardBuilder spread(Class... spreadTypes) {
   binder = binder.spread(spreadTypes);
   return this;
 }
예제 #14
0
 public GuardBuilder drop(int index) {
   binder = binder.drop(index);
   return this;
 }
예제 #15
0
/** The executable body portion of a closure. */
public abstract class BlockBody {

  protected final Signature signature;
  protected volatile MethodHandle testBlockBody;

  public BlockBody(Signature signature) {
    this.signature = signature;
  }

  public Signature getSignature() {
    return signature;
  }

  public void setEvalType(EvalType evalType) {}

  public boolean canCallDirect() {
    return false;
  }

  public MethodHandle getTestBlockBody() {
    if (testBlockBody != null) return testBlockBody;

    return testBlockBody =
        Binder.from(boolean.class, ThreadContext.class, Block.class)
            .drop(0)
            .append(this)
            .invoke(TEST_BLOCK_BODY);
  }

  private static final MethodHandle TEST_BLOCK_BODY =
      Binder.from(boolean.class, Block.class, BlockBody.class)
          .invokeStaticQuiet(MethodHandles.lookup(), BlockBody.class, "testBlockBody");

  public static boolean testBlockBody(Block block, BlockBody body) {
    return block.getBody() == body;
  }

  protected IRubyObject callDirect(
      ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
    throw new RuntimeException(
        "callDirect not implemented in base class. We should never get here.");
  }

  protected IRubyObject yieldDirect(
      ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
    throw new RuntimeException(
        "yieldDirect not implemented in base class. We should never get here.");
  }

  public IRubyObject call(ThreadContext context, Block block, IRubyObject[] args) {
    if (canCallDirect()) {
      return callDirect(context, block, args, Block.NULL_BLOCK);
    } else {
      return yield(context, block, prepareArgumentsForCall(context, args, block.type), null);
    }
  }

  public IRubyObject call(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
    if (canCallDirect()) {
      return callDirect(context, block, args, blockArg);
    } else {
      return yield(
          context, block, prepareArgumentsForCall(context, args, block.type), null, blockArg);
    }
  }

  public final IRubyObject yield(ThreadContext context, Block block, IRubyObject value) {
    if (canCallDirect()) {
      return yieldDirect(context, block, new IRubyObject[] {value}, null);
    } else {
      return doYield(context, block, value);
    }
  }

  public final IRubyObject yield(
      ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
    if (canCallDirect()) {
      return yieldDirect(context, block, args, self);
    } else {
      IRubyObject[] preppedValue = RubyProc.prepareArgs(context, block.type, this, args);
      return doYield(context, block, preppedValue, self);
    }
  }

  /**
   * Subclass specific yield implementation.
   *
   * <p>Should not be called directly. Gets called by {@link #yield(ThreadContext, Block,
   * org.jruby.runtime.builtin.IRubyObject)} after ensuring that any common yield logic is taken
   * care of.
   */
  protected abstract IRubyObject doYield(ThreadContext context, Block block, IRubyObject value);

  /**
   * Subclass specific yield implementation.
   *
   * <p>Should not be called directly. Gets called by {@link #yield(ThreadContext, Block,
   * org.jruby.runtime.builtin.IRubyObject[], org.jruby.runtime.builtin.IRubyObject)} after ensuring
   * that all common yield logic is taken care of.
   */
  protected abstract IRubyObject doYield(
      ThreadContext context, Block block, IRubyObject[] args, IRubyObject self);

  // FIXME: This should be unified with the final versions above
  // Here to allow incremental replacement. Overriden by subclasses which support it.
  public IRubyObject yield(
      ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
    return yield(context, block, args, self);
  }

  // FIXME: This should be unified with the final versions above
  // Here to allow incremental replacement. Overriden by subclasses which support it.
  public IRubyObject yield(ThreadContext context, Block block, IRubyObject value, Block blockArg) {
    return yield(context, block, value);
  }

  public IRubyObject call(ThreadContext context, Block block) {
    IRubyObject[] args = IRubyObject.NULL_ARRAY;
    if (canCallDirect()) {
      return callDirect(context, block, args, Block.NULL_BLOCK);
    } else {
      return yield(context, block, prepareArgumentsForCall(context, args, block.type), null);
    }
  }

  public IRubyObject call(ThreadContext context, Block block, Block unusedBlock) {
    return call(context, block);
  }

  public IRubyObject yieldSpecific(ThreadContext context, Block block) {
    if (canCallDirect()) {
      return yieldDirect(context, block, null, null);
    } else {
      return yield(context, block, null);
    }
  }

  public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0) {
    IRubyObject[] args = new IRubyObject[] {arg0};
    if (canCallDirect()) {
      return callDirect(context, block, args, Block.NULL_BLOCK);
    } else {
      return yield(context, block, prepareArgumentsForCall(context, args, block.type), null);
    }
  }

  public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, Block unusedBlock) {
    return call(context, block, arg0);
  }

  public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
    if (canCallDirect()) {
      return yieldDirect(context, block, new IRubyObject[] {arg0}, null);
    } else {
      return yield(context, block, arg0);
    }
  }

  public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
    IRubyObject[] args = new IRubyObject[] {arg0, arg1};
    if (canCallDirect()) {
      return callDirect(context, block, args, Block.NULL_BLOCK);
    } else {
      return yield(context, block, prepareArgumentsForCall(context, args, block.type), null);
    }
  }

  public IRubyObject call(
      ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block unusedBlock) {
    return call(context, block, arg0, arg1);
  }

  public IRubyObject yieldSpecific(
      ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
    if (canCallDirect()) {
      return yieldDirect(context, block, new IRubyObject[] {arg0, arg1}, null);
    } else {
      return yield(context, block, new IRubyObject[] {arg0, arg1}, null);
    }
  }

  public IRubyObject call(
      ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
    IRubyObject[] args = new IRubyObject[] {arg0, arg1, arg2};
    if (canCallDirect()) {
      return callDirect(context, block, args, Block.NULL_BLOCK);
    } else {
      return yield(context, block, prepareArgumentsForCall(context, args, block.type), null);
    }
  }

  public IRubyObject call(
      ThreadContext context,
      Block block,
      IRubyObject arg0,
      IRubyObject arg1,
      IRubyObject arg2,
      Block unusedBlock) {
    return call(context, block, arg0, arg1, arg2);
  }

  public IRubyObject yieldSpecific(
      ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
    if (canCallDirect()) {
      return yieldDirect(context, block, new IRubyObject[] {arg0, arg1, arg2}, null);
    } else {
      return yield(context, block, new IRubyObject[] {arg0, arg1, arg2}, null);
    }
  }

  public abstract StaticScope getStaticScope();

  public abstract void setStaticScope(StaticScope newScope);

  /**
   * What is the arity of this block?
   *
   * @return the arity
   */
  @Deprecated
  public Arity arity() {
    return signature.arity();
  }

  /**
   * Is the current block a real yield'able block instead a null one
   *
   * @return true if this is a valid block or false otherwise
   */
  public boolean isGiven() {
    return true;
  }

  /** Get the filename for this block */
  public abstract String getFile();

  /** get The line number for this block */
  public abstract int getLine();

  public IRubyObject[] prepareArgumentsForCall(
      ThreadContext context, IRubyObject[] args, Block.Type type) {
    if (type == Block.Type.LAMBDA) {
      signature.checkArity(context.runtime, args);
    } else {
      // SSS FIXME: How is it even possible to "call" a NORMAL block?
      // I thought only procs & lambdas can be called, and blocks are yielded to.
      if (args.length == 1) {
        // Convert value to arg-array, unwrapping where necessary
        args =
            IRRuntimeHelpers.convertValueIntoArgArray(
                context,
                args[0],
                signature,
                type == Block.Type.NORMAL && args[0] instanceof RubyArray);
      } else if (getSignature().arityValue() == 1 && !getSignature().restKwargs()) {
        // discard excess arguments
        args = args.length == 0 ? context.runtime.getSingleNilArray() : new IRubyObject[] {args[0]};
      }
    }

    return args;
  }

  public ArgumentDescriptor[] getArgumentDescriptors() {
    return ArgumentDescriptor.EMPTY_ARRAY;
  }

  public static final BlockBody NULL_BODY = new NullBlockBody();
}
예제 #16
0
 public GuardBuilder printType() {
   binder = binder.printType();
   return this;
 }
예제 #17
0
 public GuardBuilder permute(int... reorder) {
   binder = binder.permute(reorder);
   return this;
 }
예제 #18
0
 public GuardBuilder fold(MethodHandle function) {
   binder = binder.fold(function);
   return this;
 }
예제 #19
0
 public GuardBuilder convert(Class returnType, Class... argTypes) {
   binder = binder.convert(returnType, argTypes);
   return this;
 }
예제 #20
0
 public GuardBuilder insert(int index, Object... values) {
   binder = binder.insert(index, values);
   return this;
 }
예제 #21
0
 public GuardBuilder collect(int index, Class type) {
   binder = binder.collect(index, type);
   return this;
 }
예제 #22
0
 public GuardBuilder varargs(int index, Class type) {
   binder = binder.varargs(index, type);
   return this;
 }
예제 #23
0
 public GuardBuilder printType(PrintStream ps) {
   binder = binder.printType(ps);
   return this;
 }
예제 #24
0
/** Created by headius on 10/23/14. */
public abstract class InvokeSite extends MutableCallSite {
  final Signature signature;
  final Signature fullSignature;
  final int arity;
  protected final String methodName;
  final MethodHandle fallback;
  private final Set<Integer> seenTypes = new HashSet<Integer>();
  private int clearCount;
  private static final AtomicLong SITE_ID = new AtomicLong(1);
  private final long siteID = SITE_ID.getAndIncrement();
  private final int argOffset;
  private boolean boundOnce;
  CacheEntry cache = CacheEntry.NULL_CACHE;

  private static final Logger LOG = LoggerFactory.getLogger("InvokeSite");

  public String name() {
    return methodName;
  }

  public final CallType callType;

  public InvokeSite(MethodType type, String name, CallType callType) {
    super(type);
    this.methodName = name;
    this.callType = callType;

    Signature startSig;

    if (callType == CallType.SUPER) {
      // super calls receive current class argument, so offsets and signature are different
      startSig = JRubyCallSite.STANDARD_SUPER_SIG;
      argOffset = 4;
    } else {
      startSig = JRubyCallSite.STANDARD_SITE_SIG;
      argOffset = 3;
    }

    int arity;
    if (type.parameterType(type.parameterCount() - 1) == Block.class) {
      arity = type.parameterCount() - (argOffset + 1);

      if (arity == 1 && type.parameterType(argOffset) == IRubyObject[].class) {
        arity = -1;
        startSig = startSig.appendArg("args", IRubyObject[].class);
      } else {
        for (int i = 0; i < arity; i++) {
          startSig = startSig.appendArg("arg" + i, IRubyObject.class);
        }
      }
      startSig = startSig.appendArg("block", Block.class);
      fullSignature = signature = startSig;
    } else {
      arity = type.parameterCount() - argOffset;

      if (arity == 1 && type.parameterType(argOffset) == IRubyObject[].class) {
        arity = -1;
        startSig = startSig.appendArg("args", IRubyObject[].class);
      } else {
        for (int i = 0; i < arity; i++) {
          startSig = startSig.appendArg("arg" + i, IRubyObject.class);
        }
      }
      signature = startSig;
      fullSignature = startSig.appendArg("block", Block.class);
    }

    this.arity = arity;

    this.fallback = prepareBinder().invokeVirtualQuiet(Bootstrap.LOOKUP, "invoke");
  }

  public static CallSite bootstrap(InvokeSite site, MethodHandles.Lookup lookup) {
    site.setInitialTarget(site.fallback);

    return site;
  }

  public IRubyObject invoke(
      ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block)
      throws Throwable {
    RubyClass selfClass = pollAndGetClass(context, self);
    SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData();
    CacheEntry entry = selfClass.searchWithCache(methodName);
    DynamicMethod method = entry.method;

    if (methodMissing(entry, caller)) {
      return callMethodMissing(entry, callType, context, self, methodName, args, block);
    }

    MethodHandle mh = getHandle(selfClass, this, method);

    updateInvocationTarget(mh, self, selfClass, entry, switchPoint);

    return method.call(context, self, selfClass, methodName, args, block);
  }

  /** Failover version uses a monomorphic cache and DynamicMethod.call, as in non-indy. */
  public IRubyObject fail(
      ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block)
      throws Throwable {
    RubyClass selfClass = pollAndGetClass(context, self);
    String name = methodName;
    CacheEntry entry = cache;

    if (entry.typeOk(selfClass)) {
      return entry.method.call(context, self, selfClass, name, args, block);
    }

    entry = selfClass.searchWithCache(name);

    if (methodMissing(entry, caller)) {
      return callMethodMissing(entry, callType, context, self, name, args, block);
    }

    cache = entry;

    return entry.method.call(context, self, selfClass, name, args, block);
  }

  public Binder prepareBinder() {
    SmartBinder binder = SmartBinder.from(signature);

    // prepare arg[]
    if (arity == -1) {
      // do nothing, already have IRubyObject[] in args
    } else if (arity == 0) {
      binder = binder.insert(argOffset, "args", IRubyObject.NULL_ARRAY);
    } else {
      binder = binder.collect("args", "arg[0-9]+");
    }

    // add block if needed
    if (signature.lastArgType() != Block.class) {
      binder = binder.append("block", Block.NULL_BLOCK);
    }

    // bind to site
    binder = binder.insert(0, "site", this);

    return binder.binder();
  }

  MethodHandle getHandle(RubyClass dispatchClass, InvokeSite site, DynamicMethod method)
      throws Throwable {
    boolean blockGiven = signature.lastArgType() == Block.class;

    MethodHandle mh = Bootstrap.buildNativeHandle(site, method, blockGiven);
    if (mh == null) mh = Bootstrap.buildIndyHandle(site, method, method.getImplementationClass());
    if (mh == null) mh = Bootstrap.buildJittedHandle(site, method, blockGiven);
    if (mh == null) mh = Bootstrap.buildGenericHandle(site, method, dispatchClass);

    assert mh != null : "we should have a method handle of some sort by now";

    return mh;
  }

  /**
   * Update the given call site using the new target, wrapping with appropriate guard and
   * argument-juggling logic. Return a handle suitable for invoking with the site's original method
   * type.
   */
  MethodHandle updateInvocationTarget(
      MethodHandle target,
      IRubyObject self,
      RubyModule testClass,
      CacheEntry entry,
      SwitchPoint switchPoint) {
    if (target == null
        || clearCount > Options.INVOKEDYNAMIC_MAXFAIL.load()
        || (!hasSeenType(testClass.id)
            && seenTypesCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load())) {
      setTarget(target = prepareBinder().invokeVirtualQuiet(lookup(), "fail"));
    } else {
      MethodHandle fallback;
      MethodHandle gwt;

      // if we've cached no types, and the site is bound and we haven't seen this new type...
      if (seenTypesCount() > 0 && getTarget() != null && !hasSeenType(testClass.id)) {
        // stack it up into a PIC
        if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
          LOG.info(methodName + "\tadded to PIC " + logMethod(entry.method));
        fallback = getTarget();
      } else {
        // wipe out site with this new type and method
        String bind = boundOnce ? "rebind" : "bind";
        if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
          LOG.info(
              methodName
                  + "\ttriggered site #"
                  + siteID
                  + " "
                  + bind); // + " (" + file() + ":" + line() + ")");
        fallback = this.fallback;
        clearTypes();
      }

      addType(testClass.id);

      SmartHandle test;
      SmartBinder selfTest = SmartBinder.from(signature.asFold(boolean.class)).permute("self");

      if (self instanceof RubySymbol
          || self instanceof RubyFixnum
          || self instanceof RubyFloat
          || self instanceof RubyNil
          || self instanceof RubyBoolean.True
          || self instanceof RubyBoolean.False) {

        test =
            selfTest
                .insert(1, "selfJavaType", self.getClass())
                .cast(boolean.class, Object.class, Class.class)
                .invoke(TEST_CLASS);

      } else {

        test =
            SmartBinder.from(signature.changeReturn(boolean.class))
                .permute("self")
                .insert(0, "selfClass", RubyClass.class, testClass)
                .invokeStaticQuiet(Bootstrap.LOOKUP, Bootstrap.class, "testType");
      }

      gwt = MethodHandles.guardWithTest(test.handle(), target, fallback);

      // wrap in switchpoint for mutation invalidation
      gwt = switchPoint.guardWithTest(gwt, fallback);

      setTarget(gwt);
    }

    return target;
  }

  public RubyClass pollAndGetClass(ThreadContext context, IRubyObject self) {
    context.callThreadPoll();
    RubyClass selfType = ((RubyBasicObject) self).getMetaClass();
    return selfType;
  }

  @Override
  public void setTarget(MethodHandle target) {
    super.setTarget(target);
    boundOnce = true;
  }

  public void setInitialTarget(MethodHandle target) {
    super.setTarget(target);
  }

  public synchronized boolean hasSeenType(int typeCode) {
    return seenTypes.contains(typeCode);
  }

  public synchronized void addType(int typeCode) {
    seenTypes.add(typeCode);
  }

  public synchronized int seenTypesCount() {
    return seenTypes.size();
  }

  public synchronized void clearTypes() {
    seenTypes.clear();
    clearCount++;
  }

  public abstract boolean methodMissing(CacheEntry entry, IRubyObject caller);

  public IRubyObject callMethodMissing(
      CacheEntry entry,
      CallType callType,
      ThreadContext context,
      IRubyObject self,
      String name,
      IRubyObject[] args,
      Block block) {
    return Helpers.selectMethodMissing(context, self, entry.method.getVisibility(), name, callType)
        .call(context, self, self.getMetaClass(), name, args, block);
  }

  private static String logMethod(DynamicMethod method) {
    return "[#" + method.getSerialNumber() + " " + method.getImplementationClass() + "]";
  }

  @JIT
  public static boolean testMetaclass(RubyClass metaclass, IRubyObject self) {
    return metaclass == ((RubyBasicObject) self).getMetaClass();
  }

  @JIT
  public static boolean testClass(Object object, Class clazz) {
    return object.getClass() == clazz;
  }

  private static final MethodHandle TEST_CLASS =
      Binder.from(boolean.class, Object.class, Class.class)
          .invokeStaticQuiet(lookup(), InvokeSite.class, "testClass");
}
예제 #25
0
 public GuardBuilder drop(int index, int count) {
   binder = binder.drop(index, count);
   return this;
 }
예제 #26
0
 public GuardBuilder filter(int index, MethodHandle... functions) {
   binder = binder.filter(index, functions);
   return this;
 }
예제 #27
0
 public GuardBuilder insert(int index, int value) {
   binder = binder.insert(index, value);
   return this;
 }
예제 #28
0
 public GuardBuilder insert(int index, Class[] types, Object... values) {
   binder = binder.insert(index, types, values);
   return this;
 }