@Specialization
    public Object methodMissing(RubyBasicObject self, Object[] args, RubyProc block) {
      notDesignedForCompilation();

      CompilerDirectives.transferToInterpreter();

      final RubySymbol name = (RubySymbol) args[0];
      final Object[] sentArgs = Arrays.copyOfRange(args, 1, args.length);
      return methodMissing(self, name, sentArgs, block);
    }
    @Specialization
    public Object methodMissing(
        RubyBasicObject self,
        Object[] args,
        @SuppressWarnings("unused") UndefinedPlaceholder block) {
      notDesignedForCompilation();

      CompilerDirectives.transferToInterpreter();

      final RubySymbol name = (RubySymbol) args[0];
      final Object[] sentArgs = Arrays.copyOfRange(args, 1, args.length);
      return methodMissing(self, name, sentArgs, null);
    }
  @Override
  public boolean doesRespondTo(VirtualFrame frame, RubyBasicObject receiverObject) {
    // TODO(CS): copy-and-paste of the above - needs to be factored out

    MethodCacheEntry entry = lookupInCache(receiverObject.getLookupNode());

    if (entry == null) {
      CompilerDirectives.transferToInterpreterAndInvalidate();

      final RubyBasicObject boxedCallingSelf =
          getContext().getCoreLibrary().box(RubyArguments.getSelf(frame.getArguments()));

      try {
        entry = new MethodCacheEntry(lookup(boxedCallingSelf, receiverObject, name), false);
      } catch (UseMethodMissingException e) {
        try {
          entry =
              new MethodCacheEntry(
                  lookup(boxedCallingSelf, receiverObject, "method_missing"), true);
        } catch (UseMethodMissingException e2) {
          throw new RaiseException(
              getContext()
                  .getCoreLibrary()
                  .runtimeError(receiverObject.toString() + " didn't have a #method_missing"));
        }
      }

      if (entry.isMethodMissing()) {
        hasAnyMethodsMissing = true;
      }

      cache.put(receiverObject.getLookupNode(), entry);

      if (cache.size() > RubyContext.GENERAL_DISPATCH_SIZE_WARNING_THRESHOLD) {
        getContext()
            .getRuntime()
            .getWarnings()
            .warn(
                IRubyWarnings.ID.TRUFFLE,
                getEncapsulatingSourceSection().getSource().getName(),
                getEncapsulatingSourceSection().getStartLine(),
                "general call node cache has " + cache.size() + " entries");
      }
    }

    return !entry.isMethodMissing();
  }
  @Override
  public Object dispatch(
      VirtualFrame frame,
      RubyBasicObject receiverObject,
      RubyProc blockObject,
      Object[] argumentsObjects) {
    MethodCacheEntry entry = lookupInCache(receiverObject.getLookupNode());

    if (entry == null) {
      CompilerDirectives.transferToInterpreterAndInvalidate();

      final RubyBasicObject boxedCallingSelf =
          getContext().getCoreLibrary().box(RubyArguments.getSelf(frame.getArguments()));

      try {
        entry = new MethodCacheEntry(lookup(boxedCallingSelf, receiverObject, name), false);
      } catch (UseMethodMissingException e) {
        try {
          entry =
              new MethodCacheEntry(
                  lookup(boxedCallingSelf, receiverObject, "method_missing"), true);
        } catch (UseMethodMissingException e2) {
          throw new RaiseException(
              getContext()
                  .getCoreLibrary()
                  .runtimeError(receiverObject.toString() + " didn't have a #method_missing"));
        }
      }

      if (entry.isMethodMissing()) {
        hasAnyMethodsMissing = true;
      }

      cache.put(receiverObject.getLookupNode(), entry);

      if (cache.size() > RubyContext.GENERAL_DISPATCH_SIZE_WARNING_THRESHOLD) {
        getContext()
            .getRuntime()
            .getWarnings()
            .warn(
                IRubyWarnings.ID.TRUFFLE,
                getEncapsulatingSourceSection().getSource().getName(),
                getEncapsulatingSourceSection().getStartLine(),
                "general call node cache has " + cache.size() + " entries");
      }
    }

    final Object[] argumentsToUse;

    if (hasAnyMethodsMissing && entry.isMethodMissing()) {
      final Object[] modifiedArgumentsObjects = new Object[1 + argumentsObjects.length];
      modifiedArgumentsObjects[0] = getContext().newSymbol(name);
      System.arraycopy(argumentsObjects, 0, modifiedArgumentsObjects, 1, argumentsObjects.length);
      argumentsToUse = modifiedArgumentsObjects;
    } else {
      argumentsToUse = argumentsObjects;
    }

    return callNode.call(
        frame,
        entry.getMethod().getCallTarget(),
        RubyArguments.pack(
            entry.getMethod().getDeclarationFrame(), receiverObject, blockObject, argumentsToUse));
  }