You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

8.3 KiB

pEpACIDLang - Language Mapping Specification for C99 (LMS-C99)

This is the document specifying the pEpACIDLang language mapping for C99.

C99, because this is the minimum language level for the generated target code. We will use features of a higher language level once there are good reasons to do that.

The interface generator implements parts of the language mapping specification.

About This Document

State: Draft, to be reviewed with fdik and positron.

ATTENTION, IMPORTANT
In this document, all types expressed as T are the IDL-types, not their corresponding c-types.
An example for clarity:
If T is the type string, the IDL-type string is meant, so the c-type of T* is char**.

Usually a variable typename is expressed as T. But sometimes T is being used to refer to an instance of type T. This imprecision is used for brevity, but only where the context does not allow for an ambiguity.

IDL-Types

Type Classes

There are only two type-classes.

  • Primitive Types
  • Object Types

IDL-Type Mapping To C

Type-Class IDL-Type C-Type
Primitive int int
Primitive uint unsigned int
Primitive size size_t
Primitive bool bool
Primitive enum enum
Object struct struct*
Object string char*
Object binary char*
Object opaque void*
Object list<T> list_T*
Object pair<F,S> pair_F_S*

Creation and Destruction

Primitive Types

  • Primitive types are always statically allocated
  • Primitive types are trivially constructable and destroyable
  • All storage classes (auto/static/extern/register) are allowed

Object Types

For any object-type T, corresponding new and free methods are being generated.

To create an object of type T correctly, there are two ways:

  1. Using its allocator-method
  2. Using any method that has a parameter of type T with mode Produce

To destroy an object of type T correctly, the only way is to use its destructor-method.

Allocators

The allocator-method has the signature:
IDL_STATUS new_T(T*)

  • The memory for T has to be dynamically allocated
  • An empty object is being created. Any object-types reachable through T are pointing to NULL.

Result:
STATUS_OK - T* is pointing to a valid object
STATUS_ERROR - Dereferencing of T* is undefined behaviour

Destructors

The destructor-method has the signature:
IDL_STATUS free_T(T*)

  1. For all object-type members of T itself, their respective destructor-methods are being called
  2. The memory for T is freed compatibly to how it was allocated using its allocator-method
  3. T* is set to NULL

Result:
STATUS_OK - All memory reachable through T* is freed and T* is NULL
STATUS_ERROR - FATAL (most implementations of free() can guarantee this will never happen)

Method Parameters

The main concerns when sharing memory (e.g. in form of parameters) are:

  • Ownership: defined as who is allowed and responsible to destroy an object.
  • Mutability: Who is allowed to change the contents of the memory location that is known to several parties.

In environments where memory management is explicit, the lifecycle of data in memory is:

  1. Memory being allocated
  2. Memory being shared with producers/consumers (e.g. as a parameter Read/Update/Consume)
  3. Read/write operations by multiple parties (needs synchronization if multithreaded environment)
  4. Make sure this memory will never be accessed again by nobody
  5. Memory has to be freed exactly once and compatibly to how it was allocated

Every such lifecycle must be completed.
All of this must be guaranteed.

Rules For Parameter Allocation

Parameters have to be allocated as follows:

These rules apply regardless of the parameter passing mode.

Parameter Passing Modes

The modes are named by describing what the callee does, from the perspective of the callee, using one verb.

  • Read - Immutable, ownership remains with caller
  • Update - Mutable, ownership remains with caller
  • Consume - Mutable, ownership goes to callee
  • Produce - Immutable, ownership goes to caller

Mapping Table for C

Type of T as a function of type-class and mode. Where T any of the respective IDL-Types (not their respective c-type, as mentioned in About This Document).

Mode/Type-Class READ UPDATE CONSUME PRODUCE
Primitive T T* - (1) - (1)
Object T T* T T*

Rules For Parameters Of Primitive-Type

(1) A primitive-type cannot be consumed or produced because it's always statically allocated.

Update

The caller has to allocate memory for T*.
The callee is allowed to mutate the memory pointed to by T*.
The caller is responsible to free the memory pointed to by T*.

Rules For Parameters Of Object-Type

Read

The caller has to guarantee that either-or:

  • T is NULL
  • T is pointing to a valid object of type *T (allocated as defined under 'Rules For Parameter Allocation')
    This guarantee applies transitively for all object-type members of *T.

The callee is not allowed to mutate or free any memory that can be reached through T. The caller is responsible to free all the memory that can be reached through T.

Update

The caller has to guarantee that:

The callee is allowed to mutate the object in two ways:

  1. Mutate any primitive-types reachable through T*
  2. Free or replace any object-types reachable trough T*

The callee has to guarantee, that upon return of the method:

Upon success of the method call:
The caller must replace all pointers to *T and to any of its transitive members by dereferencing T* and its respective transitive members.
The caller must free T and any of its transitive members.

TODO: Upon any failure of the method call:

Consume

The same rules for mode Read apply, with the exception that the caller has to guarantee to never access T again in any way.

Produce

The caller is allowed to pass a T* with an undefined value.
TODO...

Rules For Multithreading

These rules apply to all memory that can be accessed through a parameter. Such memory must be allocated in the calling thread No such memory can be shared with other TODO....