06e6e4ae007b14c5e83932f7fe99bce59caddf64
[jpf-core.git] / src / main / gov / nasa / jpf / listener / ConflictTracker.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.listener;
19
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.ListenerAdapter;
23 import gov.nasa.jpf.search.Search;
24 import gov.nasa.jpf.jvm.bytecode.*;
25 import gov.nasa.jpf.vm.*;
26 import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
27 import gov.nasa.jpf.vm.bytecode.ReadInstruction;
28 import gov.nasa.jpf.vm.bytecode.StoreInstruction;
29 import gov.nasa.jpf.vm.bytecode.WriteInstruction;
30
31 import java.io.PrintWriter;
32
33 import java.util.*;
34
35 /**
36  * Listener using data flow analysis to find conflicts between smartApps.
37  **/
38
39 public class ConflictTracker extends ListenerAdapter {
40   private final PrintWriter out;
41   private final HashSet<String> conflictSet = new HashSet<String>(); // Variables we want to track
42   private final HashSet<String> appSet = new HashSet<String>(); // Apps we want to find their conflicts
43   private final HashSet<String> manualSet = new HashSet<String>(); // Writer classes with manual inputs to detect direct-direct(No Conflict) interactions
44   private final HashMap<Integer, Node> nodes = new HashMap<Integer, Node>(); // Nodes of a graph
45   private ArrayList<NameValuePair> tempSetSet = new ArrayList<NameValuePair>();
46   private long timeout;
47   private long startTime;
48   private Node parentNode = new Node(-2);
49   private String operation;
50   private String detail;
51   private String errorMessage;
52   private int depth;
53   private int id;
54   private boolean conflictFound = false;
55   private boolean manual = false;
56
57   private final String SET_LOCATION_METHOD = "setLocationMode";
58   private final String LOCATION_VAR = "locationMode";
59   
60   public ConflictTracker(Config config, JPF jpf) {
61     out = new PrintWriter(System.out, true);
62
63     String[] conflictVars = config.getStringArray("variables");
64     // We are not tracking anything if it is null
65     if (conflictVars != null) {
66       for (String var : conflictVars) {
67         conflictSet.add(var);
68       }
69     }
70     String[] apps = config.getStringArray("apps");
71     // We are not tracking anything if it is null
72     if (apps != null) {
73       for (String var : apps) {
74         appSet.add(var);
75       }
76     }
77     String[] manualClasses = config.getStringArray("manualClasses");
78     // We are not tracking anything if it is null
79     if (manualClasses != null) {
80       for (String var : manualClasses) {
81         manualSet.add(var);
82       }
83     }
84
85     // Timeout input from config is in minutes, so we need to convert into millis
86     timeout = config.getInt("timeout", 0) * 60 * 1000;
87     startTime = System.currentTimeMillis();
88   }
89
90   boolean propagateTheChange(Node currentNode) {
91         HashSet<Node> changed = new HashSet<Node>(currentNode.getSuccessors());
92         HashMap<Node, HashSet<Node>> parentQueueMap = new HashMap<Node, HashSet<Node>>();
93         HashSet<Node> parents = new HashSet<Node>();
94         parents.add(currentNode);
95
96         for (Node node : currentNode.getSuccessors()) {
97                 parentQueueMap.put(node, parents);
98         }
99
100         while(!changed.isEmpty()) {
101                 // Get the first element of the changed set and remove it
102                 Node nodeToProcess = changed.iterator().next();
103                 changed.remove(nodeToProcess);
104
105                 // Update the changed parents
106                 parents = parentQueueMap.get(nodeToProcess);
107                 boolean isChanged = false;
108
109                 for (Node node : parents) {
110                         // Update the edge
111                         isChanged |= updateTheOutSet(node, nodeToProcess);
112                 }
113
114                 // Check for a conflict if the outSet of nodeToProcess is changed
115                 if (isChanged) {
116                         for (Node node : nodeToProcess.getSuccessors()) {
117                                 HashMap<Transition, ArrayList<NameValuePair>> setSets = nodeToProcess.getOutgoingEdges().get(node).getSetSetMap();
118                                 for (Map.Entry mapElement : setSets.entrySet()) {
119                                         Transition transition = (Transition)mapElement.getKey();
120                                         if (checkForConflict(nodeToProcess, node, transition))
121                                                 return true;
122                                 }
123                         }
124                 }
125
126                 // Update the parents list for the successors of the current node
127                 parents = new HashSet<Node>();
128                 parents.add(nodeToProcess);
129
130                 // Checking if the out set has changed or not(Add its successors to the change list!)
131                 if (isChanged) {
132                         for (Node i : nodeToProcess.getSuccessors()) {
133                                 if (!changed.contains(i))
134                                         changed.add(i);
135
136                                 // Update the list of updated parents for the current node
137                                 if (parentQueueMap.containsKey(i))
138                                         parentQueueMap.get(i).add(nodeToProcess);
139                                 else
140                                         parentQueueMap.put(i, parents);
141                         }
142                 }
143       }
144
145       return false;
146   }
147
148   String createErrorMessage(NameValuePair pair, HashMap<String, String> valueMap, HashMap<String, Integer> writerMap) {
149         String message = "Conflict found between the two apps. App"+pair.getAppNum()+
150                          " has written the value: "+pair.getValue()+
151                          " to the variable: "+pair.getVarName()+" while App"
152                          +writerMap.get(pair.getVarName())+" is overwriting the value: "
153                          +valueMap.get(pair.getVarName())+" to the same variable!";
154         System.out.println(message);    
155         return message;
156   }
157
158   boolean checkForConflict(Node parentNode, Node currentNode, Transition currentTransition) {
159         ArrayList<NameValuePair> setSet = parentNode.getOutgoingEdges().get(currentNode).getSetSetMap().get(currentTransition);
160         HashMap<String, String> valueMap = new HashMap<String, String>(); // HashMap from varName to value
161         HashMap<String, Integer> writerMap = new HashMap<String, Integer>(); //  HashMap from varName to appNum
162
163         // Update the valueMap and writerMap + check for conflict between the elements of setSet
164         for (int i = 0;i < setSet.size();i++) {
165             NameValuePair nameValuePair = setSet.get(i);
166             String varName = nameValuePair.getVarName();
167             String value = nameValuePair.getValue();
168             Integer appNum = nameValuePair.getAppNum();
169
170             if (valueMap.containsKey(varName)) {
171                 // Check if we have a same writer
172                 if (!writerMap.get(varName).equals(appNum)) {
173                         // Check if we have a conflict or not
174                         if (!valueMap.get(varName).equals(value)) {
175                                 errorMessage = createErrorMessage(nameValuePair, valueMap, writerMap);
176                                 return true;
177                         } else { // We have two writers writing the same value
178                                 writerMap.put(varName, 3); // 3 represents both apps
179                         }
180                 } else {
181                         // Check if we have more than one value with the same writer
182                         if (!valueMap.get(varName).equals(value)) {
183                                 // We have one writer writing more than one value in a same event
184                                 valueMap.put(varName, "twoValue");
185                         }
186                 }
187              } else {
188                 valueMap.put(varName, value);
189                 writerMap.put(varName, appNum);
190              }
191         }
192
193         // Check for conflict between outSet and this transition setSet
194         for (NameValuePair i : currentNode.getOutSet()) {
195              if (valueMap.containsKey(i.getVarName())) {
196                  String value = valueMap.get(i.getVarName());
197                  Integer writer = writerMap.get(i.getVarName());
198                      if ((value != null)&&(writer != null)) {
199                          if (!value.equals(i.getValue())&&!writer.equals(i.getAppNum())) { 
200                              // We have different values and different writers
201                              errorMessage = createErrorMessage(i, valueMap, writerMap);
202                              return true;
203                          }
204                      }
205              }
206        }
207
208        return false;
209   }
210
211   boolean updateTheOutSet(Node parentNode, Node currentNode) {
212         HashMap<Transition, ArrayList<NameValuePair>> setSet = parentNode.getOutgoingEdges().get(currentNode).getSetSetMap();
213         HashSet<String> updatedVarNames = new HashSet<String>();
214         Edge currentEdge = parentNode.getOutgoingEdges().get(currentNode);
215         HashMap<String, Integer> lastWriter = currentEdge.getLastWriter();
216         HashMap<String, String> lastValue = currentEdge.getLastValue();
217         boolean isChanged = false;
218
219         for (Map.Entry mapElement : setSet.entrySet()) {
220             ArrayList<NameValuePair> value = (ArrayList<NameValuePair>)mapElement.getValue();
221   
222             for (int i = 0;i < value.size();i++) {
223                         updatedVarNames.add(value.get(i).getVarName());
224             }
225         }
226
227
228         for (NameValuePair i : parentNode.getOutSet()) {
229                 if (!updatedVarNames.contains(i.getVarName()))
230                         isChanged |= currentNode.getOutSet().add(i);
231         }
232
233
234         for (Map.Entry mapElement : setSet.entrySet()) {
235             ArrayList<NameValuePair> value = (ArrayList<NameValuePair>)mapElement.getValue();
236   
237             for (int i = 0;i < value.size();i++) {
238                         if (value.get(i).getAppNum().equals(lastWriter.get(value.get(i).getVarName())) 
239                             && value.get(i).getValue().equals(lastValue.get(value.get(i).getVarName()))) {
240                                 isChanged |= currentNode.getOutSet().add(value.get(i));
241                         }
242             }
243         }
244
245         return isChanged;
246   }
247
248   void updateTheEdge(Node currentNode, Transition transition) {
249         if (parentNode.getOutgoingEdges().containsKey(currentNode)) {
250                 Edge currentEdge = parentNode.getOutgoingEdges().get(currentNode);
251                 if (currentEdge.getSetSetMap().containsKey(transition)) { // Update the transition
252                         if (manual)
253                                 currentEdge.getSetSetMap().put(transition, tempSetSet);
254                         else
255                                 currentEdge.getSetSetMap().get(transition).addAll(tempSetSet);
256                 } else { // Add a new transition
257                         currentEdge.getSetSetMap().put(transition, tempSetSet);
258                 }
259         } else {
260                 parentNode.getOutgoingEdges().put(currentNode, new Edge(parentNode, currentNode));
261                 Edge currentEdge = parentNode.getOutgoingEdges().get(currentNode);
262                 currentEdge.getSetSetMap().put(transition, tempSetSet);
263         }
264
265         // Update the last writer and last value for this edge for each varName
266         Edge currentEdge = parentNode.getOutgoingEdges().get(currentNode);
267         ArrayList<NameValuePair> setSet = currentEdge.getSetSetMap().get(transition);
268         for (int i = 0;i < setSet.size();i++) {
269                 NameValuePair nameValuePair = setSet.get(i);
270                 currentEdge.getLastWriter().put(nameValuePair.getVarName(), nameValuePair.getAppNum());
271                 currentEdge.getLastValue().put(nameValuePair.getVarName(), nameValuePair.getValue());
272         }
273   }
274
275   
276
277   static class Node {
278         Integer id;
279         HashSet<Node> predecessors = new HashSet<Node>();
280         HashSet<Node> successors = new HashSet<Node>();
281         HashSet<NameValuePair> outSet = new HashSet<NameValuePair>();
282         HashMap<Node, Edge> outgoingEdges = new HashMap<Node, Edge>();
283
284         Node(Integer id) {
285           this.id = id;
286         }
287
288         Integer getId() {
289                 return id;
290         }
291
292         HashSet<Node> getPredecessors() {
293                 return predecessors;
294         }
295
296         HashSet<Node> getSuccessors() {
297                 return successors;
298         }
299
300         HashSet<NameValuePair> getOutSet() {
301                 return outSet;
302         }
303
304         HashMap<Node, Edge> getOutgoingEdges() {
305                 return outgoingEdges;
306         }
307   }
308
309   static class Edge {
310         Node source, destination;
311         HashMap<String, Integer> lastWriter = new HashMap<String, Integer>();
312         HashMap<String, String> lastValue = new HashMap<String, String>();
313         HashMap<Transition, ArrayList<NameValuePair>> setSetMap = new HashMap<Transition, ArrayList<NameValuePair>>();
314
315         Edge(Node source, Node destination) {
316                 this.source = source;
317                 this.destination = destination;
318         }
319
320         Node getSource() {
321                 return source;
322         }
323
324         Node getDestination() {
325                 return destination;
326         }
327
328         HashMap<Transition, ArrayList<NameValuePair>> getSetSetMap() {
329                 return setSetMap;
330         }
331
332         HashMap<String, Integer> getLastWriter() {
333                 return lastWriter;
334         }
335
336         HashMap<String, String> getLastValue() {
337                 return lastValue;
338         }
339   }
340
341   static class NameValuePair {
342         Integer appNum;
343         String value;
344         String varName;
345         boolean isManual;
346
347         NameValuePair(Integer appNum, String value, String varName, boolean isManual) {
348                 this.appNum = appNum;
349                 this.value = value;
350                 this.varName = varName;
351                 this.isManual = isManual;
352         }
353
354         void setAppNum(Integer appNum) {
355                 this.appNum = appNum;
356         }
357
358         void setValue(String value) {
359                 this.value = value;
360         }
361
362         void setVarName(String varName) {
363                 this.varName = varName;
364         }
365
366         void setIsManual(String varName) {
367                 this.isManual = isManual;
368         }
369
370         Integer getAppNum() {
371                 return appNum;
372         }
373
374         String getValue() {
375                 return value;
376         }
377
378         String getVarName() {
379                 return varName;
380         }
381
382         boolean getIsManual() {
383                 return isManual;
384         }
385
386         @Override
387         public boolean equals(Object o) {
388       if (o instanceof NameValuePair) {
389         NameValuePair other = (NameValuePair) o;
390         if (varName.equals(other.getVarName()))
391           return appNum.equals(other.getAppNum());
392       }
393       return false;
394         }
395
396         @Override
397         public int hashCode() {
398                 return appNum.hashCode() * 31 + varName.hashCode();
399         }
400   }
401
402   @Override
403   public void stateRestored(Search search) {
404     id = search.getStateId();
405     depth = search.getDepth();
406     operation = "restored";
407     detail = null;
408
409     out.println("The state is restored to state with id: "+id+", depth: "+depth);
410   
411     // Update the parent node
412     if (nodes.containsKey(id)) {
413           parentNode = nodes.get(id);
414     } else {
415           parentNode = new Node(id);
416     }
417   }
418
419   @Override
420   public void searchStarted(Search search) {
421     out.println("----------------------------------- search started");
422   }
423  
424
425   @Override
426   public void stateAdvanced(Search search) {
427     String theEnd = null;
428     Transition transition = search.getTransition();
429     id = search.getStateId();
430     depth = search.getDepth();
431     operation = "forward";
432
433     // Add the node to the list of nodes
434     if (nodes.get(id) == null)
435         nodes.put(id, new Node(id));
436
437     Node currentNode = nodes.get(id);
438
439     // Update the edge based on the current transition
440     updateTheEdge(currentNode, transition);
441
442     // Reset the temporary variables and flags
443     tempSetSet = new ArrayList<NameValuePair>();
444     manual = false;
445
446     // Check for the conflict in this transition
447     conflictFound = checkForConflict(parentNode, currentNode, transition);
448
449     if (search.isNewState()) {
450       detail = "new";
451     } else {
452       detail = "visited";
453     }
454
455     if (search.isEndState()) {
456       out.println("This is the last state!");
457       theEnd = "end";
458     }
459
460     out.println("The state is forwarded to state with id: "+id+", depth: "+depth+" which is "+detail+" state: "+"% "+theEnd);
461     
462     // Updating the predecessors for this node
463     // Check if parent node is already in successors of the current node or not
464     if (!(currentNode.getPredecessors().contains(parentNode)))
465         currentNode.getPredecessors().add(parentNode);
466
467     // Update the successors for this node
468     // Check if current node is already in successors of the parent node or not
469     if (!(parentNode.getSuccessors().contains(currentNode)))
470         parentNode.getSuccessors().add(currentNode);
471
472     // Update the outset of the current node and check if it is changed or not to propagate the change
473     boolean isChanged = updateTheOutSet(parentNode, currentNode);
474     
475     // Check if the outSet of this state has changed, update all of its successors' sets if any
476     if (isChanged) {
477         for (Node node : currentNode.getSuccessors()) {
478                 HashMap<Transition, ArrayList<NameValuePair>> setSets = currentNode.getOutgoingEdges().get(node).getSetSetMap();
479                 for (Map.Entry mapElement : setSets.entrySet()) {
480                         Transition currentTransition = (Transition)mapElement.getKey();
481                         conflictFound = conflictFound || checkForConflict(currentNode, node, currentTransition);
482                 }
483         }
484         conflictFound = conflictFound || propagateTheChange(currentNode);
485     }
486     // Update the parent node
487     if (nodes.containsKey(id)) {
488           parentNode = nodes.get(id);
489     } else {
490           parentNode = new Node(id);
491     }
492   }
493
494   @Override
495   public void stateBacktracked(Search search) {
496     id = search.getStateId();
497     depth = search.getDepth();
498     operation = "backtrack";
499     detail = null;
500
501     out.println("The state is backtracked to state with id: "+id+", depth: "+depth);
502
503     // Update the parent node
504     if (nodes.containsKey(id)) {
505           parentNode = nodes.get(id);
506     } else {
507           parentNode = new Node(id);
508     }
509   }
510
511   @Override
512   public void searchFinished(Search search) {
513     out.println("----------------------------------- search finished");
514   }
515
516   private String getValue(ThreadInfo ti, Instruction inst, byte type) {
517     StackFrame frame;
518     int lo, hi;
519
520     frame = ti.getTopFrame();
521
522     if ((inst instanceof JVMLocalVariableInstruction) ||
523         (inst instanceof JVMFieldInstruction))
524     {
525       if (frame.getTopPos() < 0)
526         return(null);
527
528       lo = frame.peek();
529       hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
530
531       return(decodeValue(type, lo, hi));
532     }
533
534     if (inst instanceof JVMArrayElementInstruction)
535       return(getArrayValue(ti, type));
536
537     return(null);
538   }
539
540   private final static String decodeValue(byte type, int lo, int hi) {
541     switch (type) {
542       case Types.T_ARRAY:   return(null);
543       case Types.T_VOID:    return(null);
544
545       case Types.T_BOOLEAN: return(String.valueOf(Types.intToBoolean(lo)));
546       case Types.T_BYTE:    return(String.valueOf(lo));
547       case Types.T_CHAR:    return(String.valueOf((char) lo));
548       case Types.T_DOUBLE:  return(String.valueOf(Types.intsToDouble(lo, hi)));
549       case Types.T_FLOAT:   return(String.valueOf(Types.intToFloat(lo)));
550       case Types.T_INT:     return(String.valueOf(lo));
551       case Types.T_LONG:    return(String.valueOf(Types.intsToLong(lo, hi)));
552       case Types.T_SHORT:   return(String.valueOf(lo));
553
554       case Types.T_REFERENCE:
555         ElementInfo ei = VM.getVM().getHeap().get(lo);
556         if (ei == null)
557           return(null);
558
559         ClassInfo ci = ei.getClassInfo();
560         if (ci == null)
561           return(null);
562
563         if (ci.getName().equals("java.lang.String"))
564           return('"' + ei.asString() + '"');
565
566         return(ei.toString());
567
568       default:
569         System.err.println("Unknown type: " + type);
570         return(null);
571      }
572   }
573
574   private String getArrayValue(ThreadInfo ti, byte type) {
575     StackFrame frame;
576     int lo, hi;
577
578     frame = ti.getTopFrame();
579     lo    = frame.peek();
580     hi    = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
581
582     return(decodeValue(type, lo, hi));
583   }
584
585   private byte getType(ThreadInfo ti, Instruction inst) {
586     StackFrame frame;
587     FieldInfo fi;
588     String type;
589
590     frame = ti.getTopFrame();
591     if ((frame.getTopPos() >= 0) && (frame.isOperandRef())) {
592       return (Types.T_REFERENCE);
593     }
594
595     type = null;
596
597     if (inst instanceof JVMLocalVariableInstruction) {
598       type = ((JVMLocalVariableInstruction) inst).getLocalVariableType();
599     } else if (inst instanceof JVMFieldInstruction){
600       fi = ((JVMFieldInstruction) inst).getFieldInfo();
601       type = fi.getType();
602     }
603
604     if (inst instanceof JVMArrayElementInstruction) {
605       return (getTypeFromInstruction(inst));
606     }
607
608     if (type == null) {
609       return (Types.T_VOID);
610     }
611
612     return (decodeType(type));
613   }
614
615   private final static byte getTypeFromInstruction(Instruction inst) {
616     if (inst instanceof JVMArrayElementInstruction)
617       return(getTypeFromInstruction((JVMArrayElementInstruction) inst));
618
619     return(Types.T_VOID);
620   }
621
622   private final static byte decodeType(String type) {
623     if (type.charAt(0) == '?'){
624       return(Types.T_REFERENCE);
625     } else {
626       return Types.getBuiltinType(type);
627     }
628   }
629
630   // Find the variable writer
631   // It should be one of the apps listed in the .jpf file
632   private String getWriter(List<StackFrame> sfList, HashSet<String> writerSet) {
633     // Start looking from the top of the stack backward
634     for(int i=sfList.size()-1; i>=0; i--) {
635       MethodInfo mi = sfList.get(i).getMethodInfo();
636       if(!mi.isJPFInternal()) {
637         String method = mi.getStackTraceName();
638         // Check against the writers in the writerSet
639         for(String writer : writerSet) {
640           if (method.contains(writer)) {
641             return writer;
642           }
643         }
644       }
645     }
646
647     return null;
648   }
649
650   private void writeWriterAndValue(String writer, String value, String var) {
651     // Update the temporary Set set.
652     NameValuePair temp = new NameValuePair(1, value, var, manual);
653     if (writer.equals("App2"))
654         temp = new NameValuePair(2, value, var, manual);
655     
656     tempSetSet.add(temp);
657   }
658
659   @Override
660   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
661     if (timeout > 0) {
662       if (System.currentTimeMillis() - startTime > timeout) {
663         StringBuilder sbTimeOut = new StringBuilder();
664         sbTimeOut.append("Execution timeout: " + (timeout / (60 * 1000)) + " minutes have passed!");
665         Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sbTimeOut.toString());
666         ti.setNextPC(nextIns);
667       }
668     }
669
670     if (conflictFound) {
671       StringBuilder sb = new StringBuilder();
672       sb.append(errorMessage);
673       Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString());
674       ti.setNextPC(nextIns);
675     } else {
676       if (conflictSet.contains(LOCATION_VAR)) {
677         MethodInfo mi = executedInsn.getMethodInfo();
678         // Find the last load before return and get the value here
679         if (mi.getName().equals(SET_LOCATION_METHOD) &&
680                 executedInsn instanceof ALOAD && nextInsn instanceof ARETURN) {
681           byte type  = getType(ti, executedInsn);
682           String value = getValue(ti, executedInsn, type);
683
684           // Extract the writer app name
685           ClassInfo ci = mi.getClassInfo();
686           String writer = ci.getName();
687
688           // Update the temporary Set set.
689           writeWriterAndValue(writer, value, LOCATION_VAR);
690         }
691       } else {
692         if (executedInsn instanceof WriteInstruction) {
693           String varId = ((WriteInstruction) executedInsn).getFieldInfo().getFullName();
694
695           for (String var : conflictSet) {
696             if (varId.contains(var)) {
697               // Get variable info
698               byte type = getType(ti, executedInsn);
699               String value = getValue(ti, executedInsn, type);
700               String writer = getWriter(ti.getStack(), appSet);
701               // Just return if the writer is not one of the listed apps in the .jpf file
702               if (writer == null)
703                 return;
704
705               if (getWriter(ti.getStack(), manualSet) != null)
706                 manual = true;
707
708               // Update the temporary Set set.
709               writeWriterAndValue(writer, value, var);
710             }
711           }
712         }
713       }
714     }
715   }
716 }