In progress of refining the Tomoyo policies for process jailing; will define a set...
[iot2.git] / iotjava / iotruntime / master / ClassRuntimeInstrumenterMaster.java
1 package iotruntime.master;
2
3 // Java basic packages
4 import java.io.FileInputStream;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.util.Arrays;
9 import java.util.Collection;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Iterator;
13 import java.util.Map;
14 import java.util.Properties;
15 import java.util.Set;
16 import java.sql.*;
17 import java.lang.reflect.*;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
20
21 // ASM packages
22 import org.objectweb.asm.AnnotationVisitor;
23 import org.objectweb.asm.Attribute;
24 import org.objectweb.asm.ClassReader;
25 import org.objectweb.asm.ClassWriter;
26 import org.objectweb.asm.ClassVisitor;
27 import org.objectweb.asm.FieldVisitor;
28 import org.objectweb.asm.MethodVisitor;
29 import org.objectweb.asm.Opcodes;
30 import org.objectweb.asm.signature.SignatureReader;
31 import org.objectweb.asm.signature.SignatureVisitor;
32 import org.objectweb.asm.TypePath;
33
34 // More imports
35 import iotruntime.slave.IoTSet;
36 import iotruntime.slave.IoTRelation;
37
38 /** Class ClassRuntimeInstrumenterMaster helps instrument the bytecode.
39  *  This class basically reads annotations @config in the code,
40  *  allocates the right objects, and runs the function init in
41  *  the instrumented program code.
42  *
43  * @author      Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
44  * @version     1.0
45  * @since       2015-12-01
46  */
47 public class ClassRuntimeInstrumenterMaster extends ClassVisitor implements Opcodes {
48
49         /**
50          *  ClassRuntimeInstrumenterMaster class properties
51          *  <p>
52          *  At most we will have 3 class types
53          *  IoTSet<class_type> -> type 1: Set and type 2: class_type
54          *  IoTRelation<class_type1, class_type2>
55          *  -> type 1: IoTRelation, type 2: class_type1, type 3: class_type2
56          *  Store class type for annotation processing in strClassType
57          */
58         private String[] strClassType;
59         private static int iCntClassType = 0;
60         private String strInstrumentedClassName;
61         private String strFieldName;
62         private HashMap<String,Object> hmObj;
63         private String strObjectID;
64         private boolean bVerbose;
65
66         /**
67          * ClassRuntimeInstrumenterMaster class constants
68          */
69         private static final int INT_NUM_CLASS_TYPE = 3;
70         //private static final String STR_IOT_ANNOTATION_SIGNATURE = "Liotchecker/qual/config;";
71         private static final String STR_IOT_ANNOTATION_SIGNATURE = "Liotcode/annotation/config;";
72         private static final String STR_IOT_SET_SIGNATURE = "Liotruntime/slave/IoTSet;";
73         private static final String STR_IOT_RELATION_SIGNATURE = "Liotruntime/slave/IoTRelation;";
74         private static final String STR_IOT_CONSTRAINT_SIGNATURE = "Liotcode/annotation/constraint;";
75         private static final String STR_CONFIG_EXTENSION = ".config";
76         private static final String STR_CONFIG_FILE_PATH = "mysql/";
77
78         /**
79          * Main Constructor
80          */
81         public ClassRuntimeInstrumenterMaster(final ClassVisitor cv, String strObjID, boolean _bVerbose) {
82
83                 super(ASM5, cv);
84
85                 // Initialize strClassType
86                 strClassType = new String[INT_NUM_CLASS_TYPE];
87                 for(int i=0; i<INT_NUM_CLASS_TYPE; i++) {
88                         strClassType[i] = new String();
89                 }
90                 strInstrumentedClassName = "";
91                 strFieldName = "";
92                 hmObj = new HashMap<String,Object>();
93                 strObjectID = strObjID;
94                 bVerbose = _bVerbose;
95         }
96
97         /**
98          * Make a visit to a class. This is the method called first.
99          */
100         @Override
101         public void visit(int version, int access, String name,
102                 String signature, String superName, String[] interfaces) {
103
104                 // Get the name of this instrumented class
105                 strInstrumentedClassName = name;
106
107                 super.visit(version, access, name, signature, superName, interfaces);
108         }
109
110         /**
111          * Make a visit to a field.
112          */
113         @Override
114         public FieldVisitor visitField(int access, String name,
115                 String desc, String signature, Object value) {
116
117                 strFieldName = name;
118                 super.visitField(access, name, desc, signature, value);
119
120                 if (signature != null) {
121                         new SignatureReader(signature)
122                         .accept(new SignatureRuntimeInstrumenter(strClassType));
123                 }
124                 iCntClassType = 0;
125                 return new FieldRuntimeInstrumenter(signature, desc);
126         }
127
128         /**
129          * Visit a method when a method is encountered
130          */
131         @Override
132         public MethodVisitor visitMethod(int access, String name,
133                 String desc, String signature, String[] exceptions) {
134
135                 super.visitMethod(access, name, desc, signature, exceptions);
136                 return new MethodRuntimeInstrumenter();
137         }
138
139         /**
140          * A subclass that visits signature. This is called when traversing a class
141          * and a signature visit is needed.
142          */
143         private class SignatureRuntimeInstrumenter extends SignatureVisitor {
144
145                 public SignatureRuntimeInstrumenter(String[] strCType) {
146                         super(Opcodes.ASM5);
147
148                         // Counter for extracting +class type
149                         iCntClassType = 0;
150
151                         // Initializing input String array
152                         for (int i=0; i<INT_NUM_CLASS_TYPE; i++) {
153                                 strClassType[i] = strCType[i];
154                         }
155                 }
156
157                 @Override
158                 public void visitClassType(final String name) {
159
160                         strClassType[iCntClassType++] = name;
161                         super.visitClassType(name);
162                 }
163         }
164
165         /**
166          * A helper function that returns class name from class type pattern
167          * <p>
168          * e.g. get "ProximitySensor" from "iotcode/ProximitySensor"
169          * With this regex pattern,
170          * group(0) gives the entire input string, e.g. "iotcode/ProximitySensor"
171          * group(1) gives just the front string, e.g. "iotcode"
172          * group(2) gives just the slash, e.g. "/"
173          * group(3) gives just the back string, e.g. "ProximitySensor"
174          *
175          * @param  strInput  String to be matched by regex
176          * @return           String
177          */
178         public String getClassName(String strInput) {
179
180                 Pattern pattern = Pattern.compile("(\\S+)(\\/)(\\S+)");
181                 Matcher matcher = pattern.matcher(strInput);
182                 if (matcher.find()) {
183                         return matcher.group(3);
184                 }
185                 return null;
186         }
187
188         /**
189          * A class that instruments instructions in method through visiting a method.
190          * Instruction and variable types can be extracted.
191          */
192         class FieldRuntimeInstrumenter extends FieldVisitor {
193
194                 private String strFieldSignature;
195                 private String strFieldDesc;
196
197                 public FieldRuntimeInstrumenter(String strFSign, String strFDesc) {
198
199                         super(Opcodes.ASM5);
200                         strFieldSignature = strFSign;
201                         strFieldDesc = strFDesc;
202                 }
203
204                 /**
205                  *  This method visits annotation, so we can instrument @config here
206                  *  <p>
207                  *  Steps:
208                  *  1) Check whether it is IoTSet or IoTRelation declaration
209                  *  2) Create a new Set class instrumenter
210                  *     strClassType[0] will contain "IoTSet" or "IoTRelation"
211                  *     strClassType[1] will contain the first specific class
212                  *         e.g. IoTSet<ProximitySensor> -> strClassType[1] == "ProximitySensor"
213                  *     strClassType[2] will contain the other specific class
214                  *     (doesn't apply to IoTSet)
215                  *         e.g. IoTRelation<ProximitySensor, LightBulb>
216                  *              -> strClassType[1] == "ProximitySensor"
217                  *              -> strClassType[2] == "LightBulb"
218                  *  3) Instantiate field objects, e.g. IoTSet or IoTRelation class object
219                  *  4) Instantiate a new object of the instrumented class, e.g. AcmeThermostat
220                  *  5) Initialize the field of the instrumented class objects with actual field objects
221                  *  6) Run the init() function of the instrumented class
222                  *
223                  * @param  desc     String
224                  * @param  visible  boolean
225                  * @return          AnnotationVisitor
226                  */
227                 /**
228                  *  This method visits annotation that is meta-annotated as TYPE_USE, so we can instrument @config here
229                  */
230                 @Override
231                 public AnnotationVisitor visitAnnotation(String desc,
232                         boolean visible) {
233
234                         RuntimeOutput.print("ClassRuntimeInstrumenterMaster@AnnotationVisitor: " + desc, bVerbose);
235
236                         // Check annotation description @config
237                         if(desc.equals(STR_IOT_ANNOTATION_SIGNATURE)) {
238                                 // Check if this is a Set class, then process it
239                                 if (strFieldDesc.equals(STR_IOT_SET_SIGNATURE)) {
240                                         RuntimeOutput.print("@config: IoTSet is detected!", bVerbose);
241                                         SetInstrumenter setInstrument = new
242                                                 SetInstrumenter(getClassName(strClassType[1]),
243                                                         STR_CONFIG_FILE_PATH + strFieldName + STR_CONFIG_EXTENSION, strObjectID, bVerbose);
244
245                                         hmObj.put(strFieldName, setInstrument);
246                                         // Check if this is a Relation class, then process it
247                                 } else if (strFieldDesc.equals(STR_IOT_RELATION_SIGNATURE)) {
248                                         RuntimeOutput.print("@config: IoTRelation is detected!", bVerbose);
249                                         RelationInstrumenter relInstrument = new
250                                                 RelationInstrumenter(getClassName(strClassType[1]),
251                                                         getClassName(strClassType[2]), STR_CONFIG_FILE_PATH +
252                                                         strFieldName + STR_CONFIG_EXTENSION, bVerbose);
253
254                                         hmObj.put(strFieldName, relInstrument);
255                                 } else if (strFieldDesc.equals(STR_IOT_CONSTRAINT_SIGNATURE)) {
256                                         // TO DO: PROCESS THIS CONSTRAINT ANNOTATION
257                                         RuntimeOutput.print("ClassRuntimeInstrumenterMaster@AnnotationTypeVisitor: Constraint annotation detected!", bVerbose);
258
259                                 } else {
260                                         throw new Error("ClassRuntimeInstrumenterMaster@AnnotationTypeVisitor: " + strFieldDesc + " not recognized!");
261                                 }
262                         }
263                         return super.visitAnnotation(desc, visible);
264                 }
265         }
266
267         /**
268          * A subclass that instruments instructions in method through visiting a method.
269          * Instruction and variable types can be extracted.
270          */
271         protected class MethodRuntimeInstrumenter extends MethodVisitor {
272
273                 public MethodRuntimeInstrumenter() {
274                         super(Opcodes.ASM5);
275                 }
276
277         }
278
279         /**
280          * A method that returns HashMap hmObj
281          *
282          * @return         HashMap<String,Object>
283          */
284         public HashMap<String,Object> getFieldObjects() {
285
286                 return hmObj;
287         }
288
289         public static void main(String[] args) {
290
291                 try {
292                         // Instrumenting one file
293                         FileInputStream is = new FileInputStream(args[0]);
294
295                         ClassReader cr = new ClassReader(is);
296                         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
297                         ClassRuntimeInstrumenterMaster crim = new ClassRuntimeInstrumenterMaster(cw, "LB2", true);
298                         cr.accept(crim, 0);
299
300                         // Get the object and the class names
301                         HashMap<String,Object> hm = crim.getFieldObjects();
302                         for(Map.Entry<String,Object> map : hm.entrySet()) {
303                                 System.out.println(map.getKey());
304                                 System.out.println(map.getValue().getClass().getName());
305                                 SetInstrumenter si = (SetInstrumenter) map.getValue();
306                                 System.out.println("Field values: " + Arrays.toString(si.fieldValues(0)));
307                                 System.out.println("Field classes: " + Arrays.toString(si.fieldClasses(0)));
308                                 System.out.println("Field object ID: " + si.fieldObjectID(0));
309                                 System.out.println("Field entry type: " + si.fieldEntryType("LB1"));
310                                 System.out.println("Field entry type: " + si.fieldEntryType("LB2"));
311                                 System.out.println("Number of rows: " + si.numberOfRows());
312                         }
313
314                 } catch (IOException ex) {
315                         System.out.println("ClassRuntimeInstrumenterMaster@RunInstrumentation: IOException: "
316                                                                                                  + ex.getMessage());
317                         ex.printStackTrace();
318                 }
319         }
320 }