Generics optimization

  Status: implemented,  Fri Feb 26 11:23:44 2010
  • Created: 2009-11-10 19:53:29+0100
  • Categories: <core>
  • Target: 5.8.0
  • Author: friebe

Scope of Change

Generics will be optimized.


Rationale

  • Improve performance
  • Enable reflection for generics
  • Enable reflective creation of generics
  • Support primitives a generic arguments


Functionality

Currently, generics are implemented as follows:

  • A: A __generic instance member is required inside the declaration. It will hold an array of the component types.
  • B: The methods working with generics are required to take care of the type checks.
  • C: The create() core functionality takes care of instantiating the generics, populating the __generic member with the given types' names.

Example (abbreviated):

  class Vector extends Object {
public $__generic; // A

public function add($value) {
if (!$value instanceof $this->__generic[0]) { // B
throw new IllegalArgumentException('...');
}
// ...
}
}

$v= create('new Vector<lang.types.String>()'); // C

This has the following downsides:

  • The declaration is quite verbose and introduces a bunch of boilerplate code for the manual component type verification.
  • At runtime, two generics, e.g. a vector of strings and one integers, are "instanceof"-compatible
  • There is no way to type-hint a generic, verifying a vector's component type is string would mean manually accessing its __generic member.

Plan

A generic instance should be created at runtime named with unique name created of the base and component types. A class has two names, one that is reported by XPClass::getName() (F) and one used literally (L):

  // Creates a class named: 
// F: "util.collections.Vector`1[lang.types.String]"
// L: "Vector··String"
$ve= create('new Vector<lang.types.String>()');

// Creates a class named:
// F: "util.collections.HashTable`2[lang.types.String,lang.Generic]"
// L: "HashTable··String¸Generic"
$ht= create('new HashTable<lang.types.String, lang.Generic>()');

// Creates a class named:
// F: "util.collections.Stack`1[string]"
// L: "Stack··þstring"
$st= create('new Stack<string>()');

The same generation process happens for all generic interfaces these classes implement and the generic base classes they extend.

The middle dot (·, Alt-Gr + "." in Cygwin) is used in the XP Framework for generated classes and in fully qualified names (see RFC #0037). The cedil sign (¸, Alt-Gr + "´" in Cygwin) is used to separate the components.

Declaration

To declare a generic class using the XP framework, we will resort to annotations:

  #[@generic(self= 'K, V', interfaces= array('Map' => 'K, V')))]
class HashTable extends Object implements Map {

#[@generic(params= 'K, V')]
public function put($key, $value) { ... }

#[@generic(params= 'K')]
public function get($key) { ... }

#[@generic(return= 'V[]')]
public function values() { ... }

public function toString() { ... }
}

In XP language, this needn't be done as it syntactically supports generics:

  public class HashTable<K, V> implements Map<K, V> {

public void put(K $key, V $value) { ... }

public V get(K $key) { ... }

public V[] values() { ... }

public string toString() { ... }
}

Instantiation

To instantiate generics, the create() core functionality needs to be used. The inner workings are as follows:

  1. Parse string specifying type into class and parameters
  2. Compose unique name
  3. If this class exists, instantiate and return
  4. For all interfaces, perform generation
  5. Generate class extending lang.Object
  6. For all methods generate delegation
  7. Instantiate and return
What we will end up with is the following:

  interface Map··String¸Object {
public function put(String $key, Object $value);
public function get(String $key);
public function values();
}

class HashTable··String¸Object extends Object implements Map··String¸Object {
private $delegate;

public function __construct() {
$this->delegate= new HashTable();
}

public function put(String $key, Object $value) {
$this->delegate->put($key, $value);
}

// ...
}

Reflection

Whether a type is a generic instance should be determinable at runtime:

  // Will display: 
// Arguments: [
// 0 => lang.XPClass<lang.types.String>
// ]
$class= create('new Vector<String>()')->getClass();
if ($class->isGeneric()) {
Console::writeLine
('Arguments: ', $class->genericArguments());
}

Reflection on generic definitions should also be possible:

  // Will display:
// Components: [
// 0 => "T"
// ]
$class= XPClass::forName('util.collections.Vector');
if ($class->isGenericDefinition()) {
Console::writeLine
('Components: ', $class->genericComponents());
}

Also, reflective creation of generic types should be possible:

  $class= XPClass::forName('util.collections.Vector');
$generic= $class->newGenericType(array(XPClass::forName('lang.types.String')));

// ...and then use newInstance() methods
$vector= $generic->newInstance();

Discovering the definition type:

  // Will display:
// Definition: lang.XPClass<util.collections.Vector>
$class= create('new Vector<String>()')->getClass();
if ($class->isGeneric()) {
Console::writeLine
('Definition: ', $class->genericDefinition());
}

Type restrictions

The generic types can now be used in parameter type hints, although somewhat ugly:

  public function dump(List··String $l) { ... }



Security considerations

n/a

Speed impact

Faster.

Dependencies

RFC #0197

Related documents



Comments



<EOF>


Table of contents