Ejemplo n.º 1
0
  public InterpreterContext ensureInstrsReady() {
    if (IRRuntimeHelpers.isDebug() && !displayedCFG) {
      LOG.info(
          "Executing '"
              + closure
              + "' (pushScope="
              + pushScope
              + ", reuseParentScope="
              + reuseParentScope);
      LOG.info(closure.debugOutput());
      displayedCFG = true;
    }

    if (interpreterContext == null) {
      if (Options.IR_PRINT.load()) {
        ByteArrayOutputStream baos = IRDumper.printIR(closure, false);

        LOG.info(
            "Printing simple IR for " + closure.getName(), "\n" + new String(baos.toByteArray()));
      }

      interpreterContext = closure.getInterpreterContext();
      fullInterpreterContext = interpreterContext;
    }
    return interpreterContext;
  }
Ejemplo n.º 2
0
 protected void doDebug() {
   // FIXME: This is printing out IRScope CFG but JIT may be active and it might not reflect
   // currently executing.  Move into JIT and into interp since they will be getting CFG from
   // different sources
   // FIXME: This is only printing out CFG once.  If we keep applying more passes then we
   // will want to print out after those new passes.
   ensureInstrsReady();
   LOG.info("Executing '" + method.getName() + "'");
   if (!displayedCFG) {
     LOG.info(method.debugOutput());
     displayedCFG = true;
   }
 }
Ejemplo n.º 3
0
  void finish(boolean close) throws BadDescriptorException, IOException {
    synchronized (refCounter) {
      // if refcount is at or below zero, we're no longer valid
      if (refCounter.get() <= 0) {
        throw new BadDescriptorException();
      }

      // if channel is already closed, we're no longer valid
      if (!channel.isOpen()) {
        throw new BadDescriptorException();
      }

      // otherwise decrement and possibly close as normal
      if (close) {
        int count = refCounter.decrementAndGet();

        if (DEBUG) LOG.info("Descriptor for fileno {} refs: {}", internalFileno, count);

        if (count <= 0) {
          // if we're the last referrer, close the channel
          try {
            channel.close();
          } finally {
            unregisterDescriptor(internalFileno);
          }
        }
      }
    }
  }
Ejemplo n.º 4
0
  public IRubyObject call(
      ThreadContext context,
      IRubyObject self,
      RubyModule clazz,
      DynamicScope evalScope,
      Block block,
      String backtraceName) {
    if (IRRuntimeHelpers.isDebug()) {
      LOG.info("Graph:\n" + cfg().toStringGraph());
      LOG.info("CFG:\n" + cfg().toStringInstrs());
    }

    // FIXME: Do not push new empty arg array in every time
    return Interpreter.INTERPRET_EVAL(
        context, self, this, clazz, new IRubyObject[] {}, backtraceName, block, null);
  }
Ejemplo n.º 5
0
 public void setFlag(Node node, Flag modifier) {
   if (dump) {
     LOG.info(
         "[ASTInspector] "
             + name
             + "\n\tset flag "
             + modifier
             + " because of "
             + node.getNodeType()
             + " at "
             + node.getPosition());
   }
   flags |= modifier.flag;
 }
Ejemplo n.º 6
0
  private void finish(boolean close) throws BadDescriptorException, IOException {
    try {
      flushWrite();

      if (DEBUG) LOG.info("Descriptor for fileno {} closed by stream", descriptor.getFileno());
    } finally {
      buffer = EMPTY_BUFFER;

      // clear runtime so it doesn't get stuck in memory (JRUBY-2933)
      runtime = null;

      // finish descriptor
      descriptor.finish(close);
    }
  }
Ejemplo n.º 7
0
  /** Ensure close (especially flush) when we're finished with. */
  @Override
  public void finalize() throws Throwable {
    super.finalize();

    if (closedExplicitly) return;

    if (DEBUG) {
      LOG.info("finalize() for not explicitly closed stream");
    }

    // FIXME: I got a bunch of NPEs when I didn't check for nulls here...HOW?!
    if (descriptor != null && descriptor.isOpen()) {
      // tidy up
      finish(autoclose);
    }
  }
