Difference between pages "Rodin Developer Support" and "Rodin Index Design"

From Event-B
(Difference between pages)
Jump to navigationJump to search
imported>Tommy
 
imported>Mathieu
m (cat)
 
Line 1: Line 1:
The Developer Support provides resources for developing plug-ins for the Rodin Platform.
+
==Purpose==
  
 +
The purpose of the Rodin index manager is to store in a uniform way the entities that are declared in the database together with their occurrences.  This central repository of declarations and occurrences will allow for fast implementations of various refactoring mechanisms (such as renaming) and support for searching models or browsing them.
  
== Rodin Developer FAQ ==
+
==Specification==
  
see [[Developer FAQ]].
+
===The name table===
  
== Architecture of the Rodin Platform ==
+
Firstly, the index manager needs to store declarations.  We make the assumption that a declaration corresponds to one and only one element in the database.
  
=== Rodin Platform Core ===
+
<blockquote><em>For event-B, this assumption is true. For instance, a variable declaration is an <tt>IVariable</tt> element, an event declaration is an <tt>IEvent</tt>, etc.</em></blockquote>
  
* [[Database]]
+
However, the element handle doesn't carry directly the user-defined name for the element.  This name is stored in an attribute of the element.  Thus, one needs to access to the element in the database to retrieve this name.  We therefore want to cache this name in the index to prevent frequent accesses to the database that would hinder performance.
  
* [[Builder]]
+
<blockquote>the user-defined name of a variable is stored in its <tt>identifier</tt> attribute, the name of an event in its <tt>label</tt> attribute.</blockquote>
  
* [[Rodin Index Design]]
+
So, the first table that the index manager needs to maintain is specified as
  
* [[Indexing System]]
+
<blockquote><math>\mathit{name}\in\mathit{ELEMENT}\pfun\mathit{STRING}</math>&nbsp;,</blockquote>
  
* [[Undo Redo]]
+
where <math>\mathit{ELEMENT}</math> is the set of all element handles and <math>\mathit{STRING}</math> is the set of all character strings.
  
* [[File Root Separation]]
 
  
=== Event-B User Interface ===
+
===The occurrences table===
  
The Event-B User Interface of the Roding Platform has two major components that are concerned with either [[Modelling User Interface|modelling]] in Event-B or [[Proving User Interface|proving]] properties of models.
+
Secondly, the index manager needs to maintain the relation between declarations and occurrences. This relation is specified as
  
* [[Modelling User Interface]]
+
<blockquote><math>\mathit{occurrences}\in\mathit{ELEMENT}\pfun\mathit{LOCATION}</math>&nbsp;,</blockquote>
  
* [[Proving User Interface]]
+
where <math>\mathit{LOCATION}</math> is the set of all locations in the database, a location being either an element, or an attribute of an element, or a substring of an attribute of an element.  We also have the following function which relates a location to the element that contains the location
  
Apart from these are more minor components.
+
<blockquote><math>\mathit{element}\in\mathit{LOCATION}\tsur\mathit{ELEMENT}</math>&nbsp;.</blockquote>
  
* [[Proof Purger Design|Proof Purger]] allows to delete unused proofs.  
+
However, note that this relation is <em>not</em> maintained by the index manager, it is inherent to the definition of locations.
  
* [[Proof Skeleton Design]] is a view that displays skeletons of existing proofs
 
  
* [[Auto-Completion Design]] proposes a list of names to the user editing a model
+
==Populating the Index==
  
=== Event-B Component Library ===
+
Following the general principles of the Rodin core, the Rodin index manager is notation agnostic.  Therefore, it cannot populate the index by itself, but delegates this to plug-ins through an extension point.  These contributions are called <em>indexers</em>.
  
