File: whyml2java.inc

package info (click to toggle)
why3 1.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 45,020 kB
  • sloc: xml: 185,443; ml: 111,224; ansic: 3,998; sh: 2,578; makefile: 2,568; java: 865; python: 720; javascript: 290; lisp: 205; pascal: 173
file content (220 lines) | stat: -rw-r--r-- 10,183 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
.. _sec.java:

Compilation of WhyML modules into Java classes
''''''''''''''''''''''''''''''''''''''''''''''


The ``java`` extraction driver is used to compile WhyML files into Java classes.
The driver does not support `flat` extraction; thus, option ``--modular`` is
mandatory. Each (non empty) module `M` is translated into a class with the same
name. The imported modules are not translated unless the option ``--recursive``
is used. Since the extraction of a module requires data related to its
dependencies, the option ``--recursive`` should be used systematically.

The code generated by the Java driver can be tuned using attributes. All
attributes used by the Java driver are prefixed with :code:`@java:`.
Attributes not encountered in following examples are given in section
:ref:`sec.attributes`.

In addition to the driver, new modules have been added to the Why3 Standard
Library (see :ref:`sec.java-stdlib`).

A running example
+++++++++++++++++

In order to illustrate the Java extraction, we implement a *directory* of some
company. The directory contains *employees* and each of them stores the name
of the person and its service in the company, its phone number and the number of
its office.

We first create the module for the data structure that will store employees;
the WhyML code is given below. The module defines a type :code:`t` that is a
record with 4 fields, :code:`name`, :code:`room`, :code:`phone` and
:code:`service`. For the first three fields, we use types (:code:`integer` and
:code:`string`) that mimics those of the Java language; they are defined in
modules that come along with the driver (see :ref:`sec.java-stdlib`).

The type of the field :code:`service` is defined by an algebraic type. The
driver permits to declare inner ``Enum`` classes using algebraic types of
the WhyML language. Only algebraic constructors with no arguments are allowed
i.e the type is just an enumeration of identifiers. ``Enum`` classes are the
only form allowed by the driver.

We define a function :code:`create_member` whose purpose is to
create a record of type :code:`t`. This function is kinda redundant with the
WhyML syntax that permits to build such record directly using the usual
constructor (:code:`{ ... }`). The reader should have noticed the attribute
:why3:attribute:`java:constructor` attached to the function. It is used to mark
functions that should yield Java constructors. The driver allows only such
marked functions to build records.

.. literalinclude:: javaexamples/directory.mlw
	:lines: 1-18
	:language: whyml

We assume that the entire WhyML code described in this section is stored in a
file :file:`./directory.mlw`. We generate the file :file:`Employee.java` using
the following command (according to the option `-o` the file is created in the
current directory).

.. code-block:: shell-session

	$ why3 extract -L . -D java -o . --recursive --modular directory.Employee

:why3:tool:`extract` produces the Java code described below. After the header of
the class :code:`Employee` comes the definition of the :code:`ServiceId` that
correspond to the algebraic type :code:`service_id`. Note that identifiers are
translated using camel case.

.. literalinclude:: generated/Employee.java
	:lines: 1-9
	:language: java

To be consistent with WhyML semantics the fields of the type :code:`Employee.t`
have been translated as :code:`final` instance variables. When a field of a
record is :code:`mutable` then so is the corresponding instance variable.

.. literalinclude:: generated/Employee.java
	:lines: 11-14
	:language: java

As a rule of thumb, the first type definition encountered by the driver is
assumed to be the type of the generated class. Usually this type is a record,
the fields of which are translated as instance variables. Abstract types can
also be used when the expected class does not store any data (e.g. an
exception) or when one wants to generate an interface (see attribute
:why3:attribute:`java:class_kind:`). Since the driver looks for the first type
definition, it can be difficult to use module cloning because new types may be
added by this mechanism. The definition of a type for the class is not mandatory
when one wants to gather a collection of static methods.

In order to make generated classes usable with Java containers (especially those
implemented in :ref:`sec.java-stdlib`) the driver produces systematically the
methods :code:`equals` and :code:`hashCode`. The implementation of these methods
for :code:`Employee` objects is given below. These methods are specified
:code:`final` to prevent their redefinition. Note that these methods are not
available from the WhyML code.

.. literalinclude:: generated/Employee.java
	:lines: 15-51
	:language: java

Finally the driver translates :code:`create_employee` into a constructor of
:code:`Employee` objects. During this translation, the identifier of functions
annotated with the attribute :why3:attribute:`java:constructor` is lost. This
allows to declare several constructors with different signatures.

.. literalinclude:: generated/Employee.java
	:lines: 53-59,62
	:language: java

We now focus on the module implementing the directory. We use a :code:`Map`
container to associate to a `name` (a :code:`string`) an `Employee`. The
container, specified in the module
`mach.java.util.Map <https://www.why3.org/stdlib/mach.java.util.html#Map_>`_,
partially mimics the container from the JDK.

A directory is simply a record with only one field :code:`employees` (see section
:why3:attribute:`extraction:preserve_single_field` for a detailed description of
this attribute). We use the attribute :why3:attribute:`java:visibility:private`
to avoid direct access to :code:`employees`.

We also define a method :code:`add_employee` that permits to insert a new entry
into the directory. The contract of the method requires that no entry already
exists the same employee's name and ensures a new entry with given data has
been added.

.. literalinclude:: javaexamples/directory.mlw
	:lines: 20-43
	:language: whyml

The Java code extracted from this specification is the following (the code of
methods :code:`equals` and :code:`hashCode` has been suppressed).

.. literalinclude:: generated/Directory.java
	:lines: 1-6,33-42,45
	:language: java

The reader should have noticed that the contract of :code:`add_employee`.
This not surprising since :why3:tool:`extract` removes all `ghost` code. In the
context of a WhyML program we are guaranteed that :code:`add_employee` is called
with parameters that satisfy the precondition of the function (i.e. :code:`name`
is not an entry of the directory). However, if this method is invoked from a
client code that has not been generated with :why3:tool:`extract`, we should add
explicitly the verification of the precondition.

Let fix this issue by modifying :code:`add_employee` in such a way it raises an
exception if the precondition is not fulfilled. First, we create the class for
the exception :code:`EmployeeAlreadyExistsException`:

.. literalinclude:: javaexamples/directory.mlw
	:lines: 45-57
	:language: whyml

The creation of an exception for Java extraction is based on two points:

#. Firstly, it requires to annotate the module with the attribute
   :why3:attribute:`java:exception:` *exn-class*. The suffix of this attribute
   indicates the class from which the generated exception inherits. This suffix
   is not interpreted and is printed out `as-is` by the driver. In our example,
   we just want the exception to inherit from the standard
   :code:`RuntimeException` from the JDK.
#. Secondly, the module must define a WhyML exception, :code:`E` in our example.
   As for standard classes, it is possible to declare instance variables in
   exceptions. In our example, :code:`EmployeeAlreadyExistsException` stores
   an information message.

.. literalinclude:: generated/EmployeeAlreadyExistsException.java
	:lines: 1-4,31-40,43
	:language: java

The implementation of the method :code:`add_employee` can now be updated. First,
the contract is changed: the precondition has been removed and replaced by an
exceptional postcondition that relates the occurrence of an exception
:code:`EmployeeAlreadyExistsException` with an invalid value of the parameter
:code:`name`. Then, before the creation of an new entry, we check if
:code:`name` already exists in the directory, in which case an exception is
raised.

.. literalinclude:: javaexamples/directory.mlw
	:lines: 66-67,76-86
	:language: whyml

The Java code extracted from this new implementation is given below. Two things
have been added to the original method. First, the declaration of the exception
in the signature of the function and second, the translation of the test related to
the parameter :code:`name`.

.. literalinclude:: generated/CheckedDirectory.java
	:lines: 38-45
	:language: java

.. _sec.java-stdlib:

Extension of Why3 Standard Library
++++++++++++++++++++++++++++++++++


Several modules have been implemented to ease the extraction of Java classes.
They are gathered accroding to JDKL's packages.

Library `mach.java.lang <https://www.why3.org/stdlib/mach.java.lang.html>`_
defines types for bounded integers used in Java (:code:`Short`, :code:`Integer`
and :code:`Long`) and also strings (:code:`String`) but the latter are limited
to formatting methods. Another important module is
`mach.java.lang.Array <https://www.why3.org/stdlib/mach.java.lang.html#Array_>`_
that must be used in place of Java bounded size arrays. There are also some
standard exceptions that are used by others modules.

Library `mach.java.util <https://www.why3.org/stdlib/mach.java.util.html>`_
gathers some containers (like :code:`Map` used in this section). In the
specification of these containers we tried to stick to Java semantics. In
particular, according to the specification of
`java.util.Collection.size() <https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Collection.html#size()>`_::

  int size()
    Returns the number of elements in this collection. If this collection
    contains more than Integer.MAX_VALUE elements, returns Integer.MAX_VALUE.

  Returns:
    the number of elements in this collection