Esempio n. 1
0
  public static IRubyObject ivarGet(VariableSite site, IRubyObject self) throws Throwable {
    RubyClass realClass = self.getMetaClass().getRealClass();
    VariableAccessor accessor = realClass.getVariableAccessorForRead(site.name());

    // produce nil if the variable has not been initialize
    MethodHandle nullToNil =
        findStatic(
            Helpers.class,
            "nullToNil",
            methodType(IRubyObject.class, IRubyObject.class, IRubyObject.class));
    nullToNil = insertArguments(nullToNil, 1, self.getRuntime().getNil());
    nullToNil = explicitCastArguments(nullToNil, methodType(IRubyObject.class, Object.class));

    // get variable value and filter with nullToNil
    MethodHandle getValue;
    boolean direct = false;

    if (accessor instanceof FieldVariableAccessor) {
      direct = true;
      int offset = ((FieldVariableAccessor) accessor).getOffset();
      Class cls = REIFIED_OBJECT_CLASSES[offset];
      getValue = lookup().findGetter(cls, "var" + offset, Object.class);
      getValue = explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class));
    } else {
      getValue =
          findStatic(
              VariableAccessor.class,
              "getVariable",
              methodType(Object.class, RubyBasicObject.class, int.class));
      getValue =
          explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class, int.class));
      getValue = insertArguments(getValue, 1, accessor.getIndex());
    }

    getValue = filterReturnValue(getValue, nullToNil);

    // prepare fallback
    MethodHandle fallback = null;
    if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
        LOG.info(
            site.name()
                + "\tqet on type "
                + self.getMetaClass().id
                + " failed (polymorphic)"
                + extractSourceInfo(site));
      fallback =
          findStatic(
              Bootstrap.class,
              "ivarGetFail",
              methodType(IRubyObject.class, VariableSite.class, IRubyObject.class));
      fallback = fallback.bindTo(site);
      site.setTarget(fallback);
      return (IRubyObject) fallback.invokeWithArguments(self);
    } else {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
        if (direct) {
          LOG.info(
              site.name()
                  + "\tget field on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        } else {
          LOG.info(
              site.name()
                  + "\tget on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        }
      }
      fallback = site.getTarget();
      site.incrementChainCount();
    }

    // prepare test
    MethodHandle test =
        findStatic(
            InvocationLinker.class,
            "testRealClass",
            methodType(boolean.class, int.class, IRubyObject.class));
    test = insertArguments(test, 0, accessor.getClassId());

    getValue = guardWithTest(test, getValue, fallback);

    if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
      LOG.info(
          site.name()
              + "\tget on class "
              + self.getMetaClass().id
              + " bound directly"
              + extractSourceInfo(site));
    site.setTarget(getValue);

    return (IRubyObject) getValue.invokeExact(self);
  }
Esempio n. 2
0
  public static void ivarSet(VariableSite site, IRubyObject self, IRubyObject value)
      throws Throwable {
    RubyClass realClass = self.getMetaClass().getRealClass();
    VariableAccessor accessor = realClass.getVariableAccessorForWrite(site.name());

    // set variable value and fold by returning value
    MethodHandle setValue;
    boolean direct = false;

    if (accessor instanceof FieldVariableAccessor) {
      direct = true;
      int offset = ((FieldVariableAccessor) accessor).getOffset();
      Class cls = REIFIED_OBJECT_CLASSES[offset];
      setValue = findStatic(cls, "setVariableChecked", methodType(void.class, cls, Object.class));
      setValue =
          explicitCastArguments(
              setValue, methodType(void.class, IRubyObject.class, IRubyObject.class));
    } else {
      setValue =
          findStatic(
              accessor.getClass(),
              "setVariableChecked",
              methodType(
                  void.class, RubyBasicObject.class, RubyClass.class, int.class, Object.class));
      setValue =
          explicitCastArguments(
              setValue,
              methodType(
                  void.class, IRubyObject.class, RubyClass.class, int.class, IRubyObject.class));
      setValue = insertArguments(setValue, 1, realClass, accessor.getIndex());
    }

    // prepare fallback
    MethodHandle fallback = null;
    if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
        LOG.info(
            site.name()
                + "\tset on type "
                + self.getMetaClass().id
                + " failed (polymorphic)"
                + extractSourceInfo(site));
      fallback =
          findStatic(
              Bootstrap.class,
              "ivarSetFail",
              methodType(void.class, VariableSite.class, IRubyObject.class, IRubyObject.class));
      fallback = fallback.bindTo(site);
      site.setTarget(fallback);
      fallback.invokeExact(self, value);
    } else {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
        if (direct) {
          LOG.info(
              site.name()
                  + "\tset field on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        } else {
          LOG.info(
              site.name()
                  + "\tset on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        }
      }
      fallback = site.getTarget();
      site.incrementChainCount();
    }

    // prepare test
    MethodHandle test =
        findStatic(
            InvocationLinker.class,
            "testRealClass",
            methodType(boolean.class, int.class, IRubyObject.class));
    test = insertArguments(test, 0, accessor.getClassId());
    test = dropArguments(test, 1, IRubyObject.class);

    setValue = guardWithTest(test, setValue, fallback);

    if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
      LOG.info(
          site.name()
              + "\tset on class "
              + self.getMetaClass().id
              + " bound directly"
              + extractSourceInfo(site));
    site.setTarget(setValue);

    setValue.invokeExact(self, value);
  }
Esempio n. 3
0
  /**
   * 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;
  }