Event-B models and all proof-related information are stored in the Rodin database. The syntax of the mathematical notation, that is, expressions, predicates, and assignments, are maintained in an [[Abstract Syntax Tree|abstract syntax tree]]. Abstract syntax trees are manipulated by means of a class library that can be used independently of Eclipse. They are saved in the database in human-readable form as Unicode character strings. Event-B constructs, such as contexts and machines, are not represented as abstract syntax trees. They are stored and retrieved directly from the database (by contrast, mathematical formulas need additional parsing). Well-formedness of Event-B constructs is verified by a [[Static Checker|static checker]]. The static checker has two main purposes: (1) it generates error messages for ill-formed constructs, and (2) it filters well-formed parts of components to be subjected to proof obligation generation. The [[Proof Obligation Generator|proof obligation generator]] uses those parts of constructs that are well-formed and generates proof obligations from them. Finally, the [[Proof Manager|proof manager]] attempts to prove proof obligations and maintains existing proofs associated with proof obligations. The proof manager works automatically and interactively. When new proof obligations have been generated it attempts to discharge them automatically. If it does not succeed, it permits interactive proof (by means of the [[Proving User Interface|proving user interface]]).
+
Then, the question arises to define the granularity at which these indexers will be declared and work.  Having an indexer declared and working on a whole project would be too coarse grain. Conversely, having an indexer declared and working at the granularity of internal elements would be too fine grain. So, it has been decided that indexers are declared for file elements, based on their type. In hindsight, we are taking here the same design decision we took for the event-B tools when defining the granularity of static checking and proof obligation generation.
  
* [[Abstract Syntax Tree]]
+
Moreover, we want to keep the index at least as open as the Rodin database. So, we need to be prepared to accommodate several indexers working on the same file, as the elements contained in a file can be contributed by several plug-ins.  In this case, the side question is in which order should these indexers be run.  The solution chosen is that any indexer can declare that it should be run after another indexer.  In other words, plug-ins can define a partial order among the indexers that will be enforced by the index manager.
  
* [[Static Checker]]
+
Putting everything together, we obtain an extension point for contributing indexers with the following components
  
* [[Proof Obligation Generator]]
+
* id: unique identifier of the indexer
 +
* name: human-readable name of the indexer
 +
* class: implementation of the indexer
 +
* type: list of the file element types handled by the indexer
 +
* after: list of the ids of the indexers that must be run before the indexer
  
* [[Proof Manager]]
 
  
* [[Proof Simplification]]
+
==Index Maintenance==
  
== Extending Rodin ==
+
Of course, the database will change when users edit their models.  We therefore must define a policy for updating the index when the database is modified.  Detecting database changes is easy, thanks to the deltas that are generated by the database manager upon modification.
  
* [[Developer Documentation]]
+
The difficulty here is that there is no restriction on the occurrences of an element: an element can occur in the file where it is declared, but it can also occur in a completely different file.
  
* [[Plug-in Tutorial]]
+
<blockquote>For instance, in event-B, a constant can occur in the context where it is declared, but also in any context that extends (directly or indirectly) its declaring context.</blockquote>
  
* [[Extending the Rodin Database]]
+
So, in principle, each time a file changes, we have to re-index all files.  This is clearly not acceptable from a performance point of view.  In order to keep index updating efficient, we need to better organize the way file changes are propagated in the index.  For this, we first need to gather additional information from indexers (like dependencies between files), then we have to enforce some restrictions on the indexes contributed by indexers, so that we can compute the consequences of a file change.
  
* [[Extending the project explorer]]
+
===Dependencies===
  
* [[Extending the Structure Editor]]
+
Firstly, we ask the indexers to provide the index manager with the list of files that a given file depends on.  This is stored in the table with the following specification
  
* [[Extending the Pretty Print Page]]
+
<blockquote><math>\mathit{dependsOn}\in\mathit{FILE}\rel\mathit{FILE}</math>&nbsp;,</blockquote>
  
* [[Extending the Proof Manager]]
+
where <math>\mathit{FILE}</math> is the set of all files.
  
* [[Extending the Index Manager]]
+
{{TODO| What happens if <math>\mathit{dependsOn}</math> contains a cycle?}}
  
* [[Extending the Static Checker]]
+
But having these dependencies is not precise enough, and can still lead to a lot of unnecessary indexing.
  
* [[Index Query]]
+
<blockquote>For instance, in event-B, if the user adds a theorem to a context on which all files of a project depend (e.g., the context seen by the top-level machine of this project), then we would have to re-index all the files of the project, although it will be useless, as the theorem is not visible to the other files.</blockquote>
  
