gt:     if(f == Null.INSTANCE) {
context: package org.renjin.methods;
  
  import org.renjin.eval.Calls;
  import org.renjin.eval.Context;
  import org.renjin.eval.EvalException;
  import org.renjin.invoke.annotations.SessionScoped;
  import org.renjin.primitives.Evaluation;
  import org.renjin.repackaged.guava.base.Preconditions;
  import org.renjin.repackaged.guava.collect.Maps;
  import org.renjin.sexp.*;
  
  import java.util.HashMap;
  
  @SessionScoped
  public class MethodDispatch {
  
  
    public static final Symbol DOT_METHOD = Symbol.get(".Method");
    public static final Symbol DOT_METHODS = Symbol.get(".Methods");
    public static final Symbol DOT_DEFINED = Symbol.get(".defined");
    public static final Symbol DOT_TARGET = Symbol.get(".target");
  
  
    public static final Symbol DOT_GENERIC =  Symbol.get(".Generic");
    public static final Symbol GENERIC = Symbol.get("generic");
  
  
    public static final Symbol R_target = Symbol.get("target");
    public static final Symbol R_defined = Symbol.get("defined");
    public static final Symbol R_nextMethod = Symbol.get("nextMethod");
    public static final Symbol R_loadMethod_name = Symbol.get("loadMethod");
    public static final Symbol R_dot_target = Symbol.get(".target");
    public static final Symbol R_dot_defined = Symbol.get(".defined");
    public static final Symbol R_dot_nextMethod = Symbol.get(".nextMethod");
    public static final Symbol R_dot_Method = Symbol.get(".Method");
  
    public static final Symbol s_dot_Methods = Symbol.get(".Methods");
    public static final Symbol s_skeleton = Symbol.get("skeleton");
    public static final Symbol s_expression = Symbol.get("expression");
    public static final Symbol s_function = Symbol.get("function");
    public static final Symbol s_getAllMethods = Symbol.get("getAllMethods");
    public static final Symbol s_objectsEnv = Symbol.get("objectsEnv");
    public static final Symbol s_MethodsListSelect = Symbol.get("MethodsListSelect");
    public static final Symbol s_sys_dot_frame = Symbol.get("sys.frame");
    public static final Symbol s_sys_dot_call = Symbol.get("sys.call");
    public static final Symbol s_sys_dot_function = Symbol.get("sys.function");
    public static final Symbol s_generic = Symbol.get("generic");
    public static final Symbol s_generic_dot_skeleton = Symbol.get("generic.skeleton");
    public static final Symbol s_subset_gets = Symbol.get("[<-");
    public static final Symbol s_element_gets = Symbol.get("[[<-");
    public static final Symbol s_argument = Symbol.get("argument");
    public static final Symbol s_allMethods = Symbol.get("allMethods");
    public static final Symbol s_dot_Data = Symbol.get(".Data");
    public static final Symbol s_dot_S3Class = Symbol.get(".S3Class");
    public static final Symbol s_getDataPart = Symbol.get("getDataPart");
    public static final Symbol s_setDataPart = Symbol.get("setDataPart");
  
    public static final Symbol s_xData = Symbol.get(".xData");
    public static final Symbol s_dotData = Symbol.get(".Data");
  
    public static final Symbol R_mtable = Symbol.get(".MTable");
    public static final Symbol R_allmtable = Symbol.get(".AllMTable");
    public static final Symbol R_sigargs = Symbol.get(".SigArgs");
    public static final Symbol R_siglength = Symbol.get(".SigLength");
  
    public static final StringVector s_missing = StringVector.valueOf("missing");
  
    /* create and preserve an object that is NOT R_NilValue, and is used
       to represent slots that are NULL (which an attribute can not
       be).  The point is not just to store NULL as a slot, but also to
       provide a check on invalid slot names (see get_slot below).
  
       The object has to be a symbol if we're going to check identity by
       just looking at referential equality. */
    public static final Symbol pseudo_NULL = Symbol.get("\001NULL\001");
  
  
  
    private boolean enabled = false;
    private HashMap<String, SEXP> extendsTable = Maps.newHashMap();
    private Environment methodsNamespace;
    private boolean tableDispatchEnabled = true;
  
  
  
    public void init(Environment environment) {
      methodsNamespace = environment;
    }
  
    public boolean isEnabled() {
      return enabled;
    }
  
    public void setEnabled(boolean enabled) {
      this.enabled = enabled;
    }
  
    public SEXP getExtends(String className) {
      SEXP value = extendsTable.get(className);
      if(value == null) {
        return Null.INSTANCE;
      } else {
        return value;
      }
    }
  
    public void putExtends(String className, SEXP klass) {
      extendsTable.put(className, klass);
    }
  
    public Environment getMethodsNamespace() {
      Preconditions.checkState(methodsNamespace != null, "methods namespace is not loaded.");
      return methodsNamespace;
    }
  
    public SEXP standardGeneric(Context context, Symbol fname, Environment ev,
                                SEXP fdef) {
      if(tableDispatchEnabled) {
        return R_dispatchGeneric(context, fname, ev, fdef);
      } else {
        throw new UnsupportedOperationException();
      }
    }
  
    public SEXP R_dispatchGeneric(Context context, Symbol fname, Environment ev, SEXP fdef)   {
      SEXP method;
      SEXP f;
      SEXP val=Null.INSTANCE;
      // char *buf, *bufptr;
      int   lwidth = 0;
      boolean prim_case = false;
  
      Environment f_env;
      if(fdef instanceof Closure) {
        f_env = ((Closure) fdef).getEnclosingEnvironment();
      } else if(fdef instanceof PrimitiveFunction) {
        fdef = R_primitive_generic(fdef);
        if(!(fdef instanceof Closure)) {
          throw new EvalException("Failed to get the generic for the primitive \"%s\"", fname.asString());
        }
        f_env = ((Closure) fdef).getEnclosingEnvironment();
        prim_case = true;
      } else {
        throw new EvalException("Expected a generic function or a primitive for dispatch, " +
            "got an object of class \"%s\"", fdef.getImplicitClass());
      }
      SEXP mtable = f_env.getVariable(R_allmtable);
      if(mtable == Symbol.UNBOUND_VALUE) {
        do_mtable(fdef, ev); /* Should initialize the generic */
        mtable = f_env.getVariable(R_allmtable);
      }
      SEXP sigargs = f_env.getVariable(R_sigargs);
      SEXP siglength = f_env.getVariable(R_siglength);
  
      if(sigargs == Symbol.UNBOUND_VALUE || siglength == Symbol.UNBOUND_VALUE ||
          mtable == Symbol.UNBOUND_VALUE) {
        throw new EvalException("Generic \"%s\" seems not to have been initialized for table dispatch---need to have .SigArgs and .AllMtable assigned in its environment",
            fname.asString());
      }
      int nargs =  (int)siglength.asReal();
      ListVector.Builder classListBuilder = ListVector.newBuilder();
      StringVector thisClass;
      StringBuilder buf = new StringBuilder();
  
      for(int i = 0; i < nargs; i++) {
        Symbol arg_sym = sigargs.getElementAsSEXP(i);
        if(is_missing_arg(context, arg_sym, ev)) {
          thisClass = s_missing;
        } else {
          /*  get its class */
          SEXP arg;
          try {
            arg = context.evaluate(arg_sym, ev);
          } catch(EvalException e) {
            throw new EvalException(String.format("error in evaluating the argument '%s' in selecting a " +
                    "method for function '%s'",
                arg_sym.getPrintName(), fname.asString()), e);
          }
          thisClass = Methods.R_data_class(arg, true);
        }
        classListBuilder.set(i, thisClass);
        if(i > 0) {
          buf.append("#");
        }
        buf.append(thisClass.asString());
      }
      ListVector classes = classListBuilder.build();
      method = ((Environment)mtable).getVariable(buf.toString());
      if(method == Symbol.UNBOUND_VALUE) {
        method = do_inherited_table(context, classes, fdef, mtable, (Environment)ev);
      }
      /* the rest of this is identical to R_standardGeneric;
           hence the f=method to remind us  */
      f = method;
      if(f.isObject()) {
        f = R_loadMethod(context, f, fname.getPrintName(), ev);
      }
  
      if(f instanceof Closure) {
        val = R_execMethod(context, (Closure)f, ev);
      } else if(f instanceof PrimitiveFunction) {
        /* primitives  can't be methods; they arise only as the
        default method when a primitive is made generic.  In this
        case, return a special marker telling the C code to go on
        with the internal computations. */
        //val = R_deferred_default_method();
        throw new UnsupportedOperationException();
      } else {
        throw new EvalException("invalid object (non-function) used as method");
  
      }
      return val;
    }
  
  
  
    /* C version of the standardGeneric R function. */
    public SEXP R_standardGeneric(Context context, Symbol fsym, Environment ev, SEXP fdef) {
      String fname = fsym.getPrintName();
      Environment f_env = context.getBaseEnvironment();
      SEXP mlist = Null.INSTANCE;
      SEXP f;
      SEXP val = Null.INSTANCE;
      int nprotect = 0;
  
      //    if(!initialized)
      //      R_initMethodDispatch(NULL);
  
      if(fdef instanceof Closure) {
        f_env = ((Closure) fdef).getEnclosingEnvironment();
        mlist = f_env.getVariable(".Methods");
        if(mlist == Symbol.UNBOUND_VALUE) {
          mlist = Null.INSTANCE;
        }
      } else if(fdef instanceof PrimitiveFunction ) {
        f_env = context.getBaseEnvironment();
        //mlist = R_primitive_methods((PrimitiveFunction)fdef);
        throw new UnsupportedOperationException();
      } else {
        throw new EvalException("invalid generic function object for method selection for function '%s': expected a function or a primitive, got an object of class \"%s\"",
            fsym.getPrintName(), fdef.getAttributes().getClassVector());
      }
      if(mlist instanceof Null || mlist instanceof Closure || mlist instanceof PrimitiveFunction) {
        f = mlist;
      } else {
        //f = do_dispatch(fname, ev, mlist, TRUE, TRUE);
        throw new UnsupportedOperationException();
      }
  