I am currently implementing an Annotation that forces the fields to respect a condition through javassist. I would like to check if a field is initialized when it is being read... so, currently, I am getting the classes by loading them when they are loaded by the VM through a Translator.onLoad(ClassPool pool, String className), and using an ExprEditor on each class through overriding the edit(FieldAccess arg) method. Right now, I managed to inject code to check the condition by running the following method inside onLoad :
private void processFields(FieldsAndMethods data) {
final FieldsAndMethods copy = data;
Stack<CtClass> classes = data.getThisClass();
for(CtClass cc : classes ){
try {
cc.instrument(new ExprEditor(){
#Override
public void edit(FieldAccess arg) throws CannotCompileException{
try{
CtField field = arg.getField();
if(copy.getFields().contains(field) &&
field.hasAnnotation(Assertion.class)){
Assertion a =
((Assertion)field.getAnnotation(Assertion.class))
String condition = assertion.value();
String fieldName = field.getName();
String processCondition =
transformCondition(condition, fieldName);
if(arg.isWriter()){
String code = "{if(" + evaledCondition + ")" +
"$proceed($$) ;" +
"else throw new " +
"RuntimeException(\"The assertion " +
condition + " is false.\");}";
arg.replace(code);
}else if (arg.isReader()){
//Here is where I would like to check if the field
//has been initialized...
}
}catch(ClassNotFoundException e){
System.out.println("could not find Annotation " +
Assertion.class.getName() );
}catch(NotFoundException e){
System.out.println("could not find field " +
arg.getFieldName() );
}
}
});
} catch (CannotCompileException e) {
System.out.println("Could not interpret the expression");
System.out.println(e);
}
}
}
private String transformCondition(String condition, String fieldName){
return condition.replace(fieldName, "$1");
}
Could you point me in the right direction for finding out if a field has been initialized? Notice that a field can be either a primitive or not.
Thanks in advance.
Assumptions
I'll assume the following:
By field initialized we are talking about fields that are null.
Primitive types cannot be null so no bother to check them.
The code
This example verification will work for both static and non static fields.
I've also created the code String in several lines for better readability. Being arg a FieldAccess object, you can write the following:
if (arg.isReader() && !arg.getField().getType().isPrimitive()) {
String code = "{ java.lang.Object var = $proceed();"
+ "if(var == null) {"
+ "java.lang.System.out.println(\"not initialized " + arg.getFieldName() + "\");"
+ "}"
+ "$_=var;}";
arg.replace(code);
}
Code Explanation
As you can see, in this small example I've used a few javassist identifiers, for the complete reference about this please read the javassist official tutorial (I'm linking to the section about code modifications).
Here is what each identifier used means:
$proceed() : in the case of a field access this returns the value of the field.
$_ : this is an identifier that is mandatory when editing a FieldAccess in read mode. This token holds the value that will be used to set the field.
With this information it's easy to understand the code's idea:
Put the field value into an auxiliary object named var
Check if the field is null, if so print a warning with the field name
Set the fieldname with the value (either it's null or not);
I guess this already points you to the right direction. But let me know if you need anything else.
Related
Let's say I have a method called Object classInstance = createInstance("Page", map, factory) which creates an instance of "Page" with java reflection and does some stuff with it. For demonstration purposes I've called it "Page", but it could be any of my classes.
Now I want to add this object to a List<Page>. To call list.add(classInstance) and add it to the list I need to parse it to "Page". Is there a way to do it, given the only information I have is the string containing the class name? So instead of doing (Page) classInstance I need to do something like (Class.forName("Page")) classInstance.
I can not modify the List or the way it is added to the list.
Thank you.
Edit: here is the createInstance Method:
private static Object createInstance(String className, Map<?, ?> map, Meilenstein2Factory factory) throws InvocationTargetException, IllegalAccessException {
try {
String createMethodName = "create" + className;
Method createMethod = factory.getClass().getMethod(createMethodName);
Object classInstance = createMethod.invoke(factory);
String methodName = "";
for (Map.Entry<?, ?> entry : map.entrySet()) {
try {
methodName = "set" + entry.getKey().toString().substring(0,1).toUpperCase() + entry.getKey().toString().substring(1);
Method setNameMethod = classInstance.getClass().getMethod(methodName, getType(entry.getValue()));
setNameMethod.invoke(classInstance, parseEntry(entry.getValue()));
} catch (NoSuchMethodException e) {
LOGGER.log(null, "Attribute " + entry.getKey().toString() + " is not a valid attribute for this object. Is it spelled correctly?");
}
}
return classInstance;
} catch(NoSuchMethodException nm) {
LOGGER.log(null, "Folder " + className + " does not reference to a valid object. Is it spelled correctly?");
}
return null;
}
Edit 2: Screenshots of error and debugger
Do not bother about Page and PageImpl, I used Page in my question to simplify, but the factory accepts the Interface Page and returns an instance of PageImpl. As you can see in the second screenshot, the object is an instance of PageImpl, so this seems to be correct.
Edit 3:
Edit 4:
Something that works for now:
String methodName = "get" + "Page";
Method getListMethod = site.getClass().getMethod(methodName);
List<Object> list = (List<Object>) getListMethod.invoke(site);
list.add(page);
Your method createInstance returns a Class<Page> (the class object), but your list is a List<Page> (a list of instances).
You will need to create an instance and add it to the list, still using reflection:
list.add(classInstance.getDeclaredConstructor().newInstance());
The above is using an empty constructor for the class Page. If for example you wanted to use a constructor taking one string and one int, you would do it as such:
list.add(classInstance.getDeclaredConstructor(String.class, Integer.class).newInstance("my string", 4));
Edit:. Since classInstance is an Object and not a class, you should be able to do this:
list.add(Class.forName("Page").cast(classInstance));
I have a method:
public void extractStringFromField(Class<?> classToInspect) {
Field[] allFields = classToInspect.getDeclaredFields();
for(Field field : allFields) {
if(field.getType().isAssignableFrom(String.class)) {
System.out.println("Field name: " + field.getName());
// How to get the actual value of the string?!?!
// String strValue = ???
}
}
}
When this runs I get output like:
Field name: java.lang.String
Now how do I extract the actual string value into strValue, using reflection?
It looks like you need a reference to an instance of the class. You would want to call get and pass in the reference, casting the return to a String.
You can use get as follows:
String strValue = (String) field.get (objectReference);
In ideal situations,Class does not hold data. It merely holds the information about the structure and behavior of its instances and Instances of the Classes hold your data to use. So your extractStringFromField method can not extract values unless you pass any instances (from where it will actually extract values).
If the name of the parameter of the reference, you are passing to extract value is instance, then you can easily get what you want like bellow:
String strValue = (String)field.get(instance);
Just usefull example code for reflection fields:
Field[] fields = InsanceName.getDeclaredFields();
for (Field field : fields) { //array for fields names
System.out.println("Fields: " + Modifier.toString(field.getModifiers())); // modyfiers
System.out.println("Fields: " + field.getType().getName()); //type var name
System.out.println("Fields: " + field.getName()); //real var name
field.setAccessible(true); //var readable
System.out.println("Fields: " + field.get(InsanceName)); //get var values
System.out.println("Fields: " + field.toString()); //get "String" values
System.out.println(""); //some space for readable code
}
Just had the same issue. This Thread somewhat helped. Just for reference if somebody else stumbles upon this thread. I used the StringBuilder class to convert so basically:
StringBuilder builder = new StringBuilder();
builder.append(field.get(object))
Which has multiple advantages. One that you do not explicitly cast (which btw. causes problems with primitive types int vs. Integer) but also of being more efficient if you have multiple string operations sequentialy. In time critical code parts.
String strValue = field.getName().toString();
Full code looks like this:
public static void extractStringFromField(Class<?> Login) {
Field[] allFields = Login.getDeclaredFields();
for(Field field : allFields) {
String strValue = field.getName().toString();
// if(field.getType().isAssignableFrom(String.class)) {
System.out.println("Field name: " + strValue);
}
}
I am working on a plugin to create toString statements in my project, using CodeModel.
The resulting code should look like this:
if (variable == null) {
out.append(" " + "variable = null").append("\n");
}
(out in the code above is a simple StringBuilder)
I want to use CodeModel to automatically generate new lines and tabs in the if statements, and have so far got up to this output:
if ("variable" == null) {
out.append(" " + "variable = null").append("\n");
}
The issue is the quotes surrounding the variable, which are there as I assign a JExpression literal value for the variable value. Current implementation looks like this:
private void printComplexObject(final JMethod toStringMethod, FieldOutline fo) {
String property = fo.getPropertyInfo().getName(false);
property = property.replace("\"", "");
JType type = fo.getRawType();
JBlock block = toStringMethod.body();
JConditional nullCheck = block._if(JExpr.lit(property).eq(JExpr._null())); ...}
Is anyone aware of how this could be done using JExpression or anything else from CodeModel? The only alternative I have so far is to do it with a directStatement as follows:
toStringMethod.body().directStatement("if (" + property + " == null) { out.append ...}");
The solution is to replace the JConditional nullCheck with the following:
JConditional nullCheck = block._if(JExpr.direct(property).eq(JExpr._null()));
JExpr.direct(property) instead of .lit results in variable instead of "variable" being used in this JConditional.
I am trying to display all the strings used in a method invocation using SOOT program analysis framework. I am able to check for StringConstant but how do I get values for RefType ? Here is the sample code :
for (Value va : iv.getInvokeExpr().getArgs()) {
System.out.println("[ARGS : TYPE] " + va.getType() + " with ");
if (va instanceof StringConstant) {
System.out.print(va + " ");
} else if (va instanceof JimpleLocal) {
JimpleLocal jl = (JimpleLocal) va;
if (jl.getType() instanceof RefType) {
RefType rt = (RefType) jl.getType();
SootClass cls = rt.getSootClass();
String clsName = cls.getName();
// recursion possible - backward analysis ?
if(clsName.equals("java.lang.String")){
System.out.print("GOT STRING CLASS - HOW TO GET THE VALUE ?");
}
}
}
}
I am new to the program analysis domain, any pointers will be of great help.
Thanks
StringConstant had a getValue Methode. Just cast The value to this type. For locals your questions does not make sense, as they are variables, not constants.
How Can I check the Type of a variable . in this way :
Pseudo Code :
do{
VARIABLE = JOptionPane.showInputDialog(null,"X = ");
}while(VARIABLE != Integer);
Set_X(VARIABLE);
In this case, your VARIABLE variable will be of type String.
You can try/catch Integer.valueOf on that variable to get its Integer value.
If a NumberFormatException is thrown, it means the user input cannot be converted to Integer.
For instance (draft code):
// initializing Integer interpretation
Integer input = null;
// infinite loop - breaks only once user has entered a valid integer-parseable value
while (true) {
// trying to convert user input after showing option pane
try {
input = Integer.valueOf(JOptionPane.showInputDialog(null, "X = "));
// breaking infinite loop
break;
}
// conversion unsuccessful
catch (NumberFormatException nfe) {
// TODO handle any error message if you need to
}
}
You can't check it for any type, obviously. You can, however, try to parse it using parsers of known types and, if parsing succeeds for one, cast it to that type and move on.
For example, the integer-parsing helper function is Integer.parseInt(String). Unfortunately, it throws an exception on failure (which will make your code ugly and slow at that point) and I'm not sure there are alternatives in the base library (equivalent to, say, C#'s TryParse).
The following example uses a Class object to print the class name of an object:
void printClassName(Object obj) {
System.out.println("The class of " + obj +
" is " + obj.getClass().getName());
}
Hence this code will perform the check you require:
do{
VARIABLE = JOptionPane.showInputDialog(null,"X = ").getClass().getName();
}while(!(VARIABLE.equals("java.lang.Integer"));