• No se han encontrado resultados

PROYECTO DE LEY REFORMATORIA AL CÓDIGO PENAL Y A LA LEY GENERAL DE INSTITUCIONES

y de otros «Delitos bancarios»

2. PROYECTO DE LEY REFORMATORIA AL CÓDIGO PENAL Y A LA LEY GENERAL DE INSTITUCIONES

After we have selected some concrete tools, we can describe the development approach in more detail. We propose an iterative development process. There are three types of iteration, every type has a different cost and different purpose, see figure 5.2.

The first type of iteration exclusively uses the standard Python interpreter. A developer applies a change in a RPython source code of the application, runs the modified code in the Python interpreter and instantly sees how the change work. This type of iteration is very fast as there is no compilation. This approach perfectly fits test-driven development.

The second type of iteration deals with a testing based on formal methods. A Java byte-code is generated by PyPy from RPython sources and the generated byte-code is investigated by Java Pathfinder. This iteration is more expensive than the first one. First, one have to precisely formulate the formal properties the investigated code should meet; second, the investigation itself may consume significant computation time.

The third type of iteration deals with the final code. The C code is generated by PyPy from RPython sources and is subsequently compiled into the machine code. The machine code can be deployed to the intended target embedded device. The final code on the final hardware can be subject of various tests, for instance performance test.

The first type of iteration is very cheap and can be performed with high frequency. The second and the third type tend to be costly. However, their particular cost depends on the nature of the application and conditions. One can have a cheap set of semi-formal tests that can be performed frequently and a very complicated way how to test on the target hardware. And vice versa, one

can have a very costly set of formal tests that require hours of computing and an efficient way how to test on the target hardware.

Program Code (RPython)

Compile

Program Code (Machine Code)

Feedback from testing based on formal methods (Java Pathfinder) Development with interpreted run Program Code (Java byte−code) Feedback from interpreted run

Compile Feedback from

tests on final HW Set of Guarantees of Correctness Set of Formal Properties

Figure 5.2: Refined Scheme of the Proposed Development Approach

5.4

Conclusion

We have specified a domain for our development approach: the software and hardware platform and the range of applications it is suitable for. We have also selected the verification tool.

Real results of our development approach heavily depend on the PyPy com- piler. PyPy defines a set of limitations of the source code (RPython), it is responsible for efficiency of the output code and finally, it generates code that is an input for the model checker. That is why we have to seriously analyze this tool and the compilation process. Chapter 6 describes the relation between generated C and Java byte-code, chapter 8 describes the verification procedure in details.

Chapter 6

Analysis of the PyPy Compilation

Process

This chapter is based mostly on two sources, the first is a technical report

Compiling Dynamic Language Implementations [49], the second is an analysis

of the PyPy source codes. Properties of the PyPy compilation process have crucial impact on usefulness of the proposed development process.

The compilation process is a bit unusual so we first provide a short overview. Then we describe the subset of Python that makes the compilation feasible. Then we dive deeper into the technical details of the compilation process: ab- stract interpretation, flow graph, type inference, flow graph transformations.

The main achievement of the chapter is a formal definition of the flow graph and formally described flow graph transformation.

Last, but not least, we deal with generation of the output code (C or Java byte-code).

6.1

Translation Process Overview

Input of the translation process is a source code in the form of Python language. The source code has to meet some guidelines that are called Restricted Python, or, RPython. The output of the translation process is, for our purposes, C code Java byte-code.

In section 4.3.2, we mentioned the two-stage design. The PyPy compiler not only embraces this idea but actually enforces it. The first stage that contains definitions of data structures, classes, methods, and standalone functions is exe- cuted by the PyPy interpreter as a standard Python program. The result of this step is an initialized program’s object space, i.e., all classes and their methods are defined. Object construction is not limited by RPython constrains; one can

build classes and methods with the help of all dynamic features, including eval, which is crucial for adoption of paradigms such as AOP and DbC.

Execution of the "second stage" would follow after the object space initial- ization. If the Python program is run by an ordinary interpreter (CPython), the code after the initialization part is simply executed.

As the PyPy compiler tries to compile the program, not to run it, the "second stage" processing is slightly different. Instead of ordinary interpretation, the

abstract interpretation is used. The PyPy abstract interpreter does not execute

Python byte-code instructions but instead records them into a data structure called flow graph. A set of flow graphs—one for each function—can be viewed an alternative representation of the program’s object space.

To make the abstract interpretation feasible (finite), it has to run only over frozen program’s object space. When the abstract interpretation is in progress, no new classes or methods can be introduced. From this point on, RPython guarantees that the program code is as static as for instance in Java.

When the abstract interpretation is finished, then several flow graph transfor- mations are performed. The first transformation adds a type annotation: static data types are inferred and type information is added to every data field in the flow graph. From this point on, the program is completely statically typed. To make the type inference feasible, RPython imposes another set of constrains to the code, for instance a single variable can not change types over time.

Up to this point, the compilation process is independent of any target plat- form; the ongoing step depends on the selected target platform. The data types inferred are rather abstract, i.e., do not define the binary representation. In order to generate some real target code, the abstract types have to be mapped to the real data types of the target platform: for instance, an abstract integer becomes C’s or Java’s int.

Subsequent transformations deal with limitations of target platforms. In the case that the target platform lacks an explicit exception handling or garbage collection, these aspects are added to the flow graph. For instance, exceptions can be implemented by additional jumps and labels; garbage collection can be implemented by adding of reference counters.

When the flow graph is at the level of abstraction of the target platform, the final code is generated.

Overall compilation scheme is depicted in figure 6.1