Definition of C0Type

Most of the type information are lost when the C0 source code is compiled into the C0 bytecode. However, to perform visualization and show the value of local variable, we must know the type information of variables such that we can interpret the byte sequence accordingly.

Luckily, not all information are wiped away during the compilation. The .bc0 file contains a series of comments, which reveals the source or intention of a specific operation. The calling of native functions can also provide us with type information (e.g. readline must return a string type).

Therefore, we developed a Type Inference system along side the C0Value to collect all the traces of type information and propagate the type constraint as C0VM executes.

C0Type Definition

Defined in /src/types/c0types.d.ts, we have C0Type<T extends C0TypeClass> defined as:

type C0Type<T extends C0TypeClass> = 
    T extends C0TypeClass.value ? {
        type: T,
        value: C0ValueTypes
    } : 
    T extends (C0TypeClass.ptr) ? {
        type: T,
        kind: "arr"| "ptr",
        value: C0Type<C0TypeClass>,
    } | {
        type: T,
        kind: "struct",
        value: string,	// The type name of struct
        offset: number	// Offset from the start of struct
    } : 
    T extends (C0TypeClass.string) ? {
        type: T,
        value: "string"
    } : 
    T extends (C0TypeClass.unknown) ? {
        type: T
    } : never;

C0TypeClass

Unlike in C, where a variable is either on stack or heap, in C0 language, only primitive types and pointers are stored on stack. All aggregate type like struct, array must be stored on heap (a.k.a. "allocated memory space").

Therefore, we classify all C0Types into three classes:

  • value - All primitive types

  • ptr - All the pointers, and array (and structures)

  • string - This is a special case. string is always stored on the heap, however, dereferencing a string type is not allowed in C0 language. Therefore, it is a "un-dereferenceable pointer" and we decide to put it into a separate class alone.

  • unknown - when the type information is unknown, we will mark it honestly as <unknown> type, which acts like the any type in TypeScript and is accepted by all functions without condition.

A C0Type is defined in a nested way. For instance, a type of string[] will be represented by

{type: C0TypeClass.ptr, kind: "arr", value: {
 type: C0TypeClass.string, value: "string"
}}

Conversion between Type and String

The function defined in ./types/c0type_utility.ts will convert C0Type to string and string to C0Type.

"int*" <====> 
{type: C0TypeClass.ptr, kind: "ptr", value: {type: C0TypeClass.value, value: "int"}}

Type Information Source

The type information comes from four main sources:

  1. bipush(T) -> C0Value<C0TypeClass.value>: T

  2. newarray(T, int | unknown) -> C0Value<C0TypeClass.ptr>: T[]

  3. alloc(T) -> C0Value<C0TypeClass.ptr>: *T

  4. add(int | unknown, int | unknown) -> C0Value<C0TypeClass.value>: int Other arithmetic functions will have similar effect

  5. invokenative(args: C0Value[]) -> C0Value: T

Last updated