Quick viewing(Text Mode)

A Programming Language for Software Components Pdfauthor

A Programming Language for Software Components Pdfauthor

A for Software Components

Simon D. Kent, B.App.Sci.(Math), B.Inf.Tech.

March 2010

A dissertation submitted in partial fulfillment of the requirements for the degree Doctor of Philosophy in Computer Science

OUT

Faculty of Science and Technology Queensland University of Technology Brisbane, Australia Copyright Simon D. Kent, MMX. All rights reserved. simon d [email protected]

The author hereby grants permission to the Queensland University of Technology to reproduce and distribute publicly paper and electronic copies of this thesis document in whole or in part. “They say the heat and the flies here can drive a man insane. But you don’t have to believe that, and nor does that bright mauve elephant that just cycled past.”

Terry Pratchett (The Last Continent)

Keywords

Software Components, Programming Languages, Specifications, Interfaces, Modules, State, Re-entrance

Abstract

Component software has many benefits, most notably increased software re-use; however, the software process places heavy burdens on programming language techno- logy, which modern object-oriented programming languages do not address. In particular, software components require specifications that are both sufficiently expressive and suffi- ciently abstract, and, where possible, these specifications should be checked formally by the programming language. This dissertation presents a programming language called Mentok that provides two novel programming language features enabling improved specification of stateful com- ponent roles. Negotiable interfaces are types extended with protocols, and allow specification of changing method availability, including some patterns of out-calls and re-entrance. Type layers are extensions to module signatures that allow specifica- tion of abstract control flow constraints through the interfaces of a component-based application. Development of Mentok’s unique language features included creation of MentokC, the Mentok compiler, and formalization of key properties of Mentok in mini-languages called MentokP and MentokL.

Contents

List of Figures xvii

List of Listings xix

1 Introduction1 1.1 Software Components...... 2 1.1.1 Benefits of Component Software...... 2 1.1.2 Requirements of Component Software...... 3 1.2 Component Specifications...... 5 1.2.1 Component Specification and Programming Languages...... 6 1.3 Research Question...... 6 1.4 Contribution and Approach...... 7 1.5 Structure of the dissertation...... 7

2 Background: Software Components9 2.1 Foundation Technologies...... 9 2.1.1 Object Technology...... 10 2.1.2 Modules...... 14 2.1.3 Cross-Cutting Concerns...... 14 2.1.4 Trust and Safety...... 15 2.2 Industry Component Standards...... 16 2.2.1 Microsoft: COM to .NET...... 17 2.2.2 Java, EJB, J2/Java EE, and Eclipse...... 18 2.2.3 OMG: CORBA to CCM...... 19 2.3 Components and Programming Languages...... 19 2.3.1 Programming languages...... 20 2.4 Components, Specification and Design...... 22 2.4.1 Programming by contract...... 23 2.5 Related Fields...... 25 2.5.1 ADLs and BISLs...... 25 2.5.2 Coordination Languages...... 27 2.5.3 Protocols, Models and Objects...... 28

ix Contents

2.5.4 Model Checking...... 31

3 Scope, Problem and Solution 33 3.1 A Basic Programming Model for Software Components...... 34 3.1.1 Notes on Component Pascal...... 35 3.1.2 Interface Model...... 36 3.1.3 Object Model...... 38 3.1.4 Module Model...... 41 3.2 Problems with the Basic Model: State and Object Consistency..... 42 3.2.1 Static Interface Types: Static Method Availability...... 43 3.2.2 Static Module Signatures: Static Interface Availability...... 49 3.3 Research Question...... 53 3.4 Mentok: Enhanced Specifications...... 53 3.4.1 Negotiable Interfaces: Dynamic Method Availability...... 54 3.4.2 Type Layers: Dynamic Interface Availability...... 60 3.5 Summary...... 65

4 Negotiable Interfaces 67 4.1 Negotiable Interfaces in Mentok...... 68 4.1.1 Negotiable Interface Types...... 69 4.1.2 Objects and Negotiation...... 78 4.1.3 State System...... 84

4.2 A Formal Model of Negotiable Interfaces: MentokP ...... 87 4.2.1 Isabelle and Notation...... 91

4.2.2 MentokP Programs...... 91 4.2.3 Program Well-Formedness...... 94 4.2.4 Type System...... 95 4.2.5 State System...... 98 4.2.6 Operational Semantics...... 102 4.2.7 Results...... 106 4.3 Implementation: Negotiable Interfaces and the Mentok Compiler.... 109 4.3.1 Structure of the Compiler...... 109 4.3.2 Runtime Representations...... 110 4.4 Discussion: Problems and Extensions...... 116 4.4.1 Multiple Behaviours...... 118 4.4.2 Non-determinism...... 120 4.4.3 Reachability and Empty Types...... 121 4.5 Related Work...... 121 4.6 Summary...... 123

x Contents

5 Type Layers 125 5.1 Type Layers in Mentok...... 126 5.1.1 Type and Component Layers...... 127 5.1.2 Composition Model...... 131 5.1.3 Execution model...... 133 5.2 A Formal Model of Type Layers...... 137

5.2.1 MentokL Programs...... 137 5.2.2 Program Well-Formedness...... 138 5.2.3 State System...... 139 5.2.4 Operational Semantics...... 139 5.3 Implementation: Type Layers and the Mentok Compiler...... 140 5.3.1 Structure of the Compiler...... 140 5.3.2 Runtime Representations and Dynamic Checking...... 141 5.4 Discussion: Problems and Extensions...... 143 5.4.1 Reachability...... 144 5.4.2 Recursive Data Structures...... 144 5.5 Related Work...... 145 5.6 Summary...... 146

6 Conclusions and Future Work 147 6.1 Future Work...... 149

A MentokP : A language with negotiable interfaces 151 A.1 Names and Identifiers...... 152

A.2 Types in MentokP ...... 153 A.2.1 Type definitions...... 153 A.2.2 Type tests and functions...... 153 A.3 Primitive Values...... 155 A.3.1 Types of values...... 155 A.3.2 Default values...... 156 A.4 State Declarations...... 157 A.4.1 Declarations...... 157 A.4.2 Valuations...... 157 A.4.3 Well-formedness...... 157 A.4.4 Token Bag partial ordering...... 158

A.5 Expressions and Statements in MentokP ...... 159 A.5.1 Terms...... 160 A.5.2 Predicates and Functions Over Terms...... 161

A.6 Programs in MentokP ...... 165

xi Contents

A.6.1 Method Declarations and Implementations...... 165 A.6.2 Interface Bodies...... 165 A.6.3 Record Bodies...... 166 A.6.4 Programs...... 166 A.6.5 lookups...... 167 A.6.6 Type well-formedness...... 167 A.6.7 Lookup functions...... 168 A.6.8 Negotiable State Well-formedness...... 169 A.7 Type Relationships...... 172 A.7.1 Interface Implementation...... 172 A.7.2 Type widening...... 172 A.7.3 Type results...... 172 A.8 Static Environments...... 174 A.8.1 Environment definitions...... 174 A.8.2 Operations on State Environments...... 174 A.8.3 Environment creation...... 175 A.9 Type System Rules for Expressions and Statements...... 179 A.10 State System Rules for Expressions and Statements...... 182 A.10.1 Stateful parameter checking...... 182 A.10.2 State checking...... 182 A.11 Program Well-Formedness...... 186 A.11.1 Well-formed type lists...... 186 A.11.2 Well-formed field declarations...... 186 A.11.3 Well parameter behaviour lists...... 186 A.11.4 Well-formed method types...... 186 A.11.5 Well-formed method type lists...... 186 A.11.6 Well-formed method declaration lists...... 187 A.11.7 Well-formed interface bodies...... 187 A.11.8 Well formed method bodies...... 187 A.11.9 Well-formed record bodies...... 187 A.11.10Well-formed variable declarations...... 188 A.11.11Well-formed programs...... 188 A.12 Runtime Structures and Operations...... 189 A.12.1 Objects...... 189 A.12.2 Local Bindings...... 190 A.12.3 Operations on local bindings...... 190 A.12.4 Object Store...... 193 A.12.5 Stack Frames...... 193

xii Contents

A.13 Operational Semantics...... 196 A.13.1 Small Step Semantics...... 196 A.13.2 Program Bootstrap...... 199

B Module signatures with type layers 201

C MentokL: A language with type layers 213

C.1 Programs in MentokL ...... 214 C.1.1 Programs...... 214 C.2 Static Environments...... 216 C.3 State System Rules for Expressions and Statements...... 217 C.4 Program Well-Formedness...... 218 C.4.1 Well formed Type Layer decs...... 218 C.4.2 Well-formed programs...... 218 C.5 Runtime Structures...... 219 C.6 Operational Semantics...... 220

Bibliography 221

xiii xiv List of Figures

3.1 Basic interface model...... 37 3.2 Basic object model...... 40 3.3 Basic execution model...... 40 3.4 Module composition model...... 42 3.5 Named buffer and interface contract...... 44 3.6 Tightly bound re-entrance...... 45 3.7 Application structure defined by components...... 50 3.8 Loosely bound re-entrance...... 51 3.9 Type layer application structure...... 62 3.10 Up layer call...... 63 3.11 Cross layer call...... 63

4.1 Syntax for simple negotiable interface declarations (no ).... 71 4.2 IFoo protocol with reentrant behaviour...... 74 4.3 IBar protocol with cooperative behaviour...... 74 4.4 Syntax for declaration a negotiable interface type (with subtyping)... 75 4.5 Protocols for ISizeable, IHost, and IItem (clockwise from upper left).. 77 4.6 Protocol for IHostableContainer...... 78 4.7 Syntax for implementing methods...... 79 4.8 Syntax for USE...... 80 4.9 Negotiation semantics...... 81 4.10 Local negotiation semantics...... 83 4.11 State checking in action...... 88 4.12 IPlay interface...... 118 4.13 IPlay2 interface...... 119

5.1 Type layer diagram legend...... 128 5.2 Syntax for modules with type layers...... 129 5.3 Entering IApplicationRoot...... 134 5.4 Entering IApplication...... 134 5.5 Entering IDataSource...... 134 5.6 Illegal up layer call to IApplication...... 135

xv List of Figures

5.7 Illegal cross layer call to IPostMessage...... 135 5.8 Negotiation semantics with type layers...... 136

B.1 Type layer graph for module Access...... 201 B.2 Type layer graph for module Data...... 202 B.3 Type layer graph for module WidgetData...... 203 B.4 Type layer graph for module Widgets...... 204 B.5 Type layer graph for module App...... 205 B.6 Minimal type layer graph for module App...... 206 B.7 Type layer graph for module ModBar...... 207 B.8 Type layer graph for module ModFoo...... 207 B.9 Type layer graph for module TypeOrderCycle...... 208 B.10 Type layer graph for module ModOrderCycle...... 209 B.11 Type layer graph for module TypeEquateCycle...... 210 B.12 Type layer graph for module ModEquateCycle...... 211

xvi List of Listings

3.1 Basic interface declaration...... 37 3.2 Basic record declaration...... 39 3.3 Illegal use of named buffers...... 44 3.4 Interface specification for IModel and IView...... 47 3.5 Implementation of IModel...... 47 3.6 Implementations of IView...... 48 3.7 Loosely bound re-entrance...... 52 3.8 Negotiable interface specification of named buffer...... 55 3.9 Preventing simple errors with negotiable interfaces...... 55 3.10 Interface specification for IModel and IView...... 57 3.11 Implementation of IModel...... 58 3.12 Implementations of IView...... 59 3.13 Type layer declarations...... 61 3.14 Loosely bound re-entrance...... 64

4.1 A pair of basic negotiable interface declarations...... 72 4.2 Parent interfaces: IHost, IItem, ISizeable...... 77 4.3 Child interface: IHostableContainer...... 78 4.4 Interface protocols for figure 4.11...... 89 4.5 Code state checked by figure 4.11...... 90 4.6 Basic interface declaration...... 111 4.7 Basic interface declaration...... 112 4.8 Basic interface declaration...... 113 4.9 Mentok class declaration...... 113 4.10 Emitted C# for a Mentok class...... 114 4.11 Mentok: negotiation and parameter passing...... 115 4.12 Emitted C#: negotiation and parameter passing...... 115 4.13 Mentok: Cooperative parameters, local negotiation, and method calls.. 117 4.14 Emitted C#: Cooperative parameters, local negotiation, and method calls.117 4.15 Single behaviour IPlay interface...... 118 4.16 Multi-behaviour IPlay2 interface...... 119 4.17 Non-deterministic IPlay3 interface...... 120

xvii List of Listings

4.18 Choice for non-determinism...... 120

5.1 Signature for module LayeredModule ...... 142 5.2 Custom attributes emitted for LayeredModule...... 142 5.3 Body for module LayeredModule ...... 143 5.4 Emitted code for the body of LayeredModule...... 143 5.5 Reachabilty issues with type layers...... 144

B.1 Signature for layered module Access ...... 201 B.2 Signature for layered module Data ...... 202 B.3 Signature for layered module WidgetData ...... 203 B.4 Signature for layered module Widgets ...... 204 B.5 Signature for layered module App ...... 205 B.6 Signature for layered module ModBar ...... 207 B.7 Signature for layered module ModFoo ...... 207 B.8 Signature for layered module TypeOrderCycle ...... 208 B.9 Signature for layered module ModOrderCycle ...... 209 B.10 Signature for layered module TypeEquateCycle ...... 210 B.11 Signature for layered module ModEquateCycle ...... 211

xviii Statement of Original Authorship

The work contained in this thesis has not been previously submitted for a degree or diploma at any other higher education institution. To the best of my knowledge and belief, the thesis contains no material previously published or written by another person except where due reference is made.

Simon D. Kent March 2010

Acknowledgments

I would like to acknowledge and thank my parents for the help and support they have given me over the years. They provided me with every opportunity but always let me find my own way; I can only hope to be as good a parent to my own children. I would also like thank my extended family for their support and encouragement over the years. I was lucky enough to choose a postgraduate career with the Programming Languages and Systems research group at QUT. The members of PLAS, past and present, were and continue to be a huge influence in my life. Thanks must go to everyone who ever comprised my supervision team: Chris Ho–Stuart, for his time as my principal supervisor, Clemens Szyperski, for his inspiration, and especially Paul Roe, who started me on this path and dragged me to the finish line. I am grateful for the love and support of my friends, who provided me with moments of sanity and controlled amounts of insanity during my studies. In the space that I have, I would especially like to mention Jens Tr¨oger,who I met as a new doctoral student and who has since become one of my closest friends, Dan Lane, my oldest and closest friend, Sime Crossley and Bruce Henderson, my crew. I love you guys. I thank my children, Jessica and Michael, for letting Daddy disappear into his office and for always giving him a hug and a smile. I thank my beautiful wife, Elizabeth, for her support, understanding, tolerance, love, and many cups of coffee. I owe my success to her. I dedicate this thesis to my grandparents: my grandma, Phoebe Kent my granddad, Allan Kent, my nanna, Phyllis Massey, and my poppa, Stanley Massey.

1 Introduction

In the Beginning there was nothing, which exploded. (Terry Pratchett, Hogfather)

In the component software paradigm, software applications are built using contractually specified software components possibly supplied by independent component vendors. The move towards a component based methodology can be seen as a natural progression in soft- ware engineering. In more mature engineering disciplines, electronic engineering being the common example, the use of pre-built components speeds production, increases relia- bility, and allows vendors to specialize. Why should the same not be true for software engineering? The software component approach is attractive for technical reasons apart from the appealing notion that software might be “growing up”. Component based software engineering promises software re-use by design. Software re-use is something of a holy grail for software engineering, promised by each successive new technology but never truly delivered by any. Software components, by definition, are re-usable, composable, software abstractions; that if software is built from components, software re-use should be common place. So why then is component software not already a reality? Component software pro- mises much but it makes many technical demands as well. Software components are not simply bits and pieces that are used to build software systems, software components are also units of deployment. In a traditional software development model, an application is designed, built and tested before it is deployed. Software components are designed, built and deployed in parts and then composed after deployment. The implications of this mo- del are profound. A software component must be designed and tested before deployment, while in an “incomplete” form. A software component must be robust enough to work with other software components that may not even exist at the time the component is conceived. A software component must be composable by a third-party who may not have access to, or the skills to comprehend the source code of the component. A soft- ware component must also be trusted without the consumer personally examining the code for security faults and threats. Until very recently, the state of the art in software engineering technology could simply not deliver on these requirements. A cornerstone of software component technology is component specification. Specifica-

1 1 Introduction

tions can promote good design and guide testing. Specification of component interfaces can provide abstraction for clients and is necessary to enable composition by third parties. Components need specifications but modern programming languages commonly used for component construction and composition provide little in the way of formal support for specification. Design by contract has only recently started making inroads into mains- tream languages and it is still common to rely on the type systems of object-oriented languages to specify the interfaces of software components. As a result, component composition is performed using ad hoc configuration, and . This dissertation describes a programming language called Mentok that contains constructs designed to improve the level of component specification provided by modern programming languages. Rather than focus on those purely functional aspects of a com- ponent specifications that are ably captured by traditional programming by contract approaches, Mentok contains extensions to interface types and module signatures to specify and constrain temporal behaviours, such as object re-entrance.

1.1 Software Components

The definition of a software component as used in this dissertation was formulated at the 1996 Workshop on Component Oriented Software: “A software component is a unit of composition with contractually specified interfaces and explicit context dependencies only. A software component can be deployed indepen- dently and is subject to composition by third parties.”

Component software is fundamentally different to traditional monolithic software, since component software is deployed in parts (called components). This paradigm shift is potentially a source of a great many advantages, but also places a much larger burden on software development tools and processes.

1.1.1 Benefits of Component Software

The benefits of component software have been widely discussed (see Szyperski[Szy02] for an in-depth treatment), and can be summarized as follows:

• Software reuse: Reuse has been a critical theme of software engineering for some time - by reusing software, an application builder need not waste time rein- venting logic. Software reuse is implicit in the component software paradigm. In- dependent deployment and third-party composition promote use of pre-built com- ponents, while contractual specification of components enables their re-use under given requirements.

2 1.1 Software Components

• Encapsulation: An application builder does not need to fully understand the internal logic of the component - only the services that the component provides, which should be a higher-level abstraction of the internal logic.

• Late integration: Components can be integrated into a system after the system has been deployed (e.g. web browser plug-ins). This ability is essential for systems that must utilize new and previously unseen data or functionality.

• Flexibility:The application builder may have a range of implementations of a single component to choose from.

• Adaptability: Components may be replaced and upgraded.

• Lower cost of ownership: The component vendor’s business is the component; therefore, the vendor will need to produce high-quality components with good documentation to survive in a competitive market. The component vendor is likely to be a domain expert, and domain experts will likely produce components with fewer bugs than those produced by non-experts. This reduces the vendor’s maintenance costs.

• Reduced time to market: A corollary of many of the above points is that applications are easier to build which reduces production time.

Not surprisingly, many of these benefits follow directly from the definition of a software component, as given above. A critical part of that definition, upon which all other parts of the definition depend, is contractual specification of components.

1.1.2 Requirements of Component Software

Despite being proposed as long ago as 1968[McI68], software component markets have only just begun to emerge fairly recently. This is not because the component software approach is not desirable, but rather a reflection of the serious technical issues that must be addressed for component software to succeed. Component software mandates a completely new and deploy- ment model: component software is built and deployed in parts and is composed by independent third parties. The implications of this simple statement for software engi- neering are profound, some of which are listed below:

• Software is deployed in an incomplete form. Component deployment occurs before composition into final applications, meaning that software components need to be tested for robustness before they are composed. Furthermore, the standards for robustness are much higher for software components. It is not sufficient for a

3 1 Introduction

component to work in one specific context, as in a monolithic application, but for any context where the component is legally composable.

• Applications are built from higher abstractions. Third party composition implies that the application builder may not be the component builder and there- fore has no a priori knowledge of how the component is implemented. For third party composition to be more effective than simple source code reuse, third par- ties should be able to compose components without knowing how the component is implemented. Software components, therefore, must provide abstraction from implementation but still be usable; languages and tools must support software component abstractions. 1

• Applications contain code from several independent sources. Compo- nents are composed by third-parties, implying that the final application will exe- cute code not written by the application builder. Software engineering process and tools need to provide a model of trust, not only for correctness, but for issues of safety and security.

Add to these “big picture” concerns the many technical requirements of component software, such as a late binding mechanism for extensibility, separate compilation for component substitution, type and module safety for “wiring” components together, and versioning schemes for application life cycle management, and it is little surprise that component software has only recently become a possibility. Even less of a surprise is that most of the enabling advances for software components have come in the field of programming language technology. Programming languages provide a means for delivering powerful abstractions and formal techniques in a form that is both readily available and understandable to software engineers. Module technology, object technology, context-based proxy frameworks, and verifiable type-safety are all vital parts of today’s industrial component frameworks. It is also true, however, that programming languages do not capture all the require- ments of component software. Software components needs specification, and, for these specifications to be useful, they must be expressed and enforced formally, where pos- sible. Programming languages must, therefore, provide abstraction for expressing and consuming component specifications.

1 White-box reuse also prevents easy replacement of components, thus reducing adaptability and flexibility of component applications.

4 1.2 Component Specifications

1.2 Component Specifications

Software components provide abstractions; component vendors build the abstractions and component consumers (application builders) use the abstractions. The abstraction boundary between vendor and consumer is the component specification. Specifications communicate the encapsulated logic of a component to a consumer by describing the services that a component provides without describing the internal workings of the component. Specifications should also communicate the requirements of a component on its environment in order for the component to function safely. Without adequate specifications, third-party composition of components is not practically achievable. Discussion in the literature about what should and should not be in a component specification can be summarized by the following points:

• Specifications must be contractual. For specifications to be useful in a world with third-party composition, specification must be more than a description of a component, but also a binding agreement between the consumer and provider about the obligations of each party. Contractual specifications can serve as a point of agreement in a technical sense, as well as a legal or business sense as well.

• Specifications should be sufficiently formal. Informal specifications are use- ful when providing a description of a component’s services and requirements, but do not provide any guarantees since they cannot be meaningfully checked. For- mal specifications are preferable when it is practical for the specification to be checked[Szy02]. Furthermore, specifications which provide safety guarantee are preferable over those which can only be checked.

• Specifications must be sufficiently expressive. Component specifications should contain functional requirements, provided and required services, progress conditions, dependencies on external resources, and presence or absence of out- calls, as well as non-functional requirements, such as bounds on execution time and space[Szy02].

• Specifications must be sufficiently abstract. Component specifications define equivalence classes for components with respect to substitution. A specification that is too abstract may define an equivalence class with members that are actually incompatible with respect to substitution; such a specification does not provide enough information to enable correct composition for all the members of the equi- valence class. A specification that is too concrete defines an equivalence class with fewer members than could actually be applied in the same context; such a specifi- cation provides too much implementation detail about its component thus overly limiting substitution. White-box (source code) specifications are too concrete as

5 1 Introduction

they prohibit substitution, while black-box specifications of only interfaces and progress conditions are too abstract[BW97].

There are many approaches to specifying components; at the time of writing, howe- ver, there is no widely accepted or adopted approach for contractual specification of components that also meets the requirements of component specification. It is particu- larly noteworthy that most modern programming languages used for construction of components in industry lack any specification mechanism beyond the wiring safety of object-oriented type systems.

1.2.1 Component Specification and Programming Languages

Component specifications are most effective when expressed in and checked by program- ming languages and systems. Use of types and signatures as the basis for component spe- cification helps ensure abstractness. Interface types serve as a unit of specification for individual component roles, while module or component signatures specify composition and replaceability constraints. Static and dynamic checking of elements of component specifications in a languages and runtime system helps satisfy the formal and contrac- tual requirements by guaranteeing or restricting certain properties and behaviours. Most modern programming languages do not satisfy the final requirement of com- ponent specification: expressiveness. Requirements of extensibility, such as discove- rability and polymorphism mean that most component systems are object-based or object-like, but the static type systems of object-oriented languages cannot express tem- poral properties of objects. Progress conditions or software contracts are excellent at capturing purely functional aspects of specification but cannot easily express temporal or intermediate properties of object systems. Finally, interface types and module signa- tures when taken as a whole almost completely fail to capture application architectures or layering.

1.3 Research Question

Components need specification. Programming languages, the primary tool of a program- mer, are a natural vehicles for writing, implementing and checking component specifi- cations. The author argues that the state of the art of component specification in programming languages can be improved upon; the lack of progress towards component software has been attributed elsewhere to shortcomings in the programming languages used to define and integrate components[OZ05]. One particular area of component specification that is largely unaddressed is that of component re-entrance conditions. Szyperski[Szy02] states: “the specification problems

6 1.4 Contribution and Approach encountered in recursive re-entrant systems need to be solved in a modular way to cater for components”. This leads to the research question for this dissertation: Can a component-oriented programming language be extended with constructs that permit specification of re-entrance conditions for components? The component specifications devised should also satisfy the criteria outlined in sec- tion 1.2 above and be contractual, formal, sufficiently expressive and sufficiently abstract.

1.4 Contribution and Approach

This dissertation presents an object-based language called Mentok that permits specifi- cation of re-entrance conditions for components. Mentok is an extension of Component Pascal[Obe97] and builds upon prior research, specifically the investigation of problems of object re-entrance in components by Szyperski[Szy02], and the use of factorable mul- tisets for specification of component interfaces by Puntigam[PP99]. The original research contribution of this dissertation is the creation of two novel programming language features that enable specification of re-entrance conditions for components. Negotiable interfaces extend regular interface types with multiset protocols that allow specification of out-calls and re-entrance patterns. Type layers are extensions to module signatures that allow specification of the abstract control-flow of a component application by means of a partial order over negotiable interfaces; this abstract control- flow can constrain re-entrance across loosely-bound interfaces. The language features have been fully implemented as part of a language called Men- tok. The Mentok compiler, MentokC, was created by extending the Gardens Point Component Pascal (GPCP) compiler[GRC01] and all code samples presented in this dissertation have been compiled and executed using MentokC. Two subsets of Mentok,

MentokP and MentokL, have been created and formalized using Isabelle/HOL[NPW00].

MentokP and MentokL were created to investigate safety properties of negotiable inter- faces and type layers and are not intended to be usable programming languages in their own right.

1.5 Structure of the dissertation

Chapter2 gives a broad overview of background technologies, and related work with reference to its significance to component technology. Readers already familiar with this work can skip to later chapters. Chapter3 describes the target problems motivating this dissertation, particularly the need for specification of stateful components.

7 1 Introduction

Chapter4 presents negotiable interfaces in Mentok, and gives an overview of MentokP , a formalized subset of Mentok, which is presented in AppendixA.

Chapter5 describes type layers in Mentok, and presents MentokL, a subset of Mentok with negotiable interfaces and type layers. Chapter5 builds upon the descriptions of chapter4. Finally, chapter6 gives conclusions and directions for future work.

8 2 Background: Software Components

Everything starts somewhere, though many physicists disagree. But people have always been dimly aware of the problem with the start of things. They wonder how the snowplough driver gets to work, or how the makers of dictionaries look up the spelling of words. (Terry Pratchett, Hogfather)

The concept of software components was first raised by McIlroy [McI68]. McIlroy’s described a market in which programmers build software using commercially available software components built by the vendors of the software component industry. Over 30 years later at the time of this dissertation’s writing, component methodologies, fra- meworks and markets have finally emerged, however, McIlroy’s vision has still not been fully realized. This fact alone gives an indication of the enormity of the advances that were required to enable software technology. To illustrate this point, one needs only to examine one of the modern component frame- works and the technology it is built upon. Sun’s Java and Microsoft’s .NET platforms of- fer reasonably similar component models in EJB/J2EE, and configured .NET assemblies, both of which are mainly targeted at enterprise business solutions. At a glance, both plat- forms utilize language technology involving strong typing, object and module technology, separate compilation, reflection, dynamic loading, verification, versioning, and various libraries, services and abstractions for controlling concurrency, participating in transac- tions, invoking services on objects in other processes or physical locations, and foreign and legacy code interoperability. Under the hood of these frameworks is heavy use of the services offered by modern operating systems. This ignores the exponential growth in hardware speed and storage that have enabled software architects and engineers to build such enormously complex pieces of software.

2.1 Foundation Technologies

The definition of a software component used in this dissertation, as given in chapter1, is as follows:

9 2 Background: Software Components

“A software component is a unit of composition with contractually specified interfaces and explicit context dependencies only. A software component can be deployed indepen- dently and is subject to composition by third parties.” – WCOOP 1996 It is worth noting here that a software component, as a deployable software entity, is an executable piece of software; a software component is not a class, object, or module. That said, software components can be and are implemented using such programming language abstractions. Modules or packages are often compiled into deployable binaries, object-oriented interfaces are used as the basis of component specifications, classes are a convenient abstraction for units of implementation within components, and objects are a unit of instantiation. The following section describes foundation technologies which have contributed to the emergence of component software.

2.1.1 Object Technology

While software components should not be tied to any particular programming paradigm1, most component-oriented programming is done using object-oriented programming lan- guages. While it is true that object-oriented languages are currently the languages of choice for many types of programming, it is also true that object technology has been an enabling technology for software components. Object-oriented concepts like polymor- phism and dynamic binding can be used to program extensible component systems but the process of doing so is not defined by object-oriented languages[FF01b]. Simula I was the first programming language to use the concept of objects, which were data structures with associated operators[Dah02], but it was not until the creation of C++[Str97] that object–oriented programming became widespread. Since then, object- oriented programming has only increased in popularity and modern object-oriented pro- gramming languages such as Java[GJSB05] and C#[Mic00], are being used for increasin- gly more varied tasks, from enterprise systems to embedded devices. Classes and objects are the key concepts of object-oriented programming. Classes are the static specification of objects, and describe the data and operations, called methods, of objects of that class. Classes provide abstraction and encapsulation by separating the class’ external interface, which is accessible to other parts of a program, from the internal details of the class. JavaScript is a notable exception; it has a prototype model of objects rather than classes. Objects are usually accessed by means of a reference or pointer to a memory location. This enables polymorphism and simplifies certain programming tasks (such as dri- ven programming), it also complicates matters significantly. An object may be referenced, or aliased, from many different parts of a program and may be modified any time an alias is touched. In the absence of alias protection, reasoning about object based sys-

1 Unless it is the component-oriented paradigm!

10 2.1 Foundation Technologies tems is limited. The matter is complicated even further by polymorphism introduced by inheritance.

2.1.1.1 Inheritance

From Simula-67 onwards, all object-oriented languages have a notion of inheritance. There are many forms of inheritance, but broadly speaking, inheritance is used to design relations between classes in object-based systems, by indicating substitutability, refining specifications, or refining classes. In languages where inheritance implies substitutability (polymorphism) dynamic bin- ding is used to determine which code to execute when any of an object’s virtual methods are invoked. Dynamic binding involves looking up the class of the object at runtime to find where the code for the particular method is. In particular, dynamic binding is widely used with interfaces (fully abstract classes) in component object models and is an enabling technology for late-binding. Interface inheritance is used to specify that a new class will implement the interface or contract of an existing base class or interface. Interface inheritance usually implies that the new class will be substitutable for the parent class or interface, in which case it forms the basis for subtyping or subtype polymorphism. Theoretical bases for object- oriented subtyping can be found in examinations of extensible record calculi as given by Cardelli[Car91]. Pierce and Turner[PT94] give a type-theoretical basis for object- oriented subtyping by using existential types to encode purely functional objects. Implementation inheritance, or subclassing, is used to create a new class using the interface and implementation of an old class; the new subclass inherits the code from its parent class, and may add new methods or override the virtual methods of the parent class. In most languages, 80 being an exception, subclassing requires that an object of the subclass is substitutable for an object of the parent class. Mitchell et al[MHF93] present an untyped lamba calculus extended with primitives for defining objects, extending objects with new methods, replacing existing methods on existing objects and sending messages. Mitchell et al then develop a type system that allows methods to be specialized as they are inherited. Inheritance can further be broken down into single and , for both interface and implementation inheritance. Single inheritance schemes lead to simple acyclic class hierarchies. Multiple inheritance schemes allow for more complex class hierarchies and more polymorphism, but have to deal with a number of syntactic and semantic problems. Languages with multiple interface inheritance must handle namespace clashes when parent interfaces have methods with conflicting names or signatures. Languages that target Microsoft’s CLR, such as C# or Visual Basic .NET, handle namespace clashes

11 2 Background: Software Components

by allowing classes to explicitly implement interface methods, which takes them out of the namespace of the class. Java currently cannot deal with namespace clashes. Languages with multiple implementation inheritance must handle all the problems of multiple interface inheritance as well as handle internal data field namespace clashes, and the so-called diamond inheritance problem. Multiple implementation inheritance is currently out of favour with industry due to the perceived complexity it introduces, although the restricted form that Eiffel[Mey92] provides removes some of these complexi- ties.

2.1.1.2 Interfaces

In one sense, interfaces can be seen as fully abstract classes. This makes interfaces an ideal language level entity for specification of a role or behaviour for a class. Inter- faces are especially important in component-oriented programming as they serve as an implementation independent representation of a role in a component-based system. In- terfaces are therefore an important enabling technology for re-usability, substitutability, and extensibility in component oriented systems. In modern, object-oriented languages that support multiple implementation inheri- tance, interfaces are usually not disambiguated from fully abstract classes, since there is no real semantic difference between them. In languages with single implementation inheritance, interfaces are differentiated from fully abstract classes to enable multiple interface inheritance. Multiple interface inheritance allows enhanced polymorphism by allowing a class to implement many roles, but avoid many of the semantic complexities of multiple implementation inheritance.

2.1.1.3 Inheritance vs. composition

Inheritance as a code reuse technique has been questioned numerous times for varying reasons[Sny86, TGP89, RL89, Mag91, Szy02], and while many of the earliest worries have since been solved by advances in object-oriented languages, technical and practical issues still remain. Taenzer et al[TGP89] compared inheritance with construction for purposes of software reuse in Objective–C. While they conclude that inheritance can reduce the amount of code written, it can also lead to confusing errors when subclassed objects send messages to themselves, leading to self-interference patterns up and down the class hierarchy. They call this the “yo-yo” pattern. Furthermore, some of these problems could not be fixed without breaking encapsulation and inspecting the code of the parent classes. Szyperski[Szy02] expands on the yo-yo pattern problem further and also shows how recursive or re-entrant patterns of method calls are difficult to understand and can break contracts in subtle ways. Szyperski goes on to describe one of the biggest problems with

12 2.1 Foundation Technologies inheritance, the so-called semantic fragile base-class problem 2 The semantic fragile base- class problem occurs when the implementation of a base class changes in such a way as to break its subclasses. Small changes in self-interfering behaviour can easily be breaking changes for subclasses. Mikhajlov and Sekerinksi [MS97] examine the semantic fragile base-class problem and propose a of constraints to help avoid it; a much simpler solution is to avoid inhe- ritance except in constrained circumstances and favour a construction or composition approach. Composition approaches avoid self-interference patterns, and help prevent de- gradation of abstraction layering in object based systems. Revisiting the early empirical study of Taenzer et al[TGP89], it was noted that where inheritance based reuse often became more complex as the base class increased in size, and that the programmer was often forced to break encapsulation of the base class to fix bugs, construction of a new class using composition did not become more complex, and could be completed without breaking encapsulation to examine the contained class’ code.

2.1.1.4 Mixins

Mixins [BC90] have been proposed as a language concept to aid component-oriented programming [Fla99, Sma99, OZ05]. Mixins are like an interface with an accompanying implementation, and a class inheriting a mixin inherits the mixin’s methods and imple- mentations. Mixins are generally not complete classes, however, and should not be instantiated; rather they are class-to-class transformations, something like functors for modules. Flatt et al show in [FKF98] how extending Java with mixins can increase code reuse in a language with single inheritance, while avoiding some of the problems encountered in multiple inheritance. However, the use of mixins in component-oriented programming is a questionable one, since the use of implementation inheritance means that the problems of the fragile semantic base class and self-interference both apply. Reuse with mixins is usually limited up to compilation since most languages do not allow the inheritance hierarchy to change at runtime. In this sense mixins are just an extension to the type hierarchy and do not enable extensibility or late-binding. XOTcl is one language that allows mixins to be added into a type hierarchy, or into the type of an object at runtime. XOTCL even allows the use of per-object mixins[NZ99] . Per-object mixins may be applied to an object dynamically to give an object access to several supplemental classes. Per-object mixins are inserted into a method chain and can be used in a somewhat similar fashion to composition filters or aspects [GNZ00]. Scala[Ode06] is an object-oriented, functional language that provides mixin functiona-

2 The syntactic fragile base-class problem, while still potentially a problem, is easier to avoid and generally not a problem in mature class libraries.

13 2 Background: Software Components

lity. Traits in Scala are abstract classes that are used as mixins for class composition. Odersky and Zenger[OZ05] show use of mixin composition in the Scala compiler as a case study of language abstractions for extensible and scalable component programming.

2.1.2 Modules

Modules were proposed by Parnas[Par72] as a means of writing specifications for parts of programs, such that other software might use the specified part without any additional information. The defining characteristic of a module is the separation of interface from implementation; this permit separate compilation. Modules may encapsulate several abstractions, such as classes or ADTs, and generally cannot be instantiated.

2.1.3 Cross-Cutting Concerns

Traditional hierarchical modular design seeks to achieve separation of concerns where possible for purposes of abstraction, encapsulation and maintainability. In many systems, there are certain functions or capabilities that cannot be easily factored into traditional hierarchical modular design, and instead cut across several modules, reducing abstraction, and potential for reuse. Cross cutting concerns (CCCs) can also seriously affect composa- blity of software components when components from independent third parties attempt to provide or use services of a CCC. Classic examples of CCCs are system aspects such as concurrency control, transactional support and logging, where often the entire system needs to be inspected to understand or debug this functionality. Aspect Oriented Programming (AOP) [KLM+97] is a design and programming metho- dology that, when combined with an AOP language such as AspectJTM , allows code from cross cutting concerns to be modularized in a programming language construct called an aspect. Aspects contain all the code necessary to implement the functionality of the cross cutting concern, along with information on where the aspect code needs to be woven into the normal modules or classes. Compile-time weaving of cross-cutting concerns is not suitable for component based applications; this would require independent component vendors to use the exact same aspect code for their components in such a way that all components interoperate. A more suitable approach is to use a run-time or interception technique such as composition filters [AT98] or context-based techniques provided by the major component frameworks. Interception techniques work by intercepting method calls or returns to objects (e.g. via lightweight proxies) based on some sort of policy. Once a method call or return has been intercepted, the interception framework can execute the required code to implement the CCC. Interception techniques are less efficient than compile-time weaving, but have the advantage of being configurable without recompilation. Interception techniques are

14 2.1 Foundation Technologies ideal for component application builders who need to configure concurrency control or transaction participation of commercial components. Context based interception frameworks group objects according to policies and enforce those policies whenever a policy (or context) boundary is crossed. The major component frameworks implement most of their component services through context-based intercep- tion techniques. The .NET framework was the first framework to open the interception framework to allow programmers to develop their own contexts. One major problem with AOP and other approaches for modularizing cross cutting concerns is the lack of understanding of what happens when aspects or CCCs are compo- sed. This problem is especially problematic in AOP languages that totally separate the aspect code from the normal code and allow more than one aspect to be woven in at a particular location without warning. Monads [Mog91] have been successfully used in the pure functional programming lan- guage Haskell to model computations with side-effects such as I/O. De Meuter [De 97] noted the similarities between monads in functional languages, which allow modulari- zation of previously non-functional behaviours such as I/O, and aspects. Monads do not suffer from feature composition problems in the same manner as aspects in AOP languages, since the order and location of where monads are applied is visible in the type system of functional languages.

2.1.4 Trust and Safety

In a component-oriented software market, establishing trust between component ven- dors and consumers is vital. While contractual specifications should enable builders of component systems to construct component systems, consumers also need prima facie guarantees that a component will not cause certain errors or access privileged resources. Szyperski [Szy02] notes that trust is a matter of reducing the known to the unknown in a trusted way. Most trust approaches seek to shift the focus of trust from the code distributor to a third party. A simple, but na¨ıve approach to establishing trust is in the use of digital signatures or certificates. Signed components come with a digital signature or certificate that is verified using cryptology and/or the help of a third-party. A consumer needs to trust the signing or certificate process, and also the vendor signing the component. Sandboxing is another technique by which components are instantiated in an environ- ment with restricted privileges. Any attempt by a sand-boxed component instance to access a privileges resource is intercepted and prevented by the environment, usually throwing an error in the process. Only the execution environment and the sandbox policy need to be trusted to use such components. A more formal approach to trust is ensuring safety properties. A binary that is

15 2 Background: Software Components

provably safe in some fashion is guaranteed to abstain from potentially trust-breaking operations. Platforms such as Java and Microsoft’s .NET use verification of type safety to ensure that binaries pass strong type checking, and as a result, abstain from interfering with certain memory regions. Proof carrying code (PCC)[Nec97] is a technique in which along with any code, soft- ware vendors also distribute a safety proof that confirms that the code adheres to some safety policy. The code consumer then needs only to validate the safety proof using a checking algorithm and, assuming the proof is validated, the consumer may be confident that the consumer adheres to the safety policy. With PCC, the consumer needs only to trust the correctness of the checking algorithm to establish trust. A major problem with PCC is that the proofs themselves can be very large compared to the programs they accompany. This is because the code they prove safe is usually machine code, and at a low abstraction level.[Fra03] Code verifiers check the intermediate form of virtual machines, such as the JVM [LY99], to ensure that the semantic gap between the source language and the intermediate form is not exploited[Fra03]. Verification can ensure that a program in an intermediate form is type safe, has legal control flow, and assigns legal values to all variables before they are first used. As such, verifiers can ensure (or at least make it highly likely) that privileged memory is not read or overwritten by rogue components. The focus of trust is shifted from the vendor to the runtime and verifier. Managed exe- cution environments also provide further guarantees by providing bounds checking, and checking caller contexts for privileged library operations. Programs written in an inherently safe code format [ADvRF01, HSF02] do not need to be checked for certain kinds of errors; the code format itself can only express “legal” programs. Inherently safe code formats are based on compressed abstract syntax trees, and are much simpler to verify than typed intermediate codes, but are much more memory intensive on the client side. [Fra03]

2.2 Industry Component Standards

It was not until the early 1990’s that the first widespread use of binary components occur- red. The appearance of Rapid Application Development (RAD) environments, such as Microsoft’s Visual Basic and Borland’s Delphi, allowed programmers to “drag and drop” visual components on to window “forms” to create window applications. Components for such environments could be authored and deployed in an incomplete form, and then composed into applications after deployment. Industry component standards arose soon after, defining models for building and composing non-visual components, to implement

16 2.2 Industry Component Standards business logic and middleware. 3

2.2.1 Microsoft: COM to .NET

Microsoft’s (COM) specifies a wiring standard and deploy- ment model for binary components. Microsoft’s COM is language independent but platform specific, with the only wide-used implementation of COM being in the Win- dows line of operating systems. COM provides a reflection mechanism and allowed polymorphism by allowing a single COM class to implement multiple interfaces. COM was so successful it is now used pervasively throughout the Windows operating systems and the implementation of the CLR. Microsoft’s COM was the first official standard to enforce a freezing policy on publi- shed interfaces. That is, once a COM interface is published, it may not change. New versions of the a published interface may inherit from the old to add new functionality, but if a method name or signature needed to be changed or even removed, an entirely new interface would need to be released. While this ensured COM largely side stepped ver- sioning problems due to changing interfaces, there were still some instances of different versions of components working or not working with different applications. Microsoft’s Distributed COM (DCOM) prescribes an RPC model for COM. DCOM was more often used for RPC on LANs, as opposed to over the Internet for grid computing. COM’s biggest problem was that it was a native binary standard. COM components could run code natively without verification. While this allowed many compilers and frameworks to generate COM components, it does not prescribe that components should be verifiably type safe. “ActiveX” is a branding of COM components for browser plugins and has been vastly successful as a mechanism for distributing binary components in Internet Explorer. The fact that ActiveX components are unverifiable native binaries has proven problematic. Even with a signed certificate model of trust, many ActiveX addins for Internet Explorer are malicious, and ActiveX installations are a prime source of spyware and adware. COM+ is Microsoft’s branding for COM plus enterprise services, and was and is used largely for middleware. COM+ uses what is essentially the same object model as COM, but added an advances interception framework based on contexts, to deliver services such as concurrency control, automatic transactions, JIT activation, etc. as well as loosely bound events and object pooling. COM+ has a more advanced deployment mechanism than COM, with components being deployed as part of COM+ Applications and configured by system administrators using the COM+ Catalog. .NET radically changed Microsoft’s component strategy largely deprecating COM for

3 While it can be argued that applications for operating systems were the first type of software components, such applications were complete and stand alone and not used as parts of other software.

17 2 Background: Software Components

a number of uses, although COM still lives on in many forms. The .NET Common Language Runtime (CLR) followed the example of the JVM in providing a virtual machine with an intermediate executable format that is verifiably type safe. Unlike the JVM, which is designed exclusively for Java (the language), the CLR is touted as a multi-language platform following Microsoft’s plan of single platform, multi-language development. The intermediate language used by the CLR is Common Intermediate Language (CIL) which is strongly typed and object oriented. CIL is compiled to Microsoft’s Portable Executable (PE) form which is then JIT compiled before execution. .NET executables are deployed in the form of “assemblies”, which are DLLs or EXEs with rich metadata describing deployment, version and type information. The COM object model has gone but the COM+ services remain as part of Microsoft’s Enterprise services, and component’s built using these services can be used as part of COM+ Applications. .NET provides a much improved versioning mechanism, which even allows side-by- side execution of different versions of an assembly. This is a vast improvement over all other versioning schemes provided by platforms, largely solving many of the “DLL hell” problems that were so prevalent with Windows. It is worth noting that even as of version 2.0 of .NET, Microsoft itself has encountered version problems with the framework itself, requiring the entire platform to be versioned as a whole, rather than in a componentized, per-DLL fashion. At the time of writing, Microsoft is finalizing a release of the WinFX set of technologies, which includes the Windows Presentation Foundation (code name Avalon), Windows Workflow Foundation, and Windows Communication Foundation (code name Indigo). The WinFX platform is largely an object-oriented set of class libraries, and each new technology provides new object based abstractions (visual trees, workflows, and commu- nication libraries respectively) and application models. WinFX does not prescribe a single application model or composition story above the model of assemblies provided by the .NET platform.

2.2.2 Java, EJB, J2/Java EE, and Eclipse

The Java platform revolutionized web browsing with Java applets and the Java Virtual Machine (JVM). The Java applet model offered many advantages over binary component deployment models, such as ActiveX, as verifiable type safety prevents applets from ac- cessing privileged memory and the JVM can provide fine-grained security control via sandboxing. The Java approach relied on type safety of the Java language [GJSB05], de- monstrating that formal properties of programming languages can have useful application, without being taxing on software developers. The JavaBeans[Sun96] specification was Sun’s first attempt to provide a component

18 2.3 Components and Programming Languages model for GUI widgets in Java. The JavaBeans specification is somewhat ad hoc, as JavaBeans must follow certain naming conventions and implement certain interfaces in order to function correctly. The Enterprise Java Beans (EJB) specification introduced a component model and framework for writing components for enterprise applications. Like JavaBeans, the EJB programming model is still largely based around the implementation of certain interfaces and following certain naming conventions. Enterprise Java Beans are hosted inside an EJB Container, which provides the interception framework and runtime services for each Bean instance. While COM+ prescribes a stateless model for transactional components, EJB differentiates between stateless and stateful components, and allows a component instance to represent some state of a participant in a transaction. EJB uses a passivation/activation serialization scheme to handle the lifetime requirements of stateful beans. The Java Platform Enterprise Edition is the latest incarnation of the Java platform. Based on the Java language, the Java EE is a programming platform for n-tiered applica- tions running in distributed environments. The Java EE defines multiple component-like abstractions, including servlets, portlets, Enterprise Java Beans, and JavaServer Pages.

2.2.3 OMG: CORBA to CCM

The Common Object Request Broker Architecture (CORBA) was created by the to enable interoperation between applications running in heteroge- neous environments. CORBA is language independent, and uses the OMG Interface Definition Language (IDL) to specify the interfaces between interoperating applications. IDL is compiled to stubs and skeletons that ensure calls between clients and objects go through an Object Request Broker (ORB). Historically, CORBA can be seen as the first real wiring standard remote objects from different languages or architectures, but CORBA did not provide a deployment model for software entities until the CORBA Component Model (CCM)[OMG02] in CORBA 3. The CCM is an application framework for CORBA components, and is in many ways a language independent superset of the EJB specification. The CCM prescribes a similar model of containers and objects as EJB, with containers providing an implementation of various component services such as transaction management and concurrency control.

2.3 Components and Programming Languages

Fr¨olich and Franz[FF01b] note that there is broad agreement that “component-oriented programming is good”, but there is much less agreement on “what component-oriented

19 2 Background: Software Components

programming is” and certainly none on “how to do component-oriented” programming.4 Much of this agreement stems from the fact that there is lack of understanding of software components, let alone how to program for and with components; what is clear is that objects and classes, while providing a foundation for component-based programming, are not sufficient by themselves. Advances in programming language systems, such as verifiable type safety and auto- matic garbage collection, have brought about increases in safety and trust, as well as productivity and reliability in component systems. The advent of standards for web services and interoperation in distributed environments has enabled deployment of large, n-tiered componentized applications. The programming languages used to build such systems, typically C# and Java, are still largely object-oriented, and use object-oriented types, such as interfaces and abstract classes, as the basis of specification.

2.3.1 Programming languages

There have been many attempts and suggestions at what a component-oriented language should contain. It is widely accepted that interfaces should be an essential part of a component-oriented language, but there are some problems with type systems of modern object oriented languages that cause problems in component based programming. B¨uchi and Weck make a case for extending the type system of Java to include com- pound interface types. [BW98] Compound interface types are a mixture of structural and name equivalence: structural equivalence is used for checking the set of interfaces that a class implements, but name equivalence is used for checking the types of the individual interfaces. B¨uchi and Weck argue that compound types are necessary in a component-oriented world, where independent vendors may aggregate similar sets of interfaces under different names. Fr¨olich and Franz[FF99] note that modern object-oriented languages such as Java, are unsuitable for component-oriented programming, since they do not adequately support implementation of multiple interfaces in the one class. The problems they describe arise due to way Java merges all methods of parent interfaces into the one class or sub inter- face. Syntactic incompatibility arises when two parent interfaces have methods with incompatible signatures.5 Semantic incompatibility arises when two parent interfaces have methods with the same signature but different contracts. Fr¨olich and Franz de- tach messages from interfaces and instead declare messages at the module level in their Oberon-like language, Lagoona. This means that messages are must be fully qualified by their module name when being called or implemented, disambiguating messages that would otherwise have been incompatible. C#, and the extended Component Pascal

4 A reinterpretation on Leveson’s [Lev86] observation on the why, what and how of software safety. 5 Same method name, argument number and types, but different return type

20 2.3 Components and Programming Languages language used in this thesis achieve much the same thing by allowing interface methods to be explicitly implemented. Explicit implementation removes the method from the namespace of the implementing class, meaning that the interface methods can only be called from reference to an interface (requiring a cast first).

2.3.1.1 Composition Languages and Models

Traditional object-oriented programming languages do not provide good abstract for the common composition operations performed in component based programming. Instantia- tion, event registration, and interface plugging are all usually performed in the logic of programming languages, via methods calls and field assignments. Composition languages seek to make component composition simpler by providing abstractions for components and common operations on components. Some are declara- tive, some treat component as first class entities, and most are closed under composition operations (a composition of components is a component). Composition languages are generally not intended to replace traditional programming languages but assume that atomic components have been created by some other language and conform to a known executable standard. Most composition languages lend themselves well to diagrammatic representation, some of which have formal interpretation (e.g. the category-theoretic diagrams in [FLW03]). It is worth noting that the confusion between components and object is perpetuated in many of these approaches as the languages developed are actually object-composition lan- guages, that use composition techniques regarded as suitable for component-development. Nierstrasz and Meijler[NM95] give several requirements for a composition language. A composition language should:

• treat components as composable, possibly first class abstractions;

• have a standard object model;

• view objects as processes (either active or passive);

• have a type system that can express objects, components and plug compatibility;

• and, finally, be scalable from small to large systems.

Piccola[ALSN00] is a composition language targeting the paradigm of “Applications = Components + Scripts”. Components are viewed as black boxes, with plugs or interfaces for provided and required services, and scripts for “glue” that govern how components interact when adaptation is required. Piccola is based on the πL–calculus and so focuses describing object interactions, rather than static class hierarchies like traditional object- oriented languages.

21 2 Background: Software Components

Bean Markup Language (BML) [CWD00] is an XML-based declarative composition language for JavaBeans. BML provides operations for aggregation (containment), bin- ding events, macro expansion of templates, and recursive composition. BML also allows the inclusion of glue code in compositions to handle compositional mismatch. BML is not closed under composition. The model developed by Costa Seco and Caires in [SC00a] also provides support for containment, aggregation and forwarding. Costa Seco extend their model in [SC00b] with genericity by introducing parameterized component types which allow type informa- tion about composed components to be propagated. The calculus and type system of [Zen02b] provides primitives for forwarding, contain- ment and aggregation. While Zenger’s language extends Featherweight Java, his com- position primitives and specifications are largely orthogonal from the language. Zenger focuses on refinement of the structure of components by allowing any component defi- nition to be refined, or used a prototype, for new components. Zenger also gives a diagrammatic representation for his language extensions to demonstrate component com- position.

2.4 Components, Specification and Design

Third-party composition places a high demand on component specifications. A com- ponent specification must be detailed enough to allow a component to be correctly and usefully composed, but abstract enough to allow a component to be replaced, or used in different contexts. Component specifications need to be contractual in order to ensure quality, and protect component consumers and end-users.[Szy02] Most researchers agree that component contracts should be formally checkable, whene- ver possible. Contracts that can be checked or enforced formally promote quality before a component is deployed. Formal methods for components include (but are not limited to) refinement of specifications, contract languages or language support for programming by contract (c.f. section 2.4.1), strong typing, verification, and model checking. Szyperski [Szy02] believes component contracts should contain both functional pro- perties, such as re-entrance conditions and self-recursive patterns, and non-functional requirements, such as non-functional properties, such as bounds on execution time and bounds on faults. B¨uchi and Weck[BW97] note that while traditional interface specifications sufficiently describe the syntactic requirements of components, traditional pre- and post-conditions are often not sufficient to describe the semantic part of a component. While they agree that white-box specifications, which expose the source code to a user, break abstraction reduce replaceability and reusability, they argue that black box specifications are not

22 2.4 Components, Specification and Design expressive enough, as they do not describe call-back semantics. They argue that com- ponent specifications should be grey-box, which include limited semantic information regarding out calls. B¨uchi and Weck [BW99] develop the idea of grey box solutions further to include callbacks and out calls in Java-like interface specifications. Component specification is also required to enable component adaptation. Component adaptation approaches seek to make components reusable in as many contexts as pos- sible by adapting component interfaces and behaviours to fit new contexts. Black-box approaches to component adaptation such as superimposition [Bos99] are only possible with sufficient syntactic and semantic information. Other approaches to component adaptation, [YS97, Reu01] enhance component specifications with protocols from some process formalism and calculate mismatches in processes to determine a suitable adapter.

2.4.1 Programming by contract

Software contracts use pre-conditions, post-conditions and invariants to express proper- ties of programs and can be expressed in mathematics, informal language or program- ming language structures[MHKM95]. Contracts that are expressed in programming language constructs can be executed at run-time and can be checked when a program is run, such as those in Eiffel[Mey92] and Sather[SO96], while Tran et al[TMA03] have ad- ded support for Design by ContractTM to, Rotor, a multi-language, managed execution environment. Contracts add expressive power to a program. They allow a programmer to clearly record the requirements of a routine and the outcome of the routine. Contracts also help to provide checking and correctness. Parts of a contract can be executed as assertions at appropriate points in the program and any false assertions are detected. The advantages of design-by-contract are stated as follows in [MHKM95]:

• Better designs:

– Designs are clearer since the contract between client and server is made expli- cit.

– Designs are more systematic since programmers are encouraged to think about preconditions etc. since the approach makes this explicit

– Designs are simpler. The defensive nature of a precondition makes the conse- quences of calling a procedure illegally clear. Design by contract therefore discourages the programmer to build procedures with complex requirements resulting in better factored, simpler procedures.

– Inheritance is controlled, since preconditions must not be strengthened in subclasses.

23 2 Background: Software Components

– Exceptions are used more systematically - whenever the contract is broken an exception is raised.

• Improved reliability: – Since requirements are expressed in assertions and code it is more likely that the problem is better understood. – Assertions are tested at runtime leading to better tested code.

• Better Documentation: – Contracts form part of public view of the class. – Documentation is more reliable since the requirements are tested in code as opposed to informally stated. – Contracts provide support for some formal methods.

• Easier debugging: – Bugs are more likely to show up during development because of the assertion mechanism. – Bugs are more easily fixed during maintenance since the assertion mechanism provides a mechanism for communicating more information about a bug loca- ted at runtime.

• Support for reuse: – Assertions can provide feedback to programmers when using third party deve- loped code.

Software contracts suffer from a number of problems such as the inability to express logi- cal quantifiers, the difficulty in expressing some conditions in programming language (lea- ding to the use of informal comments instead), and the necessity of expressing contracts for abstract data types in terms of the class’ properties itself, some of which may not be public. A more serious limitation in the field of object-oriented and component programming is the inability of pre-conditions, post-conditions and invariants to express re-entrance conditions [Szy02] since pre- and post-conditions assume that an operation is atomic. The benefits of programming by contract are certainly applicable to component-oriented programming. Contracts when coupled with the static notion of type give rise to the concept of a behavioural type or dynamic type. Since this program of research includes ad- dressing re-entrance explicitly, pre-conditions, post-conditions and invariants are clearly not sufficient alone to formulate a type system for components.

24 2.5 Related Fields

2.4.1.1 Behavioural subtyping

Behavioural subtyping [LW94] requires that a subtype should never break a program when substituted for its parent type. To ensure behavioural substitutability in class or interface hierarchies in object-based languages, subtypes may only modify a parent’s contract by weakening pre-conditions and strengthening post-conditions. This principle is used in Contract Java[FF01a] to identify cases where pre- and post-conditions may be violated because of behavioural subtype errors in the class or interface hierarchy of a Java program.

2.5 Related Fields

Certain fields in software engineering and computer science share some goals or proper- ties of component software. The following section gives a brief overview of these fields, and their relationship to software components.

2.5.1 ADLs and BISLs

According to Vestal[Ves93], “an [Architecture Description Language] for software appli- cations focuses on the high-level structure of the overall application rather than the implementation details of any specific source module”. There are many approaches to ADLs, from informal description languages and graphical notations, to languages with formal semantics and accompanying tools, such as parsers, compilers, model checkers and code generators [MT97]. ADLs share some of the goals of component-oriented software but many are not strictly component-oriented according to the definition given by Szyperski[Szy02]; the entities that many ADLs describe are not components nor are they closed under composition. ADLs usually focus on connectors 6 or connections between parts of a system and analysis of interactions at the connection points. Some ADLs focus on abstract properties such as liveness while others focus on identifying gauge or probe points to allow instrumentation of systems for performance measurement. Behavioural Interface Specification Languages (BISLs) are used annotate the source code of modules or classes with expressive model-based behaviour specifications. While BISLs provide a richer mathematical language for specification than that of languages that support programming by contract, such specifications can often not be checked at runtime and are used for design and specification purposes only. Examples of BISLs include JML (Java Modeling Language) and the Larch[GH93] family of BISLs.

6 ADL connectors do not map well to any particular system of component based systems, and if they were a software entity, could easily be described as a component.

25 2 Background: Software Components

Allen and Garlan[AG97] show how WRIGHT[All97], an ADL that uses Communica- ting Sequential Processes [Hoa85] as its semantic basis, to analyze architecture descrip- tions. The protocols of the ports of the components and roles and glue connectors in WRIGHT are specified using a subset of CSP, allowing a description to be checked for deadlock freedom. While the method described does not add any semantic power to CSP, Allen and Garlan contend that the WRIGHT ADL provides important abstractions for specifying elements of an architecture, as well as providing encapsulation and distinction between types and instances in a description. Aladdin[SRW98, SW00] is a tool for performing analysis in Commercial- Off-The-Shelf (COTS) components used in existing systems using ACME[GMW97] or Rapide[The97] descriptions. Aladdin uses intra-component pathways, which connect input ports with output ports, to perform dependency analysis of the parts of an architec- tural description in order to determine potential impact of changes to a component of a system. Such specification and analysis techniques share some goals of specification in software components, but are somewhat ineffective if the specifications are not checked and enforced at the programming language level. Inveradi et al [CIW99, IWY00] both describe methods for using CHAM[BB92], a process formalism inspired by analogies in chemistry, to check describe the architecture of systems. A component specification consists of a behaviour specification and a context specification describing the expected behaviour of the environment. Inveradi et al’s method checks that the context specification of each behaviour matches the combined system behaviour. If all expected behaviours match, then the description is global deadlock free, otherwise the system may deadlock. Maps [de 00b] is an approach developed for component oriented systems. UCM does not have a precise semantics itself but can be combined with BCOOPL [de 00a] interface specifications to allow analysis. BCOOPL is a protocol based, concur- rent, object-oriented language with asynchronous message passing. Analysis allows de- tection of mismatches in the BCOOPL interfaces. Darwin[MDEK94, MK96] is an ADL used to describe distributed systems with dyna- mic structure, in which the organization of components may change during execution. Components in Darwin are modeled as having a set of provides services and a set of re- quires services. Components are bound together via services and applications are built by hierarchical composition of components into composite components; composite compo- nents have the same composition properties as basic components, meaning that Darwin’s model of components closed under composition. Darwin has a formal semantics based on the π–calculus and has been used to model the service view of an architecture, as well as the behavioural view of an architecture [CK96, CGK97, KM98, Mag99]. Behavioral modeling of applications in Darwin use labeled transition systems, and can be used to

26 2.5 Related Fields verify properties of a program, such as safety or liveness. The Koala model[vOvdLKM00] bases its model of components on Darwin, allowing specification of components with requires and provides interfaces, binding of components and hierarchical composition; like Darwin, Koala’s model of components is closed under composition. Koala was created to aid development of embedded software for consumer electronics, and provides facilities for implementing and managing components in the face of diversity of evolution of software. The UML 2.0 specification (UML 2.2[OMG09] being the latest version at the time of writing) introduced a Components , which allows modeling of “logical” com- ponents, such as business processes, and “physical” components, such as EJB or .NET components. UML component diagrams describe the logical structure of an application, in much the same way as ADLs.

2.5.2 Coordination Languages

Coordination models and languages separate the task of programming into computation (specifically sequential computation) and coordination; a system is said to comprise of a computational part consisting of a number of processes involved with manipulating data and a coordination part responsible for the communication and cooperation between these processes. Carriero and Gelerneter[CG92] suggest that traditional programming languages are incomplete as they only support an ad hoc approach towards coordina- tion. They argue that coordination is best viewed as orthogonal from computation so a coordination language should be separate from a computation language. A coordination model can be viewed as a triple (E,L,M) where E represents the entities being coordinated, L is media on which the entities are bound and M is the semantic framework for binding entities. A coordination language is the embodiment of such a model. [PA98] Coordination models can be classed as data driven (e.g. Linda and its various forms) or control driven (e.g. PCL, Conic). Data driven models provide coordination primitives or mechanisms, which processes use as part of their computation. Control driven models separate coordination from computation by providing a coordination language that is separate from the computation language. Linda[Gel85, CGZ95] was one of the first coordination languages, although technically Linda is a not a complete language itself, but a set of simple coordination primitives. In Linda, if two processes wish to coordinate, the sender process generates a data object called a tuple and places it in a shared data (tuple) space from which receiver can then retrieve it, by means of pattern matching.

27 2 Background: Software Components

2.5.3 Protocols, Models and Objects

There are many approaches that annotate or combine objects with models or protocols for the purpose of programming, typing, and analysis. Such approaches generally focus on modeling and investigating properties of object interactions. Many object-protocol combinations enable modeling of object composition and composition checking, which is very relevant to the field of component oriented programming.

2.5.3.1 Concurrency in Object Oriented Programming Languages

Briot et al[BGL98, BG98] group concurrent object oriented languages into three catego- ries: the library approach, the reflective approach, and the integrative approach. Lan- guages from the library approach provide libraries for structuring and managing concur- rent and distributed programs. Languages from the reflective approach separate normal computation from concurrency control which is described in terms of meta programs or so- called meta-object protocols; in this sense, Aspect-Oriented languages can be thought of as providing a generalized reflective approach. Languages from the integrative ap- proach combine concepts from concurrency with object-oriented concepts. In particular, languages from the integrative approach that use protocols to integrate synchronization with method calls share some similarity to the language extensions described in this thesis. Lock based synchronization, such as in Java or C#, is the most commonly adopted integrative approach to concurrency control in object-oriented languages. Lock based syn- chronization generally associates locks with objects and clients attempt to obtain those locks when entering a synchronized method or code block. Object locks in Java and C# are re-entrant in order to avoid instances of self-inflicted deadlock. In the presence of concurrency, negotiable interfaces can be seen as a generalized lock based synchroni- zation scheme in which locks can be created and destroyed. Negotiable interfaces are not re-entrant by default as a client must be able to demonstrate that it holds the lock before re-entering a method. Eiffel uses a different integrative approach to synchronization and integrates concur- rency control with contracts. The resulting scheme uses pre-conditions for concurrency control and is a very expressive form of guards. Guards associate a boolean condition with a procedure and automatically block or awaken threads based on that boolean condition. Guide [BLR94] is another language that uses guards to control concurrency. Many other concurrent object oriented languages that use protocols for concurrency control are Actor languages[Agh86]. Actor languages have active objects, which are objects integrated with processes; an active object has a unique identity, its own thread of execution, and a message queue and communicates with other objects by sending mes- sages. Actor languages use behaviour replacement synchronization schemes. Incoming

28 2.5 Related Fields messages are accepted according to some behaviour, which calculates a replacement behaviour, which in turn calculates the next incoming message to accept. PROCOL [vL91], TalkTalk [Bd96] and BCOOPL [de 00a] are concurrent object-oriented languages that use path expressions to express synchronization constraints between ac- tive objects (objects with a process, or thread of control). Path expressions are usually regular expression-like and as such give a compact method of specifying object behaviour in terms of synchronization and message passing/processing. Enabled sets or abstract state synchronization techniques such as in ACT++ [KL90] specify a set of potential states of an object and the methods that may be called while the object is in those states. Negotiable interfaces can be seen as an enabled sets approach, although the factorable bag representation makes negotiable interfaces more expressive than most enabled sets approaches. A major problem encountered with integrative synchronization approaches in concur- rent object-oriented languages is the so-called inheritance anomaly. The inheritance anomaly is the name given to a set of problems encountered when attempting to inherit from classes with synchronization constraints; in many cases, classes with synchroniza- tion can only be subclassed if a large amount of their code is rewritten. The inheritance anomaly is actually caused by a number of issues, including a lack of expressiveness in the synchronization scheme,7subclassing without following the guidelines of behaviou- ral subtyping, and “spaghetti” concurrency control code with normal logic (c.f. cross cutting concerns, section 2.1.3).

2.5.3.2 Behavioural Types

Behavioural type systems have been proposed for active objects and, more recently, inter- faces for software components. Approaches to behavioural types use process formalisms or automata to specify the behavioural requirements of objects. Type safety in such models concentrate on ensuring that objects receive messages or method invocations in a correct fashion by checking compositions of objects. While composition level checking of behaviours may be desirable in component based systems, many composition checking approaches based on behavioural types do not scale due to state-space explosion. The type systems of [Nie95] and [Weh00] utilize behaviour descriptions of both client and server objects to examine the combined behaviour of an object and its clients at composition time. Such approaches are not suitable for handling dynamically changing sets of clients or situations in which aliasing occurs as the identity of objects needs to be known statically. The type system presented and developed primarily by Puntigam in [Pun96, Pun97,

7 E.g. Subclassing an “enabled sets” stack class, with states empty, and notempty, to add a method Pop2() which pops two items off the stack requires non-trivial changes.

29 2 Background: Software Components

Pun01] and Puntigam and Peter in [PP99] has a similar scheme for decorating interfaces with (extended) bags of tokens and assigning behaviours to methods. Puntigam and Peter seek to ensure that the sequence of messages received by an object conforms to the (dynamic) type of the object and achieve this by typing individual references to an object. Whenever a reference is passed to another client, the dynamic type of the reference is split between the old and new references. This allows the clients to access services of a server object without querying the object as to its current state; however, if the client wishes to access more services than its reference allows, it must somehow obtain a reference from another client or the server to access those services. This may actually lead to clients becoming coupled, which is an unnatural behaviour for component systems. Finally, the type system developed by Reussner in [Reu00, Reu01], which based on earlier work by Reussner and Heuzeroth in [HR99, RH99], extends Java interfaces with augmented finite state automata. Reussner’s approach decorates each interface with a call automaton that specifies the legal orderings of calls, and each method of a component is with an automaton that describes which other services the method uses. Combining the call and method (or function) automata gives the complete behaviour of a component (called the EC-Automaton). The combined automaton can be checked with behaviour of other components at composition time to accept or reject legal compositions. Reussner’s type system also allows for component adaptation in the case that some required services are not present.

2.5.3.3 Petri Nets

Petri nets have been used extensively to model and analyse concurrent and asynchronous protocols[Pet77]. Petri nets have an execution semantics that can be used to investigate the state-space of a modeled system, permitting analysis of properties such as liveness and deadlock. Higher-level Petri nets, which encode information or data in places, to- kens and transitions, have been used to model objects in concurrent, object-oriented systems [SB94, Lak97, HV01, PG06]. These approaches have relevance to component programming as they focus on specification of object behaviour and enable analysis of object compositions. Lakos[Lak97] describes Object Petri Nets (OPNs), which integrate object-oriented concepts, such as inheritance, polymorphism and dynamic binding, into Petri nets. OPNs can model dynamic method availability through use of guards in transitions and objects can be active or passive. Sibertin-Blanc’s Communicative and Cooperative Nets[SB94] are used to model dyna- mic distributed systems behaviour, in which the set components can change and be recon- figured. Communicative Objects have a close relationship with active objects and commu-

30 2.5 Related Fields nicate via message passing, whereas Cooperative Objects model the method invocation semantics of passive objects using a client-server protocol. Holvoet and Verbaeten[HV01] use Sibertin-Blanc’s Petri nets to specify the protocols of active and passive objects in Ob- jective Linda; the Petri net specifications can be used to define subtyping relationships for objects in Objective Linda. Pettit and Gomaa[PG06] describe a technique for converting concurrent architectures modeled with UML communication diagrams[OMG09] into Coloured Petri Net (CPN) models[KCJ98]. Pettit and Gomaa identify behavioural stereotypes for the objects in an architecture (e.g. asynchronous interface, periodic algorithm, entities) and create a CPN template for each . A CPM model is constructed from the UML by modeling each object conforming to a behavioural stereotype with the appropriate CPN template and then refining the CPN with context specific behaviour. Pettit and Gomaa use their technique to model a cruise control process and then apply design tools to analyze performance characteristics of the resulting CPN.

2.5.4 Model Checking

Model checking is an automated technique for verifying correctness properties of a system[CGL94]; a model checker is a system or program that performs automated model checking. Systems are typically modeled as a finite state transition graphs and a search procedure is used to determine if the system satisfies certain specified temporal properties, such as absence of deadlock or error states. Model checking techniques can certainly be used to check properties of modeled component systems; however, component systems are incomplete by definition and can change after deployment and even at runtime when model checking tools may not be available or may be too expensive to employ. The Labeled Transition System Analyzer (LTSA) [Mag99] has been used to verify properties of systems modeled in Darwin. In Darwin, the behaviour of a primitive component is modeled as a simple finite state automata that communicates and changes state via “actions”; this is shared with many of the approaches to behavioural types for active objects and components in section 2.5.3.2. Composite components are the parallel composition of component behaviours. LTSA can be used to search the state space of a composition to see if certain states, like deadlock, can be reached[MKG97]. LTSA can produce traces to erroneous states and can animate composed behaviour interactively[KM98].

31 32 3 Scope, Problem and Solution

One of the universal rules of happiness is: always be wary of any helpful item that weighs less than its operating manual. (Terry Pratchett, Jingo)

Advances in programming language technology have brought about abstractions that go some way towards enabling software reuse and a component software methodology. Ad- vances in module technology and object technology have helped to enable some features such as separate compilation, substitutability, polymorphism, and extensibility. It is no surprise, therefore, that modern component technology is primarily object-based, and that component specifications are commonly based on interfaces of object-oriented type systems. 1 Object-based systems can be badly affected by object re-entrance, however, and these problems are of particular concern in component software. Object re-entrance is a poten- tially global behaviour, meaning that re-entrance can occur across component boundaries, and arise after component composition and well after deployment. As a result, errors due to re-entrance may be difficult to debug or outright prevent composition of compo- nents. Re-entrance conditions, especially across component boundaries, need to be a part of component specifications. This chapter introduces Mentok, a variant of Component Pascal with novel language features for specifying and constraining object re-entrance. The Mentok compiler, Men- tokC, is based on the Gardens Point Component Pascal (GPCP) compiler[GRC01] and provides a complete implementation of Mentok as presented in this thesis. All code examples in this thesis have been compiled and executed using MentokC. Mentok approaches specification of re-entrance conditions at the interface level by modeling object state and changing method availability. Object re-entrance is a problem when objects are stateful and when stateful objects interact, or are self-interfering. By modeling changing object state and availability of methods as a property of that state in interface types called negotiable interfaces, Mentok enables abstract specification of re-entrance conditions, including out-calls and self-interference patterns, as a property

1 Object-based languages are not strictly necessary to implement software components, but the use of interfaces as units of specification and for composition tend to make component based applications object-like as a result.

33 3 Scope, Problem and Solution

of object state. Mentok also provides a method of specifying re-entrance conditions via relationships between interfaces and between modules called type layers. Type layers enable specifica- tion of control flow in a program as a property of the interfaces used by the program. By restricting cyclic control flow patterns through static and dynamic checking of control flow through type layers, re-entrance can be constrained while still allowing patterns of re-entrance explicitly specified by negotiable interfaces or by polymorphism. Section 3.1 begins by describing the salient features of the programming model assu- med and extended by Mentok. Code snippets presented in this section are presented in Component Pascal, with some common coding conventions and syntactic abbreviations; syntax unique to Mentok or added as extension by the GPCP compiler is described explicitly. Section 3.2 examines some problems that arise when programming components with the model described in section 3.1. Stateful objects and static method and interface availability, allow unchecked re-entrance that can cause runtime errors across component boundaries. Examples and problems of object re-entrance given in this section are motivated by the extensive treatment of the problem by Szyperski in [Szy02]. Section 3.3 states the research questions that this thesis attempts to address. Finally, section 3.4 introduces two novel programming language features of Mentok, negotiable interfaces and type layers that enable specification of changing method and interface availability, and can be used to specify and constrain re-entrance conditions. Negotiable interfaces and type layers are described in greater detail in later chapters.

3.1 A Basic Programming Model for Software Components

Component Pascal is member of the Pascal[Wir71] programming language, and a su- perset of the Oberon-2 programming language. Gardens Point Component Pascal (GPCP)[GRC01] is Component Pascal compiler that targets Sun’s Java Virtual Machine[LY99] and Microsoft’s Common Language Runtime[MR04], and which provides some language extensions to Component Pascal to enable interoperability with the types systems of the JVM and CLR. With the addition of interfaces, GPCP provides a mini- mal set of language features suitable for component based programming: modules as units of deployment and composition, interfaces as units of specification and objects as units of instantiation. The next subsections informally describe the basic type, object and module models that Mentok assumes and extends. This section assumes a basic familiarity with Com- ponent Pascal[Obe97] syntax and semantics; GPCP extensions to the Component Pascal language for interfaces and explicit interface implementation[Mic00] are described in de-

34 3.1 A Basic Programming Model for Software Components tail.

3.1.1 Notes on Component Pascal

Component Pascal[Obe97] is a refinement of the Oberon-2 programming language, de- veloped by Oberon Microsystems. It is a strongly typed, object-oriented programming language with syntax and style of the Pascal family of programming languages. Garden Point Component Pascal (GPCP)[GRC01] is a derivative of Component Pascal, and the GPCP compiler produces binaries for either the CLR[MR04] or the JVM[LY99]. The CLR and the JVM have richer type systems and more language features than Component Pascal, so GPCP has been extended with non-standard syntax to enable interoperability with “native” CLR or JVM binaries (e.g. .NET Framework libraries and Java libraries); additional language constructs are provided for exception handling, enum types, CLR event types, static classes and members, as well as native operators that are not included in the Component Pascal language specification. The salient features of GPCP used by Mentok, interfaces, records, and modules, are described in detail in the following sections. There are also some features of the Com- ponent Pascal language itself that are restricted in Mentok. Implementation inheritance or subclassing is restricted across module boundaries by requiring that all public classes and all classes that implement public interfaces be sealed or final.2 Finally, in this dissertation, many Component Pascal keywords will be omitted for brevity:

• Record attributes, such as LIMITED, ABSTRACT and EXTENSIBLE are omitted, since they are mainly used for controlling implementation inheritance and visibility. A new method attribute, INTERFACE, is introduced for defining interfaces. Interfaces are similar to fully ABSTRACT records, but have different inheritance semantics.

• Method attributes, such as NEW, ABSTRACT, and EXTENSIBLE are omitted, since these are mainly used for designing implementation inheritance. All interface method definitions are assumed to be NEW and ABSTRACT.

• IN, OUT, and VAR parameters are omitted from syntax specifications since Mentok’s language features do not interact in an interesting way with these parameter kinds.

• Language features for defining visibility of declarations are also omitted, since Mentok’s language features do not change Component Pascal’s visibility model. All declarations in code snippets are assumed to be public, unless otherwise noted.

2 This design decision was made as implementation inheritance is considered problematic in component software for reasons outlined in [FF01b, Szy02].

35 3 Scope, Problem and Solution

All examples in this dissertation deal with object (reference) types, as value record and interface types do not have any significant meaning in component based programming. This will be emphasized by using the “POINTER TO Type” form for interface and record declarations.

3.1.2 Interface Model

The model of interfaces used in GPCP and in this dissertation is similar to that used in popular object-oriented programming languages with static, strong typing such as C# or Java. Interfaces are named types, that are indirect[Szy02], a requirement for late-binding and substitutability, and fully abstract, meaning that no implementation is inherited when implementing an interface. Interfaces may inherit from a set of parent interfaces, and classes may implement a set of interfaces. The common naming convention of prefixing interface names with “I” is not enforced in the language, but is used in all examples for clarity. One difference in interfaces as provided by GPCP and those provided by Mentok is that all Mentok interfaces are always explicitly implemented. Explicit implementation is optionally available in C# (as a feature of the CLR’s type system), and enables a class to implement an interface without adding the implemented methods to the namespace of the class. Explicit implementation enables implementation of sets of interfaces that have methods with syntactically clashing signatures, but adds a requirement that interface methods be invoked via an interface reference. Mentok’s type system relies on this property. Listing 3.1 illustrates several interface definitions GPCP syntax for interface declara- tion. As fully abstract, stateless classes, interfaces in GPCP and Mentok share some of the record and procedure syntax of Component Pascal but differ in the following ways:

• Interfaces are declared using record syntax using the new INTERFACE keyword as shown by all four interfaces.

• Interface inheritance is declared by use of the “(+ ...)” syntax immediately following the RECORD keyword. IHostableContainer inherits from three other interfaces, ISizeable, IItem, and IHost. 3

• Interfaces may not have any member variables since they are fully abstract.

3 The + syntax originates from the Gardens Point Component Pascal, where the inheritance declaration of a class or interface is “( BaseClass { ‘‘+’’ Interface ) } ”, but since interfaces cannot inherit from classes, and implementation inheritance is avoided across component boundaries, the initial base class identifier is always omitted.

36 3.1 A Basic Programming Model for Software Components

TYPE ISizeable =POINTERTOINTERFACERECORDEND;

IHost =POINTERTOINTERFACERECORDEND;

IItem =POINTERTOINTERFACERECORDEND;

IHostableContainer =POINTERTOINTERFACERECORD (+ ISizeable + IItem + IHost ) END;

PROCEDURE (this: ISizeable) Size(r:Rect);

PROCEDURE (this: IItem) Host(h:Host);

PROCEDURE (this: IItem) Free(h:Host);

PROCEDURE (this: IHost) Unlock();

PROCEDURE (this: IHost) Lock();

PROCEDURE (this: IHost) HostItem(i:Item);

PROCEDURE (this: IHost) FreeItem(i:Item); Listing 3.1: Basic interface declaration.

Figure 3.1: Basic interface model.

37 3 Scope, Problem and Solution

• Method definitions of an interface are not lexically nested within the record decla- ration, as in Java or C#, but instead are declared with all the other procedures of the application following the type and variable declarations of a module. An interface’s methods are identified by the type of the receiver parameter. In this thesis, the receiver will always be named “this”. Interface methods are always NEW and ABSTRACT in terms of Component Pascal’s modifiers.

Figure 3.1 presents a diagrammatic representation of IHostableContainer and its parent interfaces. This diagrammatic form, which omits some syntactic detail, will be used throughout this dissertation.

3.1.3 Object Model

The object model extended by Mentok (that of GPCP) is common to object-oriented lan- guages such as C# and Java, and is also the basis for component models and frameworks in industry. Objects are units of instantiation and provide services via one or more interfaces. Ob- jects are passive (i.e. they do not have their own process or thread of control), and method invocation is synchronous and blocking. Objects are generally stateful (i.e. ob- jects maintain state for longer than a single atomic method call) and state is manipulated and exposed as the result of calling methods provided by an object. Clients can also test and guard the type of objects, which is useful for discoverability of implemented interfaces in extensible systems. Object type hierarchies are static and type equivalence and subtyping relationships are nominal rather than structural. Object types in Mentok are declared using the RECORD syntax. Listing 3.2 illustrates a simple record declaration for a class called SizeableItem, which implements the interfaces IItem and ISizeable:

• The POINTER TO RECORD identifies SizeableItem as an object type, which will be allocated on the heap.

• The member variable declarations myHost and mySize are lexically nested inside of the record declaration.

• The methods of SizeableItem are declared in lexically separate PROCEDURE blocks and are identified as belonging to SizeableItem by the type of the receiver para- meter, which follows the PROCEDURE token and precedes the method name. By convention, the name of receiver parameter will always be this in this thesis.

38 3.1 A Basic Programming Model for Software Components

1 TYPE 2 SizeableItem =POINTERTORECORD (+ IItem + ISizeable) 3 myHost : IHost; 4 mySize : Rect; 5 END; 6 7 PROCEDURE (this: SizeableItem.IItem) Host(h:IHost); 8 BEGIN 9 ASSERT(this.myHost =NIL); 10 this.myHost := h; 11 END Host; 12 13 PROCEDURE (this: SizeableItem.IItem) Free(h:IHost); 14 BEGIN 15 ASSERT(this.myHost = h); 16 this.myHost :=NIL; 17 END Free; 18 19 PROCEDURE (this: SizeableItem.ISizeable) Size(r:Rect); 20 BEGIN 21 this.mySize := r; 22 END Size; Listing 3.2: Basic record declaration.

The syntax for inheriting interfaces is again the “(+ ...)” syntax, while the syntax for implementing interface methods deserves special note. Note that the type of the re- ceiver parameter in listing 3.2 is SizeableItem.IItem for methods Host and Free and SizeableItem.ISizeable for the method Size. The SizeableItem part of the type denotes that the method belongs to the SizeableItem class. The IItem or ISizeable denotes that the method is provided as part of the implementation of those respective interfaces. This is Mentok’s syntax for explicit interface implementation. Explicit im- plementation allows implementation of multiple interfaces with syntactically clashing signatures (i.e. same method name, parameters, but different return type) or semanti- cally clashing methods (i.e. same method names, parameters, and return types, but different intended semantics or contract). Although this syntax is specific to Mentok, explicit implementation itself is not new or novel, and the syntax is presented here for reference. In Mentok, classes are not permitted to have a default public interface (i.e. classes do not have public methods). Classes, and therefore objects, must provide publicly consumable services via explicitly implemented interfaces. This means that external clients of an object must have an interface reference to an object in order to invoke methods. As noted earlier, the implementation inheritance or subclassing features of Component Pascal are not used in Mentok, as implementation inheritance, especially

39 3 Scope, Problem and Solution

Figure 3.2: Basic object model.

Figure 3.3: Basic execution model.

40 3.1 A Basic Programming Model for Software Components across component boundaries, can lead to problematic patterns of self-interference[FF01b, Szy02] and result in the fragile base class problem described in section 2.1.1.3. Figure 3.2 gives an example of the diagrammatic representation of objects and clients that will be used throughout this dissertation. The object represented in this case is an instance of the record declared in listing 3.2. The interfaces provided by an object are represented by “pins”, to which clients have references. Figure 3.3 demonstrates an diagram that will be used to informally illustrate control flow concepts in Mentok. In this example, a client invokes a method through an interface of an object, performs a type test to discover another interface provided by the object, and invokes another method.

3.1.4 Module Model

Modules are units of separate compilation and units of deployment in GPCP, with each module being compiled into a separate binary library. Mentok uses GPCP’s module model as a simple model of components, with composition of modules performed through static compilation, or dynamic loading. Modules may declare and export types and procedures, as in normal Component Pascal. In this dissertation, interface definitions and implementations will commonly be separated into separate modules to model the concept of “interface in the middle”[Szy00]; the language does not proscribe such separation, however, as in Modula-2, Modules will often be termed definition or implementation module, but this is just a convention.4 The loading and deployment model for modules leverages the .NET assembly model. Each module is compiled to a .NET assembly (a self-describing DLL), which can be loaded dynamically and deployed individually. Component composition is achieved statically by importing modules, or dynamically by loading modules via reflection. Separation of definitions from implementations gives the import relationship three different interpretations.

• Definition imports Definition: Interface definition modules can import other definition modules to extend imported interfaces or reference imported interfaces for tightly bound, cooperating interfaces.

• Implementation imports Definition: Implementation modules can import in- terface definition modules to implement types, or import interfaces so that the implementation can use their services.

• Implementation imports Implementation: Implementation modules can im- port other implementation modules to for static composition. The imported mo- dule must now be deployed as part of a composite component.

4 Modules containing simple libraries (e.g. from platform frameworks) are not treated specially.

41 3 Scope, Problem and Solution

Figure 3.4: Module composition model.

The fourth possible interpretation, Definition imports Implementation, breaks abstraction and layering, and so is not considered. Figure 3.4 illustrates an example of module composition for a simple windows application. The application component, composed of the SimpleApp, Window, Panel, and BasicDraw modules, is loaded and run by the atomic AppLoader component. The implementation components import parts the definition component composed by the module App.

3.2 Problems with the Basic Model: State and Object Consistency

The language features and programming model described in the previous section is si- milar, if somewhat simplified, to the basic programming model used to build software

42 3.2 Problems with the Basic Model: State and Object Consistency components in modern component applications and frameworks. There is a disconnect between the amount of specification afforded by static interface types and module signa- tures, and the temporal properties of stateful objects, and running programs consisting of stateful objects. This section examines this gap in specifications, and problems of object re-entrance that can arise as a result. Later sections will show language constructs provided by Mentok that attempt to address these deficiencies.

3.2.1 Static Interface Types: Static Method Availability

Static interface types present a component role as a set of named methods, type constraints on the values that are passed to, and returned by, those methods. This means that static interface types can be used to prevent clients from calling unknown services, or from calling known services with incorrectly typed parameter or return values. Assuming these conditions are met, the methods of a static interface are available to be called at any time by any client. In general, the roles described by interfaces may be stateful. This is not to say that an interface type is not fully abstract, but the role it describes can only be implemented by a component instance or object that maintains, manipulates, and exposes some state across or during method execution. In these cases, it is possible that availability of the methods of an interface depend on the state of the implementing object. Static interface types, however, cannot express the implied state of a component role, nor how the availability of methods changes over time. Figure 3.5 gives a simple example demonstrating the lack of expressiveness of static interface types. The interface, INamedBuffer, is intending to abstractly specify a stateful role, illustrated by the state transition diagram. Initially, a named buffer is closed, and unnamed, meaning that only available method initially is the Name method. Following a call to Name, the buffer can be Opened, and then populated and emptied by sequences of calls to Put and the Get. The Close method is used on an open, and empty buffer and discards the buffer. Listing 3.3 demonstrates two simple cases in which a client misuses an object imple- menting INamedBuffer. The first case, InitializeBuffer, is a simple oversight by the programmer: the buffer is not opened before being populated. The second proce- dure, RenameBuffer is a little more subtle: the contract of the named buffer does not guarantee that a named buffer can be renamed after being closed. (The contract does not prescribe that MyId() should change as a result of being Closed). This oversight may or may not be detected depending on the implementation of INamedBuffer used for testing. Changing method availability, such as in the example above, can often be expressed

43 3 Scope, Problem and Solution

TYPE INamedBuffer =POINTERTOINTERFACERECORD (* Model Variables * * id : Id := NIL * * open : BOOL := FALSE * * slot : Item := NIL *) END;

PROCEDURE (this:INamedBuffer) Name(id : Id); (*Pre: this.id=NIL * *Post:this.id’=id *) PROCEDURE (this:INamedBuffer) Open(); (* Pre: (this.id # NIL) & ~this.open * *Post:this.open’ *) PROCEDURE (this:INamedBuffer) Put(i : Item); (* Pre: this.open & (this.slot = NIL) * *Post:this.slot’=i *) PROCEDURE (this:INamedBuffer) Get() : Item; (* Pre: this.open & (this.slot # NIL) * * Post: (this.slot’ = NIL) & * * return value = this.slot *) PROCEDURE (this:INamedBuffer) Close(); (* Pre: this.open & (this.slot = NIL) * *Post:~this.open’ *) PROCEDURE (this:INamedBuffer) MyId() : Id; (*Pre:TRUE * * Post: return value = this.id *)

Figure 3.5: Named buffer state diagram and interface contract.

PROCEDURE InitializeBuffer(); VAR b : INamedBuffer BEGIN b := NewBuffer(); (*Createbuffer *) b.Name(someID); (*Namebuffer--OK *) b.Put(item); (* Error: Buffer is not open! *)

...

PROCEDURE RenameBuffer(b:INamedBuffer); BEGIN ASSERT(b.open & b.empty); (* Buffer is open and empty *) b. Close (); (* Close buffer -- OK *) b.Name(someID); (* Error: Is the buffer * *stillnamed? *)

... Listing 3.3: Illegal use of named buffers.

44 3.2 Problems with the Basic Model: State and Object Consistency

Figure 3.6: Tightly bound re-entrance. via pre- and post condition contracts. Pre- and post-condition contracts can generally only be checked at runtime, however, meaning that simple errors must be found with a comprehensive testing strategy. This can be somewhat problematic for software compo- nents, which are deployed before composition, and deployed in many different contexts. The problem is exacerbated by the fact that, in the presence of out-calls, method execu- tion is not atomic, and objects can be re-entered.

3.2.1.1 Tightly bound re-entrance

When an object’s method executes, the object may undergo a state transformation. In the general case, method execution is not atomic, as a method may call out to other objects, causing those objects to undergo state transformations. During out-calls, ob- jects may be re-entered by invocation of another method possibly exposing intermediate state. Figure 3.6 gives an for a problematic case of re-entrance, as explored by Szyperski[Szy02]. The scenario starts with a change in the Model, which in turn begins to notify its views. When the BrokenView is notified, it objects to the change, and tells the Model to revert the change. This begins a problematic re-entrant call to Set,

45 3 Scope, Problem and Solution

which changes the value and then notifies all its clients of the second change. Finally, the model notifies the second view for a second time about the updated value. This scenario could cause errors in more than one location, but surprisingly, none of the errors will occur in the code of the BrokenView, which is the source of the errors. Listings 3.4,3.5, and 3.6 give source code listings for the IModel and IView interfaces, the implementation for Model, and implementations for BrokenView and View. The first potential problem from the scenario above occurs in the View. The View is told that the key k is updated, only after it has been removed from the Model by the BrokenView. Since the View will probably not be displaying the key in question, the call to remove the key from the View’s display may fail. This is an example of failed inter-object consistency. The Model and all its views were not in a consistent state when the re- entrant call updated the model again, leaving the second view a step behind the other objects. It is also worth noting that the View is updated a second time, as a result of the original Set. If the updated value was sent with the key, the View might show an incorrect value, breaking the contract of IView.Notify. The second problem becomes evident upon examining the contracts for IModel and IView (in listing 3.4). After a call to Set, an object implementing IModel should associate the new value with the updated key. This post-condition is satisfied for the re-entrant call by the broken view, but on completion of the outer call to Set, this post-condition is broken. Scenarios like the above only occur when objects are composed, which, for component- based applications, may be after components are deployed. Fixing such a problem is non- trivial since the code from all cooperating components may need to be inspected. This may be impossible if the source code is not available for the components, or impractical if the application builder is not a domain expert. The problem is further exacerbated since the error is not correctly isolated; the error is detected in one component (the Model) but caused by another (BrokenView). Szyperski goes on to show in [Szy02] how component contracts may be proofed by adding test functions to indicate if an object is in intermediate state. This is entirely possible but requires careful examination of the contracts of all participating interfaces. It also must be done on a case-by-case basis, since the static type systems of object-based languages permit re-entrance by default. Lack of specification of re-entrance conditions for tightly-bound interfaces is clearly problematic. So not only are re-entrance conditions in the form of method availability impossible to express in static interface types, they are also difficult to express correctly using pre- and post-condition contracts. Re-entrance can easily also occur across in interfaces (i.e. interfaces which are de- pendent in an application, but do not explicitly mention each other in their specifications). The next section examines these issues further.

46 3.2 Problems with the Basic Model: State and Object Consistency

TYPE IModel =POINTERTOINTERFACERECORD END;

PROCEDURE (this:IModel) Set(k : Key; v : Value); (*Pre:TRUE * *Post:(ValueOf(k)=v)& * * allviewsarenotified *)

PROCEDURE (this:IModel) ValueOf(k) : Value; (*Pre:TRUE * * Post: contains pair (k,v) THEN result = v * * ELSEresult=NIL *)

...

TYPE IView =POINTERTOINTERFACERECORDEND;

PROCEDURE (this:IView) Notify(k : Key); (*Pre: this.model#NIL * *Post:viewchangedfor * * this.model.GetValue(k) *) Listing 3.4: Interface specification for IModel and IView.

TYPE Model =POINTERTORECORD (+IModel) views: IViewList; table: Table; END;

PROCEDURE (this:Model.IModel) Set(k : Key; v : Value); VAR i :INTEGER; BEGIN this.table.SetValue(k,v); (* set the value *) FORi:=0TO views.Count()-1DO this.views.view(i).Notify(k); (* notify views *) END; END Set;

PROCEDURE (this:IModel) ValueOf(k) : Value; BEGIN IF this.table.Contains(k)THEN RETURN this.table.Value(v); (* return value *) ELSE RETURNNIL; (* return NIL *) END; END ValueOf;

... Listing 3.5: Implementation of IModel. 47 3 Scope, Problem and Solution

TYPE BrokenView =POINTERTORECORD (+IView) model : IModel; display: IDisplay; END;

PROCEDURE (this:BrokenView.IView) Notify(k : Key); VAR newValue : Value; BEGIN newVal = this.model.GetValue(k); IF this.Censors(k)THEN IF newVal =NILTHEN RETURN; (* Censored removed. *) ELSE this.model.SetValue(k,NIL); (* Revert the updated. *) END ELSE IF (newVal =NIL)THEN this.display.Remove(k); (* Remove k from display. *) ELSIF ~this.display.Contains(k)THEN this.display.Add(k); (* Add k to the display. *) ELSE this.display.MoveToEnd(k); (* Add k to the display. *) END; END; END Notify;

...

TYPE View =POINTERTORECORD (+IView) model : IModel; display: IDisplay; END;

PROCEDURE (this:View.IView) Notify(k : Key); VAR newValue : Value; BEGIN newVal = this.model.GetValue(k); IF (newVal =NIL)THEN this.display.Remove(k); (* Remove k from display. *) ELSIF ~this.display.Contains(k)THEN this.display.Add(k); (* Add k to the display. *) ELSE this.display.MoveToEnd(k); (* Add k to the display. *) END; END Notify; Listing 3.6: Implementations of IView.

48 3.2 Problems with the Basic Model: State and Object Consistency

3.2.2 Static Module Signatures: Static Interface Availability

Separation of interfaces and implementations, as is common with component based programming, gives rise to an interesting and potentially problematic issue: breaking of layering. Any interface definition can be statically referenced by a component without introducing cycles in the import order; the principle of specifications in the middle makes the interfaces of an application statically available to any component. In an analogous fashion to static method availability, static interface availability allows any client to make a call through any interface it statically references. Figure 3.7 illustrates this problem. The interface definition modules hint at the inten- ded application structure: a common tiered application architecture, with a presentation layer (specified by the types in module Presentation) calling into a business logic layer (module Logic), which call into the data layer (module Data). The actual application architecture is somewhat different. The application module composes two sub components - module BusinessLogic implements the business logic, and PresentationData which implements both the presentation layer and the data layer.5 So rather than a clear layered architecture, there exists an odd cyclic dependency between two modules, BusinessLogic and PresentationData, possibly leading to cyclic dependencies between loosely coupled interfaces. The logic layer may call into the data layer, which is also the presentation layer, which may call back into the logic layer! Although this is an extreme example, it illustrates how the components of application can alter the layering intended by the specification. The separation of interfaces from implementation makes the definitions available to any component (as long as they import the relevant type library). This is a flaw in the process of programming; the combined specifications of an application may hint at an application architecture but they do not define an architecture. The next section demonstrates more clearly how this can lead to cyclic patterns of re-entrance, even for loosely bound interfaces.

3.2.2.1 Loosely bound re-entrance

The static availability of interfaces can also lead to re-entrance across loosely-bound interfaces. In some cases, this is more of a problem than issues with tightly-bound re-entrance as it can lead to cyclic calling patterns over an entire program. Poorly designed applications, such as the one in the preceding section, will almost certainly have confusing cyclic calling patterns, but there are cases when cyclic calling patterns are desirable so long as they are handled correctly. Figure 3.8 elaborates a possible case of re-entrance for the code snippet in listing 3.7. The programmer has implemented the two interfaces, IPostMessage and IApp in a

5 Since the logic and combined presentation/data modules both reference the interface definitions needed to call into each other, they do not need to import each other.

49 3 Scope, Problem and Solution

Figure 3.7: Application structure defined by components.

50 3.2 Problems with the Basic Model: State and Object Consistency

Figure 3.8: Loosely bound re-entrance.

single class called Application. Again the programmer has decided to add a shortcut to the implementation, by shutting down the application immediately upon detection of a Close message in IPostMessage. The premature halt results in the data source being freed before the data source could be saved, as well as leading to a re-entrant call to deactivate the widget before it has finished handling its last message.

Once again this is a simplistic example, but it demonstrates how the static availabi- lity of interfaces can lead to poorly structured applications. Cases like this are harder to proof against with progress condition contracts since re-entrance has occurred due to dependencies across loosely bound interfaces; it is difficult to formally specify that IPostMessage should not result in calls to other, possibly unrelated, interfaces. Even when specifications themselves are intended to provide a well-structured layered architec- ture, implementations can implement them in vastly different fashions. For component based applications, where specifications are the means for communicating between consu- mers and producers, this is a critical flaw.

51 3 Scope, Problem and Solution

TYPE IApp =POINTERTOINTERFACERECORDEND;

IPostMessage =POINTERTOINTERFACERECORDEND;

PROCEDURE (this:IApp) MsgLoop();

PROCEDURE (this:IPostMessage) Post(m:IMsg); ...

TYPE Application =POINTERTORECORD (+IApp + IPostMessage) window : IWindow; dataSource : IDataSource; msgs : MsgList; END;

PROCEDURE (this:Application.IApp) MsgLoop(); VAR m : IMsg ; BEGIN LOOP IF this.msgs.Count() # 0THEN m := this.msgs.Dequeue(); CASE m.Type()OF (* Close - close the window, free the data source and exit *) | Close : this.window.Close(); this.dataSource.Free(); RETURN; (* Save - commit the data source *) | Save : this.dataSource.Commit(); (* Widget - route message to the destination *) | Widget : m.Dest().Handle(m.MsgPars()); (* ELSE Unknown message - panic! *) END; END; Runtime.Yield(); END; END MsgLoop;

PROCEDURE (this:Application.IPostMessage) Post(m : IMsg); BEGIN ASSERT(m #NIL); IF m.Type() = CloseTHEN (* Shortcut - free everything and shutdown now. *) this.window.Close(); this.dataSource.Free(); HALT(0); ELSE (* otherwise add the message to the queue *) this.msgs.Enqueue(m); END; END Post; Listing 3.7: Loosely bound re-entrance.

52 3.3 Research Question

3.3 Research Question

Section 3.1 demonstrates two related but distinct problems of object re-entrance. Neither problem can be prevented using the specification capabilities afforded by the program- ming language alone:

1. Static interface types allow methods to be invoked at any time, regardless of the state of the implementing objects; object re-entrance across tightly-bound inter- faces cannot be constrained using interface types.

2. Conventional module signatures allow interfaces to be referenced and called regard- less of the state of the program; re-entrance across loosely-bound interfaces cannot be specified and constrained using static dependencies.

These problems lead to the research questions for this dissertation: Can a component-oriented programming language be extended with constructs that permit specification of re-entrance conditions for components? Any approach to answering to the research question should address the two problems identified above and permit specification of re-entrance conditions across tightly- and loosely-bound interfaces. Furthermore, the specifications should attempt to satisfy the criteria outlined in section 1.2 and be contractual, formal, sufficiently expressive and sufficiently abstract.

3.4 Mentok: Enhanced Specifications

Component interfaces need re-entrance condition specifications, but static interface types cannot express re-entrance conditions at the method level and static module si- gnatures re-entrance conditions at the interface level. Mentok was developed to enable specification of both these conditions. The first novel language feature of Mentok is called negotiable interfaces, which at- tempt to solve the problem of specification of re-entrance conditions across method calls of an object. Negotiable interfaces enable specification of stateful component roles by adding a protocol, based on abstract tokens of state, that model changing method availa- bility. Negotiable interface protocols do not treat method execution as atomic, and allow specification of re-entrant or recursive patterns of calls, as well as out-calls. Negotiable interfaces use a combination of static and dynamic checking (the “negotiation” part of negotiable interfaces) to ensure that clients use an object according to its interface protocols, including re-entrance conditions. The second new language feature of Mentok is called type layers , which attempt to solve the problem of specification of re-entrance conditions across interfaces of an

53 3 Scope, Problem and Solution

application. Type layers build upon negotiable interfaces by adding declarative “layer” specifications to the signatures of modules. The type layers of an application’s modules combine to form an abstract control flow specification. The declarative nature of type layers enables a simple composition check to ensure that the interfaces of an application define a truly layered (acyclic) control flow. A combination of static and dynamic checking at negotiation points is then used to constrain re-entrant calling patterns in loosely-bound interfaces by preventing cyclic control flow through interfaces. This section introduces the two new language features by way of simple examples, using the cases from previous section. Negotiable interfaces and type layers are described in depth in chapters4 and5, respectively. The next two sections briefly describes how Mentok’s new language features, nego- tiable interfaces and type layers, with some examples of specifications enabled by these features.

3.4.1 Negotiable Interfaces: Dynamic Method Availability

Negotiable interfaces are an attempt to bridge the specification gap between static in- terface types and stateful roles that interface types can be used to specify. Traditional interface types present stateful roles as stateless, statically available sets of methods, and permit checking of static types at call sites. Negotiable interfaces model stateful roles with abstract state, and changing method availability as a product of that state. As a result, negotiable interfaces enable formal checking of method availability, at call sites. Rather than perform fully static checking, requiring alias analysis or protection schemes, Mentok mixes static checking with dynamic checking, providing a new programming language command, USE, for clients to negotiate with objects. To demonstrate how negotiable interfaces can model simple stateful roles with chan- ging method availability, the INamedBuffer interface from figure 3.5 has been recast into a negotiable interface in listing 3.8. The INamedBuffer negotiable interface defines several different state capabilities, cal- led tokens, which correspond to the states of figure 3.5: Unbound, Named, Empty, Full, and Closed. The initial abstract state of the interface is a single Unbound token, and each method is defined to have a behaviour matching the state transitions given in figure 3.5. For example, the Name method (line4) is specified as requiring a single Unbound token, and producing a single Named token. The errors from listing 3.3 have been recast in listings 3.8 and 3.9, taking advantage of Mentok’s USE statement. Mentok’s USE statement allows a programmer to test and transform the state of an object to a promised state. At the beginning of a USE statement, the request state is negotiated from the object, transformed by the body of the USE statement, and the promised state is returned at the end of the statement.

54 3.4 Mentok: Enhanced Specifications

1 2 TYPE 3 INamedBuffer =POINTERTOINTERFACERECORD 4 STATE Unbound, Named, Empty, 5 Full, Closed; 6 INIT [Unbound]; 7 END; 8 9 PROCEDURE (this:INamedBuffer) Name(id:Id) 10 :: [Unbound]->[Named]; 11 12 PROCEDURE (this:INamedBuffer) Open() 13 :: [Named]->[Empty]; 14 15 PROCEDURE (this:INamedBuffer) Put(i:Item) 16 :: [Empty]->[Full]; 17 18 PROCEDURE (this:INamedBuffer) Get() : Item 19 :: [Full]->[Empty]; 20 21 PROCEDURE (this:INamedBuffer) Close() 22 :: [Empty]->[Closed]; Listing 3.8: Negotiable interface specification of named buffer.

1 2 PROCEDURE InitializeBuffer(); 3 VAR 4 b : INamedBuffer; 5 BEGIN 6 b := NewBuffer(); 7 USE b :: [Unbound] -> [Full]DO 8 b.Name(someID); (* b: [Unbound] -> [Named] *) 9 b.Put(item); (*stateerror-- * 10 * required b: [Empty] * 11 * current b: [Named] *) 12 END; 13 ... 14 15 PROCEDURE RenameBuffer(b:INamedBuffer); 16 BEGIN 17 USE b :: [Empty]->[Named]DO 18 b. Close (); (* [Empty] -> [Closed] *) 19 b.Name(someID); (*stateerror: * 20 * required b: [Unbound] * 21 * current b: [Closed] *) 22 END; 23 ... Listing 3.9: Preventing simple errors with negotiable interfaces.

55 3 Scope, Problem and Solution

The routine InitializeBuffer creates a buffer and then performs a state-check and guard or USE, on line7. The body of the USE statement will attempt to Name and populate the buffer. Mentok’s state system now detects that the named buffer will not be in the correct state on line9. A static state check error is generated and the programmer must correct his or her error. The procedure RenameBuffer throws a similar error when it attempts to name a buffer in a [Closed] state. It is worth noting here that the USE statement, does not just contain a type-test, but also a type promise. In this case, the use statement promises to return the buffer in a [Named] state. Closer inspection of the INamedBuffer interface shows that this behaviour can never be satisfied. That is, the state [Named] is not reachable from an [Empty] state, and Mentok’s state check will reject any body of the USE statement as a result. Simple state-machine like roles can easily be specified using traditional pre- and post- condition contracts, of course, some of which can even be checked statically by model checking techniques. Expressing simple method availability is just a first step toward expressing more complex re-entrance conditions, for which method execution must be treated as non-atomic.

3.4.1.1 Specifying Patterns of Re-entrance

Method invocations are not atomic; objects may make recursive calls or out-calls to other objects which may make re-entrant calls in turn. Mentok allows specification of such patterns of calls using two additional kinds of behaviour:

• Re-entrant behaviours for methods allow specification of patterns of re-entrant or recursive calls for a method.

• Cooperative behaviours allow specification of the sequences of out-calls that a method may make during execution.

These two behaviours can be combined to constrain re-entrance for tightly bound inter- faces. Listing 3.10 gives negotiable interface types for the model/view example from section 3.2.1.1. IModel is declared to have two tokens, update and observe, and an initial state of an update token, and an observe token. The method Set is declared to only be avai- lable when the model is updateable and observable (it consumes [update,observe] but replaces it with [update,observe]), and the method ValueOf is available whene- ver the model is observable. Set is also declared to have a re-entrant behaviour of [observe]->[observe] (line8), meaning that during the course of execution Set, the model will be re-entered by a sequence of method calls that have the composite behaviour of [observe]->[observe] (i.e. zero or more calls to ValueOf).

56 3.4 Mentok: Enhanced Specifications

1 2 TYPE 3 IModel =POINTERTOINTERFACERECORD 4 STATE update,observe; 5 INIT [update,observe]; 6 END; 7 8 PROCEDURE (this:IModel :: [observe]->[observe]) Set(k : Key; v : Value) 9 :: [update,observe]->[update,observe]; 10 11 PROCEDURE (this:IModel) ValueOf(k) : Value 12 :: [observe]->[observe]; 13 14 ... 15 16 TYPE 17 IView =POINTERTOINTERFACERECORD 18 STATE notify; 19 INIT [notify]; 20 END; 21 22 PROCEDURE (this:IView) Notify(k : Key; m : IModel :: [observe]->[observe]) 23 :: [notify]->[notify]; Listing 3.10: Interface specification for IModel and IView.

IView is declared to have only one token type, notify, and an initial state of a single notify token. The method Notify can only be called when the view is notifiable (in this case, when the notify method is not currently executing), and has a cooperative behaviour of the form [observe]->[observe] (line 22) for its model parameter. This means that any implementation of Notify can call into the model, but can only call the method ValueOf.

Listings 3.11 and 3.12 give implementations for model/view negotiable interfaces. The call to IView.Notify on line 20 of listing 3.11 passes the re-entrancy identifier THISINT to the model’s views.6 This helps to explicitly identify out-calls which may result in re-entrance. Also of note, on line 25 of listing 3.12 the error in BrokenView is detected statically as the conditions for re-entering the model are established by the model para- meter’s cooperative behaviour. The burden of dynamic checking for the view is reduced to ensuring that the model parameter passed to Notify is the same as the model being viewed.

Negotiable interfaces can specify some patterns of re-entrance for sets of interfaces that explicitly refer to each other by specification. The next section introduces type layers, which can constrain re-entrance between interfaces that have no explicit relationships.

6 The THISINT keyword is actually syntactic sugar for casting the receiver parameter to type of the explicitly implemented interface. This is covered further in chapter4.

57 3 Scope, Problem and Solution

1 2 TYPE 3 Model =POINTERTORECORD (+IModel) 4 views: IViewList; 5 table: Table; 6 END; 7 8 PROCEDURE (this:Model.IModel :: [observe]->[observe]) 9 Set(k : Key; v : Value) :: [update,observe]->[update,observe]; 10 VAR 11 i :INTEGER; 12 view : IView; 13 BEGIN 14 (* THISINT :: [observe] *) 15 this.table.SetValue(k,v); (* set the value *) 16 FORi:=0TO views.Count()-1DO 17 view := this.views.view(i); 18 USE view :: [notify]->[notify]DO 19 (* THISINT :: [observe] *) 20 view.Notify(k,THISINT); 21 (* THISINT :: [observe] *) 22 END; 23 END; 24 (* THISINT :: [observe] *) 25 END Set; 26 27 PROCEDURE (this:IModel) ValueOf(k) : Value 28 :: [observe]->[observe]; 29 BEGIN 30 IF this.table.Contains(k)THEN 31 RETURN this.table.Value(v); (* return value *) 32 ELSE 33 RETURNNIL; (* return NIL *) 34 END; 35 END ValueOf; 36 37 ... Listing 3.11: Implementation of IModel.

58 3.4 Mentok: Enhanced Specifications

1 2 TYPE 3 BrokenView =POINTERTORECORD (+IView) 4 model : IModel; 5 display: IDisplay; 6 END; 7 8 PROCEDURE (this:BrokenView.IView) 9 Notify(k : Key;m:IModel::[observe]->[observe]) 10 :: [notify]->[notify]; 11 VAR 12 newValue : Value; 13 BEGIN 14 ASSERT(this.model = m); 15 (* m :: [observe] *) 16 newVal = m.GetValue(k); (* m :: [observe] -> [observe] *) 17 (* m :: [observe] *) 18 IF this.Censors(k)THEN 19 IF newVal =NILTHEN 20 RETURN; (* Censored removed. *) 21 ELSE 22 (*TYPEERROR:* 23 * Required :: [update,observe] * 24 * Actual ::[observe] *) 25 m.SetValue(k,NIL); 26 END 27 ELSE 28 ... 29 END; 30 END Notify; 31 32 ... 33 34 TYPE 35 View =POINTERTORECORD (+IView) 36 model : IModel; 37 display: IDisplay; 38 END; 39 40 PROCEDURE (this:View.IView) 41 Notify(k : Key;m:IModel::[observe]->[observe]) 42 :: [notify]->[notify]; 43 VAR 44 newValue : Value; 45 BEGIN 46 ASSERT(this.model = m); 47 (* m :: [observe] *) 48 newVal = m.GetValue(k); (* m :: [observe]->[observe] *) 49 (* m :: [observe] *) 50 IF (newVal =NIL)THEN 51 ... 52 END; 53 (* m :: [observe] *) 54 END Notify; Listing 3.12: Implementations of IView. 59 3 Scope, Problem and Solution

3.4.2 Type Layers: Dynamic Interface Availability

Modules were originally proposed as a means of specifying a “part” of a program, so that other pieces of software could use a module without additional information[Par72]. Modules and their interfaces are now used as a mechanism to specify the architecture of applications and, as programming language abstractions, the specifications provided by modules can be enforced by compilers and other software tools. Layering is a well-known pattern[BMR+96] that groups software abstractions into equivalence classes that have the same sets of dependencies. Layering is a useful architec- tural technique as it helps organize modules into hierarchies and describe the abstract control flow of an architecture. At the programming language level, layering of modules is expressed in module signa- tures as static dependencies; this is generally sufficient for monolithic, non-extensible applications but for component-based applications, which are incomplete by definition, there are no mechanisms for checking and enforcing layering of static dependencies for all configurations of components. This issue is exacerbated by the approach of using interfaces as “specifications in the middle” between components, as components may re- ference a range of statically defined interfaces, and break the intended layering between them. Type layers are an extension to module signatures that allow programmers to spe- cify a layering, a partial order, over modules and negotiable interfaces that specifies abstract control-flow interfaces: method calls from one negotiable interface to another must always go “down-layer”. The specified control-flow is then enforced using two mechanisms:

• A check is performed at USE negotiations to ensure control flow between negotiable interfaces respects the layering. Negotiation checking can be performed both stati- cally and dynamically, depending on the known state of the program.

• A composition check of type layers ensures that no component breaks layering by introducing cycles; cycles make the layering meaningless.

Combined, these two checks ensure that control flow follows the abstract specification defined by the type layers of an application. The composition check ensures freedom from cycles so that layering is meaningful. The control-flow check afforded by negotiable interfaces ensures that the layered control-flow is respected.

60 3.4 Mentok: Enhanced Specifications

1 COMPONENTMODULE App; 2 3 COMPOSE Presentation,Logic,Data; 4 5 COMPONENTLAYER 6 Data < Logic, 7 Logic < Presentation; 8 9 TYPELAYER 10 (* Local layering *) 11 IPostMessage < IApp, 12 IApp < IAppRoot, 13 Presentation.IWindow < IApp, 14 IPostMessage < Presentation.IWindowItem; 15 16 ... 17 18 END App; 19 20 COMPONENTMODULE Presentation; 21 22 TYPELAYER 23 24 IWindowItem < IWindow; 25 26 ... 27 28 END Presentation; Listing 3.13: Type layer declarations.

Listing 3.13 demonstrates a set of layer declarations for the application architecture originally given in section 3.2.2. Line5 give examples of coarse-grained, component layering specifications; such specifications indicate the direction of control flow through the interfaces defined in the layered modules. In the example, the layering specifies that the Logic module is layered above the Data module, meaning that the components implementing interfaces from the Logic module may call into interfaces defined by the Data module; components implementing Data interfaces cannot call into Logic interfaces. The layering relationship is transitive, so the Presentation module is the Data layer as well as the Logic layer. Line9 gives examples of fine-grained layering of individual interfaces. Fine-grained type layers specify a control-flow dependency between interfaces. For example, the in- terface IPostMessage from the Application module is layered below the IWindowItem interface from the Presentation module. The intent behind this layering is as follows: a window item can post a message to the application message queue, but the message must be handled asynchronously to allow the window item to finish its current method

61 3 Scope, Problem and Solution

Figure 3.9: Type layer application structure.

call without being re-entered. Type layers may be used to visualize the abstract architecture of a component or application. Programmers can use type layer visualizations as an aid when learning or designing a new system. Figure 3.9 is actual output from MentokC for the code sample in listing 3.13.7 The layered design of the application is visualized, with the Presentation module layered above the Logic module, which in turn is layered above the Data module. The Application module provides two types, IAppRoot and IApp, that are placed at the top of the layering, and another, IPostMessage, that is placed below some Presentation

7 Some detail has been hand trimmed from this diagram for brevity.

62 3.4 Mentok: Enhanced Specifications

Figure 3.10: Up layer call.

Figure 3.11: Cross layer call. layer types. This reflects the ability of the Presentation layer to post work items to an application message queue, and the guarantee that posting a message will never result in a call-back into the Presentation layer.

3.4.2.1 Constraining Control Flow and Re-entrance

Section 3.2.2.1 examined an erroneous case of re-entrance that occurred between objects implementing loosely-bound interfaces. The error occurred because a layering between types (IPostMessage and IDataSource) was broken. Type layers can be used to specify such constraints and, being part of the type system of a programming language, can be checked. Type layer checking ensures that program control flow follows the abstract design of type layers: a program can only call “downwards” through the layering at any given time. Note that it is still possible for an object to implement types from different layers, which means that object re-entrance is still possible; however, patterns of cyclic calls back to interfaces from higher layers are prevented. Type layers constrain the availability of the interfaces declared by a program and thus constraining the actions that a re-entrant call may initiate. Listing 3.14 revisits the problem from section 3.2.2.1, using the type layering from figure 3.9. The Post method is prevented from initiating cyclic calls to the window (line 41, illustrated in figure 3.10) and from calling across layers to close the data source

63 3 Scope, Problem and Solution

1 TYPE 2 IApp =POINTERTOINTERFACERECORD 3 STATE run,end; 4 INIT [run]; 5 END; 6 7 IPostMessage =POINTERTOINTERFACERECORD 8 STATE post; 9 INIT [post]; 10 END; 11 12 PROCEDURE (this:IApp) MsgLoop() :: [run]->[end]; 13 14 PROCEDURE (this:IPostMessage) Post(m:IMsg) :: [post]->[post]; 15 16 ... 17 18 TYPE 19 Application =POINTERTORECORD (+IApp + IPostMessage) 20 window : IWindow; 21 dataSource : IDataSource; 22 msgs : MsgList; 23 END; 24 25 PROCEDURE (this:Application.IPostMessage) Post(m : IMsg) 26 :: [post]->[post]; 27 VAR 28 d: IDataSource; 29 w: IWindow ; 30 BEGIN 31 (* Layer = IPostMessage *) 32 ASSERT(m #NIL); 33 IF m.Type() = CloseTHEN 34 (* Shortcut - free everything and shutdown now. *) 35 w := this.window; 36 (*LAYERERROR:* 37 * Requested : IWindow * 38 * Actual : IPostMessage *) 39 USE 40 w :: [active]->[inactive]DO this.window.Close() 41 END; 42 d := this.dataSource; 43 (*LAYERERROR:* 44 * Requested : IDataSource * 45 * Actual : IPostMessage *) 46 USE d :: [bound]->[free]DO this.dataSource.Free()END; 47 HALT (0); 48 ELSE 49 (* otherwise add the message to the queue *) 50 this.msgs.Enqueue(m); 51 END; 52 END Post; Listing 3.14: Loosely bound re-entrance.

64 3.5 Summary

(line 46, illustrated in figure 3.11). In both cases, it is possible to statically detect that the Post method is attempting to call up or across layers. If the current layer is not known statically, type layers can be tested and guarded by extending the semantics of USE to include a layer check ensuring that the dynamic call ordering by layers is respected.

3.5 Summary

Object based approaches to component programming are affected by problems of re- entrance and inter-object consistency. The static type systems of object based and object oriented languages do not allow specification and constraint of behaviours leading to such problems, however. Mentok provides features that allow specification and constraint of control flow through objects, namely negotiable interfaces and type layers. The next two chapters describe, in detail, negotiable interfaces and type layers in Mentok.

65 66 4 Negotiable Interfaces

That statement is either so deep it would take a lifetime to fully comprehend every particle of its meaning, or it is a load of absolute tosh. Which is it, I wonder? (Terry Pratchett, Hogfather)

Traditional object-oriented interfaces are the basis for component specifications but can only express and enforce simple wiring safety properties, such as preventing “message not- understood” errors and ensuring data values are passed to and from method invocations. This level of specification is insufficient for stateful component roles, correct implemen- tation of which requires any given implementation to encapsulate and maintain state during and between method calls. Consistency of this state often relies on correct inter- actions between clients and objects, and when method execution is non-atomic object re-entrance can expose intermediate state to clients. Component oriented programming has a different deployment paradigm and requires a new programming discipline. Component applications are extensible, and thus incom- plete by definition, meaning that component instances, or objects, cannot necessarily be tested in composition before deployment. Object re-entrance is a global system pro- perty, however, meaning that it can occur across component boundaries. Specification of components must, therefore, include specification of re-entrance conditions to enable a component to be proofed against re-entrance before deployment. This chapter presents negotiable interfaces, a language feature of Mentok that enables specification of some object re-entrance conditions. Negotiable interfaces in Mentok have been fully implemented by MentokC, the Mentok compiler. This chapter also presents

MentokP , a subset of Mentok that was created and formalized using Isabelle/HOL[Wen00,

NPW00]. MentokP models enough of Mentok to enable investigation of the safety properties of negotiable interfaces but is not intended to be a usable programming language itself. Negotiable interfaces are enhanced interface types that allow specification of method availability as a product of temporal object state, including out-calls and re-entrance. Negotiable interfaces have a dynamic representation that can be tested at runtime via a mechanism called negotiation. A static state checking step ensures that clients of Mentok objects respect the method availability protocols specified by negotiable interfaces to

67 4 Negotiable Interfaces

help prevent errors that may otherwise occur after deployment. The next section gives a informal description of the syntax, semantics, and type system features of negotiable interfaces, as implemented in Mentok. Section 4.2 describes a

formalization of a subset of Mentok called MentokP , which was created to investigate safety properties of negotiable interfaces. Section 4.3 discusses how the Mentok compiler, MentokC, and the Mentok runtime libraries implement negotiable interfaces. Section 4.4 discusses some problems with and extensions to negotiable interfaces in Mentok, and while section 4.5 discusses related work. Section 4.4 discusses some problems with and extensions to negotiable interfaces in Mentok, and while section 4.5 discusses related work. This chapter assumes a basic familiarity with Component Pascal syntax, as well as shorthand and concepts described in section 3.1.

4.1 Negotiable Interfaces in Mentok

Negotiable interfaces in Mentok can be broken down into four pieces: negotiable interface types, negotiable objects, dynamic negotiation, and static state checking.

1. Negotiable interface types are object-oriented interfaces extended with an pro- tocol that specifies method availability based on abstract state. Unlike other pro- tocol based approaches to specifying component behaviours, negotiable interface protocols do not treat methods as atomic operations, and actually include explicit notions of out-calls and re-entrance.

2. Negotiable objects are objects that provide operations via negotiable interfaces. At runtime, the abstract state based portion of an object’s negotiable interface type or types is reified. This abstract state is transformed as clients perform operations via the object’s negotiable interfaces.

3. Negotiation is a mechanism provided by the Mentok USE statement that allows clients to perform complex operations with negotiable objects. A negotiation begins with a request and promise by a client to perform a certain set of operations on a negotiable object. If the negotiation succeeds, the client is guaranteed to be able to perform those operations, otherwise the client is given no guarantees. Clients are statically prevented from performing illegal operations, (i.e. operations on an object without a guarantee), by Mentok’s state system.

4. Mentok’s state system, an extension to traditional static type systems, statically models the state of references to negotiable objects to check client code of nego- tiable interfaces. State checking statically rejects any program in which a client

68 4.1 Negotiable Interfaces in Mentok

attempts to perform an operation on an object when the state of the object is unknown, or is known to be different to the required state of the operation. State checking also ensures that clients of objects satisfy the obligations of negotiation, as well as re-entrance and out-calls as specified by negotiable interface types.

Subsection 4.1.1 introduces the concepts, syntax and diagrammatic representation of negotiable interface types, subsection 4.1.2 discusses negotiable objects and negotiation, and subsection 4.1.3 describes the Mentok’s state system.

4.1.1 Negotiable Interface Types

Negotiable interface types are interfaces enhanced with an abstract state-based protocol. That is, the temporal behaviour of a negotiable interface is modeled by a dynamically changing abstract state that models the availability of methods. The methods of a negotiable interface in turn specify how the abstract state of the interface is modified. In addition to specifying how an operation changes the state of an interface as a result of execution, negotiable interface methods can also specify self-interference patterns and out-calls that occur during the course of execution. This allows a programmer to declaratively specify complex interactions between objects. The abstract state of a negotiable interface type is represented by a multiset of tokens, the basis set of which is declared by the negotiable interface type. Each negotiable interface also specifies an initial state, which will represent the abstract state of a newly created negotiable object. Modeling the abstract state of negotiable interface protocols using a factorable representation allows state to be split apart and transformed independently. The methods of a negotiable interface specify how the abstract state is transformed, using multiset transformations called behaviours. A behaviour is a pair, written

BREQ->BRET , where the required state, BREQ, and the returned state, BRET , are both multisets of tokens. A behaviour can be applied to an interface state, S (also a multiset of tokens), if the required state BRET is a submultiset of S. Applying a beha- viour to S transforms S by subtracting the BREQ from S then adding BRET to the result, using multiset addition and subtraction. There are three kinds of behaviour a method can specify: • Method behaviours specify how a method call transforms the state of an object as the result of call and execution.

• Re-entrant behaviours specify intermediate state transformations during a me- thod invocation the occur as the result of re-entrant or recursive calls.

• Cooperative behaviours specify out-calls that must be made to cooperating objects during the course of execution.

69 4 Negotiable Interfaces

Finally, negotiable interface inheritance uses a simple interpretation of behavioural sub- typing that permits the protocols of negotiable interfaces to be aggregated and extended. Negotiable interfaces may add additional tokens and behaviours to an inherited proto- col, but may not remove tokens or change behaviours of existing methods. Interfaces that inherit from multiple interfaces can combine the protocols of inherited interfaces by performing token aliasing. Token aliasing equates tokens from inherited interfaces, thereby equating states of the inherited protocols allowing them to interact. Section 4.1.1.1 describes the syntax for declaring simple negotiable interface types and describes the diagrammatic representation of negotiable interface protocols, while section 4.1.1.2 describes negotiable interface inheritance.

4.1.1.1 Simple Negotiable Interface Types

Figure 4.1 gives Mentok’s syntax for declaring negotiable interface types without subty- ping. The full syntax with subtyping declarations is given in the following section, which treats negotiable interface inheritance. A negotiable interface type declaration (in the T ypeDef production) consists of a name, a token set declaration, an initial state (from the InterfaceBody production), and a set of method declarations (the MethodDec production).1 Unlike C# or Java, methods of an interface are not declared in a specific lexical scope. Instead, each method has a receiver parameter which specifies the type that owns the method, as in Oberon and Component Pascal. Negotiable interface method declarations allow optional specification of behaviours on in three distinct places:

1. The optional behaviour specified at the end of the declaration is the method behaviour, which specifies how the abstract state of the interface is transformed as a result of executing the method.

2. The optional behaviour on the receiver parameter (via the P arameter production) declares the re-entrant behaviour of the method. The re-entrant behaviour of a method specifies a pattern of re-entrant or recursive calls that will be made as a result of calling a method.

3. The optional behaviour on each formal parameter of the method (via the P arameter production) declare cooperative behaviours. Cooperative behaviours can only be specified on parameters with negotiable interface types, and are behaviours that apply to that parameter. During the course of executing the method, the method

1 The T oken production, which states that tokens are identifiers, is included to disambiguate identifiers for tokens, and identifiers for type names, parameter names, etc.

70 4.1 Negotiable Interfaces in Mentok

Token ::= Ident S t a t e ::= "["[ Token { "," Token } ]"]" TypeIdent ::= Ident [ TypeIdent "." TypeIdent ] Behaviour ::= State "->" S t a t e InterfaceBody ::= "INTERFACE""RECORD" "STATE"[ Token { "," Token } ]";" "INIT" S t a t e ";" "END" Type ::= InterfaceBody | RecordBody | ... TypeDef ::= Ident =Type Parameter ::= Ident ":" TypeIdent ["::" Behaviour ] Parameters ::= [ Parameter { ";" Parameter } ] MethodDec ::= "PROCEDURE""(" Parameter ")" I d e n t "(" Parameters ")" [":" TypeIdent ][ :: Behaviour ]";"

Figure 4.1: Syntax for simple negotiable interface declarations (no subtyping).

must satisfy the cooperative behaviour by calling a pattern of methods on that parameter.

Listing 4.1 gives two examples of negotiable interface type declarations:

• The STATE declaration specifies a set of tokens for an interface. On line3 of listing 4.1, the interface IFoo declares four tokens, a, b, i1 and i2, which represent the capabilities or state for the IFoo’s protocol.

• The INIT declaration specifies the initial state for an interface. A state is a multi- set of the tokens declared by the interface. Line4 of listing 4.1 shows the initial state, [a], for the interface IBar. A newly instantiated object that implements the IBar interface will be in the state [X,X].

• The method behaviour on line 13 specifies that the method IFoo.Foo1 can only be executed when the interface has a token a, which will be replaced by the token b when the method has finished executing.

• Method IFoo.Foo2 on line 15 has the re-entrant behaviour [i1]->[i2]. During execution of IFoo.Foo2, an object will be re-entered by or will recursively call a method or sequence of methods which transform its intermediate state from i1 to i2. For interface IFoo, this re-entrant method can be only be satisfied by a single call to IFoo.Foo2. This intermediate state transformation occurs as a result of the outer transformation [b]->[a] specified as the method behaviour of IFoo.Foo2.

71 4 Negotiable Interfaces

1 TYPE 2 IFoo =POINTERTOINTERFACERECORD 3 STATE a,b,i1,i2; 4 INIT [a]; 5 END; 6 7 IBar =POINTERTOINTERFACERECORD 8 STATEX,Y; 9 INIT[X,X]; 10 END; 11 12 13 PROCEDURE (this:IFoo) Foo1() :: [a]->[b]; 14 15 PROCEDURE (this:IFoo::[i1]->[i2]) Foo2() :: [b]->[a]; 16 17 PROCEDURE (this:IFoo) Foo3() :: [i1]->[i2]; 18 19 PROCEDURE (this:IBar) Bar1() :: [X,X]->[Y]; 20 21 PROCEDURE (this:IBar) Bar2() :: [Y]->[X,X]; 22 23 PROCEDURE (this:IBar) Bar3(f:IFoo::[a]->[b]) :: [X]->[]; Listing 4.1: A pair of basic negotiable interface declarations.

Re-entrant behaviours are optional, since not all methods are self-interfering; howe- ver, it is a requirement that re-entrant behaviours can only be specified for stateful methods (methods with behaviours), since method that undergoes an intermediate, observable state transformation is stateful by definition.

• Method IBar.Bar3 (on line 23) has the cooperative behaviour [a]->[b] on a parameter named f, which is of type IFoo. IBar.Bar3 can only be called if it is passed an object which holds a token, a, which as a result of execution will be transformed to b by a sequence of out-calls. This cooperating state transformation occurs as a result of the outer transformation [X]->[] specified as the method behaviour of IBar.Bar3.

Cooperative behaviours can only be declared for parameters of negotiable interface type, and are optional.2

The protocol of a negotiable interface type is represented diagrammatically as follows:

2 Cooperative behaviours can be declared for methods without behaviours as well as static and nested proce- dures, in which case they are called stateful parameters since they do not necessarily involve cooperating state transformations.

72 4.1 Negotiable Interfaces in Mentok

1. The initial state of the interface is represented as a double circle labeled with the initial state.

2. If a method can be applied to a given state in the protocol, an arrow labeled with the method name is drawn from the given state to a circle labeled with the state obtained by applying the method’s behaviour. If the new state is not the initial state it is drawn as a single labeled circle. • Re-entrant state transformations, as in are split into four parts: – the invocation arrow, represented as a dashed line with the arrow head pointing from the beginning state; – the intermediate states, represented as labeled boxes; – the intermediate state transformations which may be method, re-entrant or cooperative transformations; – and the return arrow, represented as a dashed line with the arrow head pointing to the finishing state. • Cooperative state transformations are shadowed in as dotted transformations “behind” the method transformation they occur within. Cooperative state transformations may be elaborated, or unelaborated. – Elaborated state transformations illustrate each method call and state for the cooperative state transformation. Each arrow in an elaborated state transformation is labeled with a parameter name, interface name and method name. – Unelaborated state transformations show only a single arrow from the in- state to the out-state of the cooperative behaviour The arrow is labeled with the parameter name and type. • Arrows that point into empty space indicate that only part of the protocol has been displayed. This could be due to the protocol having a infinite number of legal states.

The interfaces IFoo and IBar from listing 4.1 are represented diagrammatically in figures 4.2 and 4.3 respectively. Interface IFoo has a method, IFoo.Foo2, that has a re-entrant behaviour that must be satisfied by calling IFoo.Foo3. Method IBar.Bar3 of IBar has a cooperative behaviour that can be satisfied by calling method IFoo.Foo.

73 4 Negotiable Interfaces

Figure 4.2: IFoo protocol with reentrant behaviour.

Figure 4.3: IBar protocol with cooperative behaviour.

4.1.1.2 Negotiable Interface Inheritance

A negotiable interface can extend or inherit from zero or more negotiable interfaces, providing that no cycles are introduced into the interface type hierarchy. A negotiable interface inherits the methods, and token sets from its parents but may not change

74 4.1 Negotiable Interfaces in Mentok

Token ::= Ident TokenDec ::= Token [ ":=" "{" TypeIdent "." Token { "," TypeIdent "." Token } "}"] TypeIdent ::= Ident [ TypeIdent "." TypeIdent ] | ... S t a t e ::= "["[ Token { "," Token } ]"]" Behaviour ::= State "->" S t a t e InterfaceBody ::= "INTERFACE""RECORD"["(" { "+" TypeIdent } ")"] "STATE"[ TokenDec { "," TokenDec } ]";" "INIT" S t a t e ";" "END" Type ::= InterfaceBody | RecordBody | ... TypeDef ::= Ident =Type

Parameter ::= Ident ":" TypeIdent ["::" Behaviour ] Parameters ::= [ Parameter { ";" Parameter } ] MethodDec ::= "PROCEDURE""(" Parameter ")" I d e n t "(" Parameters ")" [":" TypeIdent ][ :: Behaviour ]";"

Figure 4.4: Syntax for declaration a negotiable interface type (with subtyping). the existing signatures, or behaviours of inherited methods, nor remove tokens. This restriction ensures that negotiable interfaces are substitutable for their parent types with respect to Mentok’s type and state system. Negotiable interfaces may, however, declare new methods and new tokens to effectively extend inherited protocols. Parent protocols can also be combined by a sub-interface by declaring an alias token, which creates an equivalence class for inherited tokens. Figure 4.4 gives the complete syntax for declaring negotiable interfaces, including syntax for negotiable interface inheritance. There are three things of note:

• As in GPCP, a parenthesized list of interface type names, each prefixed by the character ’+’, declares the list of inherited interfaces (in the InterfaceBody produc- tion).3

• Interfaces can declare new methods with new behaviours. The syntax for method declaration is unchanged, but the new methods can have behaviours that operate over the entire inherited token set, effectively extending inherited protocols with extra arrows.

• Token declarations (also in the InterfaceBody production) take two forms. A token identifier by itself is a new token declaration, which adds a new token to the

3 Records (classes) are similarly declared to implement a list of interfaces, but optionally may include a type name at the front of the list that specifies the base class.

75 4 Negotiable Interfaces

inherited token set. The second form of token declaration is the alias tokens which is declared as follows: alias := { IParent 1.t 1 , IParent 2.t 2, ...} Alias tokens are an equivalence class across one or more inherited tokens, and a name for the equivalence class for the negotiable interface protocol. Tokens in the alias equivalence class are qualified by their parent interface name in order to prevent name clashes. In the code snippet above, the alias token is named “alias”, and it aliases, or names an equivalence class for token “t 1” from interface “IParent 1”, “t 2” from interface “IParent 2”, and so on. Aliases for an equivalence class of size one can be used to rename tokens within the namespace of the new interface. Aliases for more than one inherited token can be used to combine inherited protocols, as potential states of protocols are made equivalent. Aliases only know in the namespace of the child interface; the renaming does not affect parent interfaces, or client code of parent interfaces. The following checks are performed for token aliases: – In the namespace of the new interface, each inherited token must be known by one unique name. If the parents of an interface have a common ancestor, and tokens from the ancestor have been aliased independently by the parents, the new interface must declare new aliases to ensure that those tokens have a single name, and a single equivalence class. – Tokens from the same interface may not be placed in the same equivalence class by an alias. If permitted, this could have an unintended effect on local negotiations (discussed in the next chapter).

Listing 4.2 and figure 4.5 give the source listing and protocol diagrams for three simple negotiable interfaces, ISizeable, IHost, and IItem, representing roles for sizeable, host, and hostable widgets respectively.4 Listing 4.3 give the source listing for an interface, IHostableContainer, that inherits from ISizeable, IHost, and IItem. IHostableContainer is a sizeable host that is a hostable item itself, but is only sizeable when its set of hosted children is locked, and when it has a parent host to give it a size. This restriction is specified via the token alias on line5, which declares an equivalence class named closed between the inherited closed, hosted, and sizeable. Finally, IHostableContainer declares a new method for laying out its children, called Layout, that can only be called when it is hosted, and locked. Figure 4.6 gives the protocol for IHostableContainer, which combines

4 Some details of the method signatures for these interfaces, including cooperative and re-entrant beha- viours, have been omitted for brevity.

76 4.1 Negotiable Interfaces in Mentok

TYPE ISizeable =POINTERTOINTERFACERECORD STATE sizable; INIT [sizable]; END;

IHost =POINTERTOINTERFACERECORD STATE closed,open,item; INIT [closed]; END;

IItem =POINTERTOINTERFACERECORD STATE hosted,unbound; INIT [unbound]; END;

PROCEDURE (this:ISizeable) Resize :: [sizable] ->[sizable];

PROCEDURE (this:IHost) Unlock :: [closed] ->[open];

PROCEDURE (this:IHost) Lock :: [open] ->[closed];

PROCEDURE (this:IHost) HostItem :: [open] ->[open,item];

PROCEDURE (this:IHost) FreeItem :: [open,item] ->[open];

PROCEDURE (this:IItem) Host :: [unbound] ->[hosted];

PROCEDURE (this:IItem) Free :: [hosted] ->[unbound]; Listing 4.2: Parent interfaces: IHost, IItem, ISizeable

Figure 4.5: Protocols for ISizeable, IHost, and IItem (clockwise from upper left). 77 4 Negotiable Interfaces

1 TYPE 2 IHostableContainer = 3 POINTERTOINTERFACERECORD (+ ISizeable + IItem + IHost) 4 STATE closed := 5 {IHost.closed + IItem.hosted + ISize.size}; 6 INIT [layout]; 7 END; 8 9 PROCEDURE (this: IHostableContainer) Layout 10 :: [closed]->[closed]; Listing 4.3: Child interface: IHostableContainer

Figure 4.6: Protocol for IHostableContainer.

the protocols of its parent interfaces through the closed alias token, and adds a new arrow for the Layout method.

4.1.2 Objects and Negotiation

While the protocols of negotiable interface types allow specification of some temporal behaviours of component roles, they are of little use if they are not formally checked and enforced at the programming language level. Pure static checking of Mentok’s negotiable interface protocols would require in the least global alias analysis (impossible for incomplete component applications), or an alias protection scheme such as that in [CPN98]. Instead, protocol checking in Mentok is partially dynamic. Each object that provides negotiable interfaces, called negotiable objects, holds a mul- tiset of tokens, corresponding to a negotiable interface protocol state. Clients wishing

78 4.1 Negotiable Interfaces in Mentok to use the services of a negotiable object must negotiate for state, using a Mentok’s USE command. A successful negotiation results in the client obtaining a stateful reference to the object, which guarantees the ability to call the requested services.

4.1.2.1 Classes and Negotiable Objects

Token ::= Ident S t a t e ::= "["[ Token { "," Token } ]"]" Behaviour ::= State "->" S t a t e TypeIdent ::= Ident [ TypeIdent "." TypeIdent ] Parameter ::= Ident ":" Type ["::" Behaviour ] Parameters ::= [ Parameter { ";" Parameter } ] RecordBody ::= "RECORD"["(" { "+" TypeIdent } ")"] F i e l d s "END" MethodImp ::= "PROCEDURE" "(" I d e n t ":" TypeIdent "." TypeIdent [ "::" Behaviour ]")" I d e n t "(" Parameters ")"[":" TypeIdent ] [ :: Behaviour ]";" Statements "END" I d e n t

Figure 4.7: Syntax for implementing methods.

Partial syntax for record declarations and method implementation in Mentok is given in figure 4.7. Record declarations in Mentok are similar to the in GPCP (section 3.1.3), in that each record or class can implement zero or more interfaces, and must provide a signature matching method implementation for each method declared by its interfaces. The syntax for method implementations provides for an optional re-entrant behaviour, optional cooperative behaviours on parameters, and an optional method behaviour. Instances of records that implement negotiable interfaces are called negotiable objects. For each negotiable interface it provides, a negotiable object holds a multiset of the interface’s tokens, representing a state in the interfaces protocol graph. When the object is first created, (i.e. by a NEW statement), the object is assigned the initial state declared by each interface it implements. This state is then transformed by the object’s negotiable interface methods; as a method is called, the “in” multiset of the method behaviour is consumed, and when the method returns, the “out” multiset is returned. Before a method can be called, however, clients must negotiate with negotiable objects to ensure that any method calls can legally be performed.

79 4 Negotiable Interfaces

Token ::= Ident S t a t e ::= "["[ Token { "," Token } ]"]" Behaviour ::= State "->" S t a t e Use ::= "USE" I d e n t "::" Behaviour "DO" Stmts { "|" I d e n t "::" Behaviour "DO" } ["ELSE" Stmts ] "END" Stmts ::=Use | I f | For | ...

Figure 4.8: Syntax for USE.

4.1.2.2 Negotiation

Negotiation is the term given to type-testing and guarding of negotiable interface proto- cols. Negotiation is a dynamic check, that consists of a request and a promise from a client to an object, in the form of a negotiation behaviour; the in-state of the behaviour is the request for state, and the out-state is the promised transformed state. A successful negotiation gives a client a stateful reference to a negotiable object that initially holds the requested in-state multiset of the negotiation behaviour. The client may then invoke a sequence of methods via the stateful reference, transforming the multiset of tokens as- sociated with the stateful reference until the out-state is reached, at which point the negotiation completes and the state is returned to the object. Negotiation in Mentok is provided by the USE statement, the syntax of which is given in figure 4.8. The USE statement is similar to Component Pascal’s WITH statement, in that it has multiple branches and performs a type test and guard on a variable of the statement. In the case of the USE statement, the variable, called the subject of the negotiation and the test and guard is on the protocol state of the object to which the subject refers. As with WITH, branches are tested sequentially, and when a branch test succeeds, that branch is executed with the knowledge that the subject meets the requirements of the test; no further branches are tested or executed. In the case of USE statements, a successful test is known as a successful negotiation, and the subject of the negotiation becomes a stateful reference for the body of the successful branch. A stateful reference is a reference to a negotiable object that has been granted exclusive use of some part of that object’s state. A successful negotiation not only tests negotiable object state, but locks it for use by the negotiation client. There USE statement has different semantics depending on whether the negotiation subject is a stateful reference or not. If the subject is already stateful (perhaps because of an outer USE statement), the USE command has local negotiation semantics. If the subject is not stateful, the USE command has global negotiation semantics.

80 4.1 Negotiable Interfaces in Mentok

USE

1.

i :: ??

2.

E obj. (i* = obj) & True (b < state obj)

False

3a. state obj’ -= b i’ :: b

3b. 4. Branch_True Branch_False

3c. i :: b’ state obj’ += b’ i’ :: ??

END

Figure 4.9: Negotiation semantics.

81 4 Negotiable Interfaces

Global Negotiation Figure 4.9 informally illustrates the semantics of global negotiation, for a USE command of the form: 5 USE i :: b -> b’ DO Branch True ELSE Branch False END

1. The client knows nothing about the state of the subject, i, at the start of the negotiation.

2. If the subject references some object, (obj), and the object has at least as many tokens as b, the in-state of the negotiation behaviour, then the negotiation succeeds, otherwise the negotiation fails.

3. If the negotiation succeeds: a) The requested state, b, is removed from the state of the object, obj, and associated with the subject, i. i is now said to be stateful. The state is removed from the object and granted to the client to give the client exclusive use of the object and prevent concurrent negotiations from changing the state of the object. b) The negotiation branch, Branch True, is executed and transforms the state of the subject to b’, the out-state of the negotiation behaviour. c) The state associated with the subject is returned to the object, the subject is made stateless and the negotiation ends.

4. If the negotiation fails the subject remains stateless and Branch False is executed.

Local Negotiation Figures 4.10 informally illustrates the semantics for a local negotia- tion of the same form as above.

1. The client knows that the state of the subject, i, is some multiset, b‘‘.

2. If b’’ has at least as many tokens as b, the in-state of the negotiation behaviour, then the local negotiation succeeds otherwise the local negotiation fails.

3. If the negotiation succeeds, the client may execute Branch True knowing that b‘‘ will contain all the tokens of b.

4. If the negotiation succeeds, the client may execute Branch False knowing that b’’ does not contain all the tokens of b. 5 This diagram can be easily generalized to include multiple negotiation branches with an optional ELSE branch.

82 4.1 Negotiable Interfaces in Mentok

USELOCAL

1.

i :: b”

2.

True b < b” False

3. 4. Branch_True Branch_False

END

Figure 4.10: Local negotiation semantics.

Branching statements in Mentok can introduce ambiguity in the statically know state of a stateful reference; local negotiation can be used to disambiguate that state.

Type Checking USE statements Regardless of whether local or global negotiation is being performed, negotiation subjects must always be local identifiers or parameters, with a negotiable interface type. This restriction helps to prevent issues with aliasing of global variables, and object fields. The subject of a negotiation is also read-only for the scope of the negotiation branch, meaning that it cannot be assigned to, or passed as a VAR or OUT parameter; to do so would allow a new value to be written over the stateful reference, and none of the guarantees of the negotiation would hold. Finally, negotiation behaviours must always legal behaviours of the subject’s negotiable interface protocol. This means that both the in-state and out-state can only contain tokens declared or

83 4 Negotiable Interfaces

inherited (but not aliased) by the subject’s negotiable interface type.

4.1.3 State System

Mentok’s state system uses the protocols of negotiable interface types combined with negotiation and method calls to ensure state safety for Mentok programs. A Mentok program is said to be state safe if it is statically guaranteed that any negotiable object in the program can only reach states described by the object’s negotiable interface protocols. Mentok’s state system guarantees state safety by checking the following:

• For each method call in the program, the in-states for the method behaviour and cooperative behaviours of that method are statically guaranteed to be satisfied. (This means that stateful references must be used wherever a behaviour demands it.)

• For each method implementation, the out-states for the re-entrant and cooperative behaviours are statically guaranteed to be reached at all exit points from the method body.

• For each negotiation, the out-state for the negotiation is statically guaranteed to be reached at all exit points from the negotiation body.

Readers interested in formal details of Mentok’s state system are directed to section 4.2 and A.10.

4.1.3.1 State checking

Mentok’s state system performs a kind of dataflow analysis over Mentok programs. The state system models the potential states of each interface variable over execution of a given statement or expression, to check that the protocols of negotiable interface types are enforced:

• Interface variables are modeled as stateful or stateless references. Global ne- gotiations and the bodies of methods with cooperative or re-entrant parameters both introduce stateful references. On exiting such scopes, stateful references are checked to ensure they satisfy specified behaviour.

• Calls to methods that have behaviours can transform stateful references. Stateful references must be used both to invoke methods with method behaviours, and as values to parameters that have cooperative behaviours. The state system checks these requirements and models state changes according to the method’s signature.

84 4.1 Negotiable Interfaces in Mentok

• The state system models each branch of a conditional statements separately, and then joins the outcome for each branch to statically model the set of potential states for each interface variable. Branches of local negotiations are subject to joining as any other condition statement; however, local negotiations perform bran- ching based on the state of stateful references, and so each branch is checked with a partitioned set of states for the negotiation subject.

• Looping constructs are checked conservatively in order to simplify the state che- cking algorithm.

Stateful and Stateless References As the state system analysis a given statement or expression, each negotiable interface in a scope variable is modeled stateful or stateless. A stateless reference has no known state, and cannot be used in contexts where a stateful reference is required. Stateful references are modeled as having a set of potential states, where each potential state is a multiset of tokens of its interface type. An identifier becomes stateful in two different ways:

• An identifier that is the subject of a successful global negotiation (a successful test in a USE statement), is modeled as stateful for the body of the negotiation. The state of the subject at the beginning of the body is the in-state of the behaviour. At each exit point of the negotiation body, the state system requires that subject must have the out-state of the behaviour as its only potential state.

• Parameters with re-entrant or cooperative behaviours are modeled as stateful for the entire body of the method. At the beginning of the method, each parameter is modeled as having the in-state of its respective behaviour, and at exit points, the state system requires that the subject must have the out-state of the behaviour as its only potential state.

Exit points for negotiations and methods can include RETURN or BREAK statements, or the end of the scope.

Method and Procedure Calls Parameter evaluation in Mentok is strict, so state che- cking of method and procedure calls always begins with modeling, in execution order, evaluation of parameter and receiver expressions. After this has been performed, the state system checks and models state changes due to method and cooperative behaviours. The invocation side of the call is checked in full before the method return is checked, so as to correctly model method call semantics:

1. Each parameter with a cooperative behaviour is checked and modeled in sequential order:

85 4 Negotiable Interfaces

a) Values passed to cooperative parameters must be stateful references. b) A stateful reference must be guaranteed to provide at least the in-state multi- set of tokens of the cooperative behaviour. This means that every potential state of the stateful reference must be a super-multiset of the in-state. c) A stateful reference that satisfies the requirements of a cooperative behaviour has the behaviour’s in-state subtracted from all potential states.

2. For methods with method behaviours, the receiver expression is then checked in a similar fashion. The expression must be stateful reference that provides sufficient state (after all the parameters have been modeled). The in-state of the method behaviour is then removed from the reference’s modeled states.

Checking and modeling each parameter and the receiver expression in sequential order is necessary to ensure that state in use by a cooperative parameter cannot also be used for another parameter or the method call behaviour. If the invocation checking succeeds, the method or procedure is assumed to execute correctly and transform the passed state according to its signature:

1. If a method with a method behaviour is being checked, the behaviours out-state is added to each modeled state of the receiver expression.

2. In reverse order, the out-state of each cooperative parameter is added to the stateful reference value passed to that parameter.

Conditional statements and join points Each branch of a conditional statement is modeled and checked independently of the other branches. The state environment (i.e. the modeled state of each stateful reference in scope) used to check each branch depends upon the kind of condition statement that is being modeled:

• Each branch of non-negotiation branching statements (IF, CASE, and WITH) is checked using a copy of the state environment obtained after evaluating the test expression.

• For global negotiation, each successful negotiation branch is checked using a copy of the state environment augmented by the stateful reference for the subject. The ELSE branch is checked with a straight copy of the state environment.

• Local negotiations test the local state of a stateful reference against the in-state of the negotiation behaviour. A successful local negotiation branch is checked using a state environment modeling only those states that satisfy the in-state, while ELSE branches are modeled using states of the subject that do not satisfy the in-state.

86 4.2 A Formal Model of Negotiable Interfaces: MentokP

It is also a requirement that at all exit points from a successful local negotiation branch, all potential states of the negotiation subject must satisfy the out-state of the negotiation behaviour.

On exiting a conditional statement, the state system models the static effect of the entire condition statement by joining the state environments of those branches that did not exit the current scope; each stateful reference is modeled as having a set union of the potential states from each branch. Note that on completion of a successful global negotiation the negotiation subject is made stateless, so the set of stateful references modeled by each branch’s environment is always the same.

Loops The state system requires that each stateful reference has the same set of poten- tial states on entering the loop as exiting. This approach allows a loop to be checked in one pass.

State Checking in Action Figure 4.11 gives a graphical representation of Mentok’s state check for the interfaces and code in listings 4.4 and 4.5. The code snippet in listing 4.5 has three negotiable interface variable, two of which, “i” and “w” are stateful references. The snippet begins by performing a global negotiation on line7. The negotiation body, beginning on line8, is checked with the negotiation subject, “ a”, as a stateful reference with initial state {[b]}. Calls to i.Foo() and a.Bar(i) update the states of i and a respectively. The failure branch, beginning on line 13, contains a conditional statement, and a local negotiation on “w”. After each conditional statement has been checked, the state environments for each branch are joined. Finally, after the global negotiation has completed and the environments both each branches are joined, the call on 23 to i.Fot(i) is checked. The call requires a “k” token from “i” to call the method, and a “k” token from “i” to pass “i” as a cooperation parameter. Not all potential states of “i” have two “k” tokens so the code snippet fails state checking.

4.2 A Formal Model of Negotiable Interfaces: MentokP

MentokP is a formalized subset of Mentok that was created to investigate certain pro- perties of the type and state checking of negotiable interfaces performed by MentokC.

AppendixA presents MentokP , a small language that models parts of the Mentok lan-

guage, focusing on negotiable interfaces; as such, MentokP omits many features of the

87 4 Negotiable Interfaces

a :: ?? i :: {[j,k],[j,l]} w :: {[x],[y]}

USE a :: [b]->[c] DO

a :: {[b]} a :: ?? i :: {[j,k],[j,l]} i :: {[j,k],[j,l]} w :: {[x],[y]} w :: {[x],[y]}

WHILE bool DO IF i.Foz() THEN :: [j]->[k]

a :: {[b]} a :: ?? i :: {[j,k],[j,l]} i :: {[k,k],[k,l]} w :: {[x],[y]} w :: {[x],[y]}

a :: {[b]} i.Foo() :: [j]->[j] i :: {[j,k],[j,l]} USE w :: [x] -> [z] DO w :: {[x],[y]}

a :: {[b]} a :: ?? a :: ?? i :: {[j,k],[j,l]} i :: {[k,k],[k,l]} i :: {[k,k],[k,l]} w :: {[x],[y]} w :: {[x]} w :: {[z]}

a :: ?? END w.Baz() :: [x]->[z] w.Bat() :: [y]->[z] i :: {[k,k],[k,l]} w :: {[x],[y]}

a :: {[b]} a :: ?? a :: ?? i :: {[j,k],[j,l]} i :: {[k,k],[k,l]} i :: {[k,k],[k,l]} w :: {[x],[y]} w :: {[z]} w :: {[z]}

a.Bar(i::[j]->[k]) :: [b]->[c] END

a :: {[c]} a :: ?? i :: {[k,k],[k,l]} i :: {[k,k],[k,l]} w :: {[x],[y]} w :: {[z]}

END

a :: ?? i :: {[k,k],[k,l]} w :: {[x],[y],[z]}

END

a :: ?? i :: {[k,k],[k,l]} w :: {[x],[y],[z]}

State Error!! i.Fot(i::[k]->[k]) :: [k] ->[k];

Figure 4.11: State checking in action.

88 4.2 A Formal Model of Negotiable Interfaces: MentokP

TYPE IInterfaceA =POINTERTOINTERFACERECORD STATE b,c; INIT [b]; END;

IInterfaceI =POINTERTOINTERFACERECORD STATE j,k,l; INIT [j,k]; END;

IInterfaceW =POINTERTOINTERFACERECORD STATE x,y,z; INIT [x]; END;

PROCEDURE (this:IInterfaceA) Bar(i:IInterfaceI::[j]->[k]) :: [b]->[c];

PROCEDURE (this:IInterfaceI) Foo() :BOOLEAN :: [j]->[j];

PROCEDURE (this:IInterfaceI) Foz() :BOOLEAN :: [j]->[k];

PROCEDURE (this:IInterfaceI) Fot(i:IInterfaceI::[k]->[k]) :: [k]->[k];

PROCEDURE (this:IInterfaceI) Foo() :BOOLEAN :: [j]->[j];

PROCEDURE (this:IInterfaceI) Foz() :BOOLEAN :: [j]->[l];

PROCEDURE (this:IInterfaceW) Baz() :: [x]->[z];

PROCEDURE (this:IInterfaceW) Bat() :: [y]->[z]; Listing 4.4: Interface protocols for figure 4.11.

89 4 Negotiable Interfaces

1 VAR 2 a:IInterfaceA; 3 i:IInterfaceI; 4 w:IInterfaceW; 5 ... 6 (* a::??, i::{[j,k],[j,l]}, w::{[x],[y]} *) 7 USE a::[b]->[c]DO 8 WHILE boolDO 9 bool := i.Foo(); 10 END; 11 a. Bar (i); 12 ELSE 13 IF i.Foz()THEN 14 USE w :: [x]->[z]DO 15 w. Baz (); 16 ELSE 17 w. Bat (); 18 END; 19 ELSE 20 (* do nothing *) 21 END; 22 END; 23 i. Fot (i); (* State error! *) 24 25 ... Listing 4.5: Code state checked by figure 4.11.

90 4.2 A Formal Model of Negotiable Interfaces: MentokP

Mentok language while providing a simplified model of negotiable interfaces suitable for investigating safety properties of the Mentok language.

AppendixA also presents the type and state system for MentokP , as well as a small step operational semantics. A small step semantics was chosen for MentokP so as to provide an illustrative model of Mentok’s execution model; some abstractness was sacrificed in order achieve this goal.

The type and state systems of MentokP were defined with proving type and state safety for a small step semantics in mind. As a result, the both the type system and state system can be used to check static programs, and running MentokP configurations. Type and state safety can be proven, in part, by showing that each execution step from a “safe” configuration results in another “safe configuration”.

Section 4.2.2 describes MentokP , with particular reference to differences between

Mentok and MentokP . Sections 4.2.4, 4.2.5, and 4.2.6 describe the static type system, state system, and operational semantics of MentokP , respectively. Finally, section 4.2.7 describes some properties of MentokP proven using Isabelle/HOL.

4.2.1 Isabelle and Notation

MentokP was defined and checked using Isabelle/HOL[Wen00, NPW00], and the appen- dices are the pretty-printed Isabelle/HOL theorem sources for MentokP . All results described herein were proven and checked using the Isabelle theorem prover. The proofs themselves are tactical Isabelle proof scripts and are not included in the appendix but are available from [Ken10].

The rules for MentokP ’s type system, state system and operational semantics are expression as judgments of the form:

E ` P

Such a judgment is a proposition which may be read as: in the environment, E, the property, P , holds. The author found the Bali theories, which are a formal model and safety proofs for

Java using Isabelle/HOL, to be an invaluable resource when developing MentokP . Bali is described in [ON99] amongst other places, and available as an Isabelle logic library.

4.2.2 MentokP Programs

MentokP was created to capture and prove key properties about negotiable interfaces in Mentok. As a result, MentokP is a subset of Mentok with many language features omitted, or included in a simplified form. MentokP is not itself a useful programming language itself, but provides grounds for formal verification of the concepts of Mentok.

91 4 Negotiable Interfaces

Section 4.2.2.3 details some of the restrictions and omissions from the language.

The MentokP language contains some additional structures that are not found in Mentok, but can be seen as type annotations added during checking, or states entered at runtime due to execution. These are included in the language itself to make definition of the type rules and semantics simpler; a single language definition can be used to represent the abstract syntax as well as runtime valuation. Type and state system rules (c.f. sections 4.2.4 and 4.2.4) prevent inclusion of runtime-only structures in static programs.

4.2.2.1 Programs

Like Mentok, MentokP is an object based language. A program in MentokP , (c.f. sec- tion A.6) consists of:

• a list of interface declarations, which map interface names to interface bodies,

• a list of record declarations, which map record names to record bodies,

• a main command, consisting of MentokP statements, and

• a set of variable declarations local to that command.

4.2.2.2 Types and Values

Types in MentokP (c.f. section A.2.1 ) are either primitive or object types. MentokP supports the unit, boolean, integer primitive types, as well as the NIL type, interface types, and record types, which are all object types. In particular, interface and record types are used by a program, to map interface and record names to interface and record bodies.

Interface bodies in MentokP (section A.6.2) are a subset of negotiable interfaces in Mentok. Each interface has a state declaration, consisting of a list of token names, an initial state, and a list of fully abstract method declarations. A method declaration (section A.6.1) is essentially a method signature consisting of:

• a list of parameter types,

• a list of optional cooperative behaviours,

• a return type, and

• a method behaviours.

All method declarations in MentokP must have a return value; however, a method that does not need to return a meaningful value can return the single value, Unit, of the

92 4.2 A Formal Model of Negotiable Interfaces: MentokP

unit type. MentokP does not yet model re-entrant behaviours or negotiable interface inheritance, but this would be interesting future work. Record bodies (c.f. section A.6.3) implement exactly one interface type, have a set of field declarations, and provide a set of method implementations. Each field declaration maps a field name to a type, while each method implementation extends a method declaration with a list of parameter names, and a command for the body of the method.

MentokP does not currently support multiple interface inheritance; however, with respect to negotiation semantics, an object implementing multiple interfaces can be encoded in MentokP as an object implementing a single interface. The methods and tokens of all an object’s inherited interfaces can be flattened into a single namespace, with appropriate renaming to avoid syntactic clashes if necessary. Given this, it is likely that safety properties proven about negotiable interfaces in MentokP would generalize to a model with multiple interfaces.6

Values in MentokP are defined by the value in section A.3. The typeOf function of subsection A.3.1 forms the basis used by MentokP ’s type system for assigning types to values. For primitive values and NIL, types are assigned accordingly. Reference values, which are the runtime only representation of object values, are assigned a type using a “dynamic lookup” mapping. This mechanism is leveraged by the type rules in section 4.2.4 to allow type checking of both static programs and commands in running systems.

4.2.2.3 Expressions and Statements

MentokP supports a restricted set of expressions and statements (c.f. section A.5).

MentokP only includes those expressions needed to create and reference values, as well as method invocations.7

Some expressions in MentokP contain type annotations, such as the record name for field dereferences, and interface type of a method call. The IdState expression represents a stateful reference to an interface, that is passed as a cooperative parameter; in Mentok proper, only the variable identifier part of IdState is passed as a cooperative parameter, and the behaviour annotation is added during state checking. The expression Body is a runtime-only expression that is used to represent the concept of stack frames. As is shown in the semantics later (c.f. section 4.2.6), under correct program execution, a method Call expression is stepped to a Body expression, which saves the current frame information (the localBinding), and allows execution of the method body.

6 The checking algorithms in MentokC did not need to be significantly modified to accomodate multiple interfaces. 7 Without an equality operator, MentokP is highly limited in terms of usefulness.

93 4 Negotiable Interfaces

Statements available in MentokP include branching, assignment, method termination (RETURN), and composition statements, as well as those for negotiation (i.e. USE com-

mands). MentokP has two statements for global and local negotiation, UseG and UseL,

whereas in Mentok the same syntax is used for both. MentokP also includes a Skip command, to represent a no-op, and the Complete command which is a runtime-only structure, used to model the end of a negotiation where a client returns the promises state to and object. For simplicity, RETURN statements must occur at the end of method bodies (this is enforced by the type system). LOOP statements have been completed

implemented in Mentok but are omitted from MentokP for the sake of brevity; LOOPs can be encoded as tail recursive methods, with cooperative parameters for any local variable that would be stateful. The definitions introduced thus far are simply data types used to define a program in

MentokP . The type system, state system, and well-formedness conditions introduced

in the following section define the requirements for a program to be legal in MentokP .

4.2.3 Program Well-Formedness

Program well-formedness (c.f. section A.11) is defined by predicates over the structure

of MentokP programs.

Roughly, a well-formed MentokP program is a program that contains a valid set of declarations, with no name clashes across types, variables, or tokens, no unknown type or token references, and, in which, all method bodies and the main command, pass static type checking and state checking.

4.2.3.1 Programs

A MentokP program is said to be well-formed (c.f. section A.11.11) if:

• all interface and record type names are distinct,

• all interface and record declarations are well-formed,

• the local variables declared for the main command are well-formed, and

• the main command passes a type check and state check, given the environment created from the local variables.

4.2.3.2 Interfaces

Interfaces are well-formed for a program (c.f. section A.11.7) if:

• all tokens in the state declaration list are distinctly named,

• the initial state of the interface is a legal state for the interface,

94 4.2 A Formal Model of Negotiable Interfaces: MentokP

• the method declarations are uniquely named, and

• each method declaration has a well-formed signature, meaning that all parameters have known types, cooperative parameters have legal behaviours, the return type is known, and the method behaviour is legal.

A state is termed legal for an interface within a program (c.f. section A.4.3) if, for that interface, the state is a multiset of tokens declared by the interface. A behaviour is legal for an interface if both the in and out states for the behavior are legal for the interface.

4.2.3.3 Records

Records are said to be well-formed for a given program (c.f. section A.11.9) if:

• the record is declared to implement an interface declared in the program,

• the record’s fields are uniquely named, and have well-formed types,

• the record’s methods are uniquely names,

• each method provides an implementation of a method from the implemented inter- face, and

• each method passes static type and state checking, given the static environment obtained from the method’s signature.

Type and state checking of method bodies, and the main command of the program is discussed in the following sections.

4.2.4 Type System

The type judgments of MentokP (c.f. section A.9) define whether a term, t, of Mentok is well-typed with type result, T , given a static typing environment, E, and a dynamic typing environment, dt. Type judgments in MentokP are of the form:

E, dt ` t :: T

A term, t, in MentokP is a datatype that may be either an expression, a list of expres- sions, a statement, or statements; Isabelle/HOL’s type system differentiates between each different kind of term, so each type has its own syntax: Statement: E, dt ` s : − T

95 4 Negotiable Interfaces

Statements: E, dt ` ss :≡ T

Expression: E, dt ` e :: − T

List of expressions: E, dt ` es ::≡ T

If well-typed, an expression will always be judged to have a type, and a list of expres- sions will be judged to have a list of types. A well-formed statement will be judged to have an optional type; all non- Return statements do not have a type, and are be judged to have a type result of None, while Return statements may have type result SomeT , where T is type of the returned value.

A static typing environment, E, (c.f. section A.8.1), is a pair consisting of a MentokP program and a mapping, localEnvironment, which takes variable identifiers to their static types. The program is included in the static typing environment in order to look up definitions (for types, fields and methods). The local environment models the local variables that will be available for a execution of a given stack frame. There are no

global variables in MentokP so the local environment is obtained from either a method signature (when checking the bodies of methods) or from the variables declared for the main program command. The dynamic environment, dt, is included to allow conformance checking of executing systems; this allows the type rules to be used for proving type safety for small step

operational semantics as has been used for MentokP (and as is done in Bali[ON99]). The

purpose of dt is to give types to runtime values, which, in MentokP , means providing types for Reference (object) values. When dt is empty, the following shorthand is used:

E `S t :: T

If dt is empty, no reference values will ever be judged to have a certain type. Specifying that a term must be checking with an empty dynamic environment therefore means that only static, non-runtime terms will type check. This is the form of type checking that is performed by the well-formed rules discusses in the section previous. Conformance checking of running systems is discussed in section 4.2.7, as is the instance of the dynamic environment used in conformance checking.

4.2.4.1 Expressions and Expression Lists

There are seven type rules for expressions, one for each expression in MentokP . Expres- sion lists are used for lists of parameter expressions for method calls. There are only

96 4.2 A Formal Model of Negotiable Interfaces: MentokP two type rules for expression lists, one for the empty list, one for a list of size n + 1. The Val type rule uses the typeOf lookup function to assign a type to a value. typeOf is modeled as a partial function from values to types, so if typeOfdtv is None, the value v is not well-typed. (Remember that typeOf defers to dt, the dynamic environment, for reference values. The Ident rule looks up the identifier in the local environment, and judges it to have a type if found. The rule for expressions passed to cooperative parameters, IdState, uses the Id rule to determine the type of the identifier, and also requires that the behaviour is legal for that type. Field checks that an expression is assignable to the specified record type, and that the field is declared by the record. Call makes the usual type checking requirements on a method call, to ensure that the method is supported by the receiver expression, and that the list of parameters passed to the method have the correct type according to the method signature. The New rule requires that record type to be instantiated is a well-formed record declared by the program. Note that Mentok proper has a NEW statement, rather than an expression, which creates an object an assigns it to an identifier. An expression is used for NEW in MentokP as the assignment part of a statement can be captured by the Assign statement. The Body rule is the most complex rule for expressions. As mentioned previously, a Body expression represents a stack frame for an executing method. To check that the stack frame is well-typed (in running systems only), the Body rule checks that:

• the reference for the receiver parameter, T his, has the specified record type,

• the method name for the frame is a method declared by the record,

• the actual parameter list matches the method signature, and

• the command for the state frame type checks in an environment created from the method signature.

The XNil rule handles the empty expression list case. An empty expression list is always judged to have an empty sequence of types. The XCons rule provides the n+1 rule for inductively type checking list of expressions. The rule judges an expression list to have a list of types by judging the head of the list (using an expression judgment), and then the tail of the list (using an expression list judgment).

97 4 Negotiable Interfaces

4.2.4.2 Statements

The type rules for MentokP include singular Statement rules, and for Statements, which allow composition of instances of Statement. Only the Return statement has a return type, while non-empty well-formed Statements are judged to have the type of

the last statement composed. MentokP ’s type system prevents Return statements from being composed anywhere but at the end of a sequence of statements. The Skip rule judges the no-op statement to be well-formed, and to have no return type. The rule for assignment, Assign, requires that the left hand side expression is assi- gnable (meaning it must be an identifier of some form), and that the type of right hand side expression is a subtype of the left hand side expression. The If rule checks that the test expression is boolean typed, and that both branches type check. As neither branch has a return type, there may be no return statements from within an If statement. The UseLoc rule checks the static constraints for local negotiations by ensuring that the subject of the negotiation is a interface typed variable, the behaviour is legal, and that both branches type check. The Use rule checks the static constraints for global negotiations in a similar fashion. Complete checks the identifier to end negotiation with is an interface type. The Return rule checks the type of the returned expression, and judges the statement to return that type. The Empty rule judges an empty composition of statements to be well- typed. The Empty rule is related to the Skip rule; the Skip rule judges a single no-op statement, while the Empty rule judges the no-op sequence of statements. The two rules are required by Isabelle’s type system, which differentiates between a single statement and a sequence of statements. The rule for checking compositions of statements, Compose, checks that the front of the statement composition returns no type, and that the final statement is well typed.

4.2.5 State System

The state system of MentokP is a set of rules that perform a control flow analysis over

MentokP terms to determine if the a term can legally transform a given static system

state to another. The state rules of MentokP judge whether, given an environment, E, a static state environment, S, a term t is well-stated and will produce a new state environment, S0, written:

E,S |= t :: S0

98 4.2 A Formal Model of Negotiable Interfaces: MentokP

As with type rules, state rules have a slightly different Isabelle/HOL syntax for each kind of term: Statement: E,S |= s : − S0

Statements: E,S |= ss :≡ S0

Expression: E,S |= e :: − S0

List of Expression: E,S |= es ::≡ S0

State environments (c.f. section A.8.1) model the statically known abstract state of a set of variable identifiers. A variable can have a known state if the variable has been negotiated with (in the body of a USE statement) or the variable is the name of a cooperative parameter. As the state system analyzes a MentokP term, it produces a new state system for each rule applied, modeling the potential manner in which a term transforms the state of objects reference by stateful parameters. Since the analysis is static, the state system must analyze all branches of a term, and so must model “known state” as sets of potential states. State environments are typically constructed (c.f. section A.8.3) by using a set of pa- rameter declarations (for checking method bodies, some of which may have cooperative parameters), or a set of variable declarations (for the main command). Operations for partitioning and joining state environments are defined in sections A.8.2.2 and A.8.2.3. These operations will be discussed along with the rules they are used in.

4.2.5.1 Expressions and Expression Lists

MentokP has state rules for all expressions except the IdState expression, which has special meaning in that it can only be used as a receiver expression for a method, or as a cooperative parameter value. IdState is only valid in expression contexts in which it is explicitly required (via the isStateV ar predicate), since IdState cannot be state checked as a normal expression. An extra rule is included for state checking expression lists to handle stateful parameters. The Val, Ident, and New rules all judge simple, atomic expressions that make no requirement on state, and, hence, always state check without updating the state environment. The Field rule only updates the state environment if the object expression of a field dereference updates the state environment. Dereferencing the field itself does not affect

99 4 Negotiable Interfaces

negotiable state. The Call rule is the primary mechanism for changing negotiable state:

• Only stateful variables may be used to invoke methods, so the rule only judges Call expressions with IdState receiver expressions.

• The Call rule checks that behaviour of the receiver expression, and the parameters passed match the behaviour signature of the method being invoked.

• The rule state checks the parameter expression list and yields a new state environ- ment, S0, if successful. This models eager evaluation of the parameter expressions.

• The rule then checks that the method behaviour and all the cooperative behaviours are legal in the current state environment. This check (defined by the parStated relationship) first subtracts, from the state environment, the “in” states of all the behaviours of a method, then adds the back the “out” states. This models the actual method call semantics where all the “in” states of method behaviours are removed during invocation, and then the “out” states are added back when the method returns.

The Body rule allows checking of stack frames in executing systems. The rule checks that, for a given method call, the body of the method state checks in the state environ- ment obtained from the method’s signature, and that the resulting state environment matches that expected by the promises of the method’s signature. This final state en- vironment is then added back to the state environment calling stack frame, using the parameter expressions to determine which identifiers receive which state. Empty expression lists do no change or make requirements of the state environment, as the XNil rule shows. XConsStateful and XConsStateless judge non-empty expression lists. XConsSta- teless handles the cases where the head of the list is not IdState expression; it first state checks the head of a list, and uses the resulting environment to state check the tail. XConsStateful handles the case where the head of an expression list is an IdState expression (i.e. a value for a cooperative parameter). Cooperative parameters do not modify any state when they are evaluated, only when the method call is executed, so the tail of the expression list can be checked in the original state environment.

4.2.5.2 Statements

Statements in MentokP are the only source of branching. The state rules of MentokP handling branching by modeling all branches, and then combining the resultant state environments by performing component-wise union of state sets (c.f. section A.8.2.3).

100 4.2 A Formal Model of Negotiable Interfaces: MentokP

This is a conservative modeling strategy, since it loses some information about depen- dencies between stateful variables. Conservatively modeling the state of each variable independently prevents state space explosion and makes checking significantly simple. Ambiguity introduced by this issue can be removed by local negotiation statements.

The no-op in MentokP , Skip, does not affect or make any requirements of the state environment. The Assign rule, checks that the left hand side expression is not a stateful variable to ensure that negotiation subjects are read only. The rule checks that the left hand side expression and right hand side expression state check in that order. The If rule first state checks the test expression and then, using the resulting envi- ronment, checks each branch independently. If both branches state check, the resulting environments from the two branches are joined to give the modeled outcome for the If statement. The UseG rule state checks global negotiations:

• The subject of the negotiation must have no known state (otherwise a local nego- tiation would be taking place).

• The true branch is checked using the constraints of the negotiation behaviour. – The true branch is modeled using a state environment that models the nego- tiation subject as having the “in” state of the behaviour. – The resulting state environment from checking the true branch must model the subject’s state as the “out” state of the behaviour ensuring that the negotiation body keeps its promise.

• The false branch is checked as if the negotiation failed; no extra state or constraints on state are made on the environment.

• The resulting environments obtained from the checking the true and false branches are combined to give the modeled state for the global negotiation.

The UseL rule provides state checking for local negotiations:

• The negotiation subject must have some known set of states in the state environ- ment, which are partitioned into states which do and don’t provide (are and are not super multisets of, respectively) the required “in”state of the negotiation beha- viour.

• The true branch is checked using a state environment containing only those states that provide the “in” state. All resulting states for the subject after checking the true branch must provide the “out” state (i.e. the true branch must satisfy the promise of the local negotiation).

101 4 Negotiable Interfaces

• The false branch is checked using a state environment containing only those states do not satisfy the “in” state of the behaviour. There are no requirements on the outcome of the false branch.

• The state environments obtained from each branch are combined to give the final state environment for the local negotiation.

The Complete rule models the runtime mechanism for ending a negotiation (i.e. exi- ting a the scope of a successful USE statement). The subject of the negotiation, some identifier, i, should have some modeled set of states before negotiation is completed. Af- terwards, the state will be released back to an object, which Complete rule models by clearing i’s modeled states from the state environment. The Return rule simply state checks the return expression. For compositions of statements, the Empty rule does not interact with the state environment, and the Compose rule models state changes by checking the front of the composition, and then the last statement in the composition using the resulting state environment.

4.2.6 Operational Semantics

The semantics for MentokP (section A.13), is a small step, operational semantics. A small step operational semantics while less abstract than a denotational or axiomatic semantics, is better suited to describing the mechanisms negotiation, and method invo- cation under negotiable interfaces.

The operational semantics of MentokP is defined by rules specifying, for a given

MentokP program, P , how a configuration consisting of a frame, f, and an object store, os, advance to a new configuration, (f 0, os0). This is written:

0 0 P ` (f, os) 1(f , os )

An object store (c.f. section A.12.4) maps reference values (ref from A.3) to MentokP

objects. Objects in MentokP (c.f. section A.12.1) consist of a type tag (which is a record type name), field values (a mapping from field names to values), and a bag of negotiable interface tokens, representing the reified negotiable interface state.

A frame in MentokP (c.f. A.12.5.3) is a tuple of a MentokP term, and a local binding, which maps variable names to values and known negotiation state. The total negotiable interface state for an object consists of that held by the objects, and split across references to that object (in local bindings). References obtain negotiable state through negotiation, transform state through method calls, and return it to the object by completing a negotiation (the Complete command).

102 4.2 A Formal Model of Negotiable Interfaces: MentokP

Like the type system and state system, the “step” relationship for the operational semantics of MentokP is inductively defined by individual rules. As is expected of an operational semantics, there are no rules for advancing terms for which no further execution takes place; such terms, like the Skip statement, or value expressions, all said to be “ground”.8 On the other hand, non-ground terms may have several rules. Terms that introduce branching have several rules, one for each possible outcome. Compound terms may also have several rules; rules for advancing sub terms of the term, and rules for advancing the term when all sub terms are “ground”.

4.2.6.1 Expressions and Expression Lists

Rules for loading values from identifiers are given in Ident for simple variables, and FieldDeref for dereferencing fields. In both cases, identifier name is used to lookup the corresponding value from either the local variable bindings (for identifiers) or an object’s field binding (for fields). Field expressions can only be dereferenced if the receiver expression has been evaluated to a reference value, which is considered “ground”. If the receiver expression is not “ground”, the rule FieldDeref specifies that the receiver expression should be advanced. The New rule creates a new object for the program and the specified record type name. A new object has the initial negotiation state that is specified by the interface the record implements, and field binding populated with default valued fields. The New rule obtains a new reference, binds the reference to the new object in the object store, and replaces the New expression with the reference value. Two rules exist for method calls. The first rule, CallPars, causes evaluation steps for the parameter list of a method call. The second rule, CallInvoke, is used for actually invoking a method. CallInvoke can be applied if the following conditions are satisfied:

• the parameter list for the method call is ground,

• dereferencing the identifier through which the method is invoked obtains an object from the store,

• the object provides an implementation for the invoked method,

• the local binding contains sufficient negotiable state to invoke the method and satisfy the requirements of the method’s cooperative parameters, and

• a new local binding can be created from the parameter values to execute the method body.

8 Predicates for determining ground terms are given in section A.5.2.

103 4 Negotiable Interfaces

If these conditions are satisfied, the CallInvoke rule steps a method call to Body expres- sion, which provides all the information needed to create a new stack frame. Creation of the new local binding, and satisfying the negotiable requirements of a method call are defined by the newLocals relationship (c.f. section A.12.5.1). Informally, newLocals uses the parameters passed to a method to strip the “in” state of negotiable behaviours from the current local binding to create a local binding for the method body. newLocals only operates over local bindings because methods can only transform known negotiable state. The rules for advancing Body expressions specify the conditions and outcomes for exe- cuting individual steps within an executing method invocation (rule BodyStep), and returning from a method invocation (rule BodyReturn). BodyStep advances the sta- tement sequence encapsulated by a Body expression, which can update the local binding for the Body expression, as well as the object store. BodyReturn takes a completed method invocation that is ready to return a ground expression, and updates the negotia- tion state of the local binding according to the method behaviour. The returnLocals relationship (c.f. section A.12.5.2) defines the mechanism for and conditions for retur- ning negotiation state from a callee stack frame to caller stack frame; under the correct conditions returnLocals returns the “out” state of method’s negotiable behaviours to the local binding of the caller frame. Once again, returnLocals does not affect the store, since the state transformations of method calls only affect locally known state. The rules for advancing expression lists are simple; XConsHead advances the head of an expression list, and XConsTail advances the tail of the list when the head of the list is ground.

4.2.6.2 Statements

Assignment statements are advanced by four separate rules. AssignL and AssignR step the left hand and right hand side expressions of an assignment statement. Left hand expressions are stepped first until they are left-hand side ground (defined by the predicate lGround), which is a ground form up to the final dereferencing of identifiers. This allows assignment of a value (from the RHS) to a identifier (from the LHS). AssignLoc handles the case of assignment of a value to an identifier, and updates the local binding accordingly. AssignFld handles assignment to a field of an object, and updates the field binding of an object accordingly. Rules for If statements are straightforward; IfExp advances a (non-ground) test expression, and IfTrue and IfFalse chooses an appropriate branch, based on the boolean test value. ReturnExp advances a non-ground return expression; Return statements with ground

104 4.2 A Formal Model of Negotiable Interfaces: MentokP expressions are also considered ground, and are dealt with in the BodyReturn rule. The semantics for local negotiation are expressed in UseLocTrue and UseLocFalse. Both rules test the locally known state of interface identifier, UseLocTrue choosing the true branch if the local state contains the necessary tokens, otherwise UseLocFalse chooses the false branch. Note that there is no rule to handle the case where the identifier is unknown, null valued or stateless, as the type and state systems statically prevent these cases from occurring. Global negotiation is expressed in the three rules UseNIL, UseGlbTrue and Use- GlbFalse:

• UseNIL handles the case where the negotiation identifier is null valued, and chooses the false branch, since there is no object in the store to negotiate with.

• UseGlbTrue handles the case where the identifier, i, holds a reference to an object that provides the required state for the negotiation. The state is split from the object, stored in the local binding, and the command to execute is advanced to the true branch composed with a Complete statement, which will end the negotiation when executed.

• UseGlbFalse handles the case where the negotiation subject does not have the required state that the negotiation asks for. The false statement is chosen in this case.

A Complete statement is composed at the end of a successful negotiation in order to return the negotiated state back to the object. This operation is seen in the Complete rule, which removes the local state of a negotiation subject and returns it to the state in the object store. Finally, the composition operator for statements has four rules. The first rule, Com- poseFront, advances the statement sequence at the front of a composition. The next two rules, ComposeLast and ComposeLastS advance the final statement of a composi- tion when the front is empty. Two rules are needed here to satisfy the type constraints of

MentokP language; ComposeLast handles a statement advancing to a statement, and ComposeLastS, a statement advancing to a statement sequence. ComposeSKIP handles trailing Skip statements by advancing to an empty statement sequence with no side effects.

4.2.6.3 Running a Program

The definition for run (c.f. section A.13.2) defines the bootstrapping command for creating a configuration from a new program:

• the main command of the program is used as the term for the configuration,

105 4 Negotiable Interfaces

• the variables for the main command are used to initialize a new local binding with default values, and

• an empty store is used.

4.2.7 Results

One of the main reasons for creating MentokP was verification of Mentok’s state safety feature. State safety, as it was informally introduced in section 4.1.3, is meant to gua- rantee that any negotiable object in running Mentok application can only reach those abstract states defined by its negotiable interface protocols. This property has been

proven for MentokP configurations “running” on the small step operational semantics defined inA.

The proof for state safety relies on a safety result with respect to MentokP ’s type system; intuitively this must be so, as without a guarantee that legal values are always assigned to typed identifiers, negotiations and method calls in Mentok would never function correctly. Proving type and state safety results both rely on definitions of conformance for running programs. Definitions for conformance and theorems for type safety and state safety are given informally below. Two main methods of proof are used: proof by structural induction

over the terms of MentokP and proof by structural induction over the rules of the operational semantics.

Proof by structural induction over the terms of MentokP is possible because MentokP terms are defined as recursive Isabelle/HOL datatypes; each of kind of term (expression, list of expressions, statement and statements) has at least one non-recursive base case. For example, the V al expression is a base case as it does not contain another expression in its structure. Proof by structural induction over terms therefore involves:

• Proving the theorem holds for each base case in the term datatype.

• Proving the theorem holds for all recursive cases, assuming that the theorem holds for any subterms of the case.

Proof by structural induction over the rules operational semantics of MentokP is also based on the recursive structure of the rules. Rules that contain no propositions expressed in the form of other rules are base cases.

• The theorem is shown to hold for each base rule in the semantics.

• The theorem is shown to hold for all other rules assuming that the theorem holds for the rule’s propositions. For example, when proving the theorem for the IfExp rule, the theorem is assumed to hold for stepping the boolean test expression.

106 4.2 A Formal Model of Negotiable Interfaces: MentokP

Finally, theorems are shown to hold for the configuration yielded by run, for a well- formed program, P . All definitions and theorems were specified, type checked, and, in the case of theorems, proven using Isabelle/HOL[Ken10].

4.2.7.1 Conformance

Conformance in MentokP is defined as a predicate over a running configuration of a given MentokP program. A configuration, C, is said to conform to a program, P , if, and only if, all values in the configuration are typed correctly:

• All objects in the store must have the correct set of fields, field values and tokens according to the object type tags.

• Each frame on the stack must have an appropriate set of local variables, with correct values and states according to the method signature of the frame.

4.2.7.2 Type Safety

The type safety theorem for MentokP assumes the following properties hold for a given program, P , and configuration, C:

• P is adjudged to be well-formed;

• C, conforms to P ;

• the executing term of the configuration, C, is well-typed, using the object store of C to judge the types of reference values;

• the executing term of the configuration, C, is well-stated, using the local binding of C to judge the current state of references to negotiable interface values; and

• the executing term of C is not ground, or halted by dereferencing a null reference.

If the above properties hold, the theorem states that:

• there exists a unique step in the system to a new configuration, C0;

• the new configuration, C0, conforms to P ;

• the executing term of C0, is well-typed, using the object store of C0 to judge the types of reference values; the new term will be judged to have a narrower type than the judgment obtained for C;

• the executing term of C0, is well-stated, using the local binding of C0 to judge the current state of references to negotiable interface values; the new term will be judged to yield a subset of the state environment obtained for C.

107 4 Negotiable Interfaces

The proof of this theorem is in two parts. The first part of the proof establishes that for any step in a conformant, well-typed and well-stated configuration, the resulting configuration is also conformant, well-typed and -stated. This is proven by structural

induction over the operational semantics of MentokP . The second part of the proof establishes that for any non-ground, non-halted, confor- mant, well-typed and well-stated configuration, there exists a unique step in the opera- tional semantics to advance the configuration. Proof is by induction of the structure of

the MentokP language. Finally, it can be shown that a configuration obtained from running a well-formed program (c.f. section A.13.2) is conformant, well-typed and well-stated. In combination with the type safety theorem, this means that any configuration that is executing under normal conditions will continue to execute in a type safe manner until it yields a result (a ground value), or halts due to a null value dereference.

4.2.7.3 State Safety

The type safety theorem for MentokP establishes that as a configuration executes under normal conditions, it does so in a type safe manner. This means that the state system rules continue to hold at all times during normal execution conditions. The type safety theorem says nothing about the actual states that negotiable objects reach during normal execution, and whether these states are considered “safe” with respect to the appropriate negotiable interface protocol. The state safety theorem relies on definitions for the current state of an object, and the reachability of one state from another, for a given negotiable interface, and term:

• The current state of an object is defined as the multiset sum of the tokens asso- ciated with an object in the store, and all of the tokens associated with references to the object in the configuration.

• If state A and state B are multisets of tokens for a given negotiable interface, I, and given term, t, then A is said to be reachable from B if a sequence of methods from I can be called such that A is obtained from B.

The state safety theorem for MentokP states that, if a well-typed, well-stated, confor- mant configuration, C, of a program, P , steps to a new configuration C0, then the following properties will hold:

• For any reference, r, referring to an object, obj, in C, r will refer to an object, obj0, in C0, such that both objects have the same type, and the state of obj0 is reachable from the state of obj.

108 4.3 Implementation: Negotiable Interfaces and the Mentok Compiler

• Any reference, r0, in C0 that is not present in C will have a state reachable from the initial state of its negotiable interface.

The proof of state safety is by structural induction over the operational semantics of

MentokP . Since any configuration obtained from running a well-formed program (c.f. section A.13.2) has an empty store, all objects in program running under normal conditions will have a state reachable from its initial negotiable interface state.

4.3 Implementation: Negotiable Interfaces and the Mentok Compiler

The Mentok compiler, MentokC, is a multistage compiler that emits code for the Com- mon Language Runtime. MentokC is based on the Gardens Point Component Pascal (GPCP) compiler and, in addition to regular Gardens Point Component Pascal syntax, MentokC compiles all of the language features presented in this thesis, including nego- tiable interfaces. In order to implement the runtime checking for negotiable interfaces, MentokC emits calls to the Mentok runtime libraries, which contain runtime data struc- tures and logic for negotiation. Section 4.3.1 gives a high-level overview of the additional stages built into MentokC to check and emit code for negotiable interfaces in Mentok. The rules and data structures used by MentokC for state checking programs with negotiable interfaces are largely captured by the informal description in 4.1.3.1, and MentokP ’s state system, and are not described again in this section. Section 4.3.2 describes implementation details of Mentok’s runtime systems, specifi- cally the concrete CLR representation for negotiable interfaces, stateful references, nego- tiable objects, and negotiation itself.

4.3.1 Structure of the Compiler

The additional functionality of MentokC used to compile and emit code for negotiable interfaces is largely straightforward. Simple changes to GPCP’s scanner and parser enable recognition of Mentok specific syntax, and some additional functionality is added to perform name resolution and static type checking for negotiable interfaces, method calls, and USE statements.9 If a program that scans, parses, and type checks, MentokC then performs two additional passes: state checking and program rewriting. MentokC’s first additional pass performs state checking and annotation, as described in 4.1.3.1 and partially formalized in MentokP ’s state system. MentokC’s state checking 9 Changes to scanning, parsing and (traditional) type checking are straightforward and not novel enough to merit discussion here.

109 4 Negotiable Interfaces

pass traverses the abstract syntax for each procedure body of a type-checked a Mentok program, modeling the manner in which the program modifies the protocol state of each negotiable interface reference. On detection of a state error, MentokC terminates state checking for a given program block and all blocks downstream to avoid reporting spurious errors introduced by the first error. A successfully state checked program is annotated with protocol hints, and then fed to the program rewriting pass. If all checking passes, MentokC rewrites the resulting annotated, abstract syntax tree as a normal GPCP program. Each Mentok specific construct is rewritten as a (less abstract) GPCP construct, which is then emitted by the GPCP back end as concrete CLR types, data structures and instructions. Negotiable interfaces are obviously not supported by are not supported by the CLR’s type system or its stack-based instruction set; however, the CLR does provide mechanisms for annotating various constructs with custom meta data. The following section describes how MentokC maps and emits code to represent various negotiable interface concepts.

4.3.2 Runtime Representations

MentokC leverages the rich, object-oriented type system and instruction set of the CLR to implement the purely object-based part of negotiable interfaces. The CLR obviously does not provide primitive types or instructions for negotiable interfaces and negotiation, so MentokC must emit metadata, data structures and instructions to implement these features. This section describes the code emitted by MentokC to implement the semantics of negotiable interfaces; the object-based representation of negotiable state, annotated types, and instructions and calls to runtime libraries. The runtime libraries and data structures that implement the semantics of Mentok are not designed to be immune to unsafe manipulation by non-Mentok code; verifiable state-safety of the intermediate format emitted by Mentok was not a goal of this thesis. MentokC emits CIL (Common Intermediate Language) directly, but examples of emit- ted code in this section are given in C# for readability. In some cases, emitted code samples are simplified versions of those emitted by MentokC.

• Object locks represent object state, Keys represent stateful references.

• Interface types annotated with attributes, methods with attributes and extra para- meters for keys.

• Classes, with object locks, calls to Keys.Transform.

• Negotiation, method call sites.

110 4.3 Implementation: Negotiable Interfaces and the Mentok Compiler

(* Token mapping: {closed:0,open:1,item:2} *) IHost =POINTERTOINTERFACERECORD STATE closed,open,item; INIT [closed]; END;

(* Token mapping: {unfrozen:0,frozen:1} *) IFreezable =POINTERTOINTERFACERECORD STATE unfrozen,frozen; INIT [unfrozen]; END;

(* Token mapping: {free:0,frozen:1,closed:2,item:3} * * IHost : {closed:2,open:0,item:3} * * IFreezable : {unfrozen:0,frozen:1} *) IFreezableHost =POINTERTOINTERFACERECORD ( + IFreezable + IHost) STATE free := {IHost.open,IFreezable.unfrozen}; INIT [closed]; END; Listing 4.6: Basic interface declaration.

4.3.2.1 Interface Types

All runtime aspects of negotiable interfaces are based around the negotiable interface concept of state: multisets of tokens. Representing each token as an object, and a multiset of tokens as an object containing lists of these tokens can be expensive in both time and memory for complex protocols, especially those with unbounded numbers of tokens. Instead, MentokC uses an array-based representation for token multisets by assigning a unique array index to each token, and representing counts of tokens as integers in an array. The mapping from each token to a state array index is established at compile time. MentokC also creates a map from inherited tokens to state array indices for each pa- rent of a negotiable interface to enable projection of a state array into the space of a super type. These mappings are then used to represent token multiset requirements of various method behaviours, negotiation behaviours, and even by the representation of the dynamically changing negotiable interface state. Listing 4.6 gives three interfaces along with potential mappings from tokens to array indices (given in comments above each type). For example, a multiset of tokens for interface IHost is represented by an array of length three, with tokens closed, open, and item mapped to indices zero, one, and two, respectively. Under this mapping, a zero-based array, s = {0,1,3} (s[0] = 0, s[1] = 1, and s[2] = 3) represents the state [open,item,item,item].

111 4 Negotiable Interfaces

(* Token mapping: {closed:0,open:1,item:2} *) IHost =POINTERTOINTERFACERECORD STATE closed,open,item; INIT [closed]; END;

(* {1,0,0} -> {0,1,0} *) PROCEDURE (this:IHost) Unlock :: [closed] ->[open];

(* {0,1,0} -> {1,0,0} *) PROCEDURE (this:IHost) Lock :: [open] ->[closed];

(* {0,1,0} -> {0,1,1} *) PROCEDURE (this:IHost) HostItem :: [open] ->[open,item];

(* {0,1,1} -> {0,1,0} *) PROCEDURE (this:IHost) FreeItem :: [open,item] ->[open]; Listing 4.7: Basic interface declaration.

Interface IFreezableHost extends interfaces IHost and IFreezable, and declares an alias token, free for the inherited tokens IHost.open and IFreezable.unfrozen. Un- der the mapping given in listing 4.6, the state array {1,0,0,3} represents [free,item,item,item]. This array can be projected into an IHost state array, {1,0,0,3}:[free,item,item,item], and the IFreezable state array, {1,0}:[unfrozen]. Listing 4.7 gives the method declarations of IHost with method behaviour annotations described in terms of the state array mapping. Section 4.3.2.2 describes how these type annotations are used by the Mentok runtime libraries to provide the dynamic representation of negotiable interfaces. Listing 4.8 the equivalent C# of the code MentokC might emit for IHost. The first thing to note is that the token declarations have been emitted as custom attributes, annotating the interface itself. Each Token custom attribute is constructed using the name, array index, and initial count for the token, as declared in Mentok. Each method is annotated with a method behaviour attribute, describing the array based representation of the behaviour. Also of interest, is the keys parameter of each negotiable method. The Keys class is provided by the Mentok runtime libraries, and is used to represent the state part of a stateful reference. As later sections will show, Keys are obtained through negotiation and a passed to routines with cooperative parameters.

4.3.2.2 Classes and Objects

The annotated interface types emitted by MentokC provide static meta data for the Men- tok runtime system; classes that implement negotiable interfaces have the responsibility

112 4.3 Implementation: Negotiable Interfaces and the Mentok Compiler

[Token("open", 0, 0), Token("closed", 1, 1), Token("item", 2, 0)] public interface IHost { [MethodBehaviour(new int[]{1,0,0}, new int[]{0,1,0})] void IHost.Unlock(..., Mentok.RTS.Keys keys);

[MethodBehaviour(new int[]{0,1,0}, new int[]{1,0,0})] void IHost.Lock(..., Mentok.RTS.Keys keys);

[MethodBehaviour(new int[]{0,1,0}, new int[]{0,1,1})] void IHost.HostItem(..., Mentok.RTS.Keys keys);

[MethodBehaviour(new int[]{0,1,1}, new int[]{0,1,0})] void IHost.FreeItem(..., Mentok.RTS.Keys keys); } Listing 4.8: Basic interface declaration.

Host =POINTERTORECORD (+IHost) END;

PROCEDURE (this:Host.IHost) Unlock :: [closed] ->[open]; BEGIN ... END Unlock; Listing 4.9: Mentok class declaration. of constructing and calling runtime system structures and libraries to reify the dynamic part of negotiable interface types. Listing 4.9 gives a partial implementation for a class, Host, that implements the IHost negotiable interface from listing 4.6. Listing 4.10 gives the code emitted by MentokC for the Host class. The first item of note is that the Host class implements the IObjectLocks interface from the Mentok runtime libraries. MentokC also emits a special field, objectLocks, that is created during the construc- tor, and return by the single property on the IObjectLocks interface. The ObjectLocks class abstracts the implementation of the object’s negotiable state. When constructed, an instance of ObjectLocks uses reflection to construct and initialize an array-based representation of the object’s initial state for each negotiable interface the object imple- ments. The ObjectLocks class is used internally by the runtime system to implement negotiation; it has no public interface, and cannot be manipulated except through calls to the Mentok runtime.

113 4 Negotiable Interfaces

public sealed class Host : IHost, Mentok.RTS.IObjectLocks { private readonly Mentok.RTS.ObjectLocks _objectLocks;

public Host() { this._objectLocks = new Mentok.RTS.ObjectLocks(this); }

void IHost.Unlock(...,Mentok.RTS.Keys keys) { // p r o l o g u e keys.Transform(); // ... implementation }

Mentok.RTS.ObjectLocks Mentok.RTS.IObjectLocks.Locks { get{ return this._objectLocks; } } } Listing 4.10: Emitted C# for a Mentok class.

The last item of note is in the IHost.Unlock method. The method has the same signature as declared in the interface, and so it receives the compiler generated keys parameter. MentokC also generates a simple prologue to call Transform on the keys object. During the call to Transform, the Mentok runtime libraries use reflection to examine the call stack, obtain the executing method, and apply the method behaviour as declared (and annotated) on the interface type. The Transform method then sub- tracts away the in-state from the Keys object, and adds the out-state before continuing execution of the method.

4.3.2.3 Client Code: Method Calls and Negotiation

Clients of negotiable interfaces perform negotiation to obtain stateful references, which can then be used to invoke methods. Most of the negotiation semantics is implemented by the Mentok runtime libraries, and abstracted by the Keys runtime class. Listing 4.11 demonstrates a simple, global negotiation with an IHost negotiable object. If the negotiation succeeds, the client immediate passes the stateful reference to host as a cooperative parameter to another procedure.

114 4.3 Implementation: Negotiable Interfaces and the Mentok Compiler

PROCEDURE UnlockHost(host:IHost); BEGIN USE host :: [closed] -> [open]DO PrintUnlockHost(host); (* [closed] -> [open] *) ELSE Console.WriteString("Not Closed"); END; END UnlockHost; Listing 4.11: Mentok: negotiation and parameter passing. static void UnlockHost(IHost host) { Mentok.RTS.Keys host_keys = Mentok.RTS.Keys.Negotiate(host, typeof(IHost), new int[]{1,0,0}, new int[]{0,1,0}); if (host_keys.CaseLabel == 0) { PrintUnlockHost(host, host_keys); host_keys.Return(); } else { Console.WriteLn(); } } Listing 4.12: Emitted C#: negotiation and parameter passing.

Listing 4.12 gives the equivalent C# to the code emitted by MentokC for listing 4.11:

• The negotiation is mapped to a call to a static method, Negotiate, of the Keys runtime library class. The call to Negotiate passes the reference to the negotiable object, the type of the interface to negotiate with, and then the behaviour in array form to request.

• The returned Keys object, host keys, has a property, CaseLabel indicating which branch of the statement to take.

• If the negotiation succeeds, the client code takes the first branch, passes the host, and host keys to the routine, and then returns the negotiable state back to the object, via the call to host keys.Return.

• If the negotiation fails, the host keys object contains no negotiable state, and the else branch is taken.

Internally, the runtime libraries take care of maintaining the object state, performing the dynamic testing for negotiation, and checking consistency of the returned state after

115 4 Negotiable Interfaces

a negotiation. The runtime library provides an overload for negotiations that have more than one test branch, allowing them to pass in more than one negotiation request at a time. Figure 4.13 gives the code listing for the PrintUnlockHost procedure called from the previous listing. The procedure has a cooperative parameter through which it calls the Unlock method, and performs a local negotiation. Listing 4.14 demonstrates the emitted code for listing 4.13:

• The procedure has an extra parameter, of type Keys, to represent the cooperative parameter, host.

• The body of the procedure begins with a compiler emitted call to split the in-state of the cooperative procedure. This prevents extra state from the outer negotiation leaking into this scope and effecting the results of local negotiation.

• The calls to host.Unlock take a reference to a Keys object. As the previous section explained, the keys are transformed according to the method behaviour as part of method execution.

• The local negotiation call is emitted as a call to the Keys runtime library, only now the local keys reference itself is passed as the subject of the negotiation. Internally, the negotiation logic operates against the state held by the keys, rather than querying a negotiable object. Branching for local negotiations is the same as that for global negotiations allowing local and global negotiations to be mixed in the one request.

• The body of the local negotiation has no extra logic, as local negotiations have no global effects.

• The method ends with a compiler generated call to return the split (and transfor- med) local keys back to the calling scope.

4.4 Discussion: Problems and Extensions

Negotiable interfaces allow expression of method availability, re-entrance conditions, and out-calls in the type of an interface. The multiset based protocol scheme chosen for negotiable interfaces is simple, permitting expression (and checking) of protocols with a potentially infinite number of discrete states. Negotiable interfaces, as presented thus far, cannot be used to describe some useful and common properties of stateful component roles. The negotiable interface protocol model is also too expressive in at least one way. This section examines several expressiveness issues for negotiable interface protocols, and solutions investigated to address them.

116 4.4 Discussion: Problems and Extensions

PROCEDURE PrintUnlockHost(host:IHost::[closed]->[open]); BEGIN IF printFirstTHEN Console.WriteString("First"); host.Unlock(); END; USE host :: [closed] -> [open]DO host.Unlock(); Console.WriteString("Second"); END; END PrintUnlockHost; Listing 4.13: Mentok: Cooperative parameters, local negotiation, and method calls. static void PrintUnlockHost(IHost host, Keys host_keys) { local_keys = host_keys.Split(typeof(IHost), new int[]{1,0,0}); if (HostModule.printFirst) { Console.WriteString("First"); } else { host.Unlock(host_keys); } Keys keys = Keys.Negotiate(local_keys, typeof(IHost), new int[]{1,0,0}, new int[]{0,1,0}); if (keys.CaseLabel == 0) { host.Unlock(keys); Console.WriteString("Second"); } local_keys.Return(); } Listing 4.14: Emitted C#: Cooperative parameters, local negotiation, and method calls.

117 4 Negotiable Interfaces

Figure 4.12: IPlay interface.

TYPE IPlay =POINTERTOINTERFACERECORD STATE stopped,playing,paused; INIT [stopped]; END;

PROCEDURE (this:IPlay) Play() :: [stopped]->[playing];

PROCEDURE (this:IPlay) Stop() :: [playing]->[stopped];

PROCEDURE (this:IPlay) Pause() :: [playing]->[paused];

PROCEDURE (this:IPlay) Resume() :: [paused]->[playing];

PROCEDURE (this:IPlay) Step() :: [paused]->[paused]; Listing 4.15: Single behaviour IPlay interface

4.4.1 Multiple Behaviours

The requirement that each method declared by a negotiable interface has one method behaviour prevents expression of protocols in which a single method can be applied to multiple, distinct states. This means that a negotiable interface protocol cannot express a class of simple automata, in which multiple arrows share a single label. In practice, this leads to an over-factoring of interfaces, as each state transition in an automata requires a separate method declaration. Listing 4.15 shows an interface that suffers from exactly the problem above. The IPlay interface cannot be stopped while it is paused, but must played first. Furthermore, the method Resume may well have virtually the same implementation as Play, but a client cannot call Play to resume from a paused state as shown by figure 4.12. A solution to this problem is to allow multiple method behaviours per method. Mul- tiple behaviours for methods increase the expressiveness of protocols and reduces the need for local negotiations to disambiguate state, since methods can be applied to dis-

118 4.4 Discussion: Problems and Extensions

Figure 4.13: IPlay2 interface. TYPE IPlay2 =POINTERTOINTERFACERECORD STATE stopped,playing,paused; INIT [stopped]; END;

PROCEDURE (this:IPlay2) Play() :: [stopped] -> [playing] | [paused] -> [playing];

PROCEDURE (this:IPlay2) Stop() :: [playing] -> [stopped]; | [paused] -> [stopped];

PROCEDURE (this:IPlay2) Pause() :: [playing]->[paused];

PROCEDURE (this:IPlay2) Step() :: [paused]->[paused]; Listing 4.16: Multi-behaviour IPlay2 interface joint states. Listing 4.16 and figure 4.13 demonstrate the definition and protocol for a revised play interface with multiple behaviours. IPlay2 addresses the problems described above by adding an extra behaviour for both Play and Stop, and removing the Resume method. Stop can now be called from a playing or paused state, and Play from a stopped or paused state. Introduction of multiple behaviours does introduce some potential ambiguities into the model. Multiple behaviours that operate on the same in-state allow expression of non-determinism in negotiable interface protocols (see the next section).

119 4 Negotiable Interfaces

TYPE IPlay3 =POINTERTOINTERFACERECORD STATE stopped,playing,paused; INIT [stopped]; END;

PROCEDURE (this:IPlay3) Play() :: [stopped] -> { [playing] | [error] };

... Listing 4.17: Non-deterministic IPlay3 interface

PROCEDURE (this:Play.IPlay3) Play() :: [stopped] -> { [playing] | [error] }; BEGIN IF StreamOk(this.stream)THEN this.InternalPlay(); RETURNSTATE::[playing]; ELSE this.LogError(); RETURNSTATE::[error]; END; END Play; Listing 4.18: Choice for non-determinism

4.4.2 Non-determinism

The protocols of negotiable interfaces cannot be used to describe non-deterministic beha- viours of stateful objects; each method behaviour produces one deterministic outcome for a given state input. The IPlay.Play method given in listing 4.15 in the previous section always transforms an object from the stopped state to the playing state. A programmer may also wish to express the situation in which the Play fails (perhaps due to a missing codec or invalid stream). Listing 4.17 gives a possible syntax for describing this behaviour in a non-deterministic negotiable interface protocol. Introducing non-determinism into the negotiable interface protocol is not sufficient for introducing non-determinism into the negotiable interface language feature as a whole. Each implementation of IPlay3.Play needs to specify the state outcome of the method as well, based on some internal logic. Listing 4.18 gives an example of the syntax investigated for non-deterministic negotiable interfaces. The syntax for the RETURN statement is augmented with a STATE expression, describing the resulting state for a particular code path.

120 4.5 Related Work

4.4.3 Reachability and Empty Types

In order use a negotiable interface, a client must specify an interaction behaviour via the USE statement or parameters with cooperative behaviours. It is possible for a client to specify a behaviour that can never be satisfied by any client code; a behaviour is unsatisfiable if the out-state can never be reached from the in-state. For the IPlay interface of listing 4.12, the behaviour [stopped]->[playing,paused] is unsatisfiable, because the [playing,paused] state is not reachable from [stopped] state, using only the transformations specified by method behaviours of IPlay. If a client declares an unsatisfiable behaviour at any time, the client will never be able provide an implementation of that behaviour. For USE statements, this is not a major problem, since it means that the client’s code will never compile. If an unreachable coope- rative behaviour is declared on a method of an interface, the interface can be potentially compiled, but no client will ever be able to implement it. Without reachability checking, negotiable interfaces can be used to declare empty types. In practice, reachability is a usability issue, rather than a correctness or soundness is- sue. As a result, full (potentially slow) reachability checking is not necessary for writing applications with negotiable interfaces. Most negotiable interface protocols are simple enough that partial reachability checking can be used to good effect instead. Partial rea- chability checking for Mentok was provided by recursively constructing, and traversing the protocol graph, beginning at the in-state of a behaviour, and terminating after a finite number of steps.

4.5 Related Work

Pre- and post-condition contracts, such as in Eiffel[Mey92] and Sather [SO96], are the most common way of expressing the requirements of stateful objects. Pre- and post- conditions contracts can be easily broken by simple cases of object re-entrance and groups of interacting contracts often need to be carefully inspected and proofed to prevent re-entrance[Szy98]. Furthermore, pre- and post-condition contracts cannot ex- press out-calls [BW99] and hence cannot be used to express certain semantic require- ments of stateful objects. Many concurrent object-oriented languages use object protocols to provide concur- rency control for objects[BG98]. In most cases, such languages have active objects (and hence are called are Actor languages[Agh86]). Active objects are objects which have their own processes, and communicate with other by asynchronous message passing. Languages with synchronous message passing are most popular for component based programming and are considered more efficient than Actor languages. Furthermore, the protocols used by concurrency O-O languages are exclusively used for expressing concur-

121 4 Negotiable Interfaces

rency control - type checking of sequences of messages is not a focus, nor is expressing re-entrance conditions, since the object model is significantly different to modern O-O languages. A major issue with object protocols in concurrent object oriented languages is the so-called inheritance anomaly[MY93]. The inheritance anomaly refers to the problems that arise in when implementation inheritance is used with classes with concur- rency control, often preventing inheritance, or requiring the subclass to rewrite large sections of the parent class. Since Mentok disallows implementation inheritance at the component boundary, many problems of the inheritance anomaly do not apply to the work in this dissertation. Some instances of inheritance anomaly arise from lack of expres- siveness of the protocols used in a language. Negotiable interfaces do suffer from this problem; any attempt to use negotiable interfaces in a language where implementation inheritance is emphasized would need to address this by making the protocol sufficiently expressive. Type systems for active objects have received a great deal of focus in the literature [Nie95, PP99, Weh00]. Approaches to type systems for active objects generally use a process formalism to express the behaviours of objects; each method of an object specifies the state the object that must be satisfied before invocation and the replacement behaviour after the method has finished executing. Application of such approaches to modern object-based languages is difficult, as objects are generally passive. Type systems for active objects usually only permit static checking and, as a result, suffer from state-space explosion and cannot handle dynamically changing clients. The use of factorable multisets in [PP99] and related papers was an inspiration for using multisets in negotiable interfaces. The object model and objectives of the nego- tiable interfaces is different to that introduced by Puntigam, which is mainly used to specify behaviours of active objects that communicate via asynchronous message passing with unbounded buffers. Puntigam’s model does not consider re-entrance, or non-atomic method execution. Multisets and vector addition systems also form the basis of other formalisms such as are used in other formalisms for concurrency such as CHAM [BB92], Gamma[BLM90, BL93] and higher level Petri nets [SB94, Lak97, KCJ98]. Protocol types for component composition analysis have been by a number of researchers[LM95, CMK98, Reu01, CFP+03]. Most approaches in this field are very simi- lar to type systems for active objects, which some utilize process calculi to describe the behaviour of components; some protocol types for components even treat component ins- tances as to active objects, and focus on checking for deadlock freedom. Reussner[Reu01] uses automata that describe the behaviour of components to check that components are “compatible”. Reussner’s type system can also be used to adapt component behaviour in the case when only a subset of a component’s requested services are available. Ca-

122 4.6 Summary nal et al[CFP+03] add protocols based on the polyadic pi-calculus to CORBA interfaces, which allows them to statically check properties, such as deadlock freedom, and to dy- namically check adherence to component protocols. The roles of Canal et al are only specified after components have been implemented, meaning that conformance of com- ponents to their specifications can only be checked at runtime. This as a shortcoming in the methodology as specifications should be developed before implementation and should guide construction of such components. Canal et al’s approach is practical in the case that COTS components are used to build component applications. Composi- tion techniques are complementary to the facilities provided by Mentok - negotiable interfaces specify component roles prior to implementation and thus guide construction, while composition techniques focus on checking protocol matching after construction but before composition. Vault is a programming language with static protocol checking[DF01] designed for che- cking protocols in system software. Vault is based on the Capability Calculus[CWM99] and is inspired by early work on type-state checking. Vault aims at low-level systems programming Vault and is C-like but it does include a notion of interfaces enhanced with protocols. Vault’s type checking is purely static which means Vault must deal with changing sets of resources or clients by using anonymous tracked types. The static type checking of Vault necessarily deals with join points differently to negotiable interfaces and requires that tracked resources have one unambiguous state at any join point (such as immediately after an IF statement). Vault is not object-based, nor is it intended for ex- tensible programming so it does not address multiple interface implementation, interface subtyping, or concepts of object re-entrance.

4.6 Summary

The negotiable interface approach to improving interface specification can be summari- zed as follows:

• Interface types are enhanced with protocols, which abstractly describe the po- tential states and state transformations for a given interface. Importantly, these protocols treat method execution as non-atomic, which allows inclusion of explicit constructs for specifying patterns of re-entrance and out-calls.

• Objects are extended with a dynamic type representation, reflecting the runtime state of the object in terms of the protocols of the negotiable interfaces it imple- ments.

• A new programming language command, USE, is provided to allow clients to query and guard the dynamic part of negotiable interface types.

123 4 Negotiable Interfaces

• The state system of Mentok statically guarantees that all objects will be used and transformed according to negotiable interface protocols.

Negotiable interfaces are useful for specifying patterns of behaviour in individual in- terfaces, or between sets of tightly-bound, cooperating interfaces. The next chapter describes a complementing feature, called type layers, which allows specification of beha- viour patterns across an entire application.

124 5 Type Layers

In fact, the mere act of opening the box will determine the state of the cat, although in this case there were three determinate states the cat could be in: these being Alive, Dead, and Bloody Furious. (Schr¨odinger’sMoggy explained, Terry Pratchett, Lords and Ladies)

The roles of component architectures are defined by interfaces and each interface specifi- cation describes the functional and non-functional requirements of a role. In particular, the specifications of tightly-bound interfaces should specify the nature of their dependen- cies on each other. Not all dependencies between the roles of an architecture can or should be specified at the interface level, however. Component based applications are incomplete by definition, so some roles may not be used in particular applications, while other applications may include roles that are independently defined. It stands to reason, however, that all the roles of an application are in some way related to one another, even if their relation states that they do not interact. Interface specifications can only refer to other interfaces with which definite relationship exists, so determining the relation- ship two interfaces (say, a transitive dependency) have within a given system is difficult without inspecting all interfaces. Import order and interface inheritance order describe some dependencies of an applica- tion, but separation of definition and implementation, combined with dynamic and late- binding reduces their effectiveness compared to traditional procedural languages. Other design aids such as UML diagrams and architectural description languages can be used to describe the overall architecture of an application; not all of these aids are formally bound to source code or constrain the implementation to reflect the intended design. The result being that, in code, interfaces are statically available to clients that can reference them. This chapter presents type layers, a language of Mentok that enables specification of re-entrance across loosely-bound interfaces. Type layers in Mentok have been fully implemented by MentokC, the Mentok compiler. This chapter also presents MentokL, a subset of Mentok that was created and formalized using Isabelle/HOL[Wen00, NPW00];

MentokL is a superset of MentokP , which was presented in chapter4. MentokL models enough of Mentok to enable investigation of the safety properties of type layers but is

125 5 Type Layers

not intended to be a usable programming language. The examples presented in this chapter and in AppendixB are derived from a component library for windowed database applications that was developed to test type layers in Mentok. The layer diagrams presented in this chapter and AppendixB are actual output from MentokC, which uses GraphViz[EGK+02] to visualize type layers. Type layers is a language feature that enables layering of modules (units of composi- tion) and interfaces (units of component specification) into a layered application archi- tecture. Type layers are declared in the source code of a program, and form a abstract control flow graph that constrains execution in a program through static and dynamic checking. Type layers can be used to describe dependencies between different parts of an application, and can be used to illustrate an abstract architectural description, based only on the roles of an application. Type layers have a graphical interpretation that can be used which can be used to easily identify type layer errors in module compositions, as well as track control flow through an application. In Mentok, type layers are combi- ned with negotiable interfaces to bound control flow through stateful roles (and therefore, stateful objects). Type layers were first presented in [Ken03]. Section 5.1 informally describes the type layers in Mentok, while section 5.2 discusses

a formalization of type layers in MentokL, which was created to enable investigation of the safety properties of type layers in Mentok. Section 5.3 gives an overview of the implementation of type layers in MentokC, the Mentok compiler, and the Mentok runtime libraries, while sections 5.4 and 5.5 examine issues and related work of type layers, respectively. This chapter assumes familiarity with negotiable interfaces, and should be read after chapter4.

5.1 Type Layers in Mentok

Type layers extend the programming model of negotiable interfaces. Negotiable inter- faces specify protocols for individual roles and dependencies between tightly-bound coope- rating roles. Type layers specify loosely bound dependencies between all the interfaces of a program. The type layer language feature in Mentok has three significant pieces:

• Module signatures are extended with dependency specifications called type layers. The dependency relationships specify a layering over the negotiable interfaces defined and imported by a module. Interfaces from upper layers can legally call down into lower layers, but cross layer, or up layer calls are illegal (ex- cept when specified by negotiable interfaces). Layering is non-strict, meaning that an interface can call down to any lower level interface, not just those immediately

126 5.1 Type Layers in Mentok

below.

• The type layers of a composition of modules are themselves composed to form a directed graph called the type layer graph. The type layer graph specifies dependencies between all the stateful roles (negotiable interfaces) of a pro- gram. Well-formed type layer graphs must form an acyclic directed graph, which gives them a layered structure. are acyclic, which gives them a layered structure; negotiable interfaces from upper layers depend on (may initiate state transforma- tions in) negotiable interfaces from lower layers.

• Negotiation is extended to enforce type layer dependencies. Clients ini- tiate state transformations via negotiations, so at each negotiation point, a check is included to ensure that any new state transformation initiated is down layer. This check ensures that cyclic dependencies between objects are constrained to tightly bound cooperation (through negotiable interface protocols) or loosely bound coope- ration via interface layering.

5.1.1 Type and Component Layers

Type layers extend the module model described in section 3.1.4. In addition to specifying module imports and declaring types, each definition module also specifies dependency relationships between the types that it declares and imports. There are two kinds of dependency relationships, called order and equate, which can be declared per-module, or per-type:

• Per-type dependencies specify individual dependencies between two (possibly loosely-bound) types:

– Type-order dependencies specify that one type is layered above another type.

– Type-equate dependencies specify that two types exist in the same layer - all dependencies applied to one type will be applied to the other type.

• Per-module dependencies specify dependencies between the all of the types decla- red in two modules:

– Module-order dependencies specify all the types in the upper module may depend upon the types in the lower module.

– Module-equate dependencies specify that two modules have the same de- pendencies, so any dependency relationship applied to one module, will also apply to the other module.

127 5 Type Layers

Figure 5.1: Type layer diagram legend.

Figure 5.1 gives the legend for type layer graphs, examples of which are given in appendixB.

• Negotiable interface types are displayed as box nodes, labeled with the fully qua- lified name of the interface. The colour of the is the colour of the defining module. – Equated interfaces share the same node, and are designated by a comma delimited label of an interface node.

• Modules are depicted as a pair of labeled triangles. The inverted triangle represents the “top” of the layering defined by the module, and the normal triangle represents the bottom of the layering defined by the module. Modules are colour coded, with each distinct module represented as a different colour. – Equated modules share the same triangle pair, and are designated by a comma delimited label in a pair of module nodes. Equated modules are not conside- red “distinct” with respect to the type layer graph” so equated modules are assigned the same colour.

• Order relationships are represented as arrows in the graph. Order relationships between modules always go from triangle to inverted triangle. Order relationships

128 5.1 Type Layers in Mentok

ModOrder ::= Ident "<" I d e n t ModEquate ::= Ident ":=" I d e n t ModLayers ::= "COMPONENT""LAYER" ModOrder | ModEquate { ’,’ ModOrder | ModEquate } TypeOrder ::= TypeIdent "<" TypeIdent TypeEquate ::= TypeIdent ":=" TypeIdent TypeLayers ::= "TYPE""LAYER" TypeOrder | TypeEquate { ’,’ TypeOrder | TypeEquate } Import ::= Ident [ ":=" I d e n t ] ImportList ::= Import { "," Import }

Module ::= ["FOREIGN" | "SYSTEM" | "COMPONENT"]"MODULE" I d e n t ";" ["IMPORT" I m p o r t L i s t ";"] ["COMPOSE" I m p o r t L i s t ";"] [ModLayers ";"] [TypeLayers ";"] RestOfModule "END" I d e n t "."

Figure 5.2: Syntax for modules with type layers.

between interfaces always go from rectangle to rectangle. An arrow is included for each order declared, even if the order is declared more than once.

• Order or equate dependencies which introduce cycles into the graph are designated as red arrows, or empty nodes outlined in red.

MentokC emits type layer graphs to allow inspection of type layers declared by a com- position of modules. MentokC also emits minimal type layer graphs for well-formed type layers (acyclic graphs) to demonstrate the final runtime architecture of an application. Minimal type layer graphs are obtained by taking a type layer graph and removing all edges that are in the transitive closure of the edges in the original graph. Figures B.5 and B.6 demonstrate a normal type layer graph and its minimal representation.

5.1.1.1 Language Extensions

Figure 5.2 gives the relevant syntax for modules with type layers.1 Mentok differentiates layered modules from foreign, system or library (i.e. those written in Component Pascal) modules by using the COMPONENT module designator. Mentok also differentiates normal imports from component imports by using COMPOSE to reference component modules. Component modules must be composed, and only component modules can compose other component modules.

1 The shorthand “RestOfModule” in place of the syntax for the remaining module content (types, variables, procedures, etc.)

129 5 Type Layers

Per-type relationships between negotiable interfaces are declared after the TYPE LAYER keywords:

• The declaration: IFoo < IBar

declares that IBar (and any types layered above IBar) may depend on the methods of IFoo (and any types layered below IFoo). Line7 of listing B.4 declares that IWindow operations may call IWindowItem operations.

• The declaration: IFoo := IBar

declares that IFoo has the same dependencies as IBar. This relationships is requi- red (and in fact, implicit) when IFoo inherits from IBar, since inheritance is an “is-a” relationship. Line 11 of listing B.3 declares that IWidgetCache operations reside in the same layer as IDataCache operations. This places an extra restriction on the interface inheritance hierarchy as it prevents an interface from existing on two separate abstraction layers.

Per-module relationships between component modules are declared after the COMPONENT LAYER keywords:

• The declaration: ModFoo < ModBar

declares that ModBar has a per-module order dependency on ModFoo. The types declared in ModBar (and any types layered above them) can depend on types from the ModFoo. For example, the module App declares that Data interfaces can Access interfaces on line7 of listing B.5.

• The declaration: ModFoo := ModBar

declares that ModBar has the same dependencies as ModFoo. The module WidgetData declares it has the same dependencies as module Data on line6 of listing B.3.

Note that all negotiable interfaces are layered between the module “top” and “bottom” nodes in the layering. Even interfaces that are not explicitly layered, such as in the interfaces of module Access in listing B.1, are still layered within the module layering.

130 5.1 Type Layers in Mentok

5.1.2 Composition Model

Type layers extend the module model for modules described in section 3.1.4 with a simple composition check over type layers. The extended model for modules allows static composition, via module imports, and dynamic composition, via runtime loading of modules. For static composition, Mentok checks at compile time as to whether a composition of modules has a legal type layer graph. For dynamic composition, Men- tok ensures that dynamically loaded modules (and sets of modules) do not break the currently executing type layer graph, while still permitting extensibility.

5.1.2.1 Static

Modules that are statically composed during compilation are checked to ensure that their combined type layer graph also composes. The static composition check ensures that:

• the type layer declarations for each module are free from cycles, and

• the combination of type layer declarations from all the modules is free from cycles.

The compilation check works by searching for cycles by means of a topological sort[CLR90] whenever a new layer declaration is added to the graph. Type and com- ponent order declarations are simple arrows in the graph and so can easily introduce cycles (see examples in listings B.8 and B.9). Type and component equate declarations can introduce cycles by merging nodes that would otherwise have been ordered (see examples in listings B.8 and B.9). The cycle checking is only intended to inform the programmer of an error; cycle removal is the responsibility of the programmer.

5.1.2.2 Dynamic

In order to permit extensibility, type layer checking must be performed whenever a component is dynamically loaded. Dynamically loaded components may be independent of each other, and may possibly import their own, independent type libraries. There are three separate approaches for dynamic layer composition that have been implemented in Mentok:

• Full layer composition performs virtually the same composition check that is performed statically, and if a cycle is detected, the component is rejected. This approach is the most flexible approach, in that it permits a larger range of com- ponents to be dynamically loaded. It is also the most costly, since the statically composed type layer graph must be able to have changes made to it at runtime. This prevents some optimizations that speed layer checking at negotiations (see

131 5 Type Layers

next section). Updates to the runtime type layer graph must also be transactional to prevent cycles being introduced to the graph in the case that a component is rejected.

Full layer composition may be suitable for prototyping and RAD efforts where the layering of a system is not yet fully understood. In such efforts, overly conser- vative checking can be problematic and full layer composition affords component developers the flexibility to modify the layering of a system they are extending in response to changing requirements. Once an application is deployed, a more restrictive composition check may be appropriate.

• Frozen base composition introduces a new definition module kind, called the EXTENSIBLE module. Mentok checks statically that type layer declarations from EXTENSIBLE modules do not alter (strengthen) the type layers from any normal COMPONENT layers.2 This reduces the amount of checking required at runtime by ensuring that compositions of independent EXTENSIBLE modules do not contain cycles. Components which introduce cycles into the extensible portion of the type layer graph or attempt to load non-extensible definition modules are not loaded.

The frozen base composition scheme is suitable for extensibility scenarios where add- ins are themselves hosts to other add-ins, as in common in Integrated Development Enviroments like Microsoft Visual Studio or Eclipse. An IDE usually comprises of a shell and several core components, such as an editor, project system, and build system, all of which may be extended or possibly even replaced. Frozen base composition protects the layering for the shell and core components of the application, but still enables extensibility and sharing of common libraries and components.

• Frozen base, orthogonal composition also uses EXTENSIBLE modules, but re- jects components that import that same EXTENSIBLE type library. This means that each independently loaded component must have completely orthogonal EXTENSIBLE definition imports. No cycles can ever be introduced by such independent modules, and the amount of checking is vastly reduced; however, this is the most restrictive form of composition checking, as it does not permit components which import exactly the same EXTENSIBLE modules.

The frozen base, orthogonal composition scheme is suitable for very restrictive dynamic add-in models, such as might be required by a web browser. The effect of this scheme prevents dynamically loaded components from taking a dependency on an extensible type library; add-ins can be built using the benefits of type

2 EXTENSIBLE modules cannot add extra arrows between nodes declared by COMPONENT modules.

132 5.1 Type Layers in Mentok

layers but are prohibited from modifying the layered architecture of the extensible application.

5.1.3 Execution model

Type layers constrain the abstract control flow through an application by dynamically changing the availability of negotiable interfaces. An application can only legally use interfaces that it is currently using, or that are lower in the type layers than the interfaces it is currently using. Mentok leverages the dynamic checking of negotiable interfaces to perform type layer checking as well: negotiation acts as the point of entry and exit for type layers. Figures 5.3-5.7 demonstrate execution graphs for programs with type layers. Mentok’s execution graphs augment minimal type layer graphs with execution arrows, and an extra TOP node. The dashed execution arrows represent attempts by the program to enter (negotiate with) a new interface; green arrows are successful negotiations, while red arrows are unsuccessful negotiations that are prevented by type layers. The TOP node is the initial starting layer for a program. Figures 5.3-5.5 demonstrate how a set of legal nested negotiations progressively moves through the type layers.3 Initially, when the program is in the TOP state, all the interfaces are available, but as each negotiation is entered, the program moves down the type layer graph, and the set of available interfaces becomes smaller. Figures 5.6 and 5.7 demonstrate two negotiations that are prevented by type layers. Figure 5.6 shows an up-layer negotiation, as a data source attempts to affect an applica- tion; the type layer specifies that applications may depend on data sources, but not vice versa. Figure 5.7 shows a cross-layer negotiation, with an data base result attempting to post a message to the application; there is no dependency relationship between either of these interfaces, indicating that they come from unrelated parts of the application, and should not interfere with each other.

5.1.3.1 Negotiation

Since type layers already operate over negotiable interfaces, it is natural to perform static and dynamic type layer checking at negotiation points. Figure 5.8 informally illustrates the modified semantics of negotiation, again for a negotiation of the form: USE i :: b -> b’ DO Branch True ELSE Branch False END

3 The negotiations may not be nested in the same lexical scope, but are executed in a nested fashion.

133 5 Type Layers

VAR approot : App.IApplicationRoot; app : App.IApplication; data : Data.IDataSource; ... USE approot::[run]->[run]DO ... USE app::[ready]->[running]DO ... USE data::[unbound]->[clean]DO ... END; ... END; ... END

Figure 5.3: Entering IApplicationRoot.

VAR approot : App.IApplicationRoot; app : App.IApplication; data : Data.IDataSource; ... USE approot::[run]->[run]DO ... USE app::[ready]->[running]DO ... USE data::[unbound]->[clean]DO ... END; ... END ... END;

Figure 5.4: Entering IApplication.

VAR approot : App.IApplicationRoot; app : App.IApplication; data : Data.IDataSource; ... USE approot::[run]->[run]DO ... USE app::[ready]->[running]DO ... USE data::[unbound]->[clean]DO ... END ... END; ... END;

Figure 5.5: 134 Entering IDataSource. 5.1 Type Layers in Mentok

VAR app : App.IApplication; data : Data.IDataSource; app2 : App.IApplication; ... USE app :: [ready]->[running]DO ... USE data :: [unbound]->[clean]DO ... USE app2::[running]->[complete]DO ... END ... END; ... END;

Figure 5.6: Illegal up layer call to IApplication.

VAR data : Data.IDataSource; rslt : Access.ISQLResult; post : App.IPostMessage; ... USE data :: [unbound]->[clean]DO ... USE rslt :: [empty]->[populated]DO ... USE post::[post]->[post]DO ... END ... END; ... END;

Figure 5.7: Illegal cross layer call to IPostMessage.

At each negotiation, the interface being negotiated with is compared to the current layer; if the interface is in the correct state, and is layered below the current layer, the negotiation succeeds. In cases where the current layer is known statically, the check can be performed at compile time and notify the programmer that the code will never execute. If a check can be statically guaranteed to always fail, (i.e. always negotiating up or across layers), the program can be rejected and an error message generated. Otherwise, if the current layer is not known statically, the check is performed at runtime as part of negotiation, and the ELSE branch is executed in the case where the layering constraints are not met.

135 5 Type Layers

USE

1.

i :: ??

2.

E obj. (i* = obj) & (b < state obj) & True type i < layer.peek()

False

3a. state obj’ -= b i’ :: b layer.push(type i)

3b. 4. Branch_True Branch_False

3c. i :: b’ state obj’ += b’ i’ :: ?? layer.pop()

END

Figure 5.8: Negotiation semantics with type layers.

136 5.2 A Formal Model of Type Layers

5.2 A Formal Model of Type Layers

This section describes MentokL, the superset of MentokP with type layers detailed in appendixC. As with type layers in Mentok, MentokL builds upon the negotiable interface features of MentokP ; appendixC describes only those parts of MentokL rules that differ from MentokP .

The following section describes type layers in MentokL programs, and the definition of the type layer relationship. Sections 5.2.1 and 5.2.3 describe how MentokL’s well- 4 formedness checks and state system differ from that of MentokP . Section 5.2.4 describe the additional layer checking in the operational semantics of MentokL.

5.2.1 MentokL Programs

MentokL programs extend the program model of MentokP (described in section 4.2.2) with type layer declarations. MentokL does not include a notion of modules, which means it also does not include component layers as exists in Mentok proper; no generality is lost by omitting component layer declarations in MentokL, as component layers are coarse grained type layer specifications that can be rewritten as a set of simple type layer specifications. The type layer declarations of a program are used to define type layer and equality relationships for a program. Later sections will describe how type layering and equality are used in state system checking and the runtime.

5.2.1.1 Programs

Section C.1.1 gives the definition for MentokL programs, which have all the components of a MentokP program, plus:

• a list of type layer declarations, e.g. TYPE LAYER I1 < I2;

• and a list of type equality declarations, e.g. TYPE LAYER I1 := I2.

Each type layer and equality declaration, is a layerDecl, which is a pair of type names.

5.2.1.2 Type Equality and Layer Relationships

Section C.1.1 gives the definitions for a type equality and layer relationships of a given program. Both relationships operate over pairs of typeLayer values in the context of a given program; a typeLayer value is a layer defined for an interface identifier, I, written Layer I, or one of the special typeLayer values, T op or Bottom.

4 MentokL requires no changes to MentokP ’s type system.

137 5 Type Layers

The type equality relationship defines equivalence classes over the typeLayer values of a program. Two typeLayer values, L and L0, are members of the same equivalence class for a program, P , if:

P ` L == L0

The type equality relationship for a given program, P , is inductively defined by taking the union of the reflexive, symmetric, transitive closure of the type equality declarations of P , with two singleton valued equivalence classes for T op and Bottom; that is, T op and Bottom are not considered to be type equivalent with any other layer, or with each other. The type layer relationship for a program, P , is also defined over typeLayer values The relationship is used to specify if a typeLayer, L, is “below” another typeLayer L0 in the layering relationship for P , and is written:

P ` L  L0

The type layer relationship for a program, P , is defined by taking the transitive closure of the type layer declarations, the layerDecl values, of P , and defining T op to be “above” all other layers, Bottom to be “below” all other layers, and using the type equality relationship of P to define substitutability of components. The following sections describe how the type layer relationship is used to statically

and dynamically enforce layering constraints in executing MentokL configurations.

5.2.2 Program Well-Formedness

Program well-formedness in MentokL is defined in section C.4, and is largely the same

as that in MentokP (c.f. section 4.2.3). Additional checks are performed over type layer declarations and the type layer relationship for the program:

• All type layer declarations (for layer and equality) must operate over known inter- face type names.

• The type layer relationship for the program must not include any cycles. That is, any program, P , is not well-formed if for any interface, I, in P :

P ` Layer I  Layer I

This requirement that the type layer relationship be anti-reflexive ensures that the type layer relationship forms an acyclic directed graph over the types of a program.

138 5.2 A Formal Model of Type Layers

Combined with the type equality relationship, the type layer relationship forms a partial order over type layer equivalence classes.

5.2.3 State System

The rules defining the state system of MentokL have the same form as the in MentokP

(c.f. section 4.2.5). The only change to the rules of MentokP is to include an additional layer check at each global negotiation statement, UseG. Section C.2 gives the definition of the currentLayer function, which, given static typing and state environments, determines the current “lowest” type layer in scope by examining all interfaces subject to negotiation and picking the type layer equivalence class that is below all others. If there is no single lowest equivalence class, no type layer negotiation is possible, and the current layer is determined to be Bottom. Section C.3 gives the single modified state system rule for global negotiations. The only change to this rule over that given in section A.10 is the inclusion of a layer check; the layer that is being negotiated for must be lower that the layer calculated by the currentLayer function.

5.2.4 Operational Semantics

Sections C.5 and C.6 give the semantics for MentokL, modified beyond that of MentokP . Section C.5 defines runtime equivalents of the current layer functions defined in section C.2. Given a local value binding and an object store, the runtimeCurrentLayer func- tion determines the set of values that are subject to ongoing negotiations and calculates the minimum type layer using the type layer and equivalence relationships. Section C.6 gives the two runtime rules for global negotiation. The first rule, UseGlb- True, has an additional check to ensure that global negotiation succeed only if the reques- ted type layer is below the current layer, in addition to passing all the requirements of negotiation in MentokP . The converse rule, UseGlbFalse, ensures negotiations always fail if the negotiation would move “up” the type layering. On first glance it may appear that both these checks are actually redundant for well- formed MentokL programs. It is true that the static type layer check in the state system ensures that the condition in UseGlbTrue holds in many cases; however, cooperative behaviours allows a program to re-enter a higher type layer from a lower, and even without cooperative behaviours, a client can negotiate down two separate layers in a single stack frame, and then call off to the higher layer, statically losing the current layer state. The dynamic check described in the rules for MentokL is necessary to enforce layering in such cases.

139 5 Type Layers

5.3 Implementation: Type Layers and the Mentok Compiler

MentokC, the compiler for Mentok developed as part of the work described in the disserta- tion, adds negotiable interfaces and type layers to a Component Pascal variant language, and emits code targeting Microsoft’s Common Language Runtime (CLR). Apart from trivial changes for scanning and parsing type layer specific syntax, MentokC performs type layer well-formedness checks, including a static composition check over a set of modules, and layer checking at negotiation points. Static composition and layer checking in MentokC are discussed in section 5.1.2.1, while type layer specific runtime system features are is discussed in 5.1.2.2. Type layers build upon and leverage negotiable interfaces and negotiation, so this section builds upon the discussion of MentokC’s implementation of negotiable interfaces in section 4.3.

5.3.1 Structure of the Compiler

Compilation of type layers builds upon the structure of MentokC described in 4.3.1. The syntax for Mentok’s type layer features is straightforward, and changes to Mentok’s scan and parse phase are of no particular interest in the dissertation. Once a program is successfully parsed, and the definitions of the program resolved and checked, MentokC checks type layer well-formedness.

5.3.1.1 Composition Checking

Static type layer well-formedness checks follow that described in section 5.1.2, and forma- lized in section C.4. First, a type layer graph is built for a module, and its immediate dependencies and checked for cycles. Static composition checking is then performed, analyzing not just a module and its immediate dependencies, but all statically known transitive dependencies as well. The type layer graph for the entire composition of mo- dules is checked for cycles. This composition check is included here for convenience, and could easily be factored out into a separate composition/deployment time tool. As a visual aid to programmers, MentokC can optionally output type layer graphs, such as those found in appendixB. MentokC incorporates the GraphViz package[EGK +02] in order to layout graphs and generate image files. Each negotiable interface in a type layer graph is represented as a labeled rectangular box, while each module is represented using a color coding that is either auto-selected, or configured by a user. Each module is also given a special “top” and “bottom” layer to visualize component layers; these special layers are visually represented as downwards and upwards facing triangles, labeled with the module name. MentokC can either produce full type layer graphs (e.g. figure B.5), in which every

140 5.3 Implementation: Type Layers and the Mentok Compiler layer declaration is represented by a single arrow. Such graphs are most useful during inspection of an application for discovery purposes, or as part of refactoring. A user can also request output of a minimal type layer graph (e.g. figure B.6) in which all duplicate arrows or arrows that can be represented as a combination of other arrows are removed. Minimal type graphs remove some of the declaration structure of type layers in favor of showing the final abstract control flow constraints for an application, thus providing an immediate visualization of the runtime behaviour of an application. The type layer graph’s output by Mentok also help visualize errors in type layer declarations. MentokC identifies type or component layer declarations that introduce a cycle by drawing them in red and assigning them zero weight for layout purposes, meaning that errors will always be drawn as “back” arrows (see figures B.9 and B.10). Cycles introduced by erroneous type or component equals declarations are identified by distinctly coloring the result node in the graph (see figures B.11 and B.12).

5.3.1.2 Layer Checking at Negotiation

Once a module has passed type layer composition checking, MentokC proceeds to per- form type and state checking. Type layer checking is performed as part of state checking, using the simple mechanism described in section and again in section 5.2.3. Any negotia- tion that can be statically determined to always fail due to layering issues (e.g. 5.6 and 5.7) are flagged as errors. The type layer context may not always be know at compile time, however, so MentokC needs to generate code to represent and check type layers at runtime.

5.3.2 Runtime Representations and Dynamic Checking

Type layer checking at runtime comprises of two separate pieces of functionality. A dynamic composition check at load time ensures that modules loaded via reflection do no introduce cycles into the type layer graph, while dynamic type layer checking ensures that control flow moves “down-layer” through negotiation points. In order to perform these checks, MentokC emits CLR custom attributes to describe the type layers for emitted modules, and generates code to build and check the type layer graph at load time. Runtime libraries for negotiation use thread local storage to store and check the current layer at negotiation points.

5.3.2.1 Type Layer Graphs

The basis of the runtime representation of type layer graphs begins with assembly level custom attributes, one for each type layer declaration in a module. Listing 5.1 demons- trates a type layer declaration block for a module called LayeredModule, and listing 5.2

141 5 Type Layers

COMPONENTMODULE LayeredModule;

COMPOSE Import1, Import2, Import3, Import4;

COMPONENTLAYER Import1 < Import2, Import3 := Import4;

TYPELAYER IInterface1 < IInterface2, IInterface1 := IInterface2; Listing 5.1: Signature for module LayeredModule

[assembly: Mentok.RTS.ModuleOrder("Import1", "Import2")] [assembly: Mentok.RTS.ModuleEquate("Import3", "Import4")] [assembly: Mentok.RTS.TypeOrder(typeof(IInterface1), typeof(IInterface2))] [assembly: Mentok.RTS.TypeEquate(typeof(IInterface3), typeof(IInterface4))] Listing 5.2: Custom attributes emitted for LayeredModule.

gives the corresponding set of custom attributes emitted by MentokC. Each kind of layer declaration has a corresponding custom attribute: TypeOrder for type layer declarations, TypeEquate for type equate declarations, ModuleOrder for component layer declarations, and ModuleEquate for component equate declarations. At runtime, the Mentok runtime libraries use reflection over the custom attributes of loaded modules to build the type layer graph. MentokC emits a call to a runtime library to build the type layer graph as a prologue to the main module body. MentokC uses the same strategy as GPCP to represent module scoped artifacts, emitting them as part of a static class with the same name as the module. Listing 5.3 gives a simple module body for the LayeredModule, while listing 5.4 gives the equivalent C# code to that emitted by MentokC. The call to the load the type layer graph, Mentok.RTS.Metatype.LoadTypeLayerGraph(), builds the portion of the type layer graph associated with the LayeredModule and its dependencies using reflection. Checking strategies for dynamically loaded modules are discussed in section 5.1.2.2.

5.3.2.2 Negotiation and Layering

The code emitted by MentokC at negotiation points is unchanged from that described in section 4.3.2.3 as the runtime libraries implement all layer checking internally. The runtime libraries use thread-specific storage to record a stack of type layers, representing the layers currently under negotiation. At each negotiation point, the runtime libraries compare the requested type layer with the top of the stack to determine the legality of

142 5.4 Discussion: Problems and Extensions

COMPONENTMODULE LayeredModule;

(*...*)

VAR i* :INTEGER;

BEGIN i := 10; Console.WriteString("Foo"); END LayeredModule. Listing 5.3: Body for module LayeredModule static class LayeredModule { public static int i;

static LayeredModule() { Mentok.RTS.Metatype.LoadTypeLayerGraph(); LayeredModule.i = 10; Console.WriteString("Foo"); } } Listing 5.4: Emitted code for the body of LayeredModule.

the negotiation. When a negotiation succeeds, the new layer is pushed onto the stack, and when a negotiation completes, the top layer is popped off the stack. Internally, each type is given a unique integer identifier that is used to generate a bit set for each type indicating which other types are “below” a given type. This is a more expensive representation to build, but it may be done once at load time and then enables order(1) type layer checking at negotiation points.

5.4 Discussion: Problems and Extensions

Type layers allow specification of control flow constraints through the types of a pro- gram. When combined together with negotiable interfaces, type layers can be used to restrict re-entrance in general, while still allowing specific cases of re-entrance through the cooperative and re-entrant behaviour constructs in negotiable interfaces. Like negotiable interfaces, type layers as presented so far suffer from issues of expres- siveness. Type layers exacerbate the problem of reachability in negotiable interfaces as well as restrict some acyclic control flow in recursive data structures.

143 5 Type Layers

5.4.1 Reachability

Type layers introduce another layer of complexity to the issue of reachability in nego- tiable interfaces (first discussed in section 4.4.3). The question of reachability is no longer whether the cooperative constraints on a method can be satisfied, but also whe- ther a client can negotiate for all the state needed to satisfy a method. TYPELAYER IFoo1 < IBar, IFoo2 < IBar;

PROCEDURE (this:IFoo1) FooBar(p:IFoo2::[a]->[b]) :: [A]->[B]; Listing 5.5: Reachabilty issues with type layers

Listing 5.5 gives a partial declaration for an interface IFoo1. A client must negotiate to enter the IFoo1 type layer before calling method FooBar since FooBar has a method behaviour. The same method also has a cooperative behaviour for a parameter of type IFoo2, meaning that the client must negotiate to enter the IFoo2 type layer before calling FooBar. This declaration is problematic, because IFoo1 and IFoo2 have no relationship in type layering, meaning that FooBar is uncallable. Simple cases of reachability issues, such as the above can be detected and prevented statically. In the case where a method does not break type layering, it is still possible that behaviour reachability may not be satisfied, however. At the time of writing, it is not well understood how type layering would impact any potential behaviour reachability analysis.

5.4.2 Recursive Data Structures

Type layers enable a programmer to abstractly define a control flow architecture over a program, in terms of the program’s interfaces and modules. As described thus far, type layers lack the expressiveness to describe and constrain control flow through some recursive data structures even if the data structures themselves are acyclic. Consider control flow through a hierarchical composition of objects, each of which represents a particular widget in a user interface. It is conceivable that each widget in the tree probably has some common stateful widget role or interface, say IWidget, which provides some layout specific functionality. A layout algorithm for this tree of widgets would recursively traverse the tree, calling into IWidget to layout the various parts of the user interface. Since type layers operate at the type level, and not the instance level, type layers cannot be used to represent and constrain control flow through such a tree - all IWidget operations are considered to exist in the same type layer. An interesting extension to type layers, called instance layering, was investigated and prototyped in Mentok to model instance level patterns. The concept of instance layering

144 5.5 Related Work is to declare patterns of interfaces that represent recursive (but acyclic) data structures, and build up instance level layering dynamically as objects are constructed. A instance layer declaration is written: IWidget’ < IWidget or IWidget < IWidget’. The first form states that any instance of IWidget created in while in an existing IWidget context is in a lower instance layer, whereas the second states that newer objects are created in a higher instance layer. By checking that no cycles exist within instance layers, the type and instance layers of a program can be shown to form a partial order.

5.5 Related Work

The constraints of type layers could be specified by software contracts [MHKM95] in a language such as Eiffel[Mey92] or Spec#[BLS05]. Pre– and post–conditions contracts are more expressive than afforded by negotiable interfaces and type layers. Type layers in Mentok provide a simple, declarative mechanism that can be used to check, at compo- sition time, whether the unity of a programs parts will interoperate. Mianjin[RS97] is a strongly-typed, object-oriented language designed for parallel pro- gramming and used in heterogeneous cluster computing environments[BKR00]. Mian- jin’s type system (presented informally in [SR00]) statically controls object re-entrance by preventing atomic methods from calling communicating or polling methods. In [KHS01], Mianjin’s type system was formalized and shown to statically restrict re-entrance and prevent certain forms of deadlock in the presence of bounded buffering schemes. Men- tok and Mianjin both extend Pascal-family languages, but are quite different in intent. In particular, Mianjin’s declarative layering mechanism is constrained to two layers (atomic and polling), whereas layers in Mentok are constrained only by the number of negotiable interfaces in a program. The creators of the Cycle-Free Methodology[ML02] observe that so-called transient errors, or errors occur infrequently and as the result of erroneous control flow as opposed to functional mistakes, are amongst the most difficult bugs to solve. Instead of proposing how such errors may be found and removed, the cycle-free methodology attempts to prevent such errors by enforcing a layered design on all programs. Objects are placed into contexts, which act as boundaries that control and restrict re-entrance. The authors use a modified version of C++, called W++, that has language constructs called Willie Classes, which are used to create contexts, and events which capture and defer up-calls. Unlike Mentok, layering in W++ is not declarative, and is instead dynamically constructed through logic in Willie Class instantiation. Keris [Zen02a, Zen04, Zen05] is language based on Java, and aims to support com- ponent software development by enhancing Java’s with a module system. Keris’ module system attempts to remedy key weaknesses in Java’s package system, such as issues of

145 5 Type Layers

granular modularity, and lack of genericity and extensibility at the module level. Ke- ris’ adds module abstractions to Java that support hierarchical composition, refinement, and specialization.

5.6 Summary

Mentok’s type layers help bridge the gap between design and implementation by inclu- ding control flow specifications as part of the programming language, enabling both graphical representation of application architecture, and enforcement of that architec- ture through static and dynamic checking. Type layers allow specification of abstract control flow in components as a property of the types used by a component, yielding enhanced module signatures and a resulting enhanced notion of safe composition.

146 6 Conclusions and Future Work

+++ Divide By Cucumber Error. Please Reinstall Universe And Reboot +++ (Terry Pratchett, Hogfather)

Component software is an approach that offers many tangible benefits for software engi- neering and for software markets. By definition, software components are deployed in an incomplete state, and, as a result, the specification requirements of software components are more technically demanding that those of more traditional, monolithic approaches; however, most modern programming languages used for construction of components provide only simple object-oriented interface types as mechanisms for component specifi- cation. This is problematic as such mechanisms do a poor job of specifying re-entrance conditions for components[Szy02]. This leads to the research question for this disserta- tion: Can a component-oriented programming language be extended with constructs that permit specification of re-entrance conditions for components? Mentok is a programming language that provides enhanced specification of stateful component roles through two unique language features: negotiable interfaces and type layers. These language features enable specification of re-entrance conditions for compo- nents. Negotiable interfaces are enhanced interface types that enable abstract specification of changing object state, and changing method availability based on that state. The specifications of negotiable interfaces treat method execution as non-atomic, and include mechanisms for specifying patterns of out-calls and recursion or re-entrancy. Negotiable interface types have a dynamic, per-object representation that allows clients to query or negotiate with an object, via a new programming language command, USE. Negotiation provides dynamic runtime checking, which, combined with static state checking, ensures that clients of negotiable interfaces use objects safely according to negotiable interface specifications. Negotiable interfaces are part of the programming language, and the constraints ex- pressed by negotiable interfaces are checkable at compile time and runtime; as such, negotiable interfaces satisfy the contractual and formal requirements of component specifications as given in section 1.2. Negotiable interfaces satisfy the abstractness requi-

147 6 Conclusions and Future Work

rement of component specifications as they do not expose implementation details except where those details (such as out-calls) need to be understood by a client. Negotiable interface protocols are expressive enough to allow specification of certain re-entrance conditions but cannot currently express other behaviours, such as non-determinacy (c.f. section 4.4.2).

Type layers were created to address the gap between the design or architecture of an application, and the set of types and modules in that application. Type layers are declarations that enhance module (and component) signatures with abstract control flow specifications that operate over types and modules. The combined type layers of a set of modules for a graph that is checked to ensure it is free from cycles, and, at runtime, type layers are used to ensure clients only negotiate “down-layer”. Both these checks constrain the set of types and operations available at any given point of execution, thereby restricting potentially re-entrant calls. Type layers also have a simple graphical representation that can be used to inspect the design of a program, or to identify composition errors in the layering of a program.

Type layers satisfy the contractual and formal requirements of component specifica- tions by extending module signatures and permitting composition and runtime checking to constrain control-flow. Type layers satisfy abstractness by specifying on control- flow requirements as an abstract, partial order over the modules and interfaces of an application; critically, implementation details of modules and components are not expo- sed by type layers. Type layers improve the expressiveness of module signatures by capturing architectural layering that cannot be expressed in link-time or compile-time dependencies alone, and the graphical representation for type layers improves readabi- lity of programs; however, like negotiable interfaces, type layers suffer from some issues of expressiveness, and currently restrict some otherwise legal behaviour, such as control flow through recursive data structures (c.f. section 5.4.2).

An implementation of the features described within this dissertation was completed as part of this doctoral program of research. MentokC, the Mentok compiler, was created using the Gardens Point Component Pascal compiler as a basis. Negotiable interfaces

and type layers have been partially formalized in the mini-languages MentokP and

MentokL, respectively, which, also respectively, are presented in appendicesA andC. The formal definitions presented in the appendices and proofs of properties of those definitions were created using the Isabelle theorem proving environment, based on the HOL (Higher Order Logic) libraries.

148 6.1 Future Work

6.1 Future Work

Discussion of problems and extensions specific to negotiable interfaces and type layers have already been given in sections 4.5 and 5.5, respectively. Another piece of future work directly relevant to this thesis would be the complete formalization of Mentok, based on the work formalizing the mini-languages MentokP , and MentokL. There are, however, some other areas of future work that are not purely sequential in nature. Mentok extends a fairly simple object-based programming language model, to which many other, interesting language features have been added. Inclusion of common lan- guage features such as exception handling would be certainly improve the usefulness of Mentok as a programming language. Integrating negotiable interfaces and type layers into richer languages provides, therefore, a rich source of future work for Mentok. Combining negotiable interfaces with traditional pre- and post-condition contracts is an obvious choice for future work. Negotiable interfaces and type layers can be used to specify and constrain object interactions, including re-entrance. The abstract state accompanying each object is not currently linked to concrete object state. Combining negotiable interfaces with programming by contract would ideally relate this abstract state to pre- and post-conditions of methods. The re-entrance protection provided by Mentok may allow invariant checking at out calls, as potentially re-entrant out calls can be identified statically, along with the abstract intermediate state of the interface. This means that the need to establish that all class invariants hold at any out calls is eliminated, since not all out calls will be re-entrant. Another possibility for future work is using negotiable interfaces and type layers for concurrency control. Protocols for concurrency control in object based languages have been investigated in the past, so giving negotiable interfaces a blocking semantic under concurrent negotiations may not be particularly novel. However, type layer graphs pres- cribe an acyclic control flow through applications, and may guarantee some interesting deadlock freedom properties if used for synchronization.

149 150 A MentokP : A language with negotiable interfaces

This appendix presents the pretty-printed Isabelle/HOL[Wen00, NPW00] theory sources for MentokP , a small object-based language with negotiable interfaces. The definitions provided in this appendix have been used to investigate safety properties of negotiable interfaces in Mentok.

151 A MentokP : A language with negotiable interfaces

A.1 Names and Identifiers

theory Names = ExcpBase:

All names for tokens, fields, methods and types are identifiers. Names for va- riables are either identifiers or the reserved word THIS. typedecl ident

ident :: "type"

datatype tokenName = ident datatype fieldName = ident datatype methodName = ident datatype typeName = ident datatype varName = This | VName ident

end

152 A.2 Types in MentokP

A.2 Types in MentokP theory Types = Names:

A.2.1 Type definitions

Types are either primitive or object types. The primitive types of MentokP are UNIT, INTEGER, or BOOLEAN. UNIT has only one value and is used primarily to allow methods to be treated uniformly as expressions. That is, methods that do not return a value are given a UNIT type and always return the single value of UNIT. The object types of MentokP are interface of record types or the special type of NIL, the NILTYPE. datatype primType = UnitT | BoolT | IntT datatype objType = NilT | RecT typeName | IfcT typeName and cpType = PrimT primType | ObjT objType translations "primType" ) (type) "Types.primType" "objType" ) (type) "Types.objType" "cpType" ) (type) "Types.cpType" syntax NilType :: "cpType" Iface :: "typeName ⇒ cpType" Record :: "typeName ⇒ cpType" translations "NilType" "ObjT NilT" "Record R" "ObjT (RecT R)" "Iface I" "ObjT (IfcT I)"

A.2.2 Type tests and functions isRec and isIfc are predicates that test if a type is a record or interface type respectively. typeNameOf is a partial function that returns the type identifier of a record or interface type, but nothing if the type is not a record or interface. consts isRec :: "cpType ⇒ bool" isRecType :: "objType ⇒ bool" primrec

153 A MentokP : A language with negotiable interfaces

"isRec (PrimT x) = False" "isRec (ObjT x) = isRecType x"

primrec "isRecType NilT = False" "isRecType (IfcT I) = False" "isRecType (RecT R) = True"

consts isIfc :: "cpType ⇒ bool" isIfcType :: "objType ⇒ bool"

primrec "isIfc (PrimT x) = False" "isIfc (ObjT x) = isIfcType x"

primrec "isIfcType NilT = False" "isIfcType (IfcT I) = True" "isIfcType (RecT R) = False"

constdefs theRecord :: "cpType ⇒ typeName" "theRecord T ≡ ε R. T = Record R" theInterface :: "cpType ⇒ typeName" "theInterface T ≡ ε I. T = Iface I"

consts typeNameOf :: "cpType ⇒ typeName option" objTypeNameOf :: "objType ⇒ typeName option"

primrec "objTypeNameOf NilT = None" "objTypeNameOf (IfcT i) = Some i" "objTypeNameOf (RecT r) = Some r" "typeNameOf (PrimT x) = None" "typeNameOf (ObjT x) = objTypeNameOf x"

end

154 A.3 Primitive Values

A.3 Primitive Values theory Values = Types:

The type ref represents runtime addresses. typedecl ref arities ref :: "type"

Values in MentokP are Unit (of type UNIT), booleans (of type BOOLEAN) , integers (of type INTEGER), NIL (of type NILTYPE) and references ( which have object types). References are runtime values only and cannot be declared statically. datatype value = Unit | Bool bool | Intg int | NIL | Reference ref translations "value" ) (type) "Values.value" "ref" ) (type) "Values.ref" consts theRef :: "value ⇒ ref" primrec "theRef (Reference r) = r"

A.3.1 Types of values typeOf defines the types of values in MentokP . typeOf takes a lookupT ype function to determine the type of references. When typeOf is used for static checking (i.e. for well- typed rules c.f. section A.9) this lookup function is empty (undefined for all inputs) thus preventing references from being typed statically. When the type rules are applied to a running system (for checking type safety), this lookup type is a function that returns the specific object type of a reference, as defined in the store (see section A.12.4). types lookupType = "ref ⇒ cpType option" translations "lookupType" ) (type) "ref ⇒ cpType option" consts typeOf :: "lookupType ⇒ value ⇒ cpType option" primrec "typeOf lut Unit = Some (PrimT UnitT)" "typeOf lut (Bool b) = Some (PrimT BoolT)" "typeOf lut (Intg i) = Some (PrimT IntT)" "typeOf lut NIL = Some (NilType)" "typeOf lut (Reference r) = lut r"

155 A MentokP : A language with negotiable interfaces

A.3.2 Default values

defaultVal defines the default values for types in MentokP . The default values for object types is always NIL. initDefaults takes a mapping from identifiers to types and returns a mapping from the same identifiers to the default values of their types. consts defPrimVal :: "primType ⇒ value" defaultVal :: "cpType ⇒ value"

primrec "defPrimVal UnitT = Unit" "defPrimVal BoolT = Bool False" "defPrimVal IntT = Intg 0"

primrec "defaultVal (PrimT P) = defPrimVal P" "defaultVal (ObjT T) = NIL"

constdefs initDefaults :: "(’a, cpType) table ⇒ (’a, value) table" "initDefaults B ≡ (λ k. case (B k) of None ⇒ None | Some T ⇒ Some (defaultVal T))"

end

156 A.4 State Declarations

A.4 State Declarations theory NegotiationDecls = Names + HOLplus:

A.4.1 Declarations

A token bag declaration is a list of token names. A behaviour declaration is a pair of token bag declarations. types behaviourDec = "tokenName list × tokenName list" types parBhv = "behaviourDec option" translations "behaviourDec" ) (type) "tokenName list × tokenName list " "parBhv" ) (type) "behaviourDec option"

A.4.2 Valuations A token bag is the multiset valuation of a token bag declaration. A behaviour is a pair of token bags (tokenName multisets). types tokenBag = "tokenName multiset" types behaviour= "tokenBag × tokenBag" translations "tokenBag" ) (type) "tokenName multiset" "behaviour" ) (type) "tokenBag × tokenBag" consts bagValue :: "tokenName list ⇒ tokenBag" behaviourVal :: "behaviourDec ⇒ behaviour" syntax bagValue_ :: "tokenName list ⇒ tokenBag" ("k_k") behaviourVal_ :: "behaviourDec ⇒ behaviour" ("k_kB") translations "kbk" "bagValue b" "kbhkB" "behaviourVal bh" primrec "k[]k = {#}" "kx#xsk = {#x#} + kxsk" defs behaviourVal_def: "kbhvkB ≡ (kfst bhvk, ksnd bhvk)"

A.4.3 Well-formedness

The basis of a token bag is the basis of its multiset value. A token bag, b, is well- formed w.r.t. a basis, x, if the basis of the b is a subset of x. A behaviour, bhv, is

157 A MentokP : A language with negotiable interfaces

well-formed w.r.t. a basis, x, both token bag components of bhv are well-formed w.r.t. x. constdefs tokenBasis :: "tokenName list ⇒ tokenName set" "tokenBasis ts ≡ set ts"

constdefs tokenBagWF :: "tokenName list ⇒ tokenName list ⇒ bool" "tokenBagWF basis bag ≡ tokenBasis bag ⊆ tokenBasis basis"

constdefs behaviourWF :: "tokenName list ⇒ behaviourDec ⇒ bool" "behaviourWF basis behave ≡ tokenBagWF basis (fst behave) ∧ tokenBagWF basis (snd behave)"

A.4.4 Token Bag partial ordering A token bag, b, is a sub bag of a second token bag, b0, if b0 contains at least all the tokens that b contains. Note on Isabelle notation: The infixl annotation specifies the binding precedence of the operator in the defintion below. An operator with binding precedence n binds more tightly than an operator with binding precendence n + m where m is a positive integer. constdefs less_tokenBag :: "tokenBag ⇒ tokenBag ⇒ bool" (infixl "E" 45) "b E b’ == ∀ a. count b a ≤ count b’ a"

end

158 A.5 Expressions and Statements in MentokP

A.5 Expressions and Statements in MentokP theory Statements = Values + NegotiationDecls:

Expressions in MentokP are values, identifiers, method calls, field dereferences and new object creations. NEW has been turned into an expression rather than a statement in order to simplify the semantics. Likewise, all method calls are expressions - a methods that would not normally return a value are representing as functions returning a UNIT and the resulting value must be assigned to a variable or field of type UNIT. Two synthetic expressions, IdState, and Body are used for modelling stateful references, and stack frames (the bodies of executing methods). The synthetic expression for stateful reference carries the behaviour of the transformed reference, and the expression for bodies carries the method call expression components, a local binding for the executing method, and the set of statements being executed. Statements in MentokP are assignments, conditional IFs, USE statements (global and local), RETURN, COMPLETE, and SKIP. COMPLETE statements are runtime statements only, and the state rules in section A.10 prevent a COMPLETE statement from being used statically. The global form of USE is disambiguated from the local form, USELOC, for clarity in type and state checking, as well as the operational semantics. Statement lists (of type statements) are Empty or a statement list composed with a statement. This form of list is used to make it easier to check that a method body only returns once, at the end of the method. types localVal = "value × (tokenBag option)" types localBinding = "(varName, localVal) table" translations "localVal" ) (type) "value × (tokenBag option)" "localBinding" ) (type) "varName ⇒ localVal option" "localBinding" ) (type) "(varName × localVal) table"datatype expression = Val value | Ident varName | Call expression typeName methodName "expression list" ("_[_]·_’(_’)" [75,75,80] 80) | Field expression typeName fieldName ("_[_]._" [73,74,74] 74) | New typeName ("NEW _" 73) | IdState varName behaviourDec ("_·_" [70,70] 71) | Body typeName methodName localBinding expression "expression list" statements and statement = Assign expression expression ("_ :== _" [80,80] 80) | If_ expression statements statements ("IF _ THEN _ ELSE _" [80,79,79] 80) | UseL expression behaviourDec statements statements ("USELOC _ :: _ DO _ ELSE _" [80,80,79,79] 80) | UseG expression behaviourDec statements statements

159 A MentokP : A language with negotiable interfaces

("USE _ :: _ DO _ ELSE _" [80,80,79,79] 80) | Return expression ("RETURN _" 80) | Complete expression ("COMPLETE _" 80) | Skip ("SKIP") and statements = Empty | Compose statements statement ("_ ;; _" [66,65] 65)

A.5.1 Terms

MentokP terms are expressions, expression lists, statements or statement lists. Type and state checking and executions are performed on MentokP terms. types "exprs" = "expression + expression list" types "stmts" = "statement + statements"

types "term" = "exprs + stmts"

translations "exprs" ) (type) "expression + expression list" "stmts" ) (type) "statement + statements" "term" ) (type) "exprs + stmts"

syntax exprInjTerm:: "expression ⇒ term" ("h_ie" 1000) exprsInjTerm:: "expression list⇒ term" ("h_iE" 1000) stmtInjTerm:: "statement ⇒ term" ("h_is" 1000) stmtsInjTerm:: "statements ⇒ term" ("h_iS" 1000)

translations "heie" * "Inl (Inl e)" "hesiE" * "Inl (Inr es)" "hsis" * "Inr (Inl s)" "hssiS" * "Inr (Inr ss)"

constdefs isExpression :: "term ⇒ bool" "isExpression t ≡ ∃ c. t=Inl (Inl c)" constdefs isExpressionList :: "term ⇒ bool" "isExpressionList t ≡ ∃ c. t=Inl (Inr c)" constdefs isStatement :: "term ⇒ bool" "isStatement t ≡ ∃ c. t=Inr (Inl c)" constdefs isStatements :: "term ⇒ bool" "isStatements t ≡ ∃ c. t=Inr (Inr c)"

axclass injTerm < "type"

160 A.5 Expressions and Statements in MentokP consts injTerm:: "’a::injTerm ⇒ term" ("h_i" 1000) instance expression::injTerm .. defs (overloaded) exprInjTerm_def: "hc::expressioni ≡ Inl (Inl c)" instance "list":: (type) injTerm .. defs (overloaded) exprlistInjTerm_def: "hc::expression listi ≡ Inl (Inr c)" instance statement::injTerm .. defs (overloaded) stmtInjTerm_def: "hc::statementi ≡ Inr (Inl c)" instance statements::injTerm .. defs (overloaded) stmtsInjTerm_def: "hc::statementsi ≡ Inr (Inr c)" syntax optionInj :: "’a::injTerm option ⇒ term option" translations "optionInj" "option_map injTerm" syntax Ref :: "ref ⇒ expression" translations "Ref r " "Val (Reference r)"

A.5.2 Predicates and Functions Over Terms consts isVar :: "expression ⇒ bool" isVal :: "expression ⇒ bool" isStateVar :: "expression ⇒ bool" isCall :: "expression ⇒ bool" isAssignable :: "expression ⇒ bool" isStatefulRef :: "(varName ⇒ ’a option) ⇒ expression ⇒ bool" primrec "isVar (Val x) = False" "isVar (x[iid]·m(es)) = False" "isVar (v[rid].f) = False" "isVar (Ident varName) = True" "isVar (NEW typeName) = False" "isVar (i·bhv) = False" "isVar (Body rid mid ls e ps ss) = False"

161 A MentokP : A language with negotiable interfaces

primrec "isVal (Val x) = True" "isVal (x[iid]·m(es)) = False" "isVal (v[rid].f) = False" "isVal (Ident varName) = False" "isVal (NEW typeName) = False" "isVal (i·bhv) = False" "isVal (Body rid mid ls e ps ss) = False"

primrec "isStateVar (Val x) = False" "isStateVar (x[iid]·m(es)) = False" "isStateVar (v[rid].f) = False" "isStateVar (Ident varName) = False" "isStateVar (NEW typeName) = False" "isStateVar (i·bhv) = True" "isStateVar (Body rid mid ls e ps ss) = False"

primrec "isCall (Val x) = False" "isCall (x[iid]·m(es)) = True" "isCall (v[rid].f) = False" "isCall (Ident varName) = False" "isCall (NEW typeName) = False" "isCall (i·bhv) = False" "isCall (Body rid mid ls e ps ss) = False"

primrec "isAssignable (Val x) = False" "isAssignable (x[iid]·m(es)) = False" "isAssignable (v[rid].f) = True" "isAssignable (Ident varName) = (varName 6= This)" "isAssignable (NEW typeName) = False" "isAssignable (i·bhv) = False" "isAssignable (Body rid mid ls e ps ss) = False"

primrec "isStatefulRef S (Val x) = False" "isStatefulRef S (x[iid]·m(es)) = False" "isStatefulRef S (v[rid].f) = False" "isStatefulRef S (Ident varName) = (S varName 6= None)" "isStatefulRef S (NEW typeName) = False" "isStatefulRef S (i·bhv) = False" "isStatefulRef S (Body rid mid ls e ps ss) = False"

consts ground :: "expression ⇒ bool" groundList:: "expression list ⇒ bool" pGround :: "expression ⇒ bool" lGround :: "expression ⇒ bool"

translations "ground" "isVal"

162 A.5 Expressions and Statements in MentokP

"groundList" "list_all pGround" primrec "pGround (Val x) = True" "pGround (x[iid]·m(es)) = False" "pGround (v[rid].f) = False" "pGround (Ident varName) = False" "pGround (NEW typeName) = False" "pGround (i·bhv) = True" "pGround (Body rid mid ls e ps ss) = False" primrec "lGround (Val x) = False" "lGround (e[rid].f) = ground e" "lGround (Ident i) = True" "lGround (NEW rid) = False" "lGround (e[iid]·m(es)) = False" "lGround (i·bhv) = False" "lGround (Body rid mid ls e ps ss) = False" constdefs groundStatement :: "statement ⇒ bool" "groundStatement s ≡ (s = SKIP) ∨ (∃ e. s = RETURN e ∧ ground e)" groundStatements :: "statements ⇒ bool" "groundStatements ss ≡ (ss = Empty ∨(∃ e. ss = Empty ;; RETURN e ∧ ground e) )" consts groundTerm :: "term ⇒ bool" groundStmts :: "stmts ⇒ bool" groundExprs :: "exprs ⇒ bool" primrec "groundTerm (Inl exs) = groundExprs exs" "groundTerm (Inr sts) = groundStmts sts" primrec "groundExprs (Inl e) = ground e" "groundExprs (Inr es) = groundList es" primrec "groundStmts (Inl s) = groundStatement s" "groundStmts (Inr ss) = groundStatements ss" consts theStateVar :: "expression ⇒ varName" theStateBhv :: "expression ⇒ behaviourDec" theParBhv :: "expression ⇒ parBhv" primrec "theStateVar (i·bhv) = i" primrec "theStateBhv (i·bhv) = bhv" primrec "theParBhv (Val x) = None" "theParBhv (e[rid].f) = None"

163 A MentokP : A language with negotiable interfaces

"theParBhv (Ident i) = None" "theParBhv (NEW rid) = None" "theParBhv (e[iid]·m(es)) = None" "theParBhv (i·bhv) = Some bhv" "theParBhv (Body rid mid ls e ps ss) = None"

end

164 A.6 Programs in MentokP

A.6 Programs in MentokP theory Programs = Statements + HOLplus:

A.6.1 Method Declarations and Implementations

Method declarations associate a name with a method type. A method type is a list of parameter types, optional parameter behaviours, a return type, and a behaviour declaration (see section A.4.1). Method implementations associate a name with a method body, which is a method type, extended with a list of parameter names and a statement list (the command of the method). For simplicity, method bodies do not declare a list of local variables. record methodType = pars :: "cpType list" parBhvs :: "parBhv list" retT :: "cpType" behaviour :: "behaviourDec" types methodDecl = "methodName × methodType" record methodBody = methodType + parIds :: "ident list" body :: "statements" types methodImp = "methodName × methodBody" translations "parBhv" ) (type) "behaviourDec option" "methodType" ) (type) "(|pars::cpType list, parBhvs::parBhv list, retT::cpType, behaviour::behaviourDec |)" "methodType" ) (type) "(|pars::cpType list, parBhvs::parBhv list, retT::cpType, behaviour::behaviourDec, ... ::’a |)" "methodBody" ) (type) "(|pars::cpType list, parBhvs::parBhv list,retT::cpType, behaviour::behaviourDec, parIds::ident list, body::statements |)" "methodBody" ) (type) "(|pars::cpType list, parBhvs::parBhv list,retT::cpType, behaviour::behaviourDec, parIds::ident list, body::statements, ... ::’a |)" "methodDecl" ) (type) "methodName × methodType" "methodImp" ) (type) "methodName × methodBody"

A.6.2 Interface Bodies

An interface body is a list of method declarations, a state descriptor (list of token names) and a token bag declaration of the initial state for an object implementing this interface.

165 A MentokP : A language with negotiable interfaces

record interfaceB = stateDesc :: "tokenName list" initBag :: "tokenName list" methodDecls :: "methodDecl list"

A.6.3 Record Bodies

A record body is a list of method implementations, a list of field declarations, and the type name of the interface that the record implements. types fieldDecl = "fieldName × cpType"

translations "fieldDecl" ) (type) "fieldName × cpType"

record recordB = interface :: typeName fieldDecls :: "fieldDecl list" methodImps :: "methodImp list"

translations "interfaceB" ) (type) "(|stateDesc::tokenName list, initBag :: tokenName list, methodDecls::methodDecl list |)" "interfaceB" ) (type) "(|stateDesc::tokenName list, initBag :: tokenName list, methodDecls::methodDecl list, ... :: ’a|)" "recordB" ) (type) "(|interface::typeName, fieldDecls::fieldDecl list, methodImps::methodImp list |)" "recordB" ) (type) "(|interface::typeName, fieldDecls::fieldDecl list, methodImps::methodImp list, ... :: ’a |)"

A.6.4 Programs

A program is a list of interface declarations, a list of record declarations, a list of variable declarations, and a command representing the main body of the program. types interfaceDecl = "typeName × interfaceB" recordDecl = "typeName × recordB" varDecl = "ident × cpType"

record program = interfaceDecls :: "interfaceDecl list" recordDecls :: "recordDecl list" mainVars :: "varDecl list" mainCmd :: "statements"

translations "interfaceDecl" ) (type) "typeName × interfaceB" "recordDecl" ) (type) "typeName × recordB"

166 A.6 Programs in MentokP

"varDecl" ) (type) "ident × cpType" "program" ) (type) "(|interfaceDecls::interfaceDecl list,recordDecls::recordDecl list, mainVars :: varDecl list, mainCmd :: statements|)" "program" ) (type) "(|interfaceDecls::interfaceDecl list,recordDecls::recordDecl list, mainVars :: varDecl list, mainCmd :: statements,... ::’a|)" The tables below are mappings created from a list of declarations (interface, record, field, method type, and method implementation declarations respectively). syntax (tables) interfaceD :: "program ⇒ (typeName, interfaceB) table" recordD :: "program ⇒ (typeName, recordB) table" fieldD :: "recordB ⇒ (fieldName, cpType) table" methodTypeD :: "interfaceB ⇒ (methodName, methodType) table" methodImpD :: "recordB ⇒ (methodName, methodImp) table" translations "interfaceD P" "table_of (interfaceDecls P)" "recordD P" "table_of (recordDecls P)" "fieldD R" "table_of (fieldDecls R)" "methodTypeD I" "table_of (methodDecls I)" "methodImpD R" "table_of (methodImps R)"

A.6.5 lookups constdefs theField_ :: "program ⇒ typeName ⇒ fieldName ⇒ cpType ⇒ bool" ("theField _ _ _ = _") "theField P rid f = T ≡ recordD P rid 6= None ∧ fieldD (the (recordD P rid)) f = Some T" constdefs theMethodType_ :: "program ⇒ typeName ⇒ methodName ⇒ methodType ⇒ bool" ("theMethodType _ _ _ = _") "theMethodType P iid m = mdec ≡ interfaceD P iid 6= None ∧ methodTypeD (the (interfaceD P iid)) m = Some mdec"

A.6.6 Type well-formedness A type is judged to be well-formed by a program if the type is primitive, the NILTYPE, or an interface or record that the program declares. syntax (predicates) isInterface :: "program ⇒ typeName ⇒ bool" isRecord :: "program ⇒ typeName ⇒ bool" translations "isInterface P I" "interfaceD P I 6= None" "isRecord P R" "recordD P R 6= None" consts

167 A MentokP : A language with negotiable interfaces

isType :: "program ⇒ cpType ⇒ bool" ("_ ` _ ♦T " [51,51] 50) isObjType :: "program ⇒ objType ⇒ bool"

primrec "P `(PrimT x) ♦T = True" "P `(ObjT x) ♦T = isObjType P x"

primrec "isObjType P NilT = True" "isObjType P (RecT R) = isRecord P R" "isObjType P (IfcT I) = isInterface P I"

A.6.7 Lookup functions The following lookup (LU) functions lookup a type in a program and return the desired value if the type is defined by the program.

A.6.7.1 Interface and Record Body Lookups constdefs ibodyLU :: "program ⇒ (cpType,interfaceB) table" "ibodyLU P T ≡ if (isIfc T) then (case (typeNameOf T) of None ⇒ None | Some x ⇒ interfaceD P x) else None" rbodyLU :: "program ⇒ (cpType,recordB) table" "rbodyLU P T ≡ if (isRec T) then (case (typeNameOf T) of None ⇒ None | Some x ⇒ recordD P x) else None"

consts theIfaceBody :: "program ⇒ cpType ⇒ interfaceB option" theRecordBody :: "program ⇒ cpType ⇒ recordB option"

translations "theRecordBody" "rbodyLU" "theIfaceBody" "ibodyLU"

A.6.7.2 Interface Body Attribute Lookups constdefs stateDescLU :: "program ⇒ (cpType,tokenName list) table" "stateDescLU P T ≡ case (ibodyLU P T) of None ⇒ None | Some ibody ⇒ Some (stateDesc ibody)" initBagLU :: "program ⇒ (cpType,tokenName list) table" "initBagLU P T ≡ case (ibodyLU P T) of None ⇒ None | Some ibody ⇒ Some (initBag ibody)" methodTypeDLU :: "program ⇒ cpType ⇒ (methodName, methodType) table"

168 A.6 Programs in MentokP

"methodTypeDLU P T ≡ case (ibodyLU P T) of None ⇒ empty | Some ibody ⇒ (%mid. methodTypeD ibody mid)" methodDeclsLU :: "program ⇒ cpType ⇒ (methodDecl list) option" "methodDeclsLU P T ≡ case (ibodyLU P T) of None ⇒ None | Some ibody ⇒ Some (methodDecls ibody)" consts theTokens :: "program ⇒ cpType ⇒ (tokenName list) option" theInitialState :: "program ⇒ cpType ⇒ (tokenName list) option" translations "theTokens" "stateDescLU" "theInitialState" "initBagLU"

A.6.7.3 Record Body Attribute Lookups constdefs interfaceLU :: "program ⇒ (cpType,typeName) table" "interfaceLU P T ≡ case (rbodyLU P T) of None ⇒ None | Some rbody ⇒ Some (interface rbody)" fieldDLU :: "program ⇒ cpType ⇒ (fieldName, cpType) table" "fieldDLU P T ≡ case (rbodyLU P T) of None ⇒ empty | Some rbody ⇒ (%f. fieldD rbody f)" fieldDeclsLU :: "program ⇒ cpType ⇒ (fieldDecl list) option" "fieldDeclsLU P T ≡ case (rbodyLU P T) of None ⇒ None | Some rbody ⇒ Some (fieldDecls rbody)" methodImpDLU :: "program ⇒ cpType ⇒ (methodName, methodBody) table" "methodImpDLU P T ≡ case (rbodyLU P T) of None ⇒ empty | Some rbody ⇒ (λ mid. methodImpD rbody mid)" methodImpsLU :: "program ⇒ cpType ⇒ (methodImp list) option" "methodImpsLU P T ≡ case (rbodyLU P T) of None ⇒ None | Some rbody ⇒ Some (methodImps rbody)" consts theInterfaceOf :: "program ⇒ cpType ⇒ typeName option" translations "theInterfaceOf" "interfaceLU"

A.6.8 Negotiable State Well-formedness A.6.8.1 Token Bag Declaration Well-formedness A token bag declaration, ts, is well-formed w.r.t. a program, P , and type, T , if the type is an interface, the token bag declaration only contains tokens that are declared by the

169 A MentokP : A language with negotiable interfaces

interface, T . constdefs wfTokenBagDecl :: "program ⇒ cpType ⇒ tokenName list ⇒ bool" ("_,_ `_ ♦b" [51,51,51] 50) "P,T `ts ♦b ≡ case (ibodyLU P T) of None ⇒ False | Some ifcB ⇒ tokenBagWF (stateDesc ifcB) ts"

A.6.8.2 Token Bag well-formedness A token bag, b, is well-formed w.r.t. a program, P , and type, T , if the type is an interface, the token bag only contains tokens that are declared by the interface, T . constdefs wfTokenBag :: "program ⇒ cpType ⇒ tokenBag ⇒ bool" ("_,_ `_ ♦#b" [51,51,51] 50) "P,T `b ♦#b ≡ case (ibodyLU P T) of None ⇒ False | Some ifcB ⇒ set_of b ⊆ set (stateDesc ifcB)"

A.6.8.3 Behaviour Declaration Well-Formedness A behaviour declaration, bh, is well-formed w.r.t. a program, P , and type, T , P and T judge both bh to be well-formed. constdefs wfBhvDec :: "program ⇒ cpType ⇒ behaviourDec ⇒ bool" ("_,_ `_ ♦B" [51,51,51] 50) "P,T `bh ♦B ≡ (P,T `fst bh ♦b) ∧ (P,T `snd bh ♦b)"

constdefs bhvMatch :: "methodType ⇒ behaviourDec × parBhv list ⇒ bool" "bhvMatch mdec bhvSig ≡ (behaviour mdec = fst bhvSig) ∧ (parBhvs mdec = snd bhvSig)"

constdefs bodyBhvMatch :: "methodBody ⇒ behaviourDec × parBhv list ⇒ bool" "bodyBhvMatch mbody bhvSig ≡ (behaviour mbody = fst bhvSig) ∧ (parBhvs mbody = snd bhvSig)"

consts theRcvVar :: "expression ⇒ varName" theRcvBhv :: "expression ⇒ behaviourDec" theMethodName :: "expression ⇒ methodName" theActuals :: "expression ⇒ expression list" theCallIface :: "expression ⇒ typeName"

primrec "theRcvVar (e[i]·mid(es)) = theStateVar e" primrec "theRcvBhv (e[i]·mid(es)) = theStateBhv e" primrec "theMethodName (e[i]·mid(es)) = mid" primrec "theActuals (e[i]·mid(es)) = es" primrec "theCallIface (e[i]·mid(es)) = i"

170 A.6 Programs in MentokP end

171 A MentokP : A language with negotiable interfaces

A.7 Type Relationships

theory TypeRelations = Programs:

A.7.1 Interface Implementation A type identifier, rid, implements another type identifier, iid, if rid is a record name, and the record implements an interface named iid. constdefs implements :: "program ⇒ (typeName × typeName) set" "implements P ≡ {(R,I). ∃ r ∈ recordD P R: I = (interface r)}"

syntax "@implements" :: "program ⇒ [typeName, typeName] ⇒ bool" ("_`_C_" [71,71,71] 70)

translations "P ` R CI" "(R,I) ∈ implements P"

A.7.2 Type widening

If a type, T1, widens, to another type, T2, according to a program P , then a value of T1 can be assigned to an identifier of type T2. All types widen to themselves, the NILTYPE widens to all object types, and a record type widens to the interface type it implements. consts widen :: "program ⇒ (cpType × cpType) set"

syntax "@widen" :: "program ⇒ [cpType , cpType ] ⇒ bool" ("_`__" [71,71,71] 70)

translations "P ` T1 T2" "(T1,T2) ∈ widen P"

inductive "widen P" intros

refl: "P ` T  T" nilT: "P ` NilType  ObjT T" impl: "P ` R C I =⇒ P ` Record R Iface I"

A.7.3 Type results

The type rules of MentokP (section A.9) assign a typeResult to well-typed terms. Terms which are expressions are assigned a type (e.g. Inl (Some T)), terms which are statements or statement lists are assigned a type option (e.g. Inl T where T is None if the term doesn’t return) and expression lists are assigned a type list (a type for each expression in the list).

172 A.7 Type Relationships

If a type result, TR1, widens to a type result, TR2, then TR1 is more specific than TR2. This means that a term judged to have type result TR1 is permitted where a term of type result TR2 is required. types typeResult = "cpType option + cpType list" translations "typeResult" ) (type) "cpType option + cpType list" consts widenTypeResult :: "program ⇒ (typeResult × typeResult) set" syntax (xsymbols) widenTypeResult_ :: "program ⇒ typeResult ⇒ typeResult ⇒ bool" ("_ ` _ + _" [71,71,71] 70) translations "P ` T + T’" "(T, T’) ∈ widenTypeResult P" inductive "widenTypeResult P" intros refl: "P ` Inl T + Inl T" widenT: "[[ P `T  T’ ]] =⇒ P ` Inl (Some T) + Inl (Some T’)" XConsEmpty: "P ` Inr [] + Inr []" XConsHead: "[[ P `T  T’; P ` Inr Ts + Inr Ts’ ]] =⇒ P ` Inr (T#Ts) + Inr (T’#Ts’)" end

173 A MentokP : A language with negotiable interfaces

A.8 Static Environments

theory Environments = Programs:

A.8.1 Environment definitions

A local environment is a mapping from variable names to types. A state environment is a mapping from variable names to sets of token bags (sets of multisets of token names). An environment is a program and local environment. types localEnvironment = "(varName, cpType) table"

types stateSet = "tokenBag set"

types stateEnvironment = "(varName, stateSet) table"

record environment = "prog" :: "program" "locals" :: "localEnvironment"

translations "localEnvironment" ) (type) "(varName, cpType) table" "localEnvironment" ) (type) "varName ⇒ cpType option" "environment" ) (type) "(|prog::program, locals::localEnvironment |)" "environment" ) (type) "(|prog::program, locals::localEnvironment,... ::’a |)" "stateSet" ) (type) "tokenBag set" "stateEnvironment" ) (type) "(varName, stateSet) table" "stateEnvironment" ) (type) "varName ⇒ stateSet option"

A.8.2 Operations on State Environments A.8.2.1 Enact Behaviour A behaviour is enacted on the state set of a local identifier by subtracting the in state of the behaviour then adding the out state of the behaviour from each token bag in the state set. constdefs enactBhv :: "stateSet ⇒ behaviour ⇒ stateSet" (infixl "⊕" 60) "bs ⊕ bhv ≡ (λ b. b + (snd bhv)) ‘ (λ b. b - (fst bhv)) ‘ bs" A local variable is stateful if the state environment maps a state set to the variable. An expression is stateful if the expression is an variable expression, and the state environment maps a state set to the variable.

A.8.2.2 State Set Partition

Partitioning a state set, bs, with a token bag, b, splits the bs into two disjoint sets. The first set contains all the token bags in the bs for which b is less than or to (according to the token bag operator E). The second set contains all other token bags in the original set bs.

174 A.8 Static Environments constdefs partition :: "stateSet ⇒ tokenBag ⇒ (stateSet × stateSet)" "partition bs b ≡ ({x. x ∈ bs ∧ b E x},{x. x ∈ bs ∧ ¬ (b E x)})"

A.8.2.3 State Environment Union

Two state environments are combined together (at a join point, e.g. after an IF state- ment) by a component-wise union of each of the state sets in the state environments. If one or both of the state environments does not map a variable to a state set, then the result of a state environment union will not map that variable to a state set. syntax stateUnion :: "stateEnvironment ⇒ stateEnvironment ⇒ stateEnvironment" (infixl "]" 65) consts unionStates :: "stateSet option ⇒ stateSet option ⇒ stateSet option" unionState :: "stateSet ⇒ stateSet option ⇒ stateSet option" primrec "unionStates None x = None" "unionStates (Some bs) x = unionState bs x" primrec "unionState bs None = None" "unionState bs1 (Some bs2) = Some (bs1 ∪ bs2)" translations "S1 ] S2" "table_combine unionStates S1 S2"

A.8.2.4 State Environment Subrelation A state environment, S1, is a sub-environment of S2 if S1 and S2 map the variables to state sets, and for each mapped variable, v, the state set mapped to v by S1 is a subset of of the state set mapped to v by S2. constdefs stateEnvSubSet :: "stateEnvironment ⇒ stateEnvironment ⇒ bool" ( infixl "⊆+" 60) "S ⊆+ S’ ≡ (dom S = dom S’) ∧ (∀ vid. ∀ bs’ ∈ S’ vid: ∃ bs ∈ S vid : bs ⊆ bs’)"

A.8.3 Environment creation mkMethLEnv creates a new local environment for a method body, while getEnv creates a new static typing environment for a method body. constdefs mkLEnv:: "varDecl list ⇒ localEnvironment" "mkLEnv vs ≡ λ k. (case k of This ⇒ None | VName i ⇒ (table_of vs) i)"

175 A MentokP : A language with negotiable interfaces

constdefs mkMethLEnv:: "cpType ⇒ methodBody ⇒ localEnvironment" "mkMethLEnv T Mb ≡ (mkLEnv (zip (parIds Mb) (pars Mb)))(This 7→ T)"

constdefs getEnv:: "program ⇒ cpType ⇒ methodBody ⇒ environment" "getEnv P T Mb ≡ (|prog = P, locals = (mkMethLEnv T Mb )|)"

constdefs tokenBagDecToStateSet :: "tokenName list ⇒ stateSet" "tokenBagDecToStateSet tb ≡ {ktbk}"

constdefs parBhvToIn :: "parBhv ⇒ stateSet option" "parBhvToIn bhvo ≡ option_map (λ bhv. {kfst bhvk}) bhvo"

constdefs parBhvsToIn :: "parBhv list ⇒ (stateSet option) list" "parBhvsToIn Ps ≡ map (option_map (tokenBagDecToStateSet o fst)) Ps"

constdefs parBhvsToOut :: "parBhv list ⇒ (stateSet option) list" "parBhvsToOut Ps ≡ map (option_map (tokenBagDecToStateSet o snd)) Ps"

constdefs stateEnvIn :: "methodBody ⇒ stateEnvironment" "stateEnvIn Mb ≡ (λ k. case k of This ⇒ None | VName v ⇒ table_of_option (zip (parIds Mb) (parBhvsToIn (parBhvs Mb))) v)"

constdefs stateEnvOut :: "methodBody ⇒ stateEnvironment" "stateEnvOut Mb ≡ (λ k. case k of This ⇒ None | VName v ⇒ table_of_option (zip (parIds Mb) (parBhvsToOut (parBhvs Mb))) v)"

consts parsStateful :: "stateEnvironment ⇒ expression list ⇒ parBhv list ⇒ bool"

primrec "parsStateful S [] Ps = (Ps = [])" "parsStateful S (e#es) Ps = (case Ps of [] ⇒ False | (bho#Ps’) ⇒ ((bho = None) ∨ (isStatefulRef S e)) ∧ parsStateful S es Ps’)"

176 A.8 Static Environments

constdefs minusAll :: "tokenBag ⇒ stateSet ⇒ stateSet" "minusAll b bs ≡ (λ b’. b’ - b) ‘ bs" constdefs plusAll :: "tokenBag ⇒ stateSet ⇒ stateSet" "plusAll b bs ≡ (λ b’. b’ + b) ‘ bs"

A.8.3.1 runtime environments constdefs makeStateEnv :: "localBinding ⇒ stateEnvironment" "makeStateEnv ls ≡ (λ n. case (ls n) of None ⇒ None | Some vb ⇒ (case (snd vb) of None ⇒ None | Some x ⇒ Some {x}))"

A.8.3.2 Add results consts addResults_ :: "(expression × expression list × ident list × stateEnvironment × stateEnvironment × stateEnvironment) set" syntax addResults_ :: "expression ⇒ expression list ⇒ ident list ⇒ stateEnvironment ⇒ stateEnvironment ⇒ stateEnvironment ⇒ bool" ("addResults _ _ _ _ _ = _") translations "addResults e ps is S S’ = S’’" "(e,ps,is,S,S’,S’’) ∈ addResults_" inductive addResults_ intros

Empty: "[[S i = Some bs ; S’’ = S(i 7→ plusAll ksnd bhvkbs) ]] =⇒ addResults (i·bhv) [] [] S S’ = S’’"

ConsVal: "[[ ground p ; addResults e ps is S S’ = S’’ ]] =⇒ addResults e (p#ps) (i#is) S S’ = S’’"

ConsStateVar: "[[S’ (VName i) = Some ({ksnd bhvk}) ; addResults e ps is S S’ = S_int ; S_int i’ = Some bs; S’’ = S_int(i’ 7→ plusAll ksnd bhvkbs) ]] =⇒ addResults e (i’·bhv#ps) (i#is) S S’ = S’’" constdefs stE :: "localBinding ⇒ stateEnvironment" "stE ls ≡ makeStateEnv ls" declare stE_def [simp]

177 A MentokP : A language with negotiable interfaces

end

178 A.9 Type System Rules for Expressions and Statements

A.9 Type System Rules for Expressions and Statements theory WellTyped = TypeRelations + Environments:

The type rules of MentokP are of the for E,lt`t::Tr, where E is an environment (section A.8.1), lt is a lookupType (section A.3.1), t is a term (section A.5.1), and T r is a typeResult (section A.7.3). Since lt is only used to lookup the types of references, lt is undefined or empty when the type rules are used for static checking. The syntax

E`S t :: Tr is used to indicate that lt is empty. consts wellTyped :: "(environment × lookupType × term × typeResult) set" syntax (xsymbols) wellTyped :: "environment ⇒ lookupType ⇒ [term,typeResult] ⇒ bool" ("_,_`_::_" [51,51,51,51] 50) wellTypedStmt :: "environment ⇒ lookupType ⇒ [statement,cpType option] ⇒ bool" ("_,_`_:-_" [51,51,51,51] 50) wellTypedStmts:: "environment ⇒ lookupType ⇒ [statements,cpType option] ⇒ bool" ("_,_`_:≡_" [51,51,51,51] 50) wellTypedExpr :: "environment ⇒ lookupType ⇒ [expression,cpType] ⇒ bool" ("_,_`_::-_" [51,51,51,51] 50) wellTypedExprs:: "environment ⇒ lookupType ⇒ [expression list,cpType list] ⇒ bool" ("_,_`_::≡_" [51,51,51,51] 50) translations "E,dt `t ::T" "(E,dt,t,T) ∈ wellTyped" "E,dt `s :- T" "E,dt `Inr (Inl s) ::(Inl T)" "E,dt `s :≡ T" "E,dt `Inr (Inr s) ::(Inl T)" "E,dt `e ::- T" "E,dt `Inl (Inl e) ::(Inl (Some T))" "E,dt `es ::≡ Ts" "E,dt `Inl (Inr es) ::(Inr Ts)" syntax (xsymbols) sWellTyped_ :: "environment ⇒ lookupType ⇒ [term,typeResult] ⇒ bool" ("_`s_::_" [51,51,51] 50) sWellTypedStmt_ :: "environment ⇒ lookupType ⇒ [statement ,cpType option] ⇒ bool" ("_`s_:-_" [51,51,51] 50) sWellTypedStmts_:: "environment ⇒ lookupType ⇒ [statements ,cpType option] ⇒ bool" ("_`s_:≡_" [51,51,51] 50) sWellTypedExpr_ :: "environment ⇒ lookupType ⇒ [expression ,cpType] ⇒ bool" ("_`s_::-_" [51,51,51] 50) sWellTypedExprs_:: "environment ⇒ lookupType ⇒ [expression list,cpType list] ⇒ bool" ("_`s_::≡_" [51,51,51] 50) translations "E `s t ::T" "(E,empty,t,T) ∈ wellTyped" "E `s s :- T" "E,empty `Inr (Inl s) ::(Inl T)" "E `s s :≡ T" "E,empty `Inr (Inr s) ::(Inl T)" "E `s e ::- T" "E,empty `Inl (Inl e) ::(Inl (Some T))" "E `s es ::≡ Ts" "E,empty `Inl (Inr es) ::(Inr Ts)" inductive wellTyped intros

179 A MentokP : A language with negotiable interfaces

Skip: "E,dt` Skip:- None"

Assign: "[[isAssignable e1; E,dt`e1::- T1; dt’ = (if (lGround e1) then dt else empty) ; E,dt’`e2::- T2; prog E` T2 T1 ]]=⇒ E,dt`e1 :== e2:- None"

If: "[[E,dt`b::- PrimT BoolT; E`s s1:≡ None; E`s s2:≡ None]] =⇒ E,dt `IF b THEN s1 ELSE s2 :- None"

UseLoc: "[[E,dt`Ident v::- Iface i; E`s ss1 :≡ None; E`s ss2 :≡ None; prog E,Iface i `b ♦B ]]=⇒ E,dt `USELOC Ident v :: b DO ss1 ELSE ss2 :- None"

Use: "[[E,dt`Ident v::- Iface i; E`s ss1 :≡ None; E`s ss2 :≡ None; prog E,Iface i `b ♦B ]]=⇒ E,dt `USE Ident v :: b DO ss1 ELSE ss2 :- None"

Complete: "[[E,dt `Ident i::- Iface iid ]]=⇒ E,dt `COMPLETE Ident i:- None"

Return: "[[E,dt `e::-T ]]=⇒ E,dt `RETURN e :- Some T"

Empty: "E,dt `Empty:≡ None"

Compose: "[[E,dt`ss:≡ None; dt’ = (if (groundStatements ss) then dt else empty) ; E,dt’`s:- T ]] =⇒ E,dt`ss ;; s:≡ T"

Val: "[[typeOf dt v = Some T ]]=⇒ E,dt `Val v ::- T"

Ident: "[[(locals E) i = Some T ]]=⇒ E,dt `Ident i ::- T"

180 A.9 Type System Rules for Expressions and Statements

IdState: "[[E,dt `Ident i ::- T ; prog E,T ` bhv ♦B]]=⇒ E,dt `i·bhv ::- T"

Field: "[[E,dt `e ::- T’; P ` T’  Record r; theField (prog E) r f = T ]]=⇒ E,dt `e[r].f ::- T"

Call: "[[E,dt `e ::- Iface i ; theMethodType (prog E) i m = mdec ; prog E `Inr Ts + Inr (pars mdec) ; E,dt `es ::≡ Ts ]]=⇒ E,dt `e[i]·m(es) ::- retT mdec"

New: "[[prog E ` Record rid ♦T ]] =⇒ E,dt`NEW rid ::- Record rid"

Body: "[[ ∃ x. ls This = Some (Reference r, x); typeOf dt (Reference r) = Some (Record rid) ; methodImpDLU (prog E) (Record rid) mid = Some Mb; getEnv (prog E) (Record rid) Mb = E’; E,dt `i·bhv ::- I ; prog E ` Record rid  I; E,dt `ps ::≡ Ts ; prog E `Inr Ts + Inr (pars Mb) ; E’,dt ` ss :≡ Some T ]] =⇒ E,dt`Body rid mid ls (i·bhv) ps ss ::- T"

XNil: "E,dt `[] ::≡ []"

XCons: "[[E,dt `e ::- T;

dt’ = (if (pGround e) then dt else empty) ; E,dt’ `es ::≡ Ts ]]=⇒ E,dt `e#es ::≡ T#Ts" end

181 A MentokP : A language with negotiable interfaces

A.10 State System Rules for Expressions and Statements

theory WellStated = TypeRelations + Environments:

A.10.1 Stateful parameter checking The parStated relationship inductively defines state checking for a list of actual parame- ters, which may include stateful references for cooperative parameters. Parameter state checking works by first checking the “in” states of all parameters against the current state environment, then checking all “out” states of parameters. Empty parameter lists and parameter values other than stateful references always pass checking, and do no transform the state environment. consts parStated :: "(stateEnvironment × expression list × stateEnvironment) set"

syntax (xsymbols) parStated :: "stateEnvironment ⇒ [expression list,stateEnvironment] ⇒ bool" ("_|=_::±_" [51,51,51] 50)

translations "S |=es ::± S’" "(S,es,S’) ∈ parStated"

inductive parStated intros

XNil: "S |= [] ::± S"

XConsStateless: "[[ ¬ isStateVar e ; S |=es::± S’ ]] =⇒ S |= e#es ::± S’"

XConsStateful: "[[ S i = Some bs; ∀ b∈bs. kbInk E b ; S(i7→minusAll (kbInk) bs) |=es::± S’ ; S’ i = Some bs’ ; S’’ = S’(i7→plusAll (kbOutk) bs’) ]] =⇒ S |= i·(bIn,bOut)#es ::± S’’"

A.10.2 State checking

The wellStated relationship judges if a MentokP term passes state checking. Given a type environment and a state environment, the judgements also determine what the new state environment will be for a term that passes state checking. Compound terms apply state checking to each component of the term, and join or sequentially chain resultant state environments to yield the environment for the entire term. All terms have state rules , except for the IdState expression, which is judged through the parStated relationship as part of method call checking.

182 A.10 State System Rules for Expressions and Statements consts wellStated :: "(environment × stateEnvironment × stateEnvironment × term ) set" syntax (xsymbols) wellStated :: "environment ⇒ stateEnvironment ⇒ [term,stateEnvironment] ⇒ bool" ("_,_|=_::_" [51,51,51,51] 50) wellStatedStmt :: "environment ⇒ stateEnvironment ⇒ [statement,stateEnvironment] ⇒ bool" ("_,_|=_:-_" [51,51,51,51] 50) wellStatedStmts :: "environment ⇒ stateEnvironment ⇒ [statements,stateEnvironment] ⇒ bool" ("_,_|=_:≡_" [51,51,51,51] 50) wellStatedExpr :: "environment ⇒ stateEnvironment ⇒ [expression,stateEnvironment] ⇒ bool" ("_,_|=_::-_" [51,51,51,51] 50) wellStatedExprs :: "environment ⇒ stateEnvironment ⇒ ⇒ bool" [expression list,stateEnvironment] ("_,_|=_::≡_" [51,51,51,51] 50) translations "E,S |=t ::S’" "(E,S,S’,t) ∈ wellStated" "E,S |=s :- S’" "E,S |=Inr (Inl s) ::S’" "E,S |=ss :≡ S’" "E,S |=Inr (Inr ss) ::S’" "E,S |=e ::- S’" "E,S |=Inl (Inl e) ::S’" "E,S |=es ::≡ S’" "E,S |=Inl (Inr es) ::S’" inductive wellStated intros

Skip: "E,S |= Skip :- S"

Assign: "[[¬ isStatefulRef S e1; E,S|=e1::- S1; E,S1|=e2::- S2 ]]=⇒ E,S|=e1 :== e2:-S2"

If: "[[E,S|=b::- S’; E,S’|=(ss1::statements) :≡ S_true; E,S’|=(ss2::statements) :≡ S_false; dom S_true = dom S_false ]] =⇒ E,S |=IF b THEN ss1 ELSE ss2 :- S_true ] S_false"

UseG: "[[S v = None; E,S(v 7→ {kfst bhk}) |=s1 :≡ S_true; S_true v = Some {ksnd bh k}; E,S |=s2 :≡ S_false; dom (S_true(v := None)) = dom S_false ]]=⇒ E,S |=USE Ident v :: bh DO s1 ELSE s2 :- S_true(v:=None) ] S_false"

183 A MentokP : A language with negotiable interfaces

UseL: "[[S v = Some bs; partition bs kfst bhk = (bs_in,bs_out); E,S(v 7→ bs_in) |=s1 :≡ S_true; S_true v = Some bs’; ∀ b ∈ bs’. ksnd bhk E b; E,S(v 7→ bs_out) |=s2 :≡ S_false; dom S_true = dom S_false ]]=⇒ E,S |=USELOC Ident v :: bh DO s1 ELSE s2 :- S_true ] S_false"

Complete: "[[S i = Some bs ]]=⇒ E,S |=COMPLETE Ident i :-S(i:=None)"

Return: "[[E,S |=e::- S1 ]]=⇒ E,S |=RETURN e :-S1"

Empty: "E,S|=Empty :≡ S"

Compose: "[[E,S|=ss:≡S1; E,S1|=s :-S2 ]] =⇒ E,S|=ss ;; s :≡S2"

Val: "E,S |=Val v ::- S"

Ident: "E,S |=Ident i ::- S"

Field: "[[E,S |=e ::- S’ ]]=⇒ E,S |=e[rid].f ::- S’"

Call: "[[theMethodType (prog E) i m = mdec; bhvMatch mdec (b,map theParBhv es); E,S |=es ::≡ S’ ; S’|=es@[v·b]::± S’’ ]]=⇒ E,S |= (v·b)[i]·m(es) ::- S’’"

New: "E,S|=NEW rid ::- S"

Body: "[[ methodImpDLU (prog E) (Record r) m = Some Mb; getEnv (prog E) (Record r) Mb = E’; bodyBhvMatch Mb (bhv,map theParBhv ps); E’,makeStateEnv ls |=ss :≡ (stateEnvOut Mb); addResults (i·bhv) ps (parIds Mb) S (stateEnvOut Mb) = S’ ]]=⇒ E,S |= Body r m ls (i·bhv) ps ss ::- S’"

184 A.10 State System Rules for Expressions and Statements

XNil: "E,S |=[] ::≡ S"

XConsStateless: "[[¬ isStateVar e ; E,S |=e ::- S’ ; E,S’ |=es ::≡ S’’ ]]=⇒ E,S |=(e#es) ::≡ S’’"

XConsStateful: "[[isStateVar e ; E,S |=es ::≡ S’’ ]]=⇒ E,S |=(e#es) ::≡ S’’" end

185 A MentokP : A language with negotiable interfaces

A.11 Program Well-Formedness

theory WellFormed = WellTyped + WellStated:

A.11.1 Well-formed type lists syntax wfTypeList :: "program ⇒ cpType list ⇒ bool"

translations "wfTypeList P Fs" "list_all (isType P) Fs"

A.11.2 Well-formed field declarations constdefs wfFieldDecl :: "program ⇒ fieldDecl ⇒ bool" "wfFieldDecl P F ≡ isType P (snd F)"

syntax wfFieldDecls :: "program ⇒ fieldDecl list ⇒ bool" translations "wfFieldDecls P Fs" "list_all (wfFieldDecl P) Fs"

A.11.3 Well parameter behaviour lists consts wfParBhv :: "program ⇒ parBhv ⇒ cpType ⇒ bool"

primrec "wfParBhv P None T = True" "wfParBhv P (Some bhv) T = ((P,T `bhv ♦B) consts wfParBhvs :: "program ⇒ parBhv list ⇒ cpType list ⇒ bool"

primrec "wfParBhvs P [] Ts = (Ts = [])" "wfParBhvs P (bho#Ps) Ts = (case Ts of [] ⇒ False | T#Ts’ ⇒ (wfParBhv P bho T) ∧ (wfParBhvs P Ps Ts’))"

A.11.4 Well-formed method types constdefs wfMethType :: "program ⇒ cpType ⇒ methodType ⇒ bool" "wfMethType P I M ≡ wfTypeList P (pars M) ∧ isType P (retT M) ∧ (isIfc I) ∧ (P,I `behaviour M ♦B) ∧ wfParBhvs P (parBhvs M) (pars M)"

A.11.5 Well-formed method type lists consts wfMethTypes :: "program ⇒ cpType ⇒ methodType list ⇒ bool"

186 A.11 Program Well-Formedness translations "wfMethTypes P I Rs" "list_all (wfMethType P I) Rs"

A.11.6 Well-formed method declaration lists constdefs wfMethDecls :: "program ⇒ cpType ⇒ methodDecl list ⇒ bool" "wfMethDecls P I Ms ≡ let ids = map fst Ms; mts = map snd Ms in wfMethTypes P I mts ∧ distinct ids"

A.11.7 Well-formed interface bodies constdefs wfIfcDec :: "program ⇒ typeName ⇒ bool" ("_ `_ ♦I " [51,51] 50) "wfIfcDec P iid ≡ case (interfaceD P iid) of None ⇒ False | Some Ib ⇒ distinct (stateDesc Ib) ∧ tokenBagWF (stateDesc Ib) (initBag Ib) ∧ unique (methodDecls Ib) ∧ (∀ mid. ∀ Mt ∈ methodTypeD Ib mid : wfMethType P (Iface iid) Mt)"

A.11.8 Well formed method bodies constdefs implementsType :: "methodType ⇒ methodBody ⇒ bool" "implementsType Mt Mb ≡ (pars Mt = pars Mb) ∧ (parBhvs Mt = parBhvs Mb) ∧ (retT Mt = retT Mb) ∧ (behaviour Mt = behaviour Mb)" constdefs wfMethBody :: "program ⇒ typeName ⇒ methodBody ⇒ bool" "wfMethBody P r Mb ≡ let E = getEnv P (Record r) Mb; ss = body Mb in distinct (parIds Mb) ∧ length (parIds Mb) = length (pars Mb) ∧ (∃ T. E `s ss :≡ Some T ∧ P `T  retT Mb) ∧ E,(stateEnvIn Mb) |=ss:≡ (stateEnvOut Mb)"

A.11.9 Well-formed record bodies constdefs wfRecDec :: "program ⇒ typeName ⇒ bool" ("_ ` _ ♦R" [51,51] 50) "wfRecDec P rid ≡ case (recordD P rid) of None ⇒ False | Some Rb ⇒ (case (interfaceD P (interface Rb)) of None ⇒ False | Some Ib ⇒ unique (fieldDecls Rb) ∧ (∀ fid. ∀ T ∈ fieldD Rb fid : P `T ♦T ) ∧

187 A MentokP : A language with negotiable interfaces

unique (methodImps Rb) ∧ (dom (methodImpD Rb) = dom (methodTypeD Ib)) ∧ (∀ mid. (∀ mT ∈ methodTypeD Ib mid: ∃ mB ∈ methodImpD Rb mid: implementsType mT mB ∧ wfMethBody P rid mB)))"

A.11.10 Well-formed variable declarations constdefs wfVarDecl :: "program ⇒ varDecl ⇒ bool" "wfVarDecl P F ≡ isType P (snd F)" wfVarDecls :: "program ⇒ varDecl list ⇒ bool" "wfVarDecls P Fs ≡ list_all (wfVarDecl P) Fs ∧ unique Fs"

A.11.11 Well-formed programs

constdefs wfProgram :: "program ⇒ bool" ("` _ ♦" 50) "wfProgram P ≡ distinct (map fst (interfaceDecls P) @ map fst (recordDecls P)) ∧ ( list_all (%idecl. P `fst idecl ♦I ) (interfaceDecls P)) ∧ ( list_all (%rdecl. P `fst rdecl ♦R) (recordDecls P)) ∧ ( wfVarDecls P (mainVars P)) ∧ (let E = (|prog = P, locals = mkLEnv (mainVars P) |); ss = mainCmd P in E `s ss :≡ None ∧ E,empty |=ss :≡ empty )"

end

188 A.12 Runtime Structures and Operations

A.12 Runtime Structures and Operations theory State = Programs:

A.12.1 Objects

An object has a tag, which defines the type of the object, a state, which is a multiset of tokens, and a mapping from field names to values.A new object is created from a program, P , and a type name, rid. If the P defines a record called rid, and the record named rid implements an interface, iid, which P also defines, then a new object of type rid, initial state of iid, and an initial field binding matching the default values of the fields of rid can be created. record object = tag :: "typeName" state :: "tokenBag" fieldVs :: "(fieldName, value) table" constdefs newObj :: "program ⇒ typeName ⇒ object option" "newObj P rid ≡ case (recordD P rid) of None ⇒ None | Some rbody ⇒ (case (interfaceD P (interface rbody)) of None ⇒ None | Some ibody ⇒ Some (|tag = rid, state = kinitBag ibody k, fieldVs = initDefaults (table_of (fieldDecls rbody)) |))" constdefs updateField :: "object ⇒ fieldName ⇒ value ⇒ object" "updateField obj f v ≡ obj(|fieldVs := fieldVs(obj)(f 7→ v)|)" constdefs updateState :: "object ⇒ tokenBag ⇒ object" "updateState obj b ≡ obj(|state := b|)" constdefs objectType :: "object ⇒ cpType" "objectType obj ≡ Record (tag obj)" constdefs objectIface :: "program ⇒ object ⇒ cpType option" "objectIface P obj ≡ case (interfaceLU P (objectType obj)) of None ⇒ None | Some iid ⇒ Some (Iface iid)"

189 A MentokP : A language with negotiable interfaces

A.12.2 Local Bindings

Value bindings map variables to values. State bindings map variables to states (token multisets). A local binding (a value binding, local binding tuple) holds the values and states of all local variables. types bhvOpt = "behaviour option"

types localVal = "value × (tokenBag option)"

types localBinding = "(varName, localVal) table"

types valBinding = "(varName, value) table"

types stateBinding = "(varName, tokenBag) table"

A.12.3 Operations on local bindings constdefs locVal :: "localBinding ⇒ valBinding" "locVal ls ≡ (λ v. option_map fst (ls v))" locState :: "localBinding ⇒ stateBinding" "locState ls ≡ (λ v. case (ls v) of None ⇒ None | Some lv ⇒ snd lv)" updateLocVal :: "localBinding ⇒ varName ⇒ value ⇒ localBinding" "updateLocVal ls n v ≡ ls(n7→(v,None))" updateLocState :: "localBinding ⇒ varName ⇒ tokenBag ⇒ localBinding" "updateLocState ls n b ≡ if (ls n 6= None) then (ls(n7→(the (locVal ls n),Some b))) else ls" clearLocState :: "localBinding ⇒ varName ⇒ localBinding" "clearLocState ls n ≡ ls(n7→(the (locVal ls n),None))"

A new method value binding is created by mapping a list of identifiers to a list of values, and mapping the keyword THIS to the receiver parameter value. A new method local binding is created by creating a new method value binding and an empty state binding (since parameters are stateless intially in MentokP . constdefs inState :: "bhvOpt ⇒ tokenBag option" "inState ≡ option_map fst" outState :: "bhvOpt ⇒ tokenBag option" "outState ≡ option_map snd"

consts newBindingRec :: "ident list ⇒ (value × bhvOpt) list ⇒ (ident,localVal) table ⇒ (ident,localVal) table"

primrec "newBindingRec [] ps sb = sb"

190 A.12 Runtime Structures and Operations

"newBindingRec (i#is) ps sb = (case ps of [] ⇒ sb | p#ps’ ⇒ newBindingRec is ps’ (sb(i7→(fst p,inState (snd p)))))" consts endBindingRec :: "ident list ⇒ (value × bhvOpt) list ⇒ (ident,localVal) table ⇒ (ident,localVal) table" primrec "endBindingRec [] ps sb = sb" "endBindingRec (i#is) ps sb = (case ps of [] ⇒ sb | p#ps’ ⇒ newBindingRec is ps (sb(i7→(fst p,outState (snd p)))))" syntax "newBinding" :: "ident list ⇒ (value × bhvOpt) list ⇒ (ident,value) table" "endBinding" :: "ident list ⇒ (value × bhvOpt) list ⇒ (ident,value) table" translations "newBinding is vs" "newBindingRec is vs empty" "endBinding is vs" "endBindingRec is vs empty" constdefs methBinding :: "ref ⇒ bhvOpt ⇒ ident list ⇒ (value × bhvOpt) list ⇒ localBinding" "methBinding r bho is ps ≡ (λ k. case k of This ⇒ Some (Reference r,inState bho) | VName v ⇒ (newBinding is ps) v)" constdefs methOutBinding :: "ref ⇒ bhvOpt ⇒ ident list ⇒ (value × bhvOpt) list ⇒ localBinding" "methOutBinding r bho is ps ≡ (λ k. case k of This ⇒ Some (Reference r,outState bho) | VName v ⇒ (endBinding is ps) v)" consts theParVal :: "localBinding ⇒ expression ⇒ (value × bhvOpt)" primrec "theParVal ls (Val v) = (v,None)" "theParVal ls (i·b) = (the (locVal ls i),Some kbkB)" constdefs parVals :: "localBinding ⇒ expression list ⇒ (value × bhvOpt) list"

191 A MentokP : A language with negotiable interfaces

"parVals ls es ≡ map (theParVal ls) es"

constdefs add_tboption :: "tokenBag option ⇒ tokenBag ⇒ tokenBag" (infixl "+ o" 65) "bo + o b ≡ case bo of None ⇒ b | Some b’ ⇒ b’ + b"

consts parInStatesRec :: "localBinding ⇒ expression list ⇒ localBinding ⇒ localBinding" parOutStatesRec :: "localBinding ⇒ expression list ⇒ localBinding ⇒ localBinding"

primrec "parInStatesRec ls [] ls’ = ls’" "parInStatesRec ls (e#es) ls’ = (let i = (theStateVar e) ; v = the (locVal ls i) in (case (theParBhv e) of None ⇒ parInStatesRec ls es ls’ | Some bhv ⇒ parInStatesRec ls es (ls’(i 7→ (v,Some (locState ls’ i + o kfst bhvk))))))"

primrec "parOutStatesRec ls [] ls’ = ls’" "parOutStatesRec ls (e#es) ls’ = (let i = (theStateVar e) ; v = the (locVal ls i) in (case (theParBhv e) of None ⇒ parOutStatesRec ls es ls’ | Some bhv ⇒ parOutStatesRec ls es (ls’(i 7→ (v,Some (locState ls i + o ksnd bhvk))))))"

syntax parInStates :: "localBinding ⇒ expression list ⇒ localBinding" parOutStates :: "localBinding ⇒ expression list ⇒ localBinding"

translations "parInStates ls es" "parInStatesRec ls es empty" "parOutStates ls es" "parOutStatesRec ls es empty"

constdefs stateBindSubset :: "localBinding ⇒ localBinding ⇒ bool" (infixl "⊆-" 65) "ls ⊆- ls’ ≡ (∀ i. ∀ b ∈ locState ls i : ∃ b’ ∈ locState ls’ i : b E b)"

constdefs bhvListEnact :: "localBinding ⇒ expression list ⇒ localBinding" ("_L _" [51,51] 50) "bhvListEnact ls es ≡ let inLs = parInStates ls es ; outLs = parOutStates ls es in (λ k. case (ls k) of None ⇒ None

192 A.12 Runtime Structures and Operations

| Some lv ⇒ (case (snd lv) of None ⇒ Some lv | Some b ⇒ Some (fst lv, Some (b - (the (locState inLs k)) + (the (locState outLs k))))))"

A.12.4 Object Store

An object store maps references to objects. types objStore = "(ref, object) table" translations "objStore" ) (type) "ref ⇒ object option" constdefs newRef :: "objStore ⇒ ref option" "newRef os ≡ if (∀ r. os r 6= None) then None else Some (ε r. os r = None)" consts theRcvRef :: "localBinding ⇒ expression ⇒ object" theMethodBody :: "program ⇒ typeName ⇒ methodName ⇒ methodBody" theMethodCmd :: "program ⇒ object ⇒ methodName ⇒ statements" translations "theRcvRef ls e" "theRef (the (locVal ls (theRcvVar e)))" "theMethodBody P rid mid" "(the (methodImpD (the (recordD P rid)) mid))" "theMethodCmd P obj mid" "body (theMethodBody P (tag obj) mid)" constdefs methodOf :: "program ⇒ objStore ⇒ ref ⇒ methodName ⇒ methodBody option" "methodOf P os r mid ≡ case (os r) of None ⇒ None | Some obj ⇒ methodImpDLU P (objectType obj) mid" constdefs dtO :: "objStore ⇒ lookupType" "dtO os ≡ λ r. option_map objectType (os r)"

A.12.5 Stack Frames A.12.5.1 New Locals consts newLocalsSet :: "(localBinding × localBinding × expression × ident list × expression list × localBinding × localBinding) set" syntax (xsymbols) newLocalsSet_ :: "[localBinding,localBinding] ⇒ [expression, ident list,expression list] ⇒

193 A MentokP : A language with negotiable interfaces

[localBinding,localBinding] ⇒ bool " ("_,_ |=_,_,_↑_,_" [50,50,50,50,50,50,50] 49)

newLocals :: "[localBinding] ⇒ [expression, ident list,expression list] ⇒ [localBinding,localBinding] ⇒ bool" ("_ |=_,_,_⇑_,_" [50,50,50,50,50,50] 49)

translations "ls_o,ls_n|=e,ps,es↑ls_o’,ls_n’" "(ls_o,ls_n,e,ps,es,ls_o’,ls_n’) ∈ newLocalsSet" "ls_o|=e,ps,es⇑ls_o’,ls_n’" "(ls_o,empty,e,ps,es,ls_o’,ls_n’) ∈ newLocalsSet"

inductive newLocalsSet intros

Receiver: "[[locVal ls_o i = Some v ; locState ls_o i = Some b ; bhv = (b_in,b_out) ; kb_ink E b ; ls_o’ = updateLocState ls_o i (b - kb_ink); ls_n’ = updateLocVal ls_n This v ]] =⇒ ls_o,ls_n|=i·bhv,[],[]↑ ls_o’,ls_n’"

ParStateless: "[[ls_o,updateLocVal ls_n (VName p) v|=e,ps,es↑ ls_o’,ls_n’ ]] =⇒ ls_o,ls_n|=e,p#ps,Val v#es↑ ls_o’,ls_n’"

ParStateful: "[[locVal ls_o a = Some v ; locState ls_o a = Some b ; bhv = (b_in,b_out) ; kb_ink E b ; ls_o’’ = updateLocState ls_o a (b - kb_ink); ls_n’’ = updateLocState (updateLocVal ls_n (VName i) v) (VName i) kb_ink ; ls_o’’,ls_n’’|=e,is,es↑ ls_o’,ls_n’ ]] =⇒ ls_o,ls_n|=e,i#is,a·bhv#es↑ls_o’,ls_n’"

A.12.5.2 Return Locals

consts returnLocalsSet :: "(localBinding × localBinding × expression × ident list × expression list × localBinding) set"

syntax (xsymbols) returnLocals :: "[localBinding,localBinding, expression,ident list,expression list] ⇒ [localBinding] ⇒ bool"

("_,_|=_,_,_⇓_" [50,50,50,50,50,50] 49)

translations "ls,ls_n|=e,ps,es⇓ls’" "(ls,ls_n,e,ps,es,ls’) ∈ returnLocalsSet"

inductive returnLocalsSet intros

194 A.12 Runtime Structures and Operations

Receiver: "[[locState ls i = Some b ; bhv = (b_in,b_out) ; ls’ = updateLocState ls i (b + kb_outk) ]] =⇒ ls,ls_n|=i·bhv,[],[]⇓ls’"

ParStateless: "ls,ls_n|=e,ps,es⇓ls’ =⇒ ls,ls_n|=e,p#ps,Val v#es⇓ls’"

ParStateful: "[[bhv = (b_in,b_out); locState ls_n (VName p) = Some kb_outk; ls,ls_n|=e,ps,es⇓ls_int; locState ls_int i = Some b ; ls’ = updateLocState ls_int i (b + kb_outk) ]] =⇒ ls,ls_n|=e,p#ps,(i·bhv)#es⇓ls’"

A.12.5.3 Frame definition types frame = "term × localBinding" translations "frame" ) (type) "term × localBinding" types frameState = "frame × objStore" translations "frameState" ) (type) "frame × objStore" constdefs cmd :: "frameState ⇒ term" "cmd fs ≡ (fst o fst) fs" locs :: "frameState ⇒ localBinding" "locs fs ≡ (snd o fst) fs" store :: "frameState ⇒ objStore" "store fs ≡ snd fs"

constdefs newFrame :: "program ⇒ objStore ⇒ localBinding ⇒ expression ⇒ frame" "newFrame P os ls e ≡ let r = theRcvRef ls e ; obj = the (os r) ; Mb = theMethodBody P (tag obj) (theMethodName e) ; es = theActuals e ; ps = parVals ls es in (hbody Mbi,(methBinding r None (parIds Mb) ps))" end

195 A MentokP : A language with negotiable interfaces

A.13 Operational Semantics

theory OpSem = State:

A.13.1 Small Step Semantics

The step relationship defines the small-step operation semantics for MentokP . Given a program, the operation semantics steps a term, local binding and store, to a new term local binding and store. Terms that have more than one outcome (e.g. conditional statements) have a rule for each outcome. Some statements and terms, such as the RETURN statement, have no rule, but are included in the structure of terms for other rules (e.g. BodyReturn rule for RETURN). consts step :: "program ⇒ ((frame × objStore) × (frame × objStore)) set"

syntax (xsymbols) step :: "[program,frame × objStore,frame × objStore] ⇒ bool" ("_`_ 1 _"[61,82,82] 81)

translations "P`fs 1 fs’" "(fs,fs’) ∈ step P"

inductive "step P" intros

AssignL: "[[¬ lGround e1; P`((he1i,ls), os) 1 ((he1’i,ls’), os’) ]]=⇒ P`((he1 :== e2i,ls), os) 1 ((he1’ :== e2i,ls’), os’)"

AssignR: "[[lGround e1; P`((he2i,ls), os) 1 ((he2’i,ls’), os’) ]]=⇒ P`((he1 :== e2i,ls), os) 1 ((he1 :== e2’i,ls’), os’)"

AssignLoc: "P`((hIdent i :== Val vi,ls), os) 1 ((hSKIPi,updateLocVal ls i v), os)"

AssignFld: "[[os r = Some obj ]]=⇒ P`((h((Ref r)[rid].f) :== Val vi,ls),os) 1 ((hSKIPi,ls), os(r 7→ updateField obj f v))"

IfExp: "[[P`((hei,ls),os) 1 ((he’i,ls’),os’) ]]=⇒ P`((hIF e THEN ss1 ELSE ss2i,ls),os) 1 ((hIF e’ THEN ss1 ELSE ss2i,ls’),os’)"

IfTrue: "P`((hIF (Val (Bool True)) THEN ss1 ELSE ss2i,ls),os) 1 ((hss1i,ls),os)"

196 A.13 Operational Semantics

IfFalse: "P`((hIF (Val (Bool False)) THEN ss1 ELSE ss2i,ls),os) 1 ((hss2i,ls),os)"

ReturnExp: "[[ P`((hei,ls), os) 1 ((he’i,ls’), os’) ]]=⇒ P`((hRETURN ei,ls), os) 1 ((hRETURN e’i,ls’), os’)" UseLocTrue: "[[ locState ls i = Some b; kfst bhv kE b ]]=⇒ P`((hUSELOC (Ident i) :: bhv DO ss1 ELSE ss2i,ls), os) 1 ((hss1i,ls), os)"

UseLocFalse: "[[ locState ls i = Some b; ¬ kfst bhv kE b ]]=⇒ P`((hUSELOC Ident i :: bhv DO ss1 ELSE ss2i,ls), os) 1 ((hss2i,ls), os)"

UseNIL: "[[ locVal ls i = Some NIL ]] =⇒ P`((hUSE Ident i :: bhv DO ss1 ELSE ss2i,ls),os) 1 ((hss2i,ls),os)"

UseGlbTrue: "[[ locState ls i = None; locVal ls i = Some (Reference r); os r = Some obj; state obj = b; kfst bhv k E b ; os’ = os(r7→updateState obj (b - kfst bhvk)); ls’ = updateLocState ls i kfst bhvk ]]=⇒ P`((hUSE Ident i :: bhv DO ss1 ELSE ss2i,ls), os) 1 ((hss1;;(COMPLETE Ident i)i,ls’), os’)"

UseGlbFalse: "[[locState ls i = None; locVal ls i = Some (Reference r); os r = Some obj; state obj = b; ¬ kfst bhv k E b ]]=⇒ P`((hUSE Ident i :: bhv DO ss1 ELSE ss2i,ls), os) 1 ((hss2i,ls), os)"

Complete: "[[locState ls i = Some b; locVal ls i = Some (Reference r); os r = Some obj; state obj = b’ ]]=⇒ P`((hCOMPLETE Ident ii,ls), os) 1 ((hSKIPi,clearLocState ls i), os(r7→updateState obj (b+b’)))"

ComposeFront: "[[P`((hssi,ls), os) 1 ( (hss’i,ls’), os’) ]]=⇒ P`((hss ;; si,ls), os) 1 ( (hss’ ;; si,ls’), os’)"

ComposeLast: "[[P`((hsi,ls), os) 1 ( (hs’i,ls’), os’) ]]=⇒ P`((hEmpty ;; si,ls), os) 1 ( (hEmpty ;; s’i,ls’), os’)"

197 A MentokP : A language with negotiable interfaces

ComposeLastS: "[[P`((hsi,ls), os) 1 ( (hss::statementsi,ls’), os’) ]]=⇒ P`((hEmpty ;; si,ls), os) 1 ( (hssi,ls’), os’)"

ComposeSKIP: "P`((hEmpty ;; SKIPi,ls), os) 1 ((hEmptyi,ls), os)"

Ident: "[[locVal ls i = Some v ]]=⇒ P `((hIdent ii, ls),os) 1 ((hVal vi, ls),os)"

FieldExp: "[[P `((hei,ls),os) 1 ((he’i,ls’),os’) ]] =⇒ P `((he[rid].fi,ls),os) 1 ((he’[rid].fi,ls’),os’)"

FieldDeref: "[[os r = Some obj; fieldVs obj f = Some v ]]=⇒ P `((h(Ref r)[rid].fi,ls),os) 1 ((hVal vi,ls),os)"

New: "[[newObj P rid = Some obj; newRef os = Some r ]]=⇒ P`((hNEW ridi,ls), os) 1 ((hRef ri,ls), os(r 7→ obj))"

CallPars: "[[P `((hesi,ls),os) 1 ((hes’i,ls’),os’) ]]=⇒ P`((he[iid]·mid(es)i,ls),os) 1 ((he[iid]·mid(es’)i,ls’),os’)"

CallInvoke: "[[groundList es ; locVal ls i = Some (Reference r); os r = Some obj; tag obj = rid; methodImpDLU P (Record rid) mid = Some Mb; body Mb = ss; ls|=(i·bhv),(parIds Mb),es⇑ls’,ls_n ]] =⇒ P`((h(i·bhv)[iid]·mid(es)i,ls),os) 1 ((hBody rid mid ls_n (i·bhv) es ssi,ls’),os)"

BodyStep: "[[P `((hssi,ls_n),os) 1 ((hss’i,ls_n’),os’) ]]=⇒ P`((hBody rid mid ls_n (i·bhv) es ssi,ls),os) 1 ((hBody rid mid ls_n’ (i·bhv) es ss’i,ls),os’)"

BodyReturn: "[[ methodImpDLU P (Record rid) mid = Some Mb; ss = Empty ;; RETURN e ; ground e ; ls,ls_n|=(i·bhv),(parIds Mb),es⇓ ls’ ]] =⇒ P`((hBody rid mid ls_n (i·bhv) es ssi,ls),os) 1 ((hei,ls’),os)"

XConsHead: "[[ P`((he::expressioni,ls),os) 1 ((he’i,ls’),os’) ]]=⇒ P`((he#esi,ls),os) 1 ((he’#esi,ls’),os’)"

XConsTail:

198 A.13 Operational Semantics

"[[ pGround e ; P`((hesi,ls),os) 1 ((hes’i,ls’),os’) ]]=⇒ P`((he#esi,ls),os) 1 ((he#es’i,ls’),os’)"

A.13.2 Program Bootstrap constdefs mainBinding :: "(ident × cpType) list ⇒ localBinding" "mainBinding mVars ≡ (λ k. case k of This ⇒ None | VName v ⇒ option_map (λ v. (v,None)) (initDefaults (table_of mVars) v))" constdefs run :: "program ⇒ (frame × objStore)" "run P ≡ ((hmainCmd Pi,mainBinding (mainVars P)),empty)" end

199 200 B Module signatures with type layers

Access

Access.ISQLResult Access.ISQLQuery

Access

Figure B.1: Type layer graph for module Access.

1 COMPONENTMODULE Access; 2 3 TYPE 4 ISQLResult =POINTERTOINTERFACERECORD... 5 6 ISQLQuery =POINTERTOINTERFACERECORD... 7 8 END Access. Listing B.1: Signature for layered module Access

201 B Module signatures with type layers

Data

Data.IAtomicUpdate

Data.IDataSource

Data.IDataCache Data.IDataNotify

Data

Figure B.2: Type layer graph for module Data.

1 2 COMPONENTMODULE Data; 3 4 TYPELAYER 5 IDataSource < IAtomicUpdate, 6 IDataCache < IDataSource, 7 IDataNotify < IDataSource; 8 9 TYPE 10 IAtomicUpdate =POINTERTOINTERFACERECORD... 11 12 IDataSource =POINTERTOINTERFACERECORD... 13 14 IDataNotify =POINTERTOINTERFACERECORD... 15 16 IDataCache =POINTERTOINTERFACERECORD... 17 18 END Data. Listing B.2: Signature for layered module Data

202 Data, WidgetData

Data.IAtomicUpdate

Data.IDataSource, WidgetData.IWidgetProperties

Data.IDataNotify Data.IDataCache, WidgetData.IWidgetCache

Data, WidgetData

Figure B.3: Type layer graph for module WidgetData.

1 COMPONENTMODULE WidgetData; 2 3 COMPOSE D := Data; 4 5 COMPONENTLAYER 6 WidgetData := Data; 7 8 9 TYPELAYER 10 IWidgetProperties := D.IDataSource, 11 IWidgetCache := D.IDataCache; 12 13 TYPE 14 IWidgetProperties =POINTERTOINTERFACERECORD (+D.IDataSource) ... 15 16 IWidgetCache =POINTERTOINTERFACERECORD (+D.IDataCache) ... 17 18 END WidgetData. Listing B.3: Signature for layered module WidgetData

203 B Module signatures with type layers

Widgets

Data, WidgetData Widgets.IWindow

Data.IAtomicUpdate Widgets.IWindowItem, Widgets.IPanel

Data.IDataSource, WidgetData.IWidgetProperties

Data.IDataNotify Widgets.IRender, Widgets.IBasicRender

Data.IDataCache, WidgetData.IWidgetCache Widgets

Data, WidgetData

Figure B.4: Type layer graph for module Widgets.

1 COMPONENTMODULE Widgets; 2 3 COMPOSE WD := WidgetData; 4 5 TYPELAYER 6 7 IWindowItem < IWindow, 8 9 WD.IWidgetProperties < IWindowItem, 10 IRender < WD.IWidgetProperties, 11 WD.IWidgetCache < IRender, 12 13 IPanel := IWindowItem, 14 IBasicRender := IRender; 15 16 TYPE 17 18 IWindow =POINTERTOINTERFACERECORD... 19 20 IWindowItem =POINTERTOINTERFACERECORD... 21 22 IPanel =POINTERTOINTERFACERECORD (+IWindowItem) ... 23 24 IRender =POINTERTOINTERFACERECORD... 25 26 IBasicRender =POINTERTOINTERFACERECORD (+IRender) ... 27 28 END Widgets. Listing B.4: Signature for layered module Widgets

204 App

App.IApplicationRoot

App.IApplication

Widgets App.IApplicationConfig

Data, WidgetData Widgets.IWindow

Data.IAtomicUpdate Widgets.IWindowItem, Widgets.IPanel

Data.IDataSource, WidgetData.IWidgetProperties

Data.IDataNotify Widgets.IRender, Widgets.IBasicRender

Data.IDataCache, WidgetData.IWidgetCache App.IPostMessage Widgets

Data, WidgetData App

Access

Access.ISQLResult Access.ISQLQuery

Access

Figure B.5: Type layer graph for module App.

1 COMPONENTMODULE App; 2 3 COMPOSE UI := Widgets, 4 A := Access , 5 D := Data ; 6 7 COMPONENTLAYER Access < Data; 8 9 TYPELAYER 10 IPostMessage < IApplication, 11 IApplicationConfig < IApplication, 12 IApplication < IApplicationRoot, 13 14 UI.IWindow < IApplicationConfig, 15 IPostMessage < UI.IWindowItem, 16 17 D.IDataSource < IApplication, 18 IPostMessage < D.IDataNotify; 19 20 TYPE 21 IApplicationRoot =POINTERTOINTERFACERECORD... 22 23 IApplicationConfig =POINTERTOINTERFACERECORD... 24 25 IApplication =POINTERTOINTERFACERECORD... 26 27 IPostMessage =POINTERTOINTERFACERECORD... 28 29 END App. Listing B.5: Signature for layered module App

205 B Module signatures with type layers

App

App.IApplicationRoot

App.IApplication

Widgets App.IApplicationConfig

Data, WidgetData Widgets.IWindow

Data.IAtomicUpdate Widgets.IWindowItem, Widgets.IPanel

Data.IDataSource, WidgetData.IWidgetProperties

Widgets.IRender, Widgets.IBasicRender Data.IDataNotify

Data.IDataCache, WidgetData.IWidgetCache Widgets App.IPostMessage

Data, WidgetData App

Access

Access.ISQLResult Access.ISQLQuery

Access

Figure B.6: Minimal type layer graph for module App.

206 ModBar

1 COMPONENTMODULE ModBar; 2 3 TYPE 4 IBar1 =POINTERTOINTERFACE... ModBar.IBar1 5 6 END ModBar. Listing B.6: Signature for layered module ModBar ModBar

Figure B.7: Type layer graph for module ModBar.

ModFoo

ModFoo.IFoo1 1 COMPONENTMODULE ModFoo; 2 3 COMPOSE ModBar; 4 ModFoo 5 COMPONENTLAYER 6 ModBar < ModFoo; 7 8 TYPE ModBar 9 IFoo1 =POINTERTOINTERFACE... 10 11 END ModFoo. ModBar.IBar1 Listing B.7: Signature for layered module ModFoo ModBar

Figure B.8: Type layer graph for module ModFoo.

207 B Module signatures with type layers

TypeOrderCycle

ModFoo TypeOrderCycle.IFooBar

ModFoo.IFoo1 TypeOrderCycle

ModFoo

ModBar

ModBar.IBar1

ModBar

Figure B.9: Type layer graph for module TypeOrderCycle.

1 COMPONENTMODULE TypeOrderCycle; 2 3 COMPOSE ModFoo,ModBar; 4 5 TYPELAYER 6 ModFoo.IFoo1 < IFooBar, 7 IFooBar < ModBar.IBar1; 8 9 TYPE 10 IFooBar =POINTERTOINTERFACE... 11 12 END TypeOrderCycle. Listing B.8: Signature for layered module TypeOrderCycle

208 ModFoo ModOrderCycle

ModFoo.IFoo1 ModOrderCycle

ModFoo

ModBar

ModBar.IBar1

ModBar

Figure B.10: Type layer graph for module ModOrderCycle.

1 COMPONENTMODULE ModOrderCycle; 2 3 COMPOSE ModFoo,ModBar; 4 5 COMPONENTLAYER 6 ModFoo < ModBar; 7 8 END ModOrderCycle. Listing B.9: Signature for layered module ModOrderCycle

209 B Module signatures with type layers

ModFoo TypeEquateCycle

ModFoo.IFoo1, TypeEquateCycle.IFooBar, ModBar.IBar1

ModFoo ModBar TypeEquateCycle

ModBar

Figure B.11: Type layer graph for module TypeEquateCycle.

1 CCOMPONENTMODULE TypeEquateCycle; 2 3 COMPOSE ModFoo,ModBar; 4 5 TYPELAYER 6 IFooBar := ModFoo.IFoo1, 7 ModBar.IBar1 := IFooBar; 8 9 TYPE 10 IFooBar =POINTERTOINTERFACE... 11 12 END TypeEquateCycle. Listing B.10: Signature for layered module TypeEquateCycle

210 ModFoo.IFoo1 ModEquateCycle

ModFoo, ModBar ModEquateCycle

ModFoo, ModBar

ModBar.IBar1

Figure B.12: Type layer graph for module ModEquateCycle.

1 COMPONENTMODULE ModEquateCycle; 2 3 COMPOSE ModFoo,ModBar; 4 5 COMPONENTLAYER 6 ModBar := ModFoo; 7 8 END ModEquateCycle. Listing B.11: Signature for layered module ModEquateCycle

211 212 C MentokL: A language with type layers

This appendix presents the pretty-printed Isabelle/HOL[Wen00, NPW00] theory sources for MentokL, a small object-based language with negotiable interfaces and type layers. The definitions provided in this appendix have been used to investigate safety properties of negotiable interfaces and type layers in Mentok.

213 C MentokL: A language with type layers

C.1 Programs in MentokL

theory Programs = Statements + HOLplus:

C.1.1 Programs

A program is a list of interface declarations, a list of record declarations, a list of variable declarations, and a command representing the main body of the program. types interfaceDecl = "typeName × interfaceB" recordDecl = "typeName × recordB" varDecl = "ident × cpType" layerDecl = "typeName × typeName"

record program = interfaceDecls :: "interfaceDecl list" recordDecls :: "recordDecl list" mainVars :: "varDecl list" mainCmd :: "statements" typeEquates :: "layerDecl list" typeOrders :: "layerDecl list"

datatype typeLayer = Top | Bottom | Layer typeName consts equate :: "program ⇒ (typeLayer × typeLayer) set"

syntax (xsymbols) equate_ :: "program ⇒ [typeLayer,typeLayer] ⇒ bool" ("_`_==_" [51,51,51] 50)

translations "P`l == l’" "(l,l’) ∈ (equate P)"

inductive "equate P" intros

topr: "P ` Top == Top"

btmr: "P ` Bottom == Bottom"

typer: "[[isInterface P i ]] =⇒ P ` Layer i == Layer i"

eq: "[[(i,i’) mem (typeEquates P) ]] =⇒ P ` Layer i == Layer i’"

sym: "[[P ` l == l’ ]] =⇒ P ` l’ == l"

214 C.1 Programs in MentokL trans: "[[P ` l == l’’ ; P `l’’ == l’ ]] =⇒ P ` l == l’" consts layer :: "program ⇒ (typeLayer × typeLayer) set" syntax (xsymbols) layer_ :: "program ⇒ [typeLayer,typeLayer] ⇒ bool" ("_`__" [51,51,51] 50) translations "P`i  i’" "(i,i’) ∈ (layer P)" inductive "layer P" intros bottom: "[[isInterface P i ]] =⇒ P ` Bottom  Layer i" top: "[[isInterface P i ]] =⇒ P ` Layer i  Top" topbtm: "P ` Bottom  Top" ord: "[[(i,i’) mem (typeOrders P) ]] =⇒ P ` Layer i  Layer i’" trans: "[[P ` l  l’’ ; P ` l’’l’ ]] =⇒ P ` l  l’" eqtrans: "[[P ` l == l’ ; P ` l’’l]] =⇒ P ` l’’  l’" eqtrans2:"[[P ` l == l’ ; P ` ll’’]] =⇒ P ` l’  l’’" end

215 C MentokL: A language with type layers

C.2 Static Environments

theory Environments = Programs:

C.2.0.1 Environment Layer

constdefs frameLayer :: "environment ⇒ typeLayer" "frameLayer E ≡ case (locals E This) of None ⇒ Top | Some T ⇒ Layer (the (interfaceLU (prog E) T))"

constdefs allLayers :: "environment ⇒ stateEnvironment ⇒ typeLayer set" "allLayers E S ≡ {Layer (theInterface T) | T. ∃ i. S i 6= None ∧ locals E i = Some T ∧ isInterface (prog E) (theInterface T)} ∪ { frameLayer E }"

constdefs currentLayer :: "environment ⇒ stateEnvironment ⇒ typeLayer" "currentLayer E S ≡ let minSet = { T. T ∈ allLayers E S ∧ (∀ T’ ∈ allLayers E S. prog E `T  T’ ∨ prog E `T == T’) } in if (∃ L. L ∈ minSet) then εL. L ∈ minSet else Bottom"

end

216 C.3 State System Rules for Expressions and Statements

C.3 State System Rules for Expressions and Statements

UseG: "[[S v = None; E,S(v 7→ {kfst bhk}) |=s1 :≡ S_true; S_true v = Some {ksnd bh k}; E,S |=s2 :≡ S_false; dom (S_true(v := None)) = dom S_false; locals E v = Some (Iface i) ; prog E `Layer i  currentLayer E S ]]=⇒ E,S |=USE Ident v :: bh DO s1 ELSE s2 :- S_true(v:=None) ] S_false"

217 C MentokL: A language with type layers

C.4 Program Well-Formedness

theory WellFormed = WellTyped + WellStated:

C.4.1 Well formed Type Layer decs constdefs wfLayerDecl :: "program ⇒ layerDecl ⇒ bool" "wfLayerDecl P ld ≡ isInterface P (fst ld) ∧ isInterface P (snd ld)"

C.4.2 Well-formed programs

constdefs wfProgram :: "program ⇒ bool" ("` _ ♦" 50) "wfProgram P ≡ distinct (map fst (interfaceDecls P) @ map fst (recordDecls P)) ∧ ( list_all (%idecl. P `fst idecl ♦I ) (interfaceDecls P)) ∧ ( list_all (%rdecl. P `fst rdecl ♦R) (recordDecls P)) ∧ ( wfVarDecls P (mainVars P)) ∧ ( list_all (wfLayerDecl P) (typeEquates P)) ∧ (list_all (wfLayerDecl P) (typeOrders P)) ∧ ( ∀ i. isInterface P i −→ ( ¬ P ` Layer i  Layer i)) ∧ (let E = (|prog = P, locals = mkLEnv (mainVars P) |); ss = mainCmd P in E `s ss :≡ None ∧ E,empty |=ss :≡ empty )"

end

218 C.5 Runtime Structures

C.5 Runtime Structures theory State = Programs:

C.5.0.1 Runtime Current Layer constdefs typeLayerOf :: "program ⇒ objStore ⇒ value ⇒ typeLayer" "typeLayerOf P os v ≡ Layer (the (interfaceLU P (objectType (the (os (theRef v))))))" constdefs runtimeFrameLayer :: "program ⇒ objStore ⇒ localBinding ⇒ typeLayer" "runtimeFrameLayer P os ls ≡ case (locVal ls This) of None ⇒ Top | Some v ⇒ typeLayerOf P os v" constdefs runtimeAllLayers :: "program ⇒ objStore ⇒ localBinding ⇒ typeLayer set" "runtimeAllLayers P os ls ≡ { typeLayerOf P os v | v. ∃ i. locState ls i 6= None ∧ locVal ls i = Some v } ∪ { runtimeFrameLayer P os ls }" constdefs runtimeCurrentLayer :: "program ⇒ objStore ⇒ localBinding ⇒ typeLayer" "runtimeCurrentLayer P os ls ≡ let minSet = { T. T ∈ runtimeAllLayers P os ls ∧ (∀ T’ ∈ runtimeAllLayers P os ls. P `T  T’ ∨ P `T == T’) } in if (∃ L. L ∈ minSet) then εL. L ∈ minSet else Bottom" end

219 C MentokL: A language with type layers

C.6 Operational Semantics

theory OpSem = State:

UseGlbTrue: "[[ locState ls i = None; locVal ls i = Some (Reference r); os r = Some obj; state obj = b; kfst bhv k E b ; P `typeLayerOf P os (Reference r)  runtimeCurrentLayer P os ls ; os’ = os(r7→updateState obj (b - kfst bhvk)); ls’ = updateLocState ls i kfst bhvk ]]=⇒ P`((hUSE Ident i :: bhv DO ss1 ELSE ss2i,ls), os) 1 ((hss1;;(COMPLETE Ident i)i,ls’), os’)"

UseGlbFalse: "[[locState ls i = None; locVal ls i = Some (Reference r); os r = Some obj; state obj = b; ¬ kfst bhv k E b ∨ ¬ (P `typeLayerOf P os (Reference r)  runtimeCurrentLayer P os ls) ]]=⇒ P`((hUSE Ident i :: bhv DO ss1 ELSE ss2i,ls), os) 1 ((hss2i,ls), os)"

end

220 Bibliography

[ADvRF01] Wolfram Amme, Niall Dalton, Jeffery von Ronne, and Michael Franz. Sa- feTSA: a type safe and referentially secure mobile-code representation based on static single assignment form. In Proceedings of the ACM SIGPLAN’01 conference on Programming language design and implemen- tation, pages 137–147. ACM Press, 2001.

[AG97] Robert Allen and David Garlan. A formal basis for architectural connection. ACM Transactions on Software Engineering and Methodo- logy, 6(3):213–249, July 1997.

[Agh86] Gul Agha. Actors: A Model of Concurrent Computation in Distributed Systems. Series in Artificial Intelligence. MIT Press, Cambridge, MA, 1986.

[All97] Robert Allen. A Formal Approach to Software Architecture. PhD thesis, Carnegie Mellon, School of Computer Science, January 1997. Issued as CMU Technical Report CMU-CS-97-144.

[ALSN00] Franz Achermann, Markus Lumpe, Jean-Guy Schneider, and Oscar Niers- trasz. Piccola - a small composition language. In Formal Methods for Distributed Programming, an Object Oriented Approach. Cambridge Uni- versity Press, 2000.

[AT98] M. Aksit and B. Tekinerdogan. Solving the modeling problems of object- oriented languages by composing multiple aspects using composition fil- ters. In S. Demeyer and J. Bosch, editors, Object-Oriented Technology ECOOP’98 Workshop Reader, Berlin, 1998. Springer-Verlag.

[BB92] G´erardBerry and G´erardBoudol. The chemical abstract machine. Jour- nal of Theoretical Computer Science, 96:217–248, 1992.

[BC90] G. Bracha and W. Cook. Mixin-based inheritance. Proceedings of OOPS- LA/ECOOP ’90, ACM SIGPLAN Notices, 25(10):303–311, 1990.

[Bd96] P. Bouwman and H. de Bruin. Talktalk. In P. Wisskirchen, editor, Object- Oriented and Mixed Programming Paradigms, volume 9 of Eurographics Focus on Computer Graphics Series, pages 125–141. Springer–Verlag, Ber- lin, Germany, 1996.

[BG98] Jean-Pierre Briot and Rachid Guerraoui. A classification of various ap- proaches for object-based parallel and distributed programming. ACM Computing Surveys, 30(3):291–329, 1998.

221 Bibliography

[BGL98] Jean-Pierre Briot, Rachid Guerraoui, and Klaus-Peter Lohr. Concur- rency and distribution in object-oriented programming. ACM Compu- ting Surveys, 30(3):291–329, 1998.

[BKR00] Ashley Beitz, Simon Kent, and Paul Roe. Optimizing heterogeneous task migration in the gardens virtual cluster computer. In Proceedings of the Heterogeneous Computing Workshop (HCW 2000), page 140. 2000.

[BL93] Jean-Pierre Banˆatreand Daniel Le M´etayer. Programming by multiset transformation. Commun. ACM, 36(1):98–111, 1993.

[BLM90] Jean-Pierre Banˆatreand Daniel Le M´etayer. The gamma model and its discipline of programming. Sci. Comput. Program., 15(1):55–77, 1990.

[BLR94] Roland Balter, Serge Lacourte, and Michel Riveill. The Guide language. The Computer Journal, 37(6):519–530, 1994.

[BLS05] Mike Barnett, K. Rustan M. Leino, and Wolfram Schulte. The spec# programming system: An overview. In International Workshop, CAS- SIS 2004, Marseille, France, March 10-14, 2004, Revised Selected Papers, number 3362 in LNCS, pages 49–69. Springer-Verlag, January 2005.

[BMR+96] Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad, and Michael Stal. Pattern-oriented software architecture: a system of patterns. John Wiley & Sons, Inc., New York, NY, USA, 1996.

[Bos99] Jan Bosch. Superimposition: A component adaptation technique. Infor- mation and Software Technology, 41(5):257–273, 25 March 1999.

[BW97] M. B¨uchi and W. Weck. A plea for grey-box components. In Foundations of Component-Based Systems ’97, 1997.

[BW98] Martin B¨uchi and Wolfgang Weck. Java needs compound types. TUCS Technical Report No 182, Turku Centre for Computer Science, May 1998.

[BW99] Martin B¨uchi and Wolgang Weck. The greybox approach: When black- box specifications hide too much. TUCS Technical Report No 297a (Revised), Turku Centre for Computer Science, August 1999.

[Car91] Luca Cardelli. Extensible records in a pure calculus of subtyping. In In Theoretical Aspects of Object-Oriented Programming, pages 373–425. MIT Press, 1991.

[CFP+03] C. Canal, L. Fuentes, E. Pimentel, J. M. Troya, and A. Vallecillo. Adding roles to CORBA objects. IEEE Transactions on Software Engineering, 29(3):242–260, March 2003.

[CG92] N. Carriero and D. Gelernter. Coordination languages and their signifi- cance. Communications of the ACM, 35(2):97–107, February 1992.

222 Bibliography

[CGK97] Shing Chi Cheung, Dimitra Giannakopoulou, and Jeff Kramer. Verifica- tion of liveness properties using compositional reachability analysis. In ESEC ’97/FSE-5: Proceedings of the 6th European SOFTWARE ENGI- NEERING conference held jointly with the 5th ACM SIGSOFT interna- tional symposium on Foundations of software engineering, pages 227–243, New York, NY, USA, 1997. Springer-Verlag New York, Inc.

[CGL94] Edmund M. Clarke, Orna Grumberg, and David E. Long. Model che- cking and abstraction. ACM Transactions on Programming Languages and Systems, 16(5):1512–1542, 1994.

[CGZ95] Nicholas Carriero, David Gelernter, and Lenore D. Zuck. Bauhaus linda. In Selected papers from the ECOOP’94 Workshop on Models and Lan- guages for Coordination of Parallelism and Distribution, Object-Based Models and Languages for Concurrent Systems, pages 66–76. Springer- Verlag, 1995.

[CIW99] Daniele Compare, Paola Inveradi, and Alexander L. Wolf. Uncovering architectural mismatch in component behaviour. Science of Computer Programming, 33:101–131, 1999.

[CK96] Shing C. Cheung and Jeff Kramer. Checking subsystem safety proper- ties in compositional reachability analysis. In ICSE ’96: Proceedings of the 18th international conference on Software engineering, pages 144–154, Washington, DC, USA, 1996. IEEE Computer Society.

[CLR90] T. H. Cormen, C. E. Leiserson, and R. L. Rivest. Introduction to Algo- rithms. MIT Press, 1990.

[CMK98] Il-Hyung Cho, John D. McGregor, and Lee Krause. A protocol-based approach to specifying interoperability between objects. In Proceedings of TOOLS’26, pages 84–96. IEEE Press, 1998.

[CPN98] David G. Clarke, John M. Potter, and James Noble. Ownership types for flexible alias protection. In OOPSLA ’98: Proceedings of the 13th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, pages 48–64, New York, NY, USA, 1998. ACM.

[CWD00] Francisco Curbera, Sanjiva Weerawarana, and Matthew J. Duftler. On component composition languages. In Proceedings of the 5th Internatio- nal Workshop on Component-Oriented Programming, Sophia Antipolis, France, 2000.

[CWM99] Karl Crary, David Walker, and Greg Morrisett. Typed memory mana- gement in a calculus of capabilities. In Conference Record of POPL 99: The 26th ACM SIGPLAN-SIGACT Symposium on Principles of Pro- gramming Languages, San Antonio, Texas, pages 262–275, New York, NY, 1999.

223 Bibliography

[Dah02] Ole-Johan Dahl. The roots of object orientation. In Manfred Broy and Ernst Dennert, editors, Software Pioneers: Contributions to Software Engineering. Springer, 2002. [De 97] WolfGang De Meuter. Monads as a theoretical foundation for aop. In International Workshop on Aspect–Oriented Programming at ECOOP 97, 1997. [de 00a] Hans de Bruin. BCOOPL: Basic concurrent object-oriented program- ming language. Software - Practice and Experience (SPE), 30:849–894, 2000. [de 00b] Hans de Bruin. Use case maps: A technique for communicating and validating behavioral aspects of architecture. In Proceedings of Landelijk Architectuur Congres, 2000. [DF01] Robert DeLine and Manuel F¨ahndrich. Enforcing high-level protocols in low-level software. In SIGPLAN Conference on Programming Language Design and Implementation, pages 59–69, 2001. [EGK+02] John Ellson, Emden Gansner, Lefteris Koutsofios, Stephen C. North, and Gordon Woodhull. Graphviz — open source graph drawing tools. j-LECT- NOTES-COMP-SCI, 2265:483, 2002. [FF99] Peter H. Fr¨ohlich and Michael Franz. Component-oriented programming in object-oriented languages. Technical report, Department of Informa- tion and Computer Science, University of California, 1999. [FF01a] Robert Bruce Findler and Matthias Felleisen. Contract soundness for object-oriented languages. In ACM Conference on Object-Oriented Pro- gramming, Systems, Languages and Applications (OOPSLA ’01), Tampa Bay, Florida, USA, 2001. [FF01b] Peter H. Fr¨ohlich and Michael Franz. On certain basic properties of component-oriented programming languages. In David H. Lorenz and Vugranam C. Sreedhar, editors, Proceedings of the First OOPSLA Work- shop on Language Mechanisms for Programming Software Components, pages 15–18, Tampa Bay, FL, 15 October 2001. Technical Report NU- CCS-01-06, College of Computer Science, Northeastern University, Bos- ton, MA 02115. [FKF98] Matthew Flatt, Shriram Krishnamurthi, and Matthias Felleisen. Classes and mixins. In Proceedings of POPL’98, pages 171–182. ACM Press, San Diego CA, USA, 1998. [Fla99] Matthew Flatt. Programming Languages for Reusable Software Compo- nents. PhD thesis, Rice University, 1999. [FLW03] J. Fiadeiro, A. Lopes, and M. Wermelinger. A mathematical semantics of architectural connectors. In R. Backhouse and J. Gibbons, editors, Generic Programming, number 2793 in LNCS, pages 190–234. Springer- Verlag, 2003.

224 Bibliography

[Fra03] Michael Franz. Safe code – it’s not just for applets anymore. In Mo- dular Programming Languages, Proceedings of the Joint Modular Lan- guages Conference (JMLC’03), number 2789 in Lecture Notes in Com- puter Science, pages 12–22, Klagenfurt, Austria, August 2003. Springer– Verlag.

[Gel85] David Gelernter. Generative communication in linda. ACM Transactions on Programming Languages and Systems, 7:80–112, 1985.

[GH93] John V. Guttag and James J. Horning. Larch: languages and tools for formal specification. Springer-Verlag New York, Inc., New York, NY, USA, 1993.

[GJSB05] James Gosling, Bill Joy, Guy L. Steele Jr., and Gilad Bracha. The java language specification, third edition. available from http://java.sun.com/docs/books/jls/download/langspec-3.0.pdf, 2005.

[GMW97] David Garlan, Robert T. Monroe, and David Wile. Acme: An archi- tecture description interchange language. In Proceedings of CASCON’97, pages 169–183, Toronto, Ontario, November 1997.

[GNZ00] M. Goedicke, G. Neumann, and U. Zdun. Design and implementation constructs for the development of flexible, component-oriented software architectures. In Proceedings of GCSE 2000, Second International Sym- posium on Generative and Component-Based Software Engineering, Er- furt,Germany, October 2000.

[GRC01] John Gough, Paul Roe, and Diane Corney. Gardens point component pascal. http://plas.fit.qut.edu.au/gpcp/Default.aspx, 2001.

[Hoa85] C. A. R. Hoare. Communicating Sequential Processes. Prentice Hall, New York, 1985.

[HR99] Dirk Heuzeroth and Ralf Reussner. Dynamic coupling of binary compo- nents and it technical support. In First Workshop on Generative and Component based Software Engineering – Young Researchers Workshop, Erfurt, 1999.

[HSF02] Vivek Haldar, Christian H. Stork, and Michael Franz. The source is the proof. In New Security Paradigms Workshop (NSPW-O2), pages 69–74. ACM Press, Virginia Beach, VA, USA, September 2002.

[HV01] T. Holvoet and P. Verbaeten. Using petri nets for specifying active ob- jects and generative communication. Lecture Notes in Computer Science: Concurrent Object-Oriented Programming and Petri Nets, Advances in Petri Nets, 2001:38–72, 2001.

[IWY00] Paola Inveradi, Alexander L. Wolf, and Daniel Yankelevich. Static che- cking of system behaviors using derived component assumptions. ACM Transactions on Software Engineering and Methodology, 2000.

225 Bibliography

[KCJ98] Lars M. Kristensen, Søren Christensen, and Kurt Jensen. The practitio- ner’s guide to coloured petri nets. International Journal on Software Tools for Technology Transfer, 2:98–132, 1998.

[Ken03] Simon Kent. A layered type system for re-entrance control. In Modular Programming Languages, Proceedings of the Joint Modular Languages Conference (JMLC’03), number 2789 in Lecture Notes in Computer Science, Klagenfurt, Austria, August 2003. Springer–Verlag.

[Ken10] Simon Kent. mentokl – a language with negotiable interfaces and type layers. Technical report, Faculty of Science and Technology, QUT, March 2010.

[KHS01] Simon Kent and Chris Ho-Stuart. Mini-mianjin operational semantics. Technical report, Queensland University of Technology, School of Compu- ter Science, May 2001.

[KL90] D. G. Kafura and K. H. Lee. ACT++: Building a concurrent C++ with actors. Journal of Object-Oriented Programming, 3(1), 1990.

[KLM+97] Gregor Kiczales, John Lamping, Anurag Mendhekar, Chris Maeda, Cris- tina Videira Lopes, Jean-Marc Loingtier, and John Irwin. Aspect orien- ted programming. In Proceedings of the European Conference on Object- Oriented Programming 97 (ECOOP), number 1241 in LNCS, Finland, June 1997. Springer–Verlag.

[KM98] Jeff Kramer and Jeff Magee. Analysing dynamic change in software architectures: A case study. pages 91–100, 1998.

[Lak97] Charles Lakos. Object oriented modelling with object petri nets. In In: Advances in Petri Nets. LNCS, pages 1–37. Springer, 1997.

[Lev86] Nancy G. Leveson. Software safety: Why, what, and how. ACM Compu- ting Surveys, 18(2):125–163, June 1986.

[LM95] Doug Lea and Jos Marlowe. Interface-based protocol specification of open systems using PSL. Lecture Notes in Computer Science, 952:374– 398, 1995.

[LW94] Barbara Liskov and Jeannette Wing. A behavioral notion of subtyping. TOPLAS, 16(6):1811–1841, November 1994.

[LY99] Tim Lindholm and Frank Yellin. The Java Virtual Machine Specification. The Java Series. Addison Wesley Longman Inc., 2 edition, 1999.

[Mag91] Boris Magnusson. Code reuse considered harmful. Journal of Object– Oriented Programming, 4(3), November 1991.

[Mag99] Jeff Magee. Behavioral analysis of software architectures using ltsa. In ICSE ’99: Proceedings of the 21st international conference on Software engineering, pages 634–637, New York, NY, USA, 1999. ACM.

226 Bibliography

[McI68] M.D. McIlroy. Mass produced software components. In Report on a Conference of the NATO Science Committee, pages 138–150, 1968.

[MDEK94] Jeff Magee, Naranker Dulay, Susan Eisenbach, and Jeff Kramer. Speci- fying distributed software architectures. pages 137–153. Springer-Verlag, 1994.

[Mey92] Bertrand Meyer. Eiffel: The Language. Prentice Hall, 1992.

[MHF93] J.C. Mitchell, F. Honsell, and K. Fisher. A lambda calculus of objects and method specialization. pages 26–38, Jun 1993.

[MHKM95] R. Mitchell, J. Howse, S. Kent, and I. Maung. Software contracts: a way forward. In ICSE-17 Workshop on Research Issues in the Intersection of Software Engineering and Programming Languages, Seattle, Washington, USA, 1995.

[Mic00] Microsoft. C# language specification. available from http://msdn.microsoft.com/vstudio/nextgen/technology/csharpdownload.asp, 2000.

[MK96] Jeff Magee and Jeff Kramer. Dynamic structure in software architectures. In In Proceedings of the Fourth ACM SIGSOFT Symposium on the Foun- dations of Software Engineering, 1996.

[MKG97] J. Magee, J. Kramer, and D. Giannakopoulou. Analysing the behaviour of distributed software architectures: a case study. In FTDCS ’97: Pro- ceedings of the 6th IEEE Workshop on Future Trends of Distributed Com- puting Systems, page 240, Washington, DC, USA, 1997. IEEE Computer Society.

[ML02] Dick Mays and Richard J. LeBlanc, Jr. The cyclefree methodology: a simple approach to building reliable, robust, real-time systems. In ICSE ’02: Proceedings of the 24th International Conference on Software Engi- neering, pages 567–575, New York, NY, USA, 2002. ACM Press.

[Mog91] Eugenio Moggi. A modular approach to denontational semantics. In Category Theory and Computer Science, number 530 in Lecture Notes in Computer Science, 1991.

[MR04] James S. Miller and Susann Ragsdale. The Common Language Infrastruc- ture Annotated Standard. pub-AW, pub-AW:adr, 2004.

[MS97] Leonid Mikhajlov and Emil Sekerinski. The fragile base class problem and its solution. Technical Report TUCS-TR-117, 29, 1997.

[MT97] Nenad Medvidovic and Richard N. Taylor. A classification and compa- rison framework for software architecture description languages. IEEE Transactions on Software Engineering, 26:70–93, 1997.

227 Bibliography

[MY93] S. Matsuoka and A. Yonezawa. Analysis of inheritance anomaly in object–oriented concurrent programming languages. In Gul Agha, Peter Wegner, and Akinori Yonezawa, editors, Research Directions in Concur- renct Object-Oriented Programming. MIT Press, Cambridge, MA, USA, 1993. [Nec97] George C. Necula. Proof-carrying code. In Conference Record of POPL ’97: The 24th ACM SIGPLAN-SIGACT Symposium on Prin- ciples of Programming Languages, pages 106–119, Paris, France, 15–17 1997. [Nie95] Oscar Nierstrasz. Regular types for active objects. In O. Nierstrasz and D. Tsichritzis, editors, Object-Oriented Software Composition, pages 99– 121. Prentice Hall, 1995. [NM95] Oscar Nierstrasz and Theo Dirk Meijler. Requirements for a composi- tion language. In Paolo Ciancarini, Oscar Nierstrasz, and Akinori Yone- zawa, editors, Object-Based Models and Langages for Concurrent Systems, pages 147–161. Springer-Verlag, 1995. [NPW00] T. Nipkow, L. Paulson, and M. Wenzel. Isabelle’s logics: Hol. http://isabelle.in.tum.de/doc/logics-HOL.pdf, 2000. [NZ99] G. Neumann and U. Zdun. Enhancing object-based system composition through per-object mixins. In Proceedings of Asia-Pacific Software Engi- neering Conference (APSEC), Takamatsu, Japan, December 1999. [Obe97] Oberon Microsystems Inc. Component pascal language report. available from http://www.oberon.ch/docu/language report.html, 1997. [Ode06] Martin Odersky. The scala language specification, version 2.0. available from http://scala.epfl.ch/docu/files/ScalaReference.pdf, 2006. [OMG02] OMG. Corba component model, v3.0. available from http://www.omg.org/technology/documents/formal/components.htm, 2002. [OMG09] OMG. Unified Modeling Language: Superstructure, version 2.2. Object Modeling Group, February 2009. [ON99] David von Oheimb and Tobias Nipkow. Machine-checking the Java speci- fication: Proving type-safety. In Jim Alves-Foss, editor, Formal Syntax and Semantics of Java, volume 1523 of LNCS, pages 119–156. Springer, 1999. [OZ05] Martin Odersky and Matthias Zenger. Scalable component abstractions. In OOPSLA ’05: Proceedings of the 20th annual ACM SIGPLAN confe- rence on Object oriented programming, systems, languages, and applica- tions, pages 41–57, New York, NY, USA, 2005. ACM Press. [PA98] George A. Papadopoulos and Farhad Arbab. Coordination models and languages. Advances in Computers, 46:329–400, 1998.

228 Bibliography

[Par72] D. L. Parnas. On the criteria to be used in decomposing systems into modules. Commun. ACM, 15(12):1053–1058, 1972.

[Pet77] James L. Peterson. Petri nets. ACM Computing Surveys, 9:223–252, 1977.

[PG06] Robert G. Pettit, IV and Hassan Gomaa. Modeling behavioral of concurrent objects. In ICSE ’06: Proceedings of the 28th international conference on Software engineering, pages 202–211, New York, NY, USA, 2006. ACM.

[PP99] Franz Puntigam and Christophe Peter. Changeable interfaces and promi- sed messages for concurrent components. In ACM Symposium on Applied Computing (SAC’99). San Antonio, Texas, 1999.

[PT94] Benjamin C. Pierce and David N. Turner. Simple type-theoretic founda- tions for object-oriented programming. Journal of Functional Program- ming, 4(2):207–247, 1994.

[Pun96] Franz Puntigam. Types for active objects based on trace semantics. In 1st IFIP Workshop on Formal Methods for Open Object-based Distribu- ted Systems (FMOODS’96). Paris, France, 1996.

[Pun97] Franz Puntigam. Coordination requirements expressed in types for active objects. In Proceedings of the European Conference on Object- Oriented Programming (ECOOP’97). Springer–Verlag, Jyv¨aaskyl¨a,Fin- land, 1997.

[Pun01] Franz Puntigam. Flexible types for a concurrent model. pages 461–472, 2001.

[Reu00] Ralf Reussner. Formal foundations of dynamic types for software compo- nents. Technical Report 08/2000, Department of Informatics, Universit¨at Karlsruhe, Department of Informatics, 2000.

[Reu01] Ralf Reussner. Enhanced component interfaces to support dynamic adap- tation and extension. In Proceedings of the 34th Annual Hawaii Interna- tional Conference on System Sciences (HICSS–34), 2001.

[RH99] Ralf Reussner and Dirk Heuzeroth. A meta-protocol and type system for the dynamic coupling of binary components. In Proceedings of the OOPSLA’99 Workshop on Object Oriented Reflection and Software Engi- neering, Denver, 1999.

[RL89] Rajendra K. Raj and Henry M. Levy. A compositional model for software reuse. The Computer Journal, 32, 1989.

[RS97] Paul Roe and Clemens Szyperski. Mianjin is gardens point: A paral- lel language taming asynchronous communication. In Fourth Australa- sian Conference on Parallel and Real–Time Systems (PART’97), number 1204 in LNCS, Newcastle, 1997. Springer–Verlag.

229 Bibliography

[SB94] C. Sibertin-Blanc. Cooperative nets. In Proceedings of 15th International Conference on the Application and Theory of Petri Nets, Lecture Notes in Computer Science 815, pages 471–490. Springer-Verlag, 1994.

[SC00a] Jo˜aoCosta Seco and Luis Caires. A basic model of typed components. In Proceedings of ECOOP 2000, number 1850 in Lecture Notes in Computer Science. Springer-Verlag, 2000.

[SC00b] Jo˜aoCosta Seco and Luis Caires. Parametric typed components. In WCOP’2000 - Workshop on Component Oriented Programming, 2000.

[Sma99] Ioannis Smaragdakis. Implementing Large-Scale Object-Oriented Compo- nents. PhD thesis, University of Texas at Austin, 1999.

[Sny86] Alan Snyder. Encapsulation and inheritance in object-oriented program- ming languages. SIGPLAN Notices, 21(11):38–45, 1986.

[SO96] David Stoutamire and Stephen Omohundro. Sather1.1. available online at http://www.icsi.berkeley.edu/∼sather/Specification/Sather1.1/index.html, 1996.

[SR00] Clemens Szyperski and Paul Roe. Mianjin: A parallel language with a type system that governs global system behaviour. In Proceedings of the Joint Modular Languages Conference 2000 (JMLC2000), LNCS, ETH, Zurich, Switzerland, 2000. Springer–Verlag.

[SRW98] J. A. Stafford, D. J. Richardson, and A. L. Wolf. Aladdin: A tool for architecture-level dependence analysis of software systems. Technical Report CU-CS-858-98, Department of Computer Science, University of Colorado, April 1998.

[Str97] Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley, third edition edition, 1997.

[Sun96] Sun Microsystems. The JavaBeans 1.01 Specification. http://java.sun.com/products/javabeans/docs/spec.html, 1996.

[SW00] Judith A. Stafford and Alexander L. Wolf. Annotating components to support component-based static analyses of software systems. In Grace Hopper Celebration of Women in Computing 2000, Hyannis, Massachu- setts, 2000.

[Szy98] Clemens Szyperski. Component Software, Beyond Object–Oriented Pro- gramming. Addison–Wesley, Harlow, Essex, 1 edition, 1998.

[Szy00] Clemens Szyperski. Components and contracts. 8(5), May 2000.

[Szy02] Clemens Szyperski. Component Software, Beyond Object–Oriented Pro- gramming. Addison–Wesley, 2 edition, 2002.

230 Bibliography

[TGP89] David Taenzer, Murthy Ganti, and Sunil Podar. Problems in object– oriented software reuse. In ECOOP 89, pages 25–38. Cambridge Univer- sity Press, July 1989.

[The97] The Rapide Team. The rapide language. http://pavg.stanford.edu/rapide/language.html, 1997.

[TMA03] Nam Tran, Christine Mingins, and David Abramson. Managed assertions for component contracts. In Integrated Design and Process Technology (IDPT–2003), June 2003.

[Ves93] Steve Vestal. A cursory overview and comparison of four architecture description languages. Technical report, Honeywell Systems & Research Center, 1993.

[vL91] J. van den Bos and C. Laffra. PROCOL: A concurrent object-language with protocols, delegation and persistence. Acta Informatica, 28:511–538, September 1991.

[vOvdLKM00] Rob van Ommering, Frank van der Linden, Jeff Kramer, and Jeff Magee. The koala component model for consumer electronics software. Computer, 33(3):78–85, 2000.

[Weh00] Heike Wehrheim. Subtyping patterns for active objects. In Procee- dings 8ter Workshop des GI Arbeitskreises GROOM (Grundlagen objekt- orientierter Modellierung): Visuelle Verhaltensmodellierung verteilter und nebenl¨aufigerSoftware-Systeme, Muenster, 2000.

[Wen00] Markus Wenzel. The isabelle/isar reference manual, 2000.

[Wir71] Niklaus Wirth. The programming language pascal. Acta Informatica, 1(1):35–64, 1971.

[YS97] D. Yellin and R. Strom. Protocol specifications and component adaptors. ACM Transactions on Programming Languages and Systems, 19(2):292– 333, 1997.

[Zen02a] Matthias Zenger. Evolving software with extensible modules. In Inter- national Workshop on Unanticipated Software Evolution, Malaga, Spain, June 2002.

[Zen02b] Matthias Zenger. Type-safe prototype-based component evolution. In Proceedings of the European Conference on Object-Oriented Programming, Malaga, Spain, June 2002.

[Zen04] Matthais Zenger. Programming Language Abstractions for Extensible Software Components. PhD thesis, EFPL, Switzerland, March 2004.

[Zen05] Matthias Zenger. Keris: evolving software with extensible modules. Jour- nal of Software Maintenance and Evolution Research and Practice, 17(5), September 2005.

231