private static boolean isDirectAccessAllowed(
      FieldNode a, ClassNode receiver, boolean isSamePackage) {
    ClassNode declaringClass = a.getDeclaringClass().redirect();
    ClassNode receiverType = receiver.redirect();

    // first, direct access from within the class or inner class nodes
    if (declaringClass.equals(receiverType)) return true;
    if (receiverType instanceof InnerClassNode) {
      while (receiverType != null && receiverType instanceof InnerClassNode) {
        if (declaringClass.equals(receiverType)) return true;
        receiverType = receiverType.getOuterClass();
      }
    }

    // no getter
    return a.isPublic() || (a.isProtected() && isSamePackage);
  }
 boolean makeGetField(
     final Expression receiver,
     final ClassNode receiverType,
     final String fieldName,
     final boolean implicitThis,
     final boolean samePackage) {
   FieldNode field = receiverType.getField(fieldName);
   // direct access is allowed if we are in the same class as the declaring class
   // or we are in an inner class
   if (field != null && isDirectAccessAllowed(field, controller.getClassNode(), samePackage)) {
     CompileStack compileStack = controller.getCompileStack();
     MethodVisitor mv = controller.getMethodVisitor();
     if (field.isStatic()) {
       mv.visitFieldInsn(
           GETSTATIC,
           BytecodeHelper.getClassInternalName(field.getOwner()),
           fieldName,
           BytecodeHelper.getTypeDescription(field.getOriginType()));
       controller.getOperandStack().push(field.getOriginType());
     } else {
       if (implicitThis) {
         compileStack.pushImplicitThis(implicitThis);
       }
       receiver.visit(controller.getAcg());
       if (implicitThis) compileStack.popImplicitThis();
       if (!controller.getOperandStack().getTopOperand().isDerivedFrom(field.getOwner())) {
         mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(field.getOwner()));
       }
       mv.visitFieldInsn(
           GETFIELD,
           BytecodeHelper.getClassInternalName(field.getOwner()),
           fieldName,
           BytecodeHelper.getTypeDescription(field.getOriginType()));
     }
     controller.getOperandStack().replace(field.getOriginType());
     return true;
   }
   ClassNode superClass = receiverType.getSuperClass();
   if (superClass != null) {
     return makeGetField(receiver, superClass, fieldName, implicitThis, false);
   }
   return false;
 }
 @SuppressWarnings("unchecked")
 private boolean makeGetPrivateFieldWithBridgeMethod(
     final Expression receiver,
     final ClassNode receiverType,
     final String fieldName,
     final boolean safe,
     final boolean implicitThis) {
   FieldNode field = receiverType.getField(fieldName);
   ClassNode classNode = controller.getClassNode();
   if (field != null
       && Modifier.isPrivate(field.getModifiers())
       && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode)
           || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode, receiverType))
       && !receiverType.equals(classNode)) {
     Map<String, MethodNode> accessors =
         (Map<String, MethodNode>)
             receiverType
                 .redirect()
                 .getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_FIELDS_ACCESSORS);
     if (accessors != null) {
       MethodNode methodNode = accessors.get(fieldName);
       if (methodNode != null) {
         MethodCallExpression mce =
             new MethodCallExpression(
                 receiver,
                 methodNode.getName(),
                 new ArgumentListExpression(
                     field.isStatic() ? new ConstantExpression(null) : receiver));
         mce.setMethodTarget(methodNode);
         mce.setSafe(safe);
         mce.setImplicitThis(implicitThis);
         mce.visit(controller.getAcg());
         return true;
       }
     }
   }
   return false;
 }