David's Coding Blog

QIQ - Memory management for PHP Pointers

PHP allows you to pass values by reference. This feature is not used much in the code I have seen, but it is still handy. How do you implement this feature in an interpreter environment?

At first, some structure is required to hold a value at runtime of the interpreter. In my PHP engine, QIQ, this is done by structs, all implementing the RuntimeValue interface:


type RuntimeValue interface {
    GetType() ValueType
}

Second, some kind of runtime environment: the interpreter must keep track of all declared variables and their values. This can be a simple map, with the variable name as key (type: string) and the value stored as RuntimeValue:


type Environment struct {
    variables map[string]values.RuntimeValue
}

And this will work fine until you want to support by-reference or often called pointers.

Slot

In order to get pointers up and running, an additional wrapper around this struct is required. In QIQ this is called a Slot and is a struct that just holds an RuntimeValue:

This separates the RuntimeValue from the variable name.


type Slot struct {
    Value RuntimeValue
}

Example: By value

The example shows a simple function that takes an integer and increases it by one. Because it's passed by value, $b gets passed the value of $a, but it is a copy, they have different slots.

After the function has been called the value of $a will still be 42.


<?php
function foo($b) { $b++; }

$a = 42;
foo($a);
echo $a; // 42;
Memory structure for by-value

Example: By reference

If we change the example and take the parameter by reference. Not the value but the slot will be passed into the functions environment. And because they now share the slot, the changes of the value of $b will change the value of $a.

After the function has been called the value of $a will be 43.


<?php
function foo(&$b) { $b++ }

$a = 42;
foo($a);
echo $a; // 43;
Memory structure for by-ref

Interpreter logic

Of course, this can only work, if the logic all around variable and parameter handling is implemented correctly. For example, depending on the definition of the function parameters, the variable will be declared with a copy of the value or by reference when calling a function:


// Declare parameter in function environment
if param.ByRef {
    functionEnv.declareVariableByRef(param.Name, slot)
} else {
    functionEnv.declareVariable(param.Name, values.DeepCopy(slot).Value)
}