private Object getMethod(Object ctx, String name)
      throws IllegalAccessException, InvocationTargetException {
    assert debug("\n  **  {method: " + name + "}");

    int st = cursor;
    String tk = cursor != end && expr[cursor] == '(' && ((cursor = balancedCapture(expr, cursor, '(')) - st) > 1 ?
        new String(expr, st + 1, cursor - st - 1) : "";
    cursor++;

    Object[] preConvArgs;
    Object[] args;
    Class[] argTypes;
    ExecutableStatement[] es;
    List<char[]> subtokens;

    if (tk.length() == 0) {
      args = preConvArgs = ParseTools.EMPTY_OBJ_ARR;
      argTypes = ParseTools.EMPTY_CLS_ARR;
      es = null;
      subtokens = null;
    }
    else {
      subtokens = parseParameterList(tk.toCharArray(), 0, -1);

      es = new ExecutableStatement[subtokens.size()];
      args = new Object[subtokens.size()];
      argTypes = new Class[subtokens.size()];
      preConvArgs = new Object[es.length];

      for (int i = 0; i < subtokens.size(); i++) {
        assert debug("subtoken[" + i + "] { " + new String(subtokens.get(i)) + " }");
        preConvArgs[i] = args[i] = (es[i] = (ExecutableStatement) subCompileExpression(subtokens.get(i), pCtx))
            .getValue(this.thisRef, this.thisRef, variableFactory);

        if (es[i].isExplicitCast()) argTypes[i] = es[i].getKnownEgressType();
      }

      if (pCtx.isStrictTypeEnforcement()) {
        for (int i = 0; i < args.length; i++) {
          argTypes[i] = es[i].getKnownEgressType();
        }
      }
      else {
        for (int i = 0; i < args.length; i++) {
          if (argTypes[i] != null) continue;

          if (es[i].getKnownEgressType() == Object.class) {
            argTypes[i] = args[i] == null ? null : args[i].getClass();
          }
          else {
            argTypes[i] = es[i].getKnownEgressType();
          }
        }
      }
    }

    if (first && variableFactory != null && variableFactory.isResolveable(name)) {
      Object ptr = variableFactory.getVariableResolver(name).getValue();

      if (ptr instanceof Method) {
        ctx = ((Method) ptr).getDeclaringClass();
        name = ((Method) ptr).getName();
      }
      else if (ptr instanceof MethodStub) {
        ctx = ((MethodStub) ptr).getClassReference();
        name = ((MethodStub) ptr).getMethodName();
      }
      else if (ptr instanceof FunctionInstance) {

        if (es != null && es.length != 0) {
          compiledInputs.addAll(Arrays.asList(es));

          intPush(es.length);

          assert debug("ANEWARRAY [" + es.length + "]");
          mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");

          assert debug("ASTORE 4");
          mv.visitVarInsn(ASTORE, 4);

          for (int i = 0; i < es.length; i++) {
            assert debug("ALOAD 4");
            mv.visitVarInsn(ALOAD, 4);
            intPush(i);
            loadField(i);

            assert debug("ALOAD 1");
            mv.visitVarInsn(ALOAD, 1);

            assert debug("ALOAD 3");
            mv.visitIntInsn(ALOAD, 3);

            assert debug("INVOKEINTERFACE ExecutableStatement.getValue");
            mv.visitMethodInsn(INVOKEINTERFACE, NAMESPACE + "compiler/ExecutableStatement", "getValue",
                "(Ljava/lang/Object;L" + NAMESPACE + "integration/VariableResolverFactory;)Ljava/lang/Object;");

            assert debug("AASTORE");
            mv.visitInsn(AASTORE);
          }
        }
        else {
          assert debug("ACONST_NULL");
          mv.visitInsn(ACONST_NULL);

          assert debug("CHECKCAST java/lang/Object");
          mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");

          assert debug("ASTORE 4");
          mv.visitVarInsn(ASTORE, 4);
        }

        if (variableFactory.isIndexedFactory() && variableFactory.isTarget(name)) {
          loadVariableByIndex(variableFactory.variableIndexOf(name));
        }
        else {
          loadVariableByName(name);
        }

        checkcast(FunctionInstance.class);

        assert debug("ALOAD 1");
        mv.visitVarInsn(ALOAD, 1);

        assert debug("ALOAD 2");
        mv.visitVarInsn(ALOAD, 2);

        assert debug("ALOAD 3");
        mv.visitVarInsn(ALOAD, 3);

        assert debug("ALOAD 4");
        mv.visitVarInsn(ALOAD, 4);

        assert debug("INVOKEVIRTUAL Function.call");
        mv.visitMethodInsn(INVOKEVIRTUAL,
            getInternalName(FunctionInstance.class),
            "call",
            "(Ljava/lang/Object;Ljava/lang/Object;L" + NAMESPACE
                + "integration/VariableResolverFactory;[Ljava/lang/Object;)Ljava/lang/Object;");

        return ((FunctionInstance) ptr).call(ctx, thisRef, variableFactory, args);
      }
      else {
        throw new OptimizationFailure("attempt to optimize a method call for a reference that does not point to a method: "
            + name + " (reference is type: " + (ctx != null ? ctx.getClass().getName() : null) + ")");
      }

      first = false;
    }
    else if (returnType != null && returnType.isPrimitive()) {
      //noinspection unchecked
      wrapPrimitive(returnType);
    }

    /**
     * If the target object is an instance of java.lang.Class itself then do not
     * adjust the Class scope target.
     */

    boolean classTarget = false;
    Class<?> cls = currType != null ? currType : ((classTarget = ctx instanceof Class) ? (Class<?>) ctx : ctx.getClass());

    currType = null;

    Method m;
    Class[] parameterTypes = null;

    /**
     * Try to find an instance method from the class target.
     */
    if ((m = getBestCandidate(argTypes, name, cls, cls.getMethods(), false, classTarget)) != null) {
      parameterTypes = m.getParameterTypes();
    }

    if (m == null && classTarget) {
      /**
       * If we didn't find anything, maybe we're looking for the actual java.lang.Class methods.
       */
      if ((m = getBestCandidate(argTypes, name, cls, Class.class.getMethods(), false)) != null) {
        parameterTypes = m.getParameterTypes();
      }
    }

    // If we didn't find anything and the declared class is different from the actual one try also with the actual one
    if (m == null && cls != ctx.getClass() && !(ctx instanceof Class)) {
      cls = ctx.getClass();
      if ((m = getBestCandidate(argTypes, name, cls, cls.getMethods(), false, classTarget)) != null) {
        parameterTypes = m.getParameterTypes();
      }
    }

    if (es != null && m != null && m.isVarArgs() && (es.length != parameterTypes.length || !(es[es.length-1] instanceof ExecutableAccessor))) {
      // normalize ExecutableStatement for varargs
      ExecutableStatement[] varArgEs = new ExecutableStatement[parameterTypes.length];
      int varArgStart = parameterTypes.length-1;
      for (int i = 0; i < varArgStart; i++) varArgEs[i] = es[i];

      String varargsTypeName = parameterTypes[parameterTypes.length-1].getComponentType().getName();
      String varArgExpr;
      if("null".equals(tk)){ //if null is the token no need for wrapping
            varArgExpr = tk;
      } else{
         StringBuilder sb = new StringBuilder("new ").append(varargsTypeName).append("[] {");
         for (int i = varArgStart; i < subtokens.size(); i++) {
           sb.append(subtokens.get(i));
           if (i < subtokens.size()-1) sb.append(",");
         }
        varArgExpr = sb.append("}").toString();
      }
      char[] token = varArgExpr.toCharArray();
      varArgEs[varArgStart] = ((ExecutableStatement)subCompileExpression(token, pCtx));
      es = varArgEs;

      if (preConvArgs.length == parameterTypes.length-1) {
        // empty vararg
        Object[] preConvArgsForVarArg = new Object[parameterTypes.length];
        for (int i = 0; i < preConvArgs.length; i++) preConvArgsForVarArg[i] = preConvArgs[i];
        preConvArgsForVarArg[parameterTypes.length-1] = Array.newInstance(parameterTypes[parameterTypes.length-1].getComponentType(), 0);
        preConvArgs = preConvArgsForVarArg;
      }
    }

    int inputsOffset = compiledInputs.size();

    if (es != null) {
      for (ExecutableStatement e : es) {
        if (e instanceof ExecutableLiteral) {
          continue;
        }

        compiledInputs.add(e);
      }
    }

    if (first) {
      assert debug("ALOAD 1 (D) ");
      mv.visitVarInsn(ALOAD, 1);
    }

    if (m == null) {
      StringAppender errorBuild = new StringAppender();

      if (parameterTypes != null) {
        for (int i = 0; i < args.length; i++) {
          errorBuild.append(parameterTypes[i] != null ? parameterTypes[i].getClass().getName() : null);
          if (i < args.length - 1) errorBuild.append(", ");
        }
      }

      if ("size".equals(name) && args.length == 0 && cls.isArray()) {
        anyArrayCheck(cls);

        assert debug("ARRAYLENGTH");
        mv.visitInsn(ARRAYLENGTH);

        wrapPrimitive(int.class);
        return getLength(ctx);
      }

      throw new CompileException("unable to resolve method: " + cls.getName() + "."
          + name + "(" + errorBuild.toString() + ") [arglength=" + args.length + "]", expr, st);
    }
    else {
      m = getWidenedTarget(m);

      if (es != null) {
        ExecutableStatement cExpr;
        for (int i = 0; i < es.length; i++) {
          if ((cExpr = es[i]).getKnownIngressType() == null) {
            cExpr.setKnownIngressType(parameterTypes[i]);
            cExpr.computeTypeConversionRule();
          }
          if (!cExpr.isConvertableIngressEgress() && i < args.length) {
            args[i] = convert(args[i], paramTypeVarArgsSafe(parameterTypes, i, m.isVarArgs()));
          }
        }
      }
      else {
        /**
         * Coerce any types if required.
         */
        for (int i = 0; i < args.length; i++) {
          args[i] = convert(args[i], paramTypeVarArgsSafe(parameterTypes, i, m.isVarArgs()));
        }
      }

      if (m.getParameterTypes().length == 0) {
        if ((m.getModifiers() & STATIC) != 0) {
          assert debug("INVOKESTATIC " + m.getName());
          mv.visitMethodInsn(INVOKESTATIC, getInternalName(m.getDeclaringClass()), m.getName(), getMethodDescriptor(m));
        }
        else {
          assert debug("CHECKCAST " + getInternalName(m.getDeclaringClass()));
          mv.visitTypeInsn(CHECKCAST, getInternalName(m.getDeclaringClass()));

          if (m.getDeclaringClass().isInterface()) {
            assert debug("INVOKEINTERFACE " + m.getName());
            mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(m.getDeclaringClass()), m.getName(),
                getMethodDescriptor(m));

          }
          else {
            assert debug("INVOKEVIRTUAL " + m.getName());
            mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(m.getDeclaringClass()), m.getName(),
                getMethodDescriptor(m));
          }
        }

        returnType = m.getReturnType();

        stacksize++;
      }
      else {
        if ((m.getModifiers() & STATIC) == 0) {
          assert debug("CHECKCAST " + getInternalName(cls));
          mv.visitTypeInsn(CHECKCAST, getInternalName(cls));
        }

          Class<?> aClass = m.getParameterTypes()[m.getParameterTypes().length - 1];
          if(m.isVarArgs()){
              if(es == null || es.length == (m.getParameterTypes().length - 1) ){
                  ExecutableStatement[] executableStatements = new ExecutableStatement[m.getParameterTypes().length];
                  if(es != null){
                      System.arraycopy(es,0,executableStatements,0,es.length);
                  }
                  executableStatements[executableStatements.length -1 ]= new ExecutableLiteral(Array.newInstance(aClass,0));
                  es = executableStatements;
              }
          }

        for (int i = 0; es != null && i < es.length; i++) {
          if (es[i] instanceof ExecutableLiteral) {
            ExecutableLiteral literal = (ExecutableLiteral) es[i];

            if (literal.getLiteral() == null) {
              assert debug("ICONST_NULL");
              mv.visitInsn(ACONST_NULL);
              continue;
            }
            else if (parameterTypes[i] == int.class && literal.intOptimized()) {
              intPush(literal.getInteger32());
              continue;
            }
            else if (parameterTypes[i] == int.class && preConvArgs[i] instanceof Integer) {
              intPush((Integer) preConvArgs[i]);
              continue;
            }
            else if (parameterTypes[i] == boolean.class) {
              boolean bool = DataConversion.convert(literal.getLiteral(), Boolean.class);
              assert debug(bool ? "ICONST_1" : "ICONST_0");
              mv.visitInsn(bool ? ICONST_1 : ICONST_0);
              continue;
            }
            else {
              Object lit = literal.getLiteral();

              if (parameterTypes[i] == Object.class) {
                if (isPrimitiveWrapper(lit.getClass())) {
                  if (lit.getClass() == Integer.class) {
                    intPush((Integer) lit);
                  }
                  else {
                    assert debug("LDC " + lit);
                    mv.visitLdcInsn(lit);
                  }

                  wrapPrimitive(lit.getClass());
                }
                else if (lit instanceof String) {
                  mv.visitLdcInsn(lit);
                  checkcast(Object.class);
                }
                continue;
              }
              else if (canConvert(parameterTypes[i], lit.getClass())) {
                Object c = convert(lit, parameterTypes[i]);
                if (c instanceof Class) {
                  ldcClassConstant((Class) c);
                }
                else {
                  assert debug("LDC " + lit + " (" + lit.getClass().getName() + ")");

                  mv.visitLdcInsn(convert(lit, parameterTypes[i]));

                  if (isPrimitiveWrapper(parameterTypes[i])) {
                    wrapPrimitive(lit.getClass());
                  }
                }
                continue;
              }
              else {
                throw new OptimizationNotSupported();
              }
            }
          }

          assert debug("ALOAD 0");
          mv.visitVarInsn(ALOAD, 0);

          assert debug("GETFIELD p" + inputsOffset);
          mv.visitFieldInsn(GETFIELD, className, "p" + inputsOffset, "L" + NAMESPACE + "compiler/ExecutableStatement;");

          inputsOffset++;

          assert debug("ALOAD 2");
          mv.visitVarInsn(ALOAD, 2);

          assert debug("ALOAD 3");
          mv.visitVarInsn(ALOAD, 3);

          assert debug("INVOKEINTERFACE ExecutableStatement.getValue");
          mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(ExecutableStatement.class), "getValue",
              "(Ljava/lang/Object;L" + NAMESPACE + "integration/VariableResolverFactory;)Ljava/lang/Object;");

          if (parameterTypes[i].isPrimitive()) {
            if (preConvArgs[i] == null ||
                (parameterTypes[i] != String.class &&
                    !parameterTypes[i].isAssignableFrom(preConvArgs[i].getClass()))) {

              ldcClassConstant(getWrapperClass(parameterTypes[i]));

              assert debug("INVOKESTATIC DataConversion.convert");
              mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "DataConversion", "convert",
                  "(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;");
            }

            unwrapPrimitive(parameterTypes[i]);
          }
          else if (preConvArgs[i] == null ||
              (parameterTypes[i] != String.class &&
                  !parameterTypes[i].isAssignableFrom(preConvArgs[i].getClass()))) {

            ldcClassConstant(parameterTypes[i]);

            assert debug("INVOKESTATIC DataConversion.convert");
            mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "DataConversion", "convert",
                "(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;");

            assert debug("CHECKCAST " + getInternalName(parameterTypes[i]));
            mv.visitTypeInsn(CHECKCAST, getInternalName(parameterTypes[i]));
          }
          else if (parameterTypes[i] == String.class) {
            assert debug("<<<DYNAMIC TYPE OPTIMIZATION STRING>>");
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf",
                "(Ljava/lang/Object;)Ljava/lang/String;");
          }
          else {
            assert debug("<<<DYNAMIC TYPING BYPASS>>>");
            assert debug("<<<OPT. JUSTIFICATION " + parameterTypes[i] + "=" + preConvArgs[i].getClass() + ">>>");

            assert debug("CHECKCAST " + getInternalName(parameterTypes[i]));
            mv.visitTypeInsn(CHECKCAST, getInternalName(parameterTypes[i]));
          }
        }

        if ((m.getModifiers() & STATIC) != 0) {
          assert debug("INVOKESTATIC: " + m.getName());
          mv.visitMethodInsn(INVOKESTATIC, getInternalName(m.getDeclaringClass()), m.getName(), getMethodDescriptor(m));
        }
        else {
          if (m.getDeclaringClass().isInterface() && (m.getDeclaringClass() != cls
              || (ctx != null && ctx.getClass() != m.getDeclaringClass()))) {
            assert debug("INVOKEINTERFACE: " + getInternalName(m.getDeclaringClass()) + "." + m.getName());
            mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(m.getDeclaringClass()), m.getName(),
                getMethodDescriptor(m));
          }
          else {
            assert debug("INVOKEVIRTUAL: " + getInternalName(cls) + "." + m.getName());
            mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(cls), m.getName(),
                getMethodDescriptor(m));
          }
        }

        returnType = m.getReturnType();

        stacksize++;
      }

      Object o = m.invoke(ctx, normalizeArgsForVarArgs(parameterTypes, args, m.isVarArgs()));


      if (hasNullMethodHandler()) {
        writeOutNullHandler(m, 1);
        if (o == null) o = getNullMethodHandler().getProperty(m.getName(), ctx, variableFactory);
      }

      currType = toNonPrimitiveType(m.getReturnType());
      return o;

    }
  }