/** * Copyright (C) 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mujava.op; import java.io.*; import java.util.*; import openjava.mop.*; import openjava.ptree.*; import java.lang.reflect.*; import mujava.MutationSystem; /** * <p>Generate ISI (Super keyword insertion) and * ISD (Super keyword deletion) mutants -- insert the <i>super</i> keyword; * delete each occurrence of the <i>super</i> keyword * </p> * @author Yu-Seung Ma * @version 1.0 */ public class ISD extends mujava.op.util.Mutator { MethodDeclaration current_method; Stack fieldStack = new Stack(); Stack methodStack = new Stack(); Stack s = new Stack(); Vector overridingFields = new Vector(); Vector overridingMethods = new Vector(); Class parent = null; int nesting = 0; public ISD(FileEnvironment file_env, ClassDeclaration cdecl, CompilationUnit comp_unit) { super( file_env, comp_unit ); } public void visit( ClassDeclaration p ) throws ParseTreeException { this.evaluateDown( p ); if (p.getName().equals(MutationSystem.CLASS_NAME)) { nesting = 1; } else if (nesting == 0) { this.evaluateUp(p); return; } else { nesting++; fieldStack.add(overridingFields); methodStack.add(overridingMethods); overridingFields = new Vector(); overridingMethods = new Vector(); } s.push(p.getName()); String package_name = comp_unit.getPackage(); String target_class = ""; if ((package_name == null)|| (package_name.equals("null"))) { for (int i=0; i<s.size(); i++) { if (i == 0) { target_class = s.get(i).toString(); } else { target_class = target_class + "$" + s.get(i).toString(); } } } else { target_class = package_name; for (int i=0; i<s.size(); i++) { if (i == 0) { target_class = target_class + "." + s.get(i).toString(); } else { target_class = target_class + "$" + s.get(i).toString(); } } } try { parent = Class.forName(target_class).getSuperclass(); if ((parent == null) || (parent.getName().equals("java.lang.Object"))) return; // Examine overriding methods, hiding variables. Class clazz = Class.forName(target_class); if (clazz == null) return; Method[] child_ms = clazz.getDeclaredMethods(); Method[] parent_ms = parent.getDeclaredMethods(); if ((child_ms == null) || (parent_ms == null)) return; for (int i=0; i<child_ms.length; i++) { for (int j=0; j<parent_ms.length; j++) { if ( !(child_ms[i].getName().equals(parent_ms[j].getName())) ) continue; if (!sameReturnType(child_ms[i].getReturnType(), parent_ms[j].getReturnType())) continue; if ( !sameParameterType(child_ms[i].getParameterTypes(), parent_ms[j].getParameterTypes())) continue; overridingMethods.add(child_ms[i]); } } Field[] child_fs = clazz.getDeclaredFields(); Field[] parent_fs = parent.getDeclaredFields(); for (int i=0; i<child_fs.length; i++) { for (int j=0; j<parent_fs.length; j++) { if ( !(child_fs[i].getName().equals(parent_fs[j].getName())) ) continue; if ( !(child_fs[i].getType().getName().equals(parent_fs[j].getType().getName())) ) continue; overridingFields.add(p.getName()); } } p.childrenAccept( this ); } catch (ClassNotFoundException e) { // do nothing } s.pop(); if (nesting > 1) { overridingFields = (Vector)fieldStack.pop(); overridingMethods = (Vector) methodStack.pop(); nesting--; } this.evaluateUp( p ); } boolean sameReturnType(Class c1, Class c2) { if ((c1 == null) && (c2 == null)) return true; if ((c1 == null) || (c2 == null)) return false; if (c1.getName().equals(c2.getName())) return true; return false; } boolean sameParameterType(Class[] par1, Class[] par2) { if ((par1 == null) || (par1.length == 0)) { if ( (par2 == null) || (par2.length == 0) ) return true; else return false; } if (par1.length != par2.length) return false; for (int i=0; i<par1.length; i++) { if (!(par1[i].getName().equals(par2[i].getName()))) return false; } return true; } boolean sameParameterType(Class[] par1, ParameterList par2) { if ((par1 == null) || (par1.length == 0)) { if ( (par2 == null) || (par2.size() == 0) ) return true; else return false; } if (par1.length != par2.size()) return false; for (int i=0; i<par1.length; i++) { if (!(par1[i].getName().equals(par2.get(i).getTypeSpecifier().getName()))) return false; } return true; } public void visit( MethodDeclaration p ) throws ParseTreeException { current_method = p; super.visit(p); } public void visit( FieldAccess p ) throws ParseTreeException { Expression ref_exp = p.getReferenceExpr(); if (ref_exp instanceof SelfAccess) { if (((SelfAccess)ref_exp).getAccessType() == SelfAccess.SUPER) { FieldAccess mutant = (FieldAccess)p.makeRecursiveCopy(); mutant.setReferenceExpr(null); outputToFile(p, mutant); } } } public void visit( AssignmentExpression p ) throws ParseTreeException { Expression newp = this.evaluateDown( p ); if (newp != p) { p.replace( newp ); return; } p.childrenAccept( this ); newp = this.evaluateUp( p ); if (newp != p) p.replace( newp ); } boolean isDifinedMethod(MethodCall p) { try { OJClass clazz = getSelfType(); OJMethod[] ms = clazz.getDeclaredMethods(); for (int i=0; i<ms.length; i++) { if (isSameMethod(ms[i], p)) return true; } } catch(Exception e) { e.printStackTrace(); } return false; } boolean isSameMethod(OJMethod m, MethodCall p) { try { if (!(m.getName().equals(p.getName()))) return false; if (!(m.getReturnType().getName().equals(getType(p).getName()))) return false; ExpressionList elist = p.getArguments(); OJClass[] plist = m.getParameterTypes(); if ( (elist == null) && (plist == null)) return true; if ( ((elist != null) && (plist == null)) || ((elist == null) && (plist != null))) return false; if (elist.size() != plist.length) return false; for (int j=0; j<elist.size(); j++) { OJClass type = getType(elist.get(j)); if (!(type.getName().equals(plist[j].getName()))) { return false; } } return true; } catch(Exception e) { e.printStackTrace(); } return true; } public void visit( MethodCall p ) throws ParseTreeException { Expression lexp = p.getReferenceExpr(); if (lexp != null) { if (lexp instanceof SelfAccess) { if ( (((SelfAccess)lexp).getAccessType() == SelfAccess.SUPER) && !(occurRecursive(p)) && isDifinedMethod(p)) { MethodCall mutant; mutant = (MethodCall)p.makeRecursiveCopy(); mutant.setReferenceExpr(null); outputToFile(p,mutant); } } else if (lexp instanceof FieldAccess) { super.visit(p); } } else { if (isOverridingMethodCall(p)) isi_outputToFile(p); } } boolean isOverridingMethodCall(MethodCall p) { try { for (int i=0; i<overridingMethods.size(); i++) { Method m = (Method)overridingMethods.get(i); if (!(p.getName().equals(m.getName()))) continue; ExpressionList elist = p.getArguments(); Class[] plist = m.getParameterTypes(); if ( (elist == null) && (plist == null)) return true; if ( ((elist != null) && (plist == null)) || ((elist == null) && (plist != null))) continue; boolean found = true; if (elist.size() != plist.length) continue; for (int j=0; j<elist.size(); j++) { OJClass type = getType(elist.get(j)); if (!(type.getName().equals(plist[j].getName()))) { found = false; break; } } if (found) return true; else return false; } return false; } catch (ParseTreeException e) { return false; } } public void visit( Variable p ) throws ParseTreeException { for (int i=0; i<overridingFields.size(); i++) { if (p.toString().equals(overridingFields.get(i).toString())) { isi_outputToFile(p); } } } /** * Output ISI mutants to files * @param mutant */ public void isi_outputToFile(MethodCall mutant) { if (comp_unit == null) return; String f_name; num++; f_name = getISISourceName(this); String mutant_dir = getISIMuantID(); try { PrintWriter out = getPrintWriter(f_name); ISI_Writer writer = new ISI_Writer(mutant_dir, out); writer.setMutant(mutant); comp_unit.accept( writer ); out.flush(); out.close(); } catch ( IOException e ) { System.err.println( "fails to create " + f_name ); } catch ( ParseTreeException e ) { System.err.println( "errors during printing " + f_name ); e.printStackTrace(); } } /** * Output ISI mutants to files * @param mutant */ public void isi_outputToFile(Variable mutant) { if (comp_unit == null) return; String f_name; num++; f_name = getISISourceName(this); String mutant_dir = getISIMuantID(); try { PrintWriter out = getPrintWriter(f_name); ISI_Writer writer = new ISI_Writer(mutant_dir,out); writer.setMutant(mutant); comp_unit.accept( writer ); out.flush(); out.close(); } catch ( IOException e ) { System.err.println( "fails to create " + f_name ); } catch ( ParseTreeException e ) { System.err.println( "errors during printing " + f_name ); e.printStackTrace(); } } /** * Write mutants to files * @param original * @param mutant */ public void outputToFile(FieldAccess original, FieldAccess mutant) { if (comp_unit == null) return; String f_name; num++; f_name = getSourceName(this); String mutant_dir = getMuantID(); try { PrintWriter out = getPrintWriter(f_name); ISK_JTD_Writer writer = new ISK_JTD_Writer(mutant_dir,out); writer.setMutant(original, mutant); comp_unit.accept( writer ); out.flush(); out.close(); } catch ( IOException e ) { System.err.println( "fails to create " + f_name ); } catch ( ParseTreeException e ) { System.err.println( "errors during printing " + f_name ); e.printStackTrace(); } } /** * Write mutants to files * @param original * @param mutant */ public void outputToFile(MethodCall original, MethodCall mutant) { if (comp_unit == null) return; String f_name; num++; f_name = getSourceName(this); String mutant_dir = getMuantID(); try { PrintWriter out = getPrintWriter(f_name); ISK_JTD_Writer writer = new ISK_JTD_Writer(mutant_dir,out); writer.setMutant(original,mutant); comp_unit.accept( writer ); out.flush(); out.close(); } catch ( IOException e ) { System.err.println( "fails to create " + f_name ); } catch ( ParseTreeException e ) { System.err.println( "errors during printing " + f_name ); e.printStackTrace(); } } private boolean occurRecursive(MethodCall p) { // compare method name if ( !(p.getName()).equals(current_method.getName()) ) return false; ExpressionList args = p.getArguments(); ParameterList pars = current_method.getParameters(); // compare parameter number if (pars.size() != args.size()) return false; if ( (pars.size()==0) && (args.size()==0)) return true; // compare paremeter type if (pars.size() > 0) { try { String par_type; String arg_type; for (int i=0; i<pars.size(); i++) { par_type = pars.get(i).getTypeSpecifier().getName(); arg_type = (getType(args.get(i))).getName(); if ( !(par_type.equals(arg_type)) ) return false; } } catch (Exception e) { return false; } } return true; } /** * Retrieve the file name * @param clazz * @return */ public String getISISourceName(mujava.op.util.Mutator clazz) { // make directory for the mutant String dir_name = MutationSystem.MUTANT_PATH + "/ISI_" + this.num; File f = new File(dir_name); f.mkdir(); // return file name String name; name = dir_name + "/" + MutationSystem.CLASS_NAME+".java"; return name; } /** * Retrieve the ID of ISI mutant * @return str - ID of the mutant */ public String getISIMuantID() { String str = "ISI_"+this.num; return str; } }