Ejemplo n.º 8
0
  /**
   * Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open
   * channel but with a specified fileno.
   *
   * @param fileno The fileno to use for the new descriptor
   * @return A duplicate ChannelDescriptor based on this one
   */
  public ChannelDescriptor dup2(int fileno) {
    synchronized (refCounter) {
      refCounter.incrementAndGet();

      if (DEBUG) LOG.info("Reopen fileno {}, refs now: {}", fileno, refCounter.get());

      return new ChannelDescriptor(
          channel,
          fileno,
          originalModes,
          fileDescriptor,
          refCounter,
          canBeSeekable,
          isInAppendMode);
    }
  }
Ejemplo n.º 9
0
  /**
   * Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open
   * channel but with a specified fileno. This differs from the fileno version by making the target
   * descriptor into a new reference to the current descriptor's channel, closing what it originally
   * pointed to and preserving its original fileno.
   *
   * @param other the descriptor to dup this one into
   */
  public void dup2Into(ChannelDescriptor other) throws BadDescriptorException, IOException {
    synchronized (refCounter) {
      refCounter.incrementAndGet();

      if (DEBUG) LOG.info("Reopen fileno {}, refs now: {}", internalFileno, refCounter.get());

      other.close();

      other.channel = channel;
      other.originalModes = originalModes;
      other.fileDescriptor = fileDescriptor;
      other.refCounter = refCounter;
      other.canBeSeekable = canBeSeekable;
      other.readableChannel = readableChannel;
      other.writableChannel = writableChannel;
      other.seekableChannel = seekableChannel;
    }
  }
Ejemplo n.º 10
0
  static void log(DefaultMethod method, String name, String message, String... reason) {
    String className = method.getImplementationClass().getBaseName();

    if (className == null) className = "<anon class>";

    StringBuilder builder =
        new StringBuilder(message + ":" + className + "." + name + " at " + method.getPosition());

    if (reason.length > 0) {
      builder.append(" because of: \"");
      for (int i = 0; i < reason.length; i++) {
        builder.append(reason[i]);
      }
      builder.append('"');
    }

    LOG.info(builder.toString());
  }
Ejemplo n.º 11
0
  public static IRubyObject interpret(Ruby runtime, Node rootNode, IRubyObject self) {
    if (runtime.is1_9()) IRBuilder.setRubyVersion("1.9");

    IRScriptBody root =
        (IRScriptBody)
            IRBuilder.createIRBuilder(runtime, runtime.getIRManager())
                .buildRoot((RootNode) rootNode);

    // We get the live object ball rolling here.  This give a valid value for the top
    // of this lexical tree.  All new scope can then retrieve and set based on lexical parent.
    if (root.getStaticScope().getModule() == null) { // If an eval this may already be setup.
      root.getStaticScope().setModule(runtime.getObject());
    }

    RubyModule currModule = root.getStaticScope().getModule();

    // Scope state for root?
    IRStaticScopeFactory.newIRLocalScope(null).setModule(currModule);
    ThreadContext context = runtime.getCurrentContext();

    try {
      runBeginEndBlocks(
          root.getBeginBlocks(), context, self, null); // FIXME: No temp vars yet...not needed?
      InterpretedIRMethod method = new InterpretedIRMethod(root, currModule);
      IRubyObject rv = method.call(context, self, currModule, "(root)", IRubyObject.NULL_ARRAY);
      runBeginEndBlocks(
          root.getEndBlocks(), context, self, null); // FIXME: No temp vars yet...not needed?
      if ((IRRuntimeHelpers.isDebug() || IRRuntimeHelpers.inProfileMode())
          && interpInstrsCount > 10000) {
        LOG.info("-- Interpreted instructions: {}", interpInstrsCount);
        /*
        for (Operation o: opStats.keySet()) {
            System.out.println(o + " = " + opStats.get(o).count);
        }
        */
      }
      return rv;
    } catch (IRBreakJump bj) {
      throw IRException.BREAK_LocalJumpError.getException(context.runtime);
    }
  }
