=Paper=
{{Paper
|id=Vol-1512/paper06
|storemode=property
|title=Recursion and Iteration Support in USE Validator with AnATLyzer
|pdfUrl=https://ceur-ws.org/Vol-1512/paper06.pdf
|volume=Vol-1512
|dblpUrl=https://dblp.org/rec/conf/models/Cuadrado15
}}
==Recursion and Iteration Support in USE Validator with AnATLyzer==
Recursion and Iteration Support in USE Validator with AnATLyzer Jesús Sánchez Cuadrado Modelling and Software Engineering Research Group (http://www.miso.es) Universidad Autónoma de Madrid (Spain) Abstract. Model finders enable numerous verification approaches based on searching the existence of models satisfying certain properties of inter- est. One of such approaches is anATLyzer, a static analysis tool for ATL transformations, which relies on USE Validator to provide fine grained analysis based on finding witness models that satisfy the OCL path con- ditions associated to particular errors. However it is limited by the fact that USE Validator does not include built-in support for analysing re- cursive operations and the iterate collection operator. This paper reports our approach to allow USE Validator to analyse OCL path conditions containing recursive operations and iterate, with the aim of widening the amount of actual transformations that can be processed by anATLyzer. We present our approach, based on unfolding recursion into a finite number of steps, and we discuss how to take into account practical aspects such as inheritance and details about the implementa- tion. Keywords: OCL, ATL, USE Validator, Recursion, Iteration, Model finder, Constraint Solver 1 Introduction Model finders are an important element of many automated verification ap- proaches in the MDE setting, since they are able to find models satisfying certain properties of interest. Concrete examples of such finders are USE Validator [4] and EMFtoCSP [3], which take as input a meta-model and a set of OCL in- variants and return a model satisfying the invariants, if any, within a certain scope (e.g., the maximum number of instantiations of the meta-model classes and ranges of attribute values for infinite types such as integers). As part of our work in the static analysis of ATL transformations [2], im- plemented in anATLyzer1 , we have used the model finder implemented by USE Validator [4] to enable the precise analysis of certain error types. This analysis involves creating an OCL path condition which is fed into USE Validator to obtain a model that satisfies it, in order to confirm the error if the model can 1 http://www.miso.es/tools/anATLyzer.html 73 be found or to discard the error if not. However, USE Validator does not sup- port recursive operations nor the iterate collection operation, hence limiting the applicability of our method in some cases. This paper reports our approach to enable the analysis of recursive opera- tions and expressions containing the iterate collection operation in an OCL-based model finder without support for them, focussing on USE Validator. For recur- sive operations we unfold the recursion upto a number of levels. In the case of iterate we similarly convert each call to a sequence of operations that imple- ments a limited number of iteration steps. We have tested our approach with USE Validator but it could be easily implemented for other systems. Paper organization. Section 2 introduces the context of this work using an example, Section 3 describes the unfolding of direct recursive operations, whereas Section 4 explains the adaptation of the previous procedure for iterate. Finally, Section 5 discusses some issues of our approach and concludes. 2 Context and motivation The context of this work is our static analysis tool for ATL transformations, called anATLyzer. It consists of a type checking phase in which confirmed fail- ures and potential errors are identified. Then, for each potential error, we com- pute its OCL path condition, which is an OCL constraint that must be satisfied by any source model that would trigger the error at runtime. Afterwards, such path condition is fed into USE Validator to search for a model, a witness model, that satisfies the condition. If found, the error is confirmed, otherwise it is dis- carded. Hence, a key element for this approach to be practical is to maximise the number of path conditions that can effectively be processed by USE Validator. More details about the approach are described in [2]. As an example let us consider a modified excerpt of the CPL2SPL transfor- mation available in the ATL Zoo2 , which establishes a translation between two telephony DSLs. Figure 1 shows an excerpt of the CPL source meta-model, and an examplary listing3 . This piece of transformation maps every SubAction source element to a LocalFunctionDeclaration in the target, and each Proxy which satisfies the isSimple predicate into a ReturnStat. In ATL, the relationships between rules are established via bindings, denoted by ←, which work as follows. The source elements obtained by evaluating the right part of a binding are looked up in the transformation trace, in order to obtain the corresponding target element created by some rule. In the example, the binding in line 20 is evaluated by exe- cuting the expression s.contents.statement which retrieves a Node source element. If such source element has been transformed by some rule, the corresponding target element is assigned to the statement feature. A smell that the transformation behaviour is not as expected is that a source element appearing in the right part of a binding has not been transformed by 2 http://www.eclipse.org/atl/atlTransformations/#CPL2SPL 3 We added a filter to the SubAction2Function rule and removed a related rule to make the example more illustrative. 74 any rule. In this setting, anATLyzer features a rule analysis component which is able to analyse rule–binding relationships to determine if a binding is fully covered by all the resolving rules. To analyse the binding in line 20 anATLyzer builds the OCL path condition shown at the bottom of Figure 1, which states the properties that a model for which the binding would be unresolved must satisfy. This OCL invariant is fed into USE Validator to search for a witness model that confirms the existence of the problem. However, in practice anATLyzer could not perform this particular analysis due to USE Validator not supporting recursive operations, as is the case of Location.statement. Next section describes how anATLyzer unfolds recursion to enable this analysis, while Section 4 explains how we deal with iterate. 3 Direct recursion This is the basic recursive schema, in which an operation calls itself in one or more call sites within the operation body. Any OCL specification with an operation featuring even this simple form of recursion is rejected by USE Val- idator. For the example path condition USE does not try to evaluate the call to Location.statement because it cannot be determined if the operation terminates. Hence, the analysis cannot be carried out. Our approach to deal with this issue is based on unfolding the recursive operation upto a finite number of steps. We perform the unfolding by copying the original operation n times, so that there are n + 1 versions of the operation. Then, each version of the operation is rewritten so that the recursive call sites do not invoke the original operation, but the next copy of the operation. The last operation in the sequence just returns OclUndefined. Listing 1 shows a sketch of this procedure. It takes the desired number of unfoldings (N) and the piece of abstract syntax corresponding to the recursive operation (OP). There are two helper functions, callSites which returns the set of recursive call sites (i.e., a set of OperationCall abstract syntax elements that invoke Op) and copy which returns a deep copy of the given abstract syntax element. 1 N = Number of unfoldings 2 OP = Original operation 3 4 OP0 = OP 5 for i = 1 to N 6 CSi−1 = callSites(OPi−1 ) 7 foreach cs in CSi−1 8 cs.operationName = OP.operationName + ” ” + i 9 end 10 11 OPi = copy(OP) 12 OPi .operationName = OP.operationName + ” ” + i 13 end 14 15 OPN .body = OclUndefined Listing 1: Sketch of the unfolding algorithm. 75 1 −− We consider nodes are statements, by default. 2 helper context CPL!Node def: statement : CPL!Node = 3 self; 4 5 −− The ”location” node is not a statement. 6 helper context CPL!Location def: statement : CPL!Node = 7 self.contents.statement; 8 9 helper context CPL!Proxy def: isSimple : Boolean = contents Node 10 self.busy.oclIsUndefined() and Node 1 Container 11 self.redirection.oclIsUndefined(); 12 rule SubAction2Function { 13 Proxy Location SubAction 14from s : CPL!SubAction ( 15 s.contents.oclIsKindOf(CPL!Location) ) 16 to t : SPL!LocalFunctionDeclaration ( busy 17 name <− s.id, 0..1 Busy Redirection 18 returnType <− rt, 19 −− Is this binding fully covered by resolving rules? 0..1 20 statements <− s.contents.statement redirection 21 ) 22 } 23 24 rule Proxy2Return { 25 from s : CPL!Proxy ( s.isSimple ) 26 to t : SPL!ReturnStat ( 27 ... 28 ) 29 } SubAction.allInstances()−> select(s | s.contents.oclIsKindOf(Location))−> exists(s | let problem = s.contents.statement in not problem .isUndefined() and not (if problem .oclIsKindOf(Proxy) then let s2 = problem .oclAsType(Proxy) in s2.isSimple() else false endif)) Fig. 1: Excerpt of the CPL meta-model (left), two simplified rules of the CPL2SPL transformation (right), and excerpt of the path condition for the prob- lem in line 20 (bottom) In practice, this procedure needs to be extended to deal with inheritance. This means that it is not enough to duplicate the recursive operation, but every operation that could polymorphically be invoked needs to be duplicated as well. Listing 2 shows the final result as it is generated to be processed by USE, and complements the OCL path condition presented in Figure 1. Hence, using this method anATLyzer is able to obtain the witness model shown in Figure 2 that confirms the existence of the problem. As can be seen the model contains the elements required to trigger the problem: SubAction and Location objects to trigger 76 the SubAction2Function, and a Proxy element which does not satisfy the isSimple predicate, and it is thus not handled by any rule. Since the Proxy element is linked to SubAction via the contents reference, the binding in line 20 will be unresolved. This model has succesfully been obtained because just two unfolding steps are enough in this case. We heuristically set the maximum number of unfoldings to five, but we still do not have any automated mechanism to set parameter to a safe value for those specific cases in which such reasoning could be possible. For instance, the upper bound of a recursive operation (possibly polymorphic) with no parameters would be the maximum number of instances of the class, and the involved subclasses, set as the the model finder scope. abstract class Node operations statement() : Node = self statement 1() : Node = self statement 2() : Node = self statement 3() : Node = self end class Location < Node, NodeContainer attributes url : String clear : String operations statement() : Node = self.contents.statement 1() statement 1() : Node = self.contents.statement 2() statement 2() : Node = self.contents.statement 3() statement 3() : Node = OclUndefined end Listing 2: Unfolded code as generated for USE Validator : SubAction : Location contents contents : Redirection : Proxy redirection Fig. 2: Witness model obtained for the path condition. An alternative to this approach is to inline the operation body n times, using let expressions to bind parameters. However, we prefer the one presented here because it is easier to handle polymorphic calls as explained. 4 Iterate The OCL iterate collection operation is a general iteration operation with the form col→iterate(it; acc =| ). Operationally, it iterates over the elements of the collection assigning them to the it variable in each iteration step. 77 Each time, the given body is evaluated and the acc variable is updated with the result of the evaluation, so that it has a new value in the following iteration or it is the final result of the operation. As an example, the following code implements the select collection operation in terms of iterate. −− Given: col−>select(it | ) where col is a Set col−>iterate(it; acc = Set { } | if then acc−>including(it) else acc endif) In practice, USE Validator is able to evaluate most OCL iteration constructs, such as select, any, etc. However, it cannot evaluate iterate which poses a limita- tion for anATLyzer since path conditions containing iterate cannot be processed. Our approach to deal with this issue is based on unfolding the iteration steps. Until now we support iteration over sets, applying the following strategy. For each call to iterate we generate n operations, being n the number of unfoldings, and each of these operations follows the schema shown in Listing 3. First, we check if the collection is empty (line 10) in case the iteration must terminate returning the currently computed value (acc). If the set is not empty, one element is picked using any (line 13), and then a new set is obtained filtering out the picked element from the original set (line 14). To the best of our knowledge this is the only strategy to implement iteration over sets in OCL. Aftewards, the body of the original iterate is evaluated, and the next iteration operation is invoked. Finally, the recursion is ended at depth n by just returning OclUndefined (line 20). −− Given an expression: col−>iterate(it : Tit ; acc : Tacc = | isEmpty() then acc else let it : Tit = col−>any( | true) in let rest : Set(Tcol ) = col−>select(v | v <> it) in let value : Tcol = in iterate auxi+1 (rest, value) endif ... def iterate auxn (col : Set(Tcol ), acc : Tacc ) : Tacc = OclUndefined end Listing 3: Schema for unfolding iterate, using USE syntax We make use of a special class named ThisModule to allow global operations to be defined. Notably, the iteration operations are defined within this class. In this way, every call to iterate is rewritten to an expression similar to thisMod- 78 ule.iterate aux0(col, ), where the thisModule variable is an instance of This- Module that must be introduced in the scope of the rewritten expression. We also generate unique identifiers for the iteration operations, to avoid name clashes if there are several calls iterate in the same path condition. Finally, we also keep track of the variables in the scope of the original iterate, and if needed, we extend the signature of the iteration operation to pass such variables as parameters. 5 Conclusions and discussion In this paper we have presented our approach to enable USE Validator analyse recursive operations and the iterate collection operation in the context of anAT- Lyzer. In both cases we perform an unfolding of the body of the operations upto a finite number of steps. We have run a small number of tests in which these approaches have shown to be useful, since most of the times a small scope is enough to find the required witness model [1]. Nevertheless, it is part of our future work to carry out more experiments to determine the precision of our approach. In addition, there are some practical considerations to be taken into account, which are discussed in the following. Given that there is a limit in the number of unfoldings, the last step of the unfolding needs to return some value. Ideally, a bottom value should be used to indicate a kind of “stack overflow”, in the sense that the recursion has ended prematurely before finishing the computation. In OCL the closest relative to a bottom value is invalid which conforms to OclInvalid, which in turn conforms to any other type, but any call applied to its unique instance results in invalid itself. However, this is not supported by USE, and thus another value must be used. Selecting such value is difficult in the general case, since it could interact with other expressions processing the return value. We use OclUndefined both for recursion and iterate but we are aware that it may affect the accuracy of the solving process. In this line, an important consequence of unfolding a limited number of times is that the analysis of anATLyzer may not be accurate. A potential error can be wrongly marked as “discarded” only because more unfoldings steps would be needed to provide an accurate answer. Another issue that affects the accuracy of the approach is that USE only supports sets. Therefore, operations such as at, for sequences, cannot be pro- cessed. Devising mechanisms to deal with sequences is part of our future work. Hence we aim at studying and adapting other works dealing with these issues, notably the approach proposed in [5] which relies on SMT solving and a more sophisticated unfolding algorithm. Finally, we have not addressed yet how to unfold mutual recursion, although we believe that a similar strategy is possible, but taking into account the com- plete call graph of the transformation. This is also part of our future work. Acknowledgements. This work has been supported by the Spanish MINECO (TIN2011-24139 and TIN2014-52129-R), the R&D programme of the Madrid Re- gion (S2013/ICE-3006), and the EU commission (FP7-ICT-2013-10, #611125). 79 References 1. A. Andoni, D. Daniliuc, and S. Khurshid. Evaluating the “small scope hypothesis”. Technical Report MIT-LCS-TR-921, MIT CSAIL, 2003. 2. J. S. Cuadrado, E. Guerra, and J. de Lara. Uncovering errors in ATL model trans- formations using static analysis and constraint solving. In ISSRE, pages 34–44. IEEE, 2014. 3. C. A. Gonzalez, F. Büttner, R. Clariso, and J. Cabot. Emftocsp: A tool for the lightweight verification of emf models. In Proceedings of the First International Workshop on Formal Methods in Software Engineering: Rigorous and Agile Ap- proaches, pages 44–50. IEEE Press, 2012. 4. M. Kuhlmann, L. Hamann, and M. Gogolla. Extensive validation of OCL models by integrating SAT solving into USE. In TOOLS (49), volume 6705 of LNCS, pages 290–306. Springer, 2011. 5. P. Suter, A. S. Köksal, and V. Kuncak. Satisfiability modulo recursive programs. In Static Analysis, pages 298–315. Springer, 2011. 80