from . import JNISimProcedure
from ...engines.soot.values import SimSootValue_InstanceFieldRef
from archinfo.arch_soot import SootFieldDescriptor, ArchSoot

import logging
l = logging.getLogger('angr.procedures.java_jni.getfieldid')

class GetFieldID(JNISimProcedure):

    return_ty = 'reference'

    def run(self, ptr_env, obj_class_, ptr_field_name, ptr_field_sig):

        # object class name
        obj_class = self.state.jni_references.lookup(obj_class_)
        obj_class_name = obj_class.class_name

        # field 
        field_name = self._load_string_from_native_memory(ptr_field_name)

        # field type
        field_sig = self._load_string_from_native_memory(ptr_field_sig)
        field_type = ArchSoot.decode_type_signature(field_sig)

        # walk up in class hierarchy
        class_hierarchy = self.state.javavm_classloader.get_class_hierarchy(obj_class_name)
        for class_ in class_hierarchy:
            # check for every class, if it contains the field
            if self._class_contains_field(class_, field_name, field_type):
                # if so, create the field_id and return an opaque reference to it
                field_id = SootFieldDescriptor(class_.name, field_name, field_type)
                return self.state.jni_references.create_new_reference(field_id)

        else:
            # field couldn't be found 
            # => return null and (TODO:) throw an NoSuchFieldError
            l.debug("Couldn't find field '{field_name}' in classes {class_names}."
                    "".format(class_names=[str(c.name) for c in class_hierarchy],
                              field_name=field_name))
            return 0

    def _class_contains_field(self, field_class, field_name, field_type):
        # check if a field with the given name exists
        if not field_name in field_class.fields:
            return False
        # check the type
        if field_class.fields[field_name][1] != field_type:
            return False
        return True 

@@ -283,20 +283,44 @@
 y = 
-'reference'%0A
+None%0A    is_static_field = None%0A    
 %0A   
@@ -343,19 +343,21 @@
 tr_env, 
-obj
+field
 _class_,
@@ -389,17 +389,16 @@
 d_sig):%0A
-%0A
         
@@ -403,25 +403,13 @@
   # 
-object class name
+class
 %0A   
@@ -409,27 +409,29 @@
 ass%0A        
-obj
+field
 _class = sel
@@ -460,19 +460,21 @@
 .lookup(
-obj
+field
 _class_)
@@ -486,62 +486,14 @@
     
-obj_class_name = obj_class.class_name%0A%0A        # field
+# name
  %0A  
@@ -564,17 +564,16 @@
 d_name)%0A
-%0A
         
@@ -573,22 +573,16 @@
        #
- field
  type%0A  
@@ -715,16 +715,237 @@
 d_sig)%0A%0A
+        return self._get_field_id(field_class.class_name, field_name,%0A                                  field_type, self.is_static_field)%0A%0A    def _get_field_id(self, class_name, field_name, field_type, is_static_field):%0A
         
@@ -1053,12 +1053,8 @@
 chy(
-obj_
 clas
@@ -1226,32 +1226,49 @@
 name, field_type
+, is_static_field
 ):%0A             
@@ -1933,16 +1933,33 @@
 eld_type
+, is_static_field
 ):%0A     
@@ -2093,83 +2093,411 @@
     
-# check the type%0A        if field_class.fields%5Bfield_name%5D%5B1%5D != field_type
+field = field_class.fields%5Bfield_name%5D%0A        %0A        # check type%0A        if field%5B1%5D != field_type:%0A            return False%0A        %0A        # if field is static, check if field attributes contains STATIC%0A        # otherwise, check that field attributes does not contain STATIC%0A        if (not is_static_field and %22STATIC%22 in field%5B0%5D) or %5C%0A           (is_static_field and not %22STATIC%22 in field%5B0%5D)
 :%0A  
@@ -2497,33 +2497,32 @@
 0%5D):%0A           
- 
 return False%0A   
@@ -2510,32 +2510,33 @@
    return False%0A
+%0A
         return T
@@ -2540,8 +2540,197 @@
 n True %0A
+%0Aclass GetInstanceFieldID(GetFieldID):%0A%0A    return_ty = 'reference'%0A    is_static_field = False%0A%0Aclass GetStaticFieldID(GetFieldID):%0A%0A    return_ty = 'reference'%0A    is_static_field = True%0A

Add frame for static field access