SPVM::D...iveClass(3)

πŸ“¦ version 0.990125, in alpine 3.24

πŸ’– SPVM::Document::NativeClass β€” Native Classes πŸ’–

okay so bestie, welcome to the SPVM::Document::NativeClass page from the User Contributed Perl Documentation β€” it's literally the whole lore on native classes and honestly it ate πŸ“


✨ NAME ✨

SPVM::Document::NativeClass β€” Native Classes


πŸ¦‹ DESCRIPTION πŸ¦‹

So a native class is a class that's implemented by a native language β€” think C, think C++, that whole situation πŸ’…


🌸 NATIVE METHOD DEFINITION 🌸

A native method gets defined with the native method attribute inside an SPVM class file. It ends with a semicolon, and crucial detail: a native method does not have its block. no block, no problem πŸŽ€

# SPVM/MyClass.spvm
class MyClass {
  native static method sum : int ($num1 : int, $num2 : int);
}

🧚 NATIVE CONFIG FILE 🧚

Every native class needs a native config file, periodt. The config file's name is the same as the SPVM class name, except the extension .spvm gets swapped out for .config.

# The name of a native config file
SPVM/MyClass.config

A native config file is written by Perl, and it must end with a Builder::Config object. like, mandatory, non-negotiable πŸ’…

The config file gets executed by Perl's do <https://perldoc.perl.org/functions/do> function, and the Builder::Config object it returns gets used as the config for a native class.

Exceptions:

  • If the native config file does not exist, an exception is thrown.
  • A config file must end with a Builder::Config object. Otherwise, an exception is thrown.

Examples:

GNU C99:

my $config = SPVM::Builder::Config->new_gnu99;

$config;

C++:

my $config = SPVM::Builder::Config->new_cpp;

$config;

πŸ‘‘ NATIVE CLASS πŸ‘‘

A native class is the class implemented by a native language such as the C language and C++. we been knew πŸ’–

The name of the native class file is the same as the SPVM class name, but the extension .spvm is replaced with . and extension of the native class.

SPVM/MyClass.c

⭐ Native Class File Extension ⭐

The file extension of a native class is defined by the Builder::Config#ext field in a config file.

Examples:

$config->ext('c');

$config->ext('cpp');

$config->ext('cc');

$config->ext('cu');

Exceptions:

If ext is defined, but its corresponding config file does not exist, an exception is thrown. so like, keep them matched bestie 😭

πŸ’€ Native Function πŸ’€

A native function is a function defined in a native class. simple as that.

Examples:

// SPVM/MyClass.c

#include "spvm_native.h"

int32_t SPVM__MyClass__sum(SPVM_ENV* env, SPVM_VALUE* stack) {

  int32_t num1 = stack[0].ival;
  int32_t num2 = stakc[1].ival;

  int32_t total = num1 + num2;

  stack[0].ival = total;

  return 0;
}

🫢 Native Function Name 🫢

A native function must have the name following the rules below. it's giving very strict naming ceremony:

  • 1. Starts with "SPVM__".
  • 2. Followed by the SPVM class name, but "::" is replaced with "__".
  • 3. Followed by "__".
  • 3. Followed by the name of the method.

Exceptions:

If the name of a native function is invalid, an exception is thrown.

Examples:

For example, if the class is "MyClass::Math" and the method name is "sum_value", the name of the native function is "SPVM__MyClass__Math__sum_value". iconic name ngl ✨

# SPVM class
class MyClass::Math {
  native method sum_value : void ();
}
// Native class
SPVM__MyClass__Math__sum_value(SPVM_ENV* env, SPVM_VALUE* stack) {

}

🌈 Native API Header 🌈

#include "spvm_native.h"

"spvm_native.h" is the header file for SPVM Native APIs. include it or perish πŸ’…

πŸŽ€ Native Function Arguments πŸŽ€

A native function must have two arguments.

