private Object getBeanProperty(Object ctx, String property)
      throws IllegalAccessException, InvocationTargetException {

    assert debug("\n  **  ENTER -> {bean: " + property + "; ctx=" + ctx + "}");

    if ((pCtx == null ? currType : pCtx.getVarOrInputTypeOrNull(property)) == Object.class
        && !pCtx.isStrongTyping()) {
      currType = null;
    }

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


    boolean classRef = false;

    Class<?> cls;
    if (ctx instanceof Class) {
      if (MVEL.COMPILER_OPT_SUPPORT_JAVA_STYLE_CLASS_LITERALS
          && "class".equals(property)) {
        ldcClassConstant((Class<?>) ctx);

        return ctx;
      }

      cls = (Class<?>) ctx;
      classRef = true;
    }
    else if (ctx != null) {
      cls = ctx.getClass();
    }
    else {
      cls = null;
    }

    if (hasPropertyHandler(cls)) {
      PropertyHandler prop = getPropertyHandler(cls);
      if (prop instanceof ProducesBytecode) {
        ((ProducesBytecode) prop).produceBytecodeGet(mv, property, variableFactory);
        return prop.getProperty(property, ctx, variableFactory);
      }
      else {
        throw new RuntimeException("unable to compileShared: custom accessor does not support producing bytecode: "
            + prop.getClass().getName());
      }
    }

    Member member = cls != null ? getFieldOrAccessor(cls, property) : null;

    if (member != null && classRef && (member.getModifiers() & Modifier.STATIC) == 0) {
      member = null;
    }

    if (member != null && hasGetListeners()) {
      mv.visitVarInsn(ALOAD, 1);
      mv.visitLdcInsn(member.getName());
      mv.visitVarInsn(ALOAD, 3);
      mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "integration/GlobalListenerFactory", "notifyGetListeners",
          "(Ljava/lang/Object;Ljava/lang/String;L" + NAMESPACE + "integration/VariableResolverFactory;)V");

      notifyGetListeners(ctx, member.getName(), variableFactory);
    }

    if (first) {
      if ("this".equals(property)) {
        assert debug("ALOAD 2");
        mv.visitVarInsn(ALOAD, 2);
        return thisRef;
      }
      else if (variableFactory != null && variableFactory.isResolveable(property)) {

        if (variableFactory.isIndexedFactory() && variableFactory.isTarget(property)) {
          int idx;
          try {
            loadVariableByIndex(idx = variableFactory.variableIndexOf(property));
          }
          catch (Exception e) {
            throw new OptimizationFailure(property);
          }


          return variableFactory.getIndexedVariableResolver(idx).getValue();
        }
        else {
          try {
            loadVariableByName(property);
          }
          catch (Exception e) {
            throw new OptimizationFailure("critical error in JIT", e);
          }

          return variableFactory.getVariableResolver(property).getValue();
        }
      }
      else {
        assert debug("ALOAD 1");
        mv.visitVarInsn(ALOAD, 1);
      }
    }

    if (member instanceof Field) {
      return optimizeFieldMethodProperty(ctx, property, cls, member);
    }
    else if (member != null) {
      Object o;

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

      try {
        o = ((Method) member).invoke(ctx, EMPTYARG);

        if (returnType != member.getDeclaringClass()) {
          assert debug("CHECKCAST " + getInternalName(member.getDeclaringClass()));
          mv.visitTypeInsn(CHECKCAST, getInternalName(member.getDeclaringClass()));
        }

        returnType = ((Method) member).getReturnType();

        assert debug("INVOKEVIRTUAL " + member.getName() + ":" + returnType);
        mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(member.getDeclaringClass()), member.getName(),
            getMethodDescriptor((Method) member));
      }
      catch (IllegalAccessException e) {
        Method iFaceMeth = determineActualTargetMethod((Method) member);
        if (iFaceMeth == null)
          throw new PropertyAccessException("could not access field: " + cls.getName() + "." + property, expr, st, e, pCtx);

        assert debug("CHECKCAST " + getInternalName(iFaceMeth.getDeclaringClass()));
        mv.visitTypeInsn(CHECKCAST, getInternalName(iFaceMeth.getDeclaringClass()));

        returnType = iFaceMeth.getReturnType();

        assert debug("INVOKEINTERFACE " + member.getName() + ":" + returnType);
        mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(iFaceMeth.getDeclaringClass()), member.getName(),
            getMethodDescriptor((Method) member));

        o = iFaceMeth.invoke(ctx, EMPTYARG);
      }
      catch (IllegalArgumentException e) {
        if (member.getDeclaringClass().equals(ctx)) {
          try {
            Class c = Class.forName(member.getDeclaringClass().getName() + "$" + property);

            throw new CompileException("name collision between innerclass: " + c.getCanonicalName()
                + "; and bean accessor: " + property + " (" + member.toString() + ")", expr, tkStart);
          }
          catch (ClassNotFoundException e2) {
            //fallthru
          }
        }
        throw e;
      }


      if (hasNullPropertyHandler()) {
        if (o == null) o = getNullPropertyHandler().getProperty(member.getName(), ctx, variableFactory);
        writeOutNullHandler(member, 0);
      }

      currType = toNonPrimitiveType(returnType);
      return o;
    }
    else if (ctx instanceof Map && (((Map) ctx).containsKey(property) || nullSafe)) {
      assert debug("CHECKCAST java/util/Map");
      mv.visitTypeInsn(CHECKCAST, "java/util/Map");

      assert debug("LDC: \"" + property + "\"");
      mv.visitLdcInsn(property);

      assert debug("INVOKEINTERFACE: get");
      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
      return ((Map) ctx).get(property);
    }
    else if (first && "this".equals(property)) {
      assert debug("ALOAD 2");
      mv.visitVarInsn(ALOAD, 2); // load the thisRef value.

      return this.thisRef;
    }
    else if ("length".equals(property) && ctx.getClass().isArray()) {
      anyArrayCheck(ctx.getClass());

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

      wrapPrimitive(int.class);
      return getLength(ctx);
    }
    else if (LITERALS.containsKey(property)) {
      Object lit = LITERALS.get(property);

      if (lit instanceof Class) {
        ldcClassConstant((Class) lit);
      }

      return lit;
    }
    else {
      Object ts = tryStaticAccess();

      if (ts != null) {
        if (ts instanceof Class) {
          ldcClassConstant((Class) ts);
          return ts;
        }
        else if (ts instanceof Method) {
          writeFunctionPointerStub(((Method) ts).getDeclaringClass(), (Method) ts);
          return ts;
        }
        else {
          Field f = (Field) ts;
          return optimizeFieldMethodProperty(ctx, property, cls, f);
        }
      }
      else if (ctx instanceof Class) {
        /**
         * This is our ugly support for function pointers.  This works but needs to be re-thought out at some
         * point.
         */
        Class c = (Class) ctx;
        for (Method m : c.getMethods()) {
          if (property.equals(m.getName())) {
            if (pCtx!=null&& pCtx.getParserConfiguration()!=null?pCtx.getParserConfiguration().isAllowNakedMethCall():MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL) {
              assert debug("POP");
              mv.visitInsn(POP);
              assert debug("INVOKESTATIC " + m.getName());
              mv.visitMethodInsn(INVOKESTATIC, getInternalName(m.getDeclaringClass()), m.getName(),
                  getMethodDescriptor(m));

              returnType = m.getReturnType();

              return m.invoke(null, EMPTY_OBJ_ARR);
            }
            else {
              writeFunctionPointerStub(c, m);
              return m;
            }
          }
        }


        try {
          Class subClass = findClass(variableFactory, c.getName() + "$" + property, pCtx);
          ldcClassConstant(subClass);
          return subClass;
        }
        catch (ClassNotFoundException cnfe) {
          // fall through.
        }

      }
      else if (pCtx!=null&& pCtx.getParserConfiguration()!=null?pCtx.getParserConfiguration().isAllowNakedMethCall():MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL) {
        return getMethod(ctx, property);
      }

      if (ctx == null) {
        throw new PropertyAccessException("unresolvable property or identifier: " + property, expr, st, pCtx);
      }
      else {
        throw new PropertyAccessException("could not access: " + property + "; in class: "
            + ctx.getClass().getName(), expr, st, pCtx);
      }
    }
  }