summaryrefslogtreecommitdiff
path: root/java/field.c
blob: 3427218f20cd80d2159af93c09b150fc77fe3315 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <string.h>
#include <stdlib.h>

#include "field.h"
#include "config.h"
#include "misc.h"

/* ----------------------------------------------------------------------

   A sketch on how field modifications could be processed:

   A field modification results in a "set" or in an "assoc"
   command. Neither is the field name *necessarily* the name of the "set"
   command (or "assoc", if it would support names), nor is the field
   value necessarily the respective value. Instead, the following rules
   are used to determine the structure of the fields.

   (A) If the value type is "printable" (i. e. primitive, or a wrapper
   class for a primitive type, or java.lang.String, or an enum (more
   cases?)), then a set command is printed, with the name and the value
   of the field.

   (B) If the value is a visible object, and it is not configured as
   sub-structure (see below), an "assoc" command is printed, between this
   object and the value object. (As soon as associations get a name, the
   field name is used for this.)

   (C) If the value is an invisible object, or a visible object which is
   configured as sub-structure, regard it as structured, according to the
   rules which follow.

   Structured values are divided into sub-values with sub-names; the
   field name, followed by a dot, is used as base-name; all sub-values
   and sub-names are processed recursively according to the same rules,
   while the base-name is eventually prepended to all sub-names.

   (C1) If the value is of type java.util.Map, and the keys are printable
   (see (A)), use the keys as sub-names, the values as sub-values. ("The
   keys are printable": does this mean that the type for the keys is a
   class which is generally printable?)

   (C2) If the value is of type java.util.Map, and the keys are not
   printable (see also C1), regard the value as unsorted list of
   elements, which are themselves structured sub-values
   (java.util.Map.Entry?).

   (C3) If the value is of type java.util.List, use the indices as
   sub-names, and the elements as sub-values.

   (C4) If the value is an unsorted collection, i. e. of type
   java.util.Collection, but not of java.util.Map or java.util.List,
   apply a random order and proceed as in (C4). (Unsorted collections are
   not really supported in RTFL.)

   The rules (C1) to (C4) apply not when configured otherwise ("do not
   show logical structure").

   (C5) Otherwise (not of type java.util.Collection or java.util.Map),
   examine fields of the value; regard the names of its fields as
   sub-names; their values as sub-values;

   (Alternative: Java Beans?)

   For simplification, ignore C1 to C4 in the first place and focus on
   C5; then implement C1 to C4 by and by.
   
   ---------------------------------------------------------------------- */

static void print_field (jvmtiEnv *jvmti, JNIEnv* jni, jclass field_klass,
                         jobject object, jfieldID field, jvalue value,
                         char *base_name);
static void print_string_field (jvmtiEnv *jvmti, JNIEnv* jni, char *object_str,
                                char *base_name, char *field_name,
                                jstring string);

void JNICALL field_modification (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
                                 jmethodID method, jlocation location,
                                 jclass field_klass, jobject object,
                                 jfieldID field, char signature_type,
                                 jvalue new_value)
{
   // The class name is irrelevant here, since classes are already
   // filtered inclass_prepare(), where SetFieldModificationWatch()
   // is called.

   print_field (jvmti, jni, field_klass, object, field, new_value, "");
}

void print_field (jvmtiEnv *jvmti, JNIEnv* jni, jclass field_klass,
                  jobject object, jfieldID field, jvalue value, char *base_name)
{
   jvmtiError error;
   char object_buf1[SIZE_OBJECT_BUF], object_buf2[SIZE_OBJECT_BUF];
   char *class_name = NULL, *field_name = NULL, *field_sig = NULL;

   if ((error = (*jvmti)->GetFieldName (jvmti, field_klass, field, &field_name,
                                        &field_sig, NULL))
       != JVMTI_ERROR_NONE)
      jvmti_error (jvmti, error, "GetFieldName");
   else {
      //printf ("==> %s - %s\n", field_name, field_sig);

      fill_object_buf (jni, object_buf1, object);

      if ((class_name = get_class_name_from_sig (field_sig, FALSE))) {
         if (include_class (class_name)) {
            // The field represents an instance of a class which is also
            // included: this is an association, not a simple field.
            // ("field_name" could be used here, as soon as associations get
            // a name.)
            
            if (value.l) {
               fill_object_buf (jni, object_buf2, value.l);
               RTFL_OBJ_PRINT ("assoc", "s:s", object_buf1, object_buf2);
            }
         } else {
            if (strcmp (class_name, "java.lang.String")) {
               print_string_field (jvmti, jni, object_buf1, base_name,
                                   field_name, (jstring)(value.l));
                                   
            }                                  
         }
      }
   }
   
   jvmti_dealloc (jvmti, field_name);
   jvmti_dealloc (jvmti, field_sig);
   simple_free (class_name);
}

void print_string_field (jvmtiEnv *jvmti, JNIEnv* jni, char *object_str,
                         char *base_name, char *field_name, jstring string)
{
   if (string == NULL)
      RTFL_OBJ_PRINT ("set", "s:ss:s", object_str, base_name, field_name,
                      "null");
   else {
      // TODO: JNI calls somehow cause an abortion. Just some test
      // code.

      //jsize len = (*jni)->GetStringLength (jni, string);
      //const jchar *chars = (*jni)->GetStringChars (jni, string, NULL);

      //char *chars0 = (char*)malloc (len + 1);
      //memcpy (chars0, chars, len);
      //chars0[len] = 0;

      char *chars0 = "?\"?\"?";
      
      RTFL_OBJ_PRINT ("set", "s:ss:\"q\"", object_str, base_name, field_name,
                      chars0);
      
      //free (chars0);
      //(*jni)->ReleaseStringChars (jni, string, chars);
   }
}