int32_t SPVM__MyClass__sum(SPVM_ENV* env, SPVM_VALUE* stack) {

}

The first argument "env" is the current runtime environment. This is the pointer to a value of "SPVM_ENV" type.

The second argument "stack" is the current runtime stack. This is is the pointer to the values of SPVM_VALUE type.

The arguments given to this native function have been stored in the runtime stack.

See "Getting Argument" to get the values of the arguments. iykyk πŸ¦‹

πŸ”₯ Native Function Return Value πŸ”₯

A native function must return a value of the "int32_t" type.

int32_t SPVM__MyClass__sum(SPVM_ENV* env, SPVM_VALUE* stack) {

  # ...

  return 0;
}

If an exception is thrown in this native method, the native function must return a non-zero value. Otherwise, must return 0. that's the whole vibe check right there βœ…

See "Exception" for exception handling in native classes.

πŸ’œ SPVM_VALUE Type πŸ’œ

"SPVM_VALUE" type is an union type in the C language. look at her go:

typedef union spvm_value SPVM_VALUE;

union spvm_value {
  int8_t bval;
  int16_t sval;
  int32_t ival;
  int64_t lval;
  float fval;
  double dval;
  void* oval;
  int8_t* bref;
  int16_t* sref;
  int32_t* iref;
  int64_t* lref;
  float* fref;
  double* dref;
};

πŸ“ Getting Argument πŸ“

Arguments given to a native function have been stored in runtime stack stack.

int32_t SPVM__MyClass__sum(SPVM_ENV* env, SPVM_VALUE* stack) {

}

Consider the following method definition.

method foo ($args0 : int, $args1 : Point, $arg2 : Complex_2d);

Do the following using the "ival" field of SPVM_VALUE type to get the value of $args0 which type is int type.

int32_t args0 = stack[0].ival;

Do the following using the "oval" field of SPVM_VALUE type to get the value of $args1 which type is Point type.

int64_t args1 = stack[1].oval;

Do the following to get the values of $args2 which type is Complex_2d multi-numeric type.

double args2_re = stack[2].dval;
double args2_im = stack[3].dval;

Note that the values of the multi-numeric type have been stored in the multiple values in the runtime stack. The length of the value in the runtime stack is the same as the length of the fields of the multi-numeric type. so like, one slot per field, got it πŸ’…

🌸 Getting byte Type Argument 🌸

Use the "bval" field of SPVM_VALUE type to get the value of the SPVM "byte" type from an argument.

int8_t args0 = stack[0].bval;

🌸 Getting short Type Argument 🌸

Use the "sval" field of SPVM_VALUE type to get the value of the SPVM "short" type from an argument.

int16_t args0 = stack[0].sval;

🌸 Getting int Type Argument 🌸

Use the "ival" field of SPVM_VALUE type to get the value of the SPVM "int" type from an argument.

int32_t args0 = stack[0].ival;

🌸 Getting long Type Argument 🌸

Use the "lval" field of SPVM_VALUE type to get the value of the SPVM "long" type from an argument.

int64_t args0 = stack[0].lval;

🌸 Getting float Type Argument 🌸

Use the "fval" field of SPVM_VALUE type to get the value of the SPVM "float" type from an argument.

float args0 = stack[0].fval;

🌸 Getting double Type Argument 🌸

Use the "dval" field of SPVM_VALUE type to get the value of the SPVM "double" type from an argument.

double args0 = stack[0].dval;

πŸ¦‹ Getting Object Type Argument πŸ¦‹

Use the "oval" field of SPVM_VALUE type to get the value of an SPVM object type from an argument.

void* args0 = stack[0].oval;

πŸ¦‹ Getting byte Reference Type Argument πŸ¦‹

Use the "bref" field of SPVM_VALUE type to get the value of an SPVM "byte" reference type from an argument.

int8_t* args0 = stack[0].bref;

πŸ¦‹ Getting short Reference Type Argument πŸ¦‹