== Useful Hints ==
+
Therefore, we also ask that the indexer reports the declarations that are exported (i.e., made visible)  by a file to the files that depend on it.  This information is stored in the table specified as
  
=== Version Control ===
+
<blockquote><math>\mathit{exports}\in\mathit{FILE}\rel\mathit{ELEMENT}</math>&nbsp;.</blockquote>
 +
<!--blockquote><math>\mathit{exports}\in\mathit{FILE}\rel(\mathit{ELEMENT}\cprod\mathit{STRING})</math>&nbsp;.</blockquote-->
  
All sources of the core Rodin platform (and of some plug-ins) are managed under version control in SourceForge.  The repository currently used is Subversion and can be accessed using URL [https://rodin-b-sharp.svn.sourceforge.net/svnroot/rodin-b-sharp  https://rodin-b-sharp.svn.sourceforge.net/svnroot/rodin-b-sharp].
 
  
=== Building against a version of Rodin ===
+
===Restrictions===
  
To develop extensions to the Rodin platform your code build needs access to a consistent (version-wise) set of Rodin platform plug-ins. (Don't just check out the latest versions from 'Head' because it may be under development and in an inconsistent state). An easy way to set up your workspace is to import the Rodin platform source code from SVN into your workspace using the 'Releng' plug-in.
+
Then, to minimize re-indexing, we ask that indexers behave as good citizens and enforce some restrictions on the elements and locations that an indexer can contribute to the index.
See [http://wiki.event-b.org/index.php/Developer_FAQ#Installing_the_sources_from_Subversion_in_Eclipse Installing the sources from Subversion in Eclipse] for further instructions.
 
  
Alternatively, you can set your plugin development target platform to any Rodin installation you have installed (Eclipse-Preferences-Plug-in Development-Target Platform). This is useful as a final test that everything will work once installed into Rodin but because it uses a 'built' platform, you don't get access to the Rodin source code (e.g. for de-bugging).
+
To express these constraints, we need a new constant relation that gives the file in which an element is declared
  
=== Testing ===
+
<blockquote><math>\mathit{file}\in\mathit{ELEMENT}\rel\mathit{FILE}</math>&nbsp;.</blockquote>
  
=== Debugging ===
+
This relation is a constant and is inherent to the database structure, it is not maintained by the index manager, only used by it.
  
=== Publishing ===
+
Firstly, we ask that an indexer running on file <math>f</math> contributes names only for elements belonging to this file, i.e., these elements must belong to set
  
A Plug-in developed for the Rodin Platform will normally consist of a collection of eclipse 'plugin' projects and a single eclipse 'feature' project. The feature project contains branding information such as logo's icons and licensing details. It is also used to identify your Plug-in so that users can install it. To build your Plug-in use an eclipse 'site' project. This will build the jar files for your plugin projects and a jar for your feature. See eclipse documentation for more details.
+
<blockquote><math>
 +
  \mathit{file}^{-1}[\{f\}]
 +
</math>&nbsp;.</blockquote>
  
Now you need to make your Plug-in available for users to install via the Main Rodin Update site (which comes built-in to the Rodin platform).
+
Secondly, the indexer must contribute only occurrences in file <math>f</math>, that is the occurrences provided by an indexer must belong to the set
  
Create a new release folder in the FRS (On Sourceforge Rodin project website - Admin-file releases) noting the naming conventions (e.g. Plugin_<mypluginName>). Now you can upload your jar files using the controls on the releases webpage). Note that you should include a zip of the complete source projects to comply with Sourceforge rules.
+
<blockquote><math>
You should not repeat files that have not changed. The Feature jar will take care of unchanged plugin jars and use the existing links. Only new jars should be included in a particular release.
+
  (\mathit{element}\fcomp\mathit{file})^{-1}[\{f\}]
See here for details:- http://alexandria.wiki.sourceforge.net/File+Release+System+-+Offering+Files+for+Download
+
</math>&nbsp;.</blockquote>
  
Finally, the update site must be updated to redirect the update requests to the files on the FRS.
+
Thirdly, an indexer only contributes occurrences for elements declared in the file on which it is run or exported by a file on which this file depends directly, i.e., the elements for which an indexer contributes an occurrence must belong to the set
# From the sourceforge SVN repository, check out the project org.rodinp.updateSite.
 
# Edit the file site.xml to add your feature and plug-in archive paths ([[Details for Maintaining Main Rodin Update Site]])
 
# Test the changes by performing the install into a Rodin installation, via the local update site in your workspace.
 
# Commit the changes back into SVN
 
# Upload the new version of the update site onto the Rodin webspace ([[Details for Uploading Main Rodin Update Site]]).
 
  
 +
<blockquote><math>
 +
  \mathit{file}^{-1}[\{f\}]\bunion
 +
  (\mathit{dependsOn}\fcomp\mathit{exports})[\{f\}]
 +
</math>&nbsp;.</blockquote>
  
=== Upgrading and maintain ===
+
Finally, an indexer can only export elements for which it can contribute occurrences, as detailed in the previous point.
  
Please note that you should always keep an existing version of your plug-in for the antepenultimate version of Rodin when you want to perform some clean-up of your update site.
 
Indeed, not all the users switch to the latest version of Rodin by the time of its release, and certainly would like to use your plug-ins anyway.
 
  
[[Category:Developer documentation]]
+
===Updating algorithm===
[[Category:Rodin Platform]]
+
 
 +
Given these restrictions, we need to update the index for a file only if
 +
* either the file has changed,
 +
* or the elements visible in the file have changed
 +
since the last time it was indexed.
 +
 
 +
Formally, the last property means that the following set has changed
 +
 
 +
<blockquote><math>
 +
  (\mathit{dependsOn}\fcomp\mathit{exports})[\{f\}]\domres\mathit{names}
 +
</math>&nbsp;.</blockquote>
 +
 
 +
 
 +
==Concurrency==
 +
 
 +
{{TODO|Describe concurrency issues: concurrent access and modifications}}
 +
 
 +
==Persistence==
 +
 
 +
{{TODO|How is the indexed made persistent across sessions?}}
 +
 
 +
[[Category:Design]]

Revision as of 07:39, 5 September 2008

Purpose

The purpose of the Rodin index manager is to store in a uniform way the entities that are declared in the database together with their occurrences. This central repository of declarations and occurrences will allow for fast implementations of various refactoring mechanisms (such as renaming) and support for searching models or browsing them.

Specification

The name table

Firstly, the index manager needs to store declarations. We make the assumption that a declaration corresponds to one and only one element in the database.

For event-B, this assumption is true. For instance, a variable declaration is an IVariable element, an event declaration is an IEvent, etc.

However, the element handle doesn't carry directly the user-defined name for the element. This name is stored in an attribute of the element. Thus, one needs to access to the element in the database to retrieve this name. We therefore want to cache this name in the index to prevent frequent accesses to the database that would hinder performance.

the user-defined name of a variable is stored in its identifier attribute, the name of an event in its label attribute.

So, the first table that the index manager needs to maintain is specified as

\mathit{name}\in\mathit{ELEMENT}\pfun\mathit{STRING} ,

where \mathit{ELEMENT} is the set of all element handles and \mathit{STRING} is the set of all character strings.


The occurrences table

Secondly, the index manager needs to maintain the relation between declarations and occurrences. This relation is specified as

\mathit{occurrences}\in\mathit{ELEMENT}\pfun\mathit{LOCATION} ,

where \mathit{LOCATION} is the set of all locations in the database, a location being either an element, or an attribute of an element, or a substring of an attribute of an element. We also have the following function which relates a location to the element that contains the location

\mathit{element}\in\mathit{LOCATION}\tsur\mathit{ELEMENT} .

However, note that this relation is not maintained by the index manager, it is inherent to the definition of locations.


Populating the Index

Following the general principles of the Rodin core, the Rodin index manager is notation agnostic. Therefore, it cannot populate the index by itself, but delegates this to plug-ins through an extension point. These contributions are called indexers.

Then, the question arises to define the granularity at which these indexers will be declared and work. Having an indexer declared and working on a whole project would be too coarse grain. Conversely, having an indexer declared and working at the granularity of internal elements would be too fine grain. So, it has been decided that indexers are declared for file elements, based on their type. In hindsight, we are taking here the same design decision we took for the event-B tools when defining the granularity of static checking and proof obligation generation.

Moreover, we want to keep the index at least as open as the Rodin database. So, we need to be prepared to accommodate several indexers working on the same file, as the elements contained in a file can be contributed by several plug-ins. In this case, the side question is in which order should these indexers be run. The solution chosen is that any indexer can declare that it should be run after another indexer. In other words, plug-ins can define a partial order among the indexers that will be enforced by the index manager.

Putting everything together, we obtain an extension point for contributing indexers with the following components

  • id: unique identifier of the indexer
  • name: human-readable name of the indexer
  • class: implementation of the indexer
  • type: list of the file element types handled by the indexer
  • after: list of the ids of the indexers that must be run before the indexer


Index Maintenance

Of course, the database will change when users edit their models. We therefore must define a policy for updating the index when the database is modified. Detecting database changes is easy, thanks to the deltas that are generated by the database manager upon modification.

The difficulty here is that there is no restriction on the occurrences of an element: an element can occur in the file where it is declared, but it can also occur in a completely different file.

For instance, in event-B, a constant can occur in the context where it is declared, but also in any context that extends (directly or indirectly) its declaring context.

So, in principle, each time a file changes, we have to re-index all files. This is clearly not acceptable from a performance point of view. In order to keep index updating efficient, we need to better organize the way file changes are propagated in the index. For this, we first need to gather additional information from indexers (like dependencies between files), then we have to enforce some restrictions on the indexes contributed by indexers, so that we can compute the consequences of a file change.

Dependencies

Firstly, we ask the indexers to provide the index manager with the list of files that a given file depends on. This is stored in the table with the following specification

\mathit{dependsOn}\in\mathit{FILE}\rel\mathit{FILE} ,

where \mathit{FILE} is the set of all files.

TODO: What happens if \mathit{dependsOn} contains a cycle?

But having these dependencies is not precise enough, and can still lead to a lot of unnecessary indexing.

For instance, in event-B, if the user adds a theorem to a context on which all files of a project depend (e.g., the context seen by the top-level machine of this project), then we would have to re-index all the files of the project, although it will be useless, as the theorem is not visible to the other files.

Therefore, we also ask that the indexer reports the declarations that are exported (i.e., made visible) by a file to the files that depend on it. This information is stored in the table specified as

\mathit{exports}\in\mathit{FILE}\rel\mathit{ELEMENT} .


Restrictions

Then, to minimize re-indexing, we ask that indexers behave as good citizens and enforce some restrictions on the elements and locations that an indexer can contribute to the index.

To express these constraints, we need a new constant relation that gives the file in which an element is declared

\mathit{file}\in\mathit{ELEMENT}\rel\mathit{FILE} .

This relation is a constant and is inherent to the database structure, it is not maintained by the index manager, only used by it.

Firstly, we ask that an indexer running on file f contributes names only for elements belonging to this file, i.e., these elements must belong to set


  \mathit{file}^{-1}[\{f\}]
 .

Secondly, the indexer must contribute only occurrences in file f, that is the occurrences provided by an indexer must belong to the set


  (\mathit{element}\fcomp\mathit{file})^{-1}[\{f\}]
 .

Thirdly, an indexer only contributes occurrences for elements declared in the file on which it is run or exported by a file on which this file depends directly, i.e., the elements for which an indexer contributes an occurrence must belong to the set


  \mathit{file}^{-1}[\{f\}]\bunion
  (\mathit{dependsOn}\fcomp\mathit{exports})[\{f\}]
 .

Finally, an indexer can only export elements for which it can contribute occurrences, as detailed in the previous point.


Updating algorithm

Given these restrictions, we need to update the index for a file only if

  • either the file has changed,
  • or the elements visible in the file have changed

since the last time it was indexed.

Formally, the last property means that the following set has changed


  (\mathit{dependsOn}\fcomp\mathit{exports})[\{f\}]\domres\mathit{names}
 .


Concurrency

TODO: Describe concurrency issues: concurrent access and modifications

Persistence

TODO: How is the indexed made persistent across sessions?