154505795c0dac5a0bb39e297dd8f73fadacc445
[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_SET_SIGNATURE = "Liotruntime/slave/IoTSet;";
72         private static final String STR_IOT_RELATION_SIGNATURE = "Liotruntime/slave/IoTRelation;";
73         private static final String STR_IOT_CONSTRAINT_SIGNATURE = "Liotcode/annotation/constraint;";
74         private static final String STR_CONFIG_EXTENSION = ".config";
75         private static final String STR_CONFIG_FILE_PATH = "mysql/";
76
77         /**
78          * Main Constructor
79          */
80         public ClassRuntimeInstrumenterMaster(final ClassVisitor cv, String strObjID, boolean _bVerbose) {
81
82                 super(ASM5, cv);
83
84                 // Initialize strClassType
85                 strClassType = new String[INT_NUM_CLASS_TYPE];
86                 for(int i=0; i<INT_NUM_CLASS_TYPE; i++) {
87                         strClassType[i] = new String();
88                 }
89                 strInstrumentedClassName = "";
90                 strFieldName = "";
91                 hmObj = new HashMap<String,Object>();
92                 strObjectID = strObjID;
93                 bVerbose = _bVerbose;
94         }
95
96         /**
97          * Make a visit to a class. This is the method called first.
98          */
99         @Override
100         public void visit(int version, int access, String name,
101                 String signature, String superName, String[] interfaces) {
102
103                 // Get the name of this instrumented class
104                 strInstrumentedClassName = name;
105
106                 super.visit(version, access, name, signature, superName, interfaces);
107         }
108
109         /**
110          * Visit class annotation
111          */
112         @Override
113         public AnnotationVisitor visitAnnotation(String desc,
114                 boolean visible) {
115
116                 return super.visitAnnotation(desc, visible);
117         }
118
119         /**
120          * Make a visit to a field.
121          */
122         @Override
123         public FieldVisitor visitField(int access, String name,
124                 String desc, String signature, Object value) {
125
126                 strFieldName = name;
127                 super.visitField(access, name, desc, signature, value);
128
129                 if (signature != null) {
130                         new SignatureReader(signature)
131                         .accept(new SignatureRuntimeInstrumenter(strClassType));
132                 }
133                 iCntClassType = 0;
134                 return new FieldRuntimeInstrumenter(signature, desc);
135         }
136
137         /**
138          * Visit a method when a method is encountered
139          */
140         @Override
141         public MethodVisitor visitMethod(int access, String name,
142                 String desc, String signature, String[] exceptions) {
143
144                 super.visitMethod(access, name, desc, signature, exceptions);
145                 return new MethodRuntimeInstrumenter();
146         }
147
148         /**
149          * A subclass that visits signature. This is called when traversing a class
150          * and a signature visit is needed.
151          */
152         private class SignatureRuntimeInstrumenter extends SignatureVisitor {
153
154                 public SignatureRuntimeInstrumenter(String[] strCType) {
155                         super(Opcodes.ASM5);
156
157                         // Counter for extracting +class type
158                         iCntClassType = 0;
159
160                         // Initializing input String array
161                         for (int i=0; i<INT_NUM_CLASS_TYPE; i++) {
162                                 strClassType[i] = strCType[i];
163                         }
164                 }
165
166                 @Override
167                 public void visitClassType(final String name) {
168
169                         strClassType[iCntClassType++] = name;
170                         super.visitClassType(name);
171                 }
172         }
173
174         /**
175          * A helper function that returns class name from class type pattern
176          * <p>
177          * e.g. get "ProximitySensor" from "iotcode/ProximitySensor"
178          * With this regex pattern,
179          * group(0) gives the entire input string, e.g. "iotcode/ProximitySensor"
180          * group(1) gives just the front string, e.g. "iotcode"
181          * group(2) gives just the slash, e.g. "/"
182          * group(3) gives just the back string, e.g. "ProximitySensor"
183          *
184          * @param  strInput  String to be matched by regex
185          * @return           String
186          */
187         public String getClassName(String strInput) {
188
189                 Pattern pattern = Pattern.compile("(\\S+)(\\/)(\\S+)");
190                 Matcher matcher = pattern.matcher(strInput);
191                 if (matcher.find()) {
192                         return matcher.group(3);
193                 }
194                 return null;
195         }
196
197         /**
198          * A class that instruments instructions in method through visiting a method.
199          * Instruction and variable types can be extracted.
200          */
201         class FieldRuntimeInstrumenter extends FieldVisitor {
202
203                 private String strFieldSignature;
204                 private String strFieldDesc;
205
206                 public FieldRuntimeInstrumenter(String strFSign, String strFDesc) {
207
208                         super(Opcodes.ASM5);
209                         strFieldSignature = strFSign;
210                         strFieldDesc = strFDesc;
211                 }
212
213                 /**
214                  *  This method visits annotation, so we can instrument @config here
215                  *  <p>
216                  *  Steps:
217                  *  1) Check whether it is IoTSet or IoTRelation declaration
218                  *  2) Create a new Set class instrumenter
219                  *     strClassType[0] will contain "IoTSet" or "IoTRelation"
220                  *     strClassType[1] will contain the first specific class
221                  *         e.g. IoTSet<ProximitySensor> -> strClassType[1] == "ProximitySensor"
222                  *     strClassType[2] will contain the other specific class
223                  *     (doesn't apply to IoTSet)
224                  *         e.g. IoTRelation<ProximitySensor, LightBulb>
225                  *              -> strClassType[1] == "ProximitySensor"
226                  *              -> strClassType[2] == "LightBulb"
227                  *  3) Instantiate field objects, e.g. IoTSet or IoTRelation class object
228                  *  4) Instantiate a new object of the instrumented class, e.g. AcmeThermostat
229                  *  5) Initialize the field of the instrumented class objects with actual field objects
230                  *  6) Run the init() function of the instrumented class
231                  *
232                  * @param  desc     String
233                  * @param  visible  boolean
234                  * @return          AnnotationVisitor
235                  */
236                 /**
237                  *  This method visits annotation that is meta-annotated as TYPE_USE, so we can instrument @config here
238                  */
239                 @Override
240                 public AnnotationVisitor visitAnnotation(String desc,
241                         boolean visible) {
242
243                         //RuntimeOutput.print("ClassRuntimeInstrumenterMaster@AnnotationVisitor: " + desc, bVerbose);
244
245                         // Check annotation description @config
246                         if(desc.equals(STR_IOT_ANNOTATION_SIGNATURE)) {
247                                 // Check if this is a Set class, then process it
248                                 if (strFieldDesc.equals(STR_IOT_SET_SIGNATURE)) {
249                                         RuntimeOutput.print("@config: IoTSet is detected!", bVerbose);
250                                         SetInstrumenter setInstrument = new
251                                                 SetInstrumenter(getClassName(strClassType[1]),
252                                                         STR_CONFIG_FILE_PATH + strFieldName + STR_CONFIG_EXTENSION, strObjectID, bVerbose);
253
254                                         hmObj.put(strFieldName, setInstrument);
255                                         // Check if this is a Relation class, then process it
256                                 } else if (strFieldDesc.equals(STR_IOT_RELATION_SIGNATURE)) {
257                                         RuntimeOutput.print("@config: IoTRelation is detected!", bVerbose);
258                                         RelationInstrumenter relInstrument = new
259                                                 RelationInstrumenter(getClassName(strClassType[1]),
260                                                         getClassName(strClassType[2]), STR_CONFIG_FILE_PATH +
261                                                         strFieldName + STR_CONFIG_EXTENSION, bVerbose);
262
263                                         hmObj.put(strFieldName, relInstrument);
264                                 } else if (strFieldDesc.equals(STR_IOT_CONSTRAINT_SIGNATURE)) {
265                                         // TO DO: PROCESS THIS CONSTRAINT ANNOTATION
266                                         RuntimeOutput.print("ClassRuntimeInstrumenterMaster@AnnotationTypeVisitor: Constraint annotation detected!", bVerbose);
267
268                                 } else {
269                                         throw new Error("ClassRuntimeInstrumenterMaster@AnnotationTypeVisitor: " + strFieldDesc + " not recognized!");
270                                 }
271                         }
272                         return super.visitAnnotation(desc, visible);
273                 }
274         }
275
276         /**
277          * A subclass that instruments instructions in method through visiting a method.
278          * Instruction and variable types can be extracted.
279          */
280         protected class MethodRuntimeInstrumenter extends MethodVisitor {
281
282                 public MethodRuntimeInstrumenter() {
283                         super(Opcodes.ASM5);
284                 }
285
286         }
287
288         /**
289          * A method that returns HashMap hmObj
290          *
291          * @return         HashMap<String,Object>
292          */
293         public HashMap<String,Object> getFieldObjects() {
294
295                 return hmObj;
296         }
297
298         public static void main(String[] args) {
299
300                 try {
301                         // Instrumenting one file
302                         FileInputStream is = new FileInputStream(args[0]);
303
304                         ClassReader cr = new ClassReader(is);
305                         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
306                         ClassRuntimeInstrumenterMaster crim = new ClassRuntimeInstrumenterMaster(cw, "LB2", true);
307                         cr.accept(crim, 0);
308
309                         // Get the object and the class names
310                         HashMap<String,Object> hm = crim.getFieldObjects();
311                         for(Map.Entry<String,Object> map : hm.entrySet()) {
312                                 System.out.println(map.getKey());
313                                 System.out.println(map.getValue().getClass().getName());
314                                 SetInstrumenter si = (SetInstrumenter) map.getValue();
315                                 System.out.println("Field values: " + Arrays.toString(si.fieldValues(0)));
316                                 System.out.println("Field classes: " + Arrays.toString(si.fieldClasses(0)));
317                                 System.out.println("Field object ID: " + si.fieldObjectID(0));
318                                 System.out.println("Field entry type: " + si.fieldEntryType("LB1"));
319                                 System.out.println("Field entry type: " + si.fieldEntryType("LB2"));
320                                 System.out.println("Number of rows: " + si.numberOfRows());
321                         }
322
323                 } catch (IOException ex) {
324                         System.out.println("ClassRuntimeInstrumenterMaster@RunInstrumentation: IOException: "
325                                                                                                  + ex.getMessage());
326                         ex.printStackTrace();
327                 }
328         }
329 }