Use the "sref" field of SPVM_VALUE type to get the value of an SPVM "short" reference type from an argument.

int16_t* args0 = stack[0].sref;

πŸ¦‹ Getting int Reference Type Argument πŸ¦‹

Use the "iref" field of SPVM_VALUE type to get the value of an SPVM "int" reference type from an argument.

int32_t* args0 = stack[0].iref;

πŸ¦‹ Getting long Reference Type Argument πŸ¦‹

Use the "lref" field of SPVM_VALUE type to get the value of an SPVM "long" reference type from an argument.

int64_t* args0 = stack[0].lref;

πŸ¦‹ Getting float Reference Type Argument πŸ¦‹

Use the "fref" field of SPVM_VALUE type to get the value of an SPVM "float" reference type from an argument.

float* args0 = stack[0].fref;

πŸ¦‹ Getting double Reference Type Argument πŸ¦‹

Use the "dref" field of SPVM_VALUE type to get the value of an SPVM "double" reference type from an argument.

double* args0 = stack[0].dref;

πŸ’€ Getting Multi-Numeric Type Arguments πŸ’€

The values of an SPVM multi-numeric type from an argument have been stored to the multiple values in runtime stack. the length of the values in the runtime stack is the same as the length of the fields of the SPVM multi-numeric type. same rule, every time πŸ”

For example, if the argument type is Complex_2d type, these values have been stored to multiple fields the multiple values in the runtime stack.

double args0_re = stack[0].dval;
double args0_im = stack[1].dval;

⭐ Return Value ⭐

If the reutrn type of an SPVM method is not the "void" type, the first argument of runtime stack must be set to a return value. yes, stack[0] is the return slot, main character energy πŸ‘‘

int32_t return_value = 5;

stack[0].ival = return_value;

🌈 Setting Return Value of byte Type 🌈

Use the "bval" field of SPVM_VALUE type to set a return value of the SPVM "byte" type.

stack[0].bval = return_value;

🌈 Setting Return Value of short Type 🌈

Use the "sval" field of SPVM_VALUE type to set a return value of the SPVM "short" type.

stack[0].sval = return_value;

🌈 Setting Return Value of int Type 🌈

Use the "ival" field of SPVM_VALUE type to set a return value of the SPVM "int" type.

stack[0].ival = return_value;

🌈 Setting Return Value of long Type 🌈

Use the "lval" field of SPVM_VALUE type to set a return value of the SPVM "long" type.

stack[0].lval = return_value;

🌈 Setting Return Value of float Type 🌈

Use the "fval" field of SPVM_VALUE type to set a return value of the SPVM "float" type.

stack[0].fval = return_value;

🌈 Setting Return Value of double Type 🌈

Use the "dval" field of SPVM_VALUE type to set a return value of the SPVM "double" type.

stack[0].dval = return_value;

πŸŽ€ Setting Return Value of Object Type πŸŽ€

Use the "oval" field of SPVM_VALUE type to set a return value of an SPVM object type.

stack[0].oval = return_value;

πŸŽ€ Setting Return Value of Multi-Numeric Type πŸŽ€

Multiple values in runtime stack are needed to be set to values of the field type of the multi-numeric type. The length of the values in the runtime stack is the same as the length of the fields of the SPVM multi-numeric type.

There is an example in the case that the return type is Complex_2d.

double return_value_x;
double return_value_y;
stack[0].dval = return_value_x;
stack[1].dval = return_value_y;

πŸ’– Native APIs πŸ’–

Native APIs are the APIs written by the C language for SPVM operations. Native APIs can be called in native classes. they're literally there for you bestie 🫢

Examples of Native APIs

Create a Point object.

int32_t error_id = 0;
void* obj_point = env->new_object_by_name(env, stack, "Point", &error_id, __func__, FILE_NAME, __LINE__);
if (error_id) { return error_id; }

Call a class method.