Ejemplo n.º 12
0
 public static void saveToCodeCache(
     Ruby ruby, byte[] bytecode, String packageName, File cachedClassFile) {
   String codeCache = RubyInstanceConfig.JIT_CODE_CACHE;
   File codeCacheDir = new File(codeCache);
   if (!codeCacheDir.exists()) {
     ruby.getWarnings().warn("jruby.jit.codeCache directory " + codeCacheDir + " does not exist");
   } else if (!codeCacheDir.isDirectory()) {
     ruby.getWarnings()
         .warn("jruby.jit.codeCache directory " + codeCacheDir + " is not a directory");
   } else if (!codeCacheDir.canWrite()) {
     ruby.getWarnings().warn("jruby.jit.codeCache directory " + codeCacheDir + " is not writable");
   } else {
     if (!new File(codeCache, packageName).isDirectory()) {
       boolean createdDirs = new File(codeCache, packageName).mkdirs();
       if (!createdDirs) {
         ruby.getWarnings()
             .warn("could not create JIT cache dir: " + new File(codeCache, packageName));
       }
     }
     // write to code cache
     FileOutputStream fos = null;
     try {
       if (RubyInstanceConfig.JIT_LOADING_DEBUG)
         LOG.info("writing jitted code to to " + cachedClassFile);
       fos = new FileOutputStream(cachedClassFile);
       fos.write(bytecode);
     } catch (Exception e) {
       e.printStackTrace();
       // ignore
     } finally {
       try {
         fos.close();
       } catch (Exception e) {
       }
     }
   }
 }
