Skip to main content
Version: Next

Classes

Syntax

Classes are a kind of struct that support more advanced features.

class Node {
    Node next;
    int value;

    Node(int val) {
        this.value = val;
    }
}

Usage

Classes are pass-by-reference and are always instantiated on the heap by internally using amethyst:gc/malloc. In most cases, manually allocating memory on the heap is unecessary as classes are instantiated with the new keyword like so:

var start = new Node(5);
var end = new Node(10);
start.next = end;

Amethyst internally uses immutable references to access classes on the heap. This allows easy control over changing which reference points to which object.

Virtual Methods

Classes can have virtual methods that can be overridden by subclasses. Virtual methods are defined using the virtual function modifier like so:

class Vec {
    int x;
    int y;

    virtual int sum() {
        return this.x + this.y;
    }
}

class SubVec implements Vec {
    // Must also declare as virtual
    // Signature must be identical to the original method
    virtual int sum() {
        return 7;
    }
}

Amethyst stores class type information at runtime at storage amethyst:runtime type_info with keys corresponding to the full names of classes.

Differences from Structs

  • Classes can hold any type of property, including references and other classes.
  • Classes are pass-by-reference.
  • Classes have a hidden @type property that holds the ID of the class.
  • Constructors have a hidden this parameter just like methods.
warning

As you may know, garbage collection is slow. Additionally, in the resource constrained environment of Minecraft commands, dereferencing pointers is also expensive. Using classes can lead to a large chain of pointers which in turn can prevent Amethyst from generating efficient code. Unless classes are needed, it is recommended to use structs and global variables instead. Unlike most native programming languages compiled to machine code, the number of instructions (or commands) is much more important than the memory layout for performance.

While the garbage collection system always runs in the background, if the heap is not used then the performance impact is negligible at worst.

Garbage Collection

Objects allocated on the heap will be deleted if no surviving references to it are found. The garbage collector will only run if new objects have been allocated.

Places references are checked for:

  • Global variables
  • Lists if strongly-typed
  • Class properties

Blocked locations for references:

  • Struct properties
  • Entity properties
  • Maps
    • This may change in the future if a reliable way to iterate over NBT object keys is found. Feel free to create an issue if you know of one.

To get around these limitations, use weak references.

warning

Storing lists or classes as nbt or nbt& will cause nested references to not be correctly marked as alive. Additionally, references to references (T&&) do not check nested properties.

Additionally, the garbage collection algorithm will start to lag the game at around a few hundred objects. A future asynchronous algorithm may be applied in the future.

Manual Marking

On every collection cycle, the #amethyst:gc/mark function tag is called. The purpose of these functions is to locate alive references that may contain heap-allocated data. In this state, calling void amethyst:gc/mark(nbt&) will mark the reference as alive for the garbage collector.

Example:

#amethyst:gc/mark
void my_mark() {
    amethyst:gc/mark(my_reference_that_for_some_reason_wasnt_marked);
}