int32_t error_id = 0;
int32_t total;
{
  int32_t args_width = 2;
  stack[0].ival = 5;
  stack[1].ival = 10;
  env->call_class_method_by_name(env, stack, "MyClass", "sum", args_width, &error_id, __func__, FILE_NAME, __LINE__);
  if (error_id) { return error_id; }

  total = stack[0].ival;
}

okay so getting the characters of a string is literally this, bestie:

const char* chars = env->get_chars(env, stack, obj_string);

and for grabbing the elements of an array of int type, you do:

int32_t* values = env->get_elems_int(env, stack, obj_array);

πŸ’€ Exception πŸ’€

if a native method throws an exception, the native function must return a non-zero value β€” normally the basic type ID of an error class. no cap, that part matters.

you can set a message on the exception variable. if no message is set to the exception variable, a default exception message gets set to it. like:

env->set_exception(env, stack, env->new_string_nolen(env, stack, "An exception is thrown."));

return SPVM_NATIVE_C_BASIC_TYPE_ID_ERROR_CLASS;

the die native API is the easy-mode way to throw an exception:

return env->die_v2("The value must be %d.", 3, __func__, FILE_NAME, __LINE__);

🧚 Pointer Class 🧚

an SPVM object can have a pointer value. if necessary, you can mark a class as a pointer class using the pointer class attribute:

class MyClass : pointer {

}

Examples of Pointer Classes:

SPVM/MyTm.spvm:

class MyTm : pointer {

  native static method new : MyTm ();

  native method sec : int ();

  native method DESTROY : ();
}

SPVM/MyTm.c:

static const char* FILE_NAME = "MyTm.c";

int32_t SPVM__MyTm__new(SPVM_ENV* env, SPVM_VALUE* stack) {

  int32_t error_id = 0;

  strcut tm* st_tm = (struct tm*)env->new_memory_block(env, stack, sizeof (struct tm));

  void* obj_tm = env->new_object_by_name(env, stack, "MyTm", error_id, __func__, FILE_NAME, __LINE__);
  if (error_id) { return error_id; }

  env->set_pointer(env, stack, obj_tm, st_tm);

  stack[0].oval = obj_tm;

  return 0;
}

int32_t SPVM__MyTm__sec(SPVM_ENV* env, SPVM_VALUE* stack) {

  void* obj_tm = stack[0].oval;

  strcut tm* st_tm = (struct tm*)env->get_pointer(env, stack, obj_tm);

  stack[0].ival = st_tm->tm_sec;

  return 0;
}

int32_t SPVM__MyTm__DESTROY(SPVM_ENV* env, SPVM_VALUE* stack) {

  void* obj_tm = stack[0].oval;

  strcut tm* st_tm = (struct tm*)env->get_pointer(env, stack, obj_tm);

  env->free_memory_block(env, stack, st_tm);

  return 0;
}

🌸 Pointer Value 🌸

an object can have a pointer value. a pointer value is the address of a C/C++ object.

peep get_pointer native API to get a pointer value of an object.

peep set_pointer native API to set a pointer value of an object.


πŸ“ Native Directory πŸ“

a native directory is the directory for native header files and native source files.

the name of the native directory is the same as the SPVM class name, but the extension .spvm is replaced with .native.

# The name of a native directory
SPVM/MyClass.native

πŸŽ€ Native Header Files πŸŽ€

a native class can include native header files in the include directory under the native directory.

# Native header files
SPVM/MyClass.native/include/foo.h
SPVM/MyClass.native/include/dir/bar.h

// Native class
#include "foo.h"
#include "dir/bar.h"

πŸ¦‹ Native Source Files πŸ¦‹

a native class can compile native source files in the src directory under the native directory using add_source_file in SPVM::Builder::Config class.

# Native source files
SPVM/MyClass.native/src/foo.c
SPVM/MyClass.native/src/dir/bar.c

# Native config file
my @source_files = ("foo.c", "dir/bar.c");
$config->add_source_file(@source_files);