Ejemplo n.º 13
0
  private static IRubyObject interpret(
      ThreadContext context,
      IRubyObject self,
      IRScope scope,
      Visibility visibility,
      RubyModule implClass,
      IRubyObject[] args,
      Block block,
      Block.Type blockType) {
    Instr[] instrs = scope.getInstrsForInterpretation();

    // The base IR may not have been processed yet
    if (instrs == null) instrs = scope.prepareForInterpretation(blockType == Block.Type.LAMBDA);

    int numTempVars = scope.getTemporaryVariableSize();
    Object[] temp = numTempVars > 0 ? new Object[numTempVars] : null;
    int n = instrs.length;
    int ipc = 0;
    Instr instr = null;
    Object exception = null;
    int kwArgHashCount =
        (scope.receivesKeywordArgs() && args[args.length - 1] instanceof RubyHash) ? 1 : 0;
    DynamicScope currDynScope = context.getCurrentScope();

    // Counter tpCount = null;

    // Init profiling this scope
    boolean debug = IRRuntimeHelpers.isDebug();
    boolean profile = IRRuntimeHelpers.inProfileMode();
    Integer scopeVersion = profile ? initProfiling(scope) : 0;

    // Enter the looooop!
    while (ipc < n) {
      instr = instrs[ipc];
      ipc++;
      Operation operation = instr.getOperation();
      if (debug) {
        LOG.info("I: {}", instr);
        interpInstrsCount++;
      } else if (profile) {
        if (operation.modifiesCode()) codeModificationsCount++;
        interpInstrsCount++;
        /*
        Counter cnt = opStats.get(operation);
        if (cnt == null) {
            cnt = new Counter();
            opStats.put(operation, cnt);
        }
        cnt.count++;
        */
      }

      try {
        switch (operation.opClass) {
          case ARG_OP:
            {
              receiveArg(
                  context,
                  instr,
                  operation,
                  args,
                  kwArgHashCount,
                  currDynScope,
                  temp,
                  exception,
                  block);
              break;
            }
          case BRANCH_OP:
            {
              if (operation == Operation.JUMP) {
                ipc = ((JumpInstr) instr).getJumpTarget().getTargetPC();
              } else {
                ipc = instr.interpretAndGetNewIPC(context, currDynScope, self, temp, ipc);
              }
              break;
            }
          case CALL_OP:
            {
              if (profile) updateCallSite(instr, scope, scopeVersion);
              processCall(
                  context, instr, operation, scope, currDynScope, temp, self, block, blockType);
              break;
            }
          case BOOK_KEEPING_OP:
            {
              switch (operation) {
                case PUSH_FRAME:
                  {
                    context.preMethodFrameAndClass(
                        implClass, scope.getName(), self, block, scope.getStaticScope());
                    context.setCurrentVisibility(visibility);
                    break;
                  }
                case PUSH_BINDING:
                  {
                    // SSS NOTE: Method scopes only!
                    //
                    // Blocks are a headache -- so, these instrs. are only added to IRMethods.
                    // Blocks have more complicated logic for pushing a dynamic scope (see
                    // InterpretedIRBlockBody)
                    // Changed by DPR
                    currDynScope =
                        DynamicScope.newDynamicScope(
                            scope.getStaticScope(), context.getCurrentScope().getDepth());
                    context.pushScope(currDynScope);
                    break;
                  }
                case CHECK_ARITY:
                  ((CheckArityInstr) instr).checkArity(context.runtime, args.length);
                  break;
                case POP_FRAME:
                  context.popFrame();
                  context.popRubyClass();
                  break;
                case POP_BINDING:
                  context.popScope();
                  break;
                case THREAD_POLL:
                  if (profile) {
                    // SSS: Not being used currently
                    // tpCount.count++;
                    globalThreadPollCount++;

                    // 20K is arbitrary
                    // Every 20K profile counts, spit out profile stats
                    if (globalThreadPollCount % 20000 == 0) {
                      analyzeProfile();
                      // outputProfileStats();
                    }
                  }
                  context.callThreadPoll();
                  break;
                case LINE_NUM:
                  context.setLine(((LineNumberInstr) instr).lineNumber);
                  break;
                case RECORD_END_BLOCK:
                  ((RecordEndBlockInstr) instr).interpret();
                  break;
              }
              break;
            }
          case OTHER_OP:
            {
              Object result = null;
              switch (operation) {
                  // --------- Return flavored instructions --------
                case BREAK:
                  {
                    BreakInstr bi = (BreakInstr) instr;
                    IRubyObject rv =
                        (IRubyObject)
                            bi.getReturnValue().retrieve(context, self, currDynScope, temp);
                    // This also handles breaks in lambdas -- by converting them to a return
                    return IRRuntimeHelpers.initiateBreak(
                        context, scope, bi.getScopeToReturnTo().getScopeId(), rv, blockType);
                  }
                case RETURN:
                  {
                    return (IRubyObject)
                        retrieveOp(
                            ((ReturnBase) instr).getReturnValue(),
                            context,
                            self,
                            currDynScope,
                            temp);
                  }
                case NONLOCAL_RETURN:
                  {
                    NonlocalReturnInstr ri = (NonlocalReturnInstr) instr;
                    IRubyObject rv =
                        (IRubyObject)
                            retrieveOp(ri.getReturnValue(), context, self, currDynScope, temp);
                    ipc = n;
                    // If not in a lambda, check if this was a non-local return
                    if (!IRRuntimeHelpers.inLambda(blockType)) {
                      IRRuntimeHelpers.initiateNonLocalReturn(
                          context, scope, ri.methodToReturnFrom, rv);
                    }
                    return rv;
                  }

                  // ---------- Common instruction ---------
                case COPY:
                  {
                    CopyInstr c = (CopyInstr) instr;
                    result = retrieveOp(c.getSource(), context, self, currDynScope, temp);
                    setResult(temp, currDynScope, c.getResult(), result);
                    break;
                  }

                case GET_FIELD:
                  {
                    GetFieldInstr gfi = (GetFieldInstr) instr;
                    IRubyObject object =
                        (IRubyObject) gfi.getSource().retrieve(context, self, currDynScope, temp);
                    VariableAccessor a = gfi.getAccessor(object);
                    result = a == null ? null : (IRubyObject) a.get(object);
                    if (result == null) {
                      result = context.nil;
                    }
                    setResult(temp, currDynScope, gfi.getResult(), result);
                    break;
                  }

                case SEARCH_CONST:
                  {
                    SearchConstInstr sci = (SearchConstInstr) instr;
                    result = sci.getCachedConst();
                    if (!sci.isCached(context, result))
                      result = sci.cache(context, currDynScope, self, temp);
                    setResult(temp, currDynScope, sci.getResult(), result);
                    break;
                  }

                  // ---------- All the rest ---------
                default:
                  result = instr.interpret(context, currDynScope, self, temp, block);
                  setResult(temp, currDynScope, instr, result);
                  break;
              }

              break;
            }
        }
      } catch (Throwable t) {
        // Unrescuable:
        //    IRReturnJump, ThreadKill, RubyContinuation, MainExitException, etc.
        //    These cannot be rescued -- only run ensure blocks
        //
        // Others:
        //    IRBreakJump, Ruby exceptions, errors, and other java exceptions.
        //    These can be rescued -- run rescue blocks

        if (debug)
          LOG.info(
              "in scope: "
                  + scope
                  + ", caught Java throwable: "
                  + t
                  + "; excepting instr: "
                  + instr);
        ipc = (t instanceof Unrescuable) ? scope.getEnsurerPC(instr) : scope.getRescuerPC(instr);
        if (debug) LOG.info("ipc for rescuer/ensurer: " + ipc);

        if (ipc == -1) {
          Helpers.throwException((Throwable) t);
        } else {
          exception = t;
        }
      }
    }

    // Control should never get here!
    // SSS FIXME: But looks like BEGIN/END blocks get here -- needs fixing
    return null;
  }
Ejemplo n.º 14
0
 public void setFlag(Flag modifier) {
   if (dump) {
     LOG.info("[ASTInspector] " + name + "\n\tset flag " + modifier);
   }
   flags |= modifier.flag;
 }
Ejemplo n.º 15
0
    @SuppressWarnings("unchecked")
    protected void compile() {
      if (bytecode != null) return;

      // check if we have a cached compiled version on disk
      String codeCache = RubyInstanceConfig.JIT_CODE_CACHE;
      File cachedClassFile = new File(codeCache + "/" + className + ".class");

      if (codeCache != null && cachedClassFile.exists()) {
        FileInputStream fis = null;
        try {
          if (RubyInstanceConfig.JIT_LOADING_DEBUG)
            LOG.info("loading cached code from: " + cachedClassFile);
          fis = new FileInputStream(cachedClassFile);
          bytecode = new byte[(int) fis.getChannel().size()];
          fis.read(bytecode);
          name = new ClassReader(bytecode).getClassName();
          return;
        } catch (Exception e) {
          // ignore and proceed to compile
        } finally {
          try {
            fis.close();
          } catch (Exception e) {
          }
        }
      }

      // Time the compilation
      long start = System.nanoTime();

      asmCompiler.startScript(staticScope);
      final ASTCompiler compiler = ruby.getInstanceConfig().newCompiler();

      CompilerCallback args =
          new CompilerCallback() {
            public void call(BodyCompiler context) {
              compiler.compileArgs(argsNode, context, true);
            }
          };

      ASTInspector inspector = new ASTInspector();
      if (ruby.getInstanceConfig().isJitDumping()) {
        inspector = new ASTInspector(className, true);
      }
      // check args first, since body inspection can depend on args
      inspector.inspect(argsNode);
      inspector.inspect(bodyNode);

      BodyCompiler methodCompiler;
      if (bodyNode != null) {
        // we have a body, do a full-on method
        methodCompiler = asmCompiler.startFileMethod(args, staticScope, inspector);
        compiler.compileBody(bodyNode, methodCompiler, true);
      } else {
        // If we don't have a body, check for required or opt args
        // if opt args, they could have side effects
        // if required args, need to raise errors if too few args passed
        // otherwise, method does nothing, make it a nop
        if (argsNode != null
            && (argsNode.getRequiredArgsCount() > 0 || argsNode.getOptionalArgsCount() > 0)) {
          methodCompiler = asmCompiler.startFileMethod(args, staticScope, inspector);
          methodCompiler.loadNil();
        } else {
          methodCompiler = asmCompiler.startFileMethod(null, staticScope, inspector);
          methodCompiler.loadNil();
          jitCallConfig = CallConfiguration.FrameNoneScopeNone;
        }
      }
      methodCompiler.endBody();
      asmCompiler.endScript(false, false);

      // if we haven't already decided on a do-nothing call
      if (jitCallConfig == null) {
        jitCallConfig = inspector.getCallConfig();
      }

      bytecode = asmCompiler.getClassByteArray();
      if (ruby.getInstanceConfig().isJitDumping()) {
        TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(System.out));
        new ClassReader(bytecode).accept(tcv, 0);
      }

      if (bytecode.length > ruby.getInstanceConfig().getJitMaxSize()) {
        bytecode = null;
        throw new NotCompilableException(
            "JITed method size exceeds configured max of "
                + ruby.getInstanceConfig().getJitMaxSize());
      }

      if (codeCache != null) {
        JITCompiler.saveToCodeCache(ruby, bytecode, packageName, cachedClassFile);
      }

      counts.compiledCount.incrementAndGet();
      counts.compileTime.addAndGet(System.nanoTime() - start);
      counts.codeSize.addAndGet(bytecode.length);
      counts.averageCompileTime.set(counts.compileTime.get() / counts.compiledCount.get());
      counts.averageCodeSize.set(counts.codeSize.get() / counts.compiledCount.get());
      synchronized (counts) {
        if (counts.largestCodeSize.get() < bytecode.length) {
          counts.largestCodeSize.set(bytecode.length);
        }
      }
    }
Ejemplo n.º 16
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);
  }
Ejemplo n.º 17
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);
  }
Ejemplo n.º 18
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;
  }