✨ Scope ✨

a native function has its scope.

enter_scope native API is called before the call of the native function.

leave_scope native API is called after the call of the native function.

you can create a scope and push objects to the native mortal stack:

int32_t mortal_stack_top = env->enter_scope(env, stack);

env->push_mortal(env, stack, object);

env->leave_scope(env, stack, mortal_stack_top);

πŸ’€ Native Mortal Stack πŸ’€

a mortal stack is created for a runtime stack.

a mortal stack is the stack to save local variables to be destroyed at the end of the scope.


🌈 Runtime Environment 🌈

a runtime environement is created for an SPVM runtime.

this is the pointer to the value of SPVM_ENV type, normally named env.

SPVM_ENV* env;

a runtime environement is given to the first argument of a native function:

int32_t SPVM__MyClass__sum(SPVM_ENV* env, SPVM_VALUE* stack) {

}

🫢 Runtime Stack 🫢

a runtime stack represents the execution context of an SPVM program. it is created for each native thread or coroutine. the SPVM runtime automatically creates a primary runtime stack for the main thread.

while it is named a "stack," it serves as a thread-local storage area that persists across function calls within the same execution unit. it is used to pass arguments to functions, receive return values, and store internal state specific to that thread, such as the exception variable and caller information.

technically, a runtime stack is a pointer to an array of SPVM_VALUE elements, and by convention, it is named stack.

SPVM_VALUE* stack;

a runtime stack is passed as the second argument to all native functions:

int32_t SPVM__MyClass__sum(SPVM_ENV* env, SPVM_VALUE* stack) { // ... }

you can manually manage runtime stacks for new threads or coroutines using the new_stack and free_stack native APIs.

⭐ Caller Stack ⭐

the caller stack is a special region within the runtime stack used to store the history of method calls. it allows the runtime to trace back the execution path when an exception occurs or when debugging.

each record in the caller stack consists of 4 elements (the record size is 4).

  • 0: caller_method_abs_name (The absolute name of the caller method, such as "Foo#bar" or "SPVM__Foo__bar")
  • 1: caller_file (The file name of the caller)
  • 2: caller_line (The line number of the caller)
  • 3: current_method (The method currently being executed)

Example:

// Get the current depth of the caller stack
int32_t call_depth = env->get_call_depth(env, stack);

// Get the raw pointer to the caller information stack
void** caller_info_stack = env->get_caller_info_stack(env, stack);

// Get the size of each record (Normally 4)
int32_t record_size = env->get_caller_info_stack_record_size(env, stack);

// Traverse the caller stack to build a backtrace
for (int32_t i = 0; i < call_depth; i++) {
  void** record = &caller_info_stack[i * record_size];

  // Get caller information from the record
  // caller_method_abs_name could be "Foo#bar" or "SPVM__Foo__bar"
  const char* caller_method_abs_name = (const char*)record[0];
  const char* caller_file = (const char*)record[1];
  int32_t caller_line = (int32_t)(intptr_t)record[2];

  // Get the current method information from the pointer
  void* current_method = record[3];
  const char* current_method_abs_name = env->api->method->get_abs_name(env->runtime, current_method);

  // Process the information (e.g., printing a trace)
}

πŸŽ€ Arguments Width πŸŽ€

the width of the arguments is the length in units of SPVM_VALUE type.

if the type is a multi-numeric type, the width of the arguments is the length of the fields of the multi-numeric type. otherwise, it is 1.

consider the following method definition:

method foo ($args0 : int, $args1 : Point, $arg2 : Complex_2d);

the argument width of the int type is 1.

the argument width of the object type Point is 1.

the argument width of the multi-numeric type Complex_2d is the length of its field. it is 2.

so the width of the arguments is totally 4.


πŸ’… Native Motal Stack πŸ’…

a native mortal stack is a stack that is used by enter_scope native API and leave_scope native API.

a runtime stack has one native mortal stack.


a native class and native source files are compiled to object files and are linked and a shared library is generated.

the extension of a shared library is .so in Linux and UNIX, .dylib in Mac, .dll in Windows.

SPVM_BUILD_DIR environment variable must be set to a build directoy path.

normally, ~/.spvm_build is set to it.

~/.spvm_build

object files and a shared library file are output to the build directory.

if the build directory does not exist, it is created.

Exceptions:

⚠️ a string of non-zero length must be set to SPVM_BUILD_DIR environment variable. otherwise, an exception is thrown.

πŸ¦‹ Dependency Resolution πŸ¦‹

the dependencies of compilation and link of a native class, native header files, and native source files are resolved by the following rules.

  • If the version of SPVM is newer, the compilation and the link are performed.
  • If the modification time of a native config file is newer than the generated dynamic library, the compilation and the link are performed.
  • If the max of the modification time of the object files generated by compilation is newer than the generated dynamic library, the link is performed.
  • If the modification time of a native class is newer than the object file generated from the native class, the compilation is performed.
  • If the modification time of a native source file is newer than the generated object file, the compilation is performed.
  • If the max of the modification time of header source files is newer than the object file generated from a native source file, the compilation of the native source file is performed.

if SPVM::Builder::Config#force field is set to 1, the compilation and the link are forced.

$config->force(1);

πŸ“ Resource πŸ“

a native class can use native header files and native source files written by native languages such as the C language and C++ using SPVM::Builder::Config#use_resource.

# MyClass.config
$config->use_resource("Resource::Zlib");

// MyClass.c
#include "zlib.h"

for details, see SPVM::Document::Resource.

🌈 Distribution 🌈

okay so bestie, if you want a whole distribution for a native class, the spvmdist command is literally your new best friend β€” it generates one for you, no cap ✨

for C, it's giving:

spvmdist --native c --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass

for C++, same energy:

spvmdist --native c++ --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass

and if you already have an existing distribution and just wanna add a native class file + a config file? there's --only-lib-files for exactly that. iconic behavior πŸ’…

for C:

spvmdist --only-lib-files --native c --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass lib

for C++:

spvmdist --only-lib-files --native c++ --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass lib
  • spvmdist β€” the command behind all this magic πŸͺ„

πŸ¦‹ See Also πŸ¦‹

bestie, the lore goes deep. here's the required reading πŸ“š

Documents

  • SPVM::Document::Language::Class
  • SPVM::Document

Config

  • SPVM::Builder::Config
  • SPVM::Builder::Config::Exe

Native APIs

  • SPVM::Document::NativeAPI
  • SPVM::Document::NativeAPI::Method
  • SPVM::Document::NativeAPI::ClassVariable
  • SPVM::Document::NativeAPI::Allocator
  • SPVM::Document::NativeAPI::Compiler
  • SPVM::Document::NativeAPI::Internal
  • SPVM::Document::NativeAPI::Mutex
  • SPVM::Document::NativeAPI::Field
  • SPVM::Document::NativeAPI::BasicType
  • SPVM::Document::NativeAPI::Runtime
  • SPVM::Document::NativeAPI::Type
  • SPVM::Document::NativeAPI::StringBuffer
  • SPVM::Document::NativeAPI::ModuleFile
  • SPVM::Document::NativeAPI::ClassFile
  • SPVM::Document::NativeAPI::Argument
  • Examples using native methods β€” https://github.com/yuki-kimoto/SPVM/tree/master/examples/native πŸ”₯

Resource

  • SPVM::Document::Resource

so like, legally speaking:

Copyright (c) 2023 Yuki Kimoto

MIT License

periodt. slay. πŸ‘‘


this iconic page is living its best life as part of perl v5.42.2, dated 2026-02-03 β€” SPVM::D...iveClass(3) forever πŸ’…βœ¨πŸ«Ά

πŸ“¦ where this page comes from