SPVM::Document::NativeClass - Native Classes
A native class is the class implemented by a native language such as the C language and C++.
A native method is defined by the native method attribute in an SPVM class file. It ends with a semicolon. A native method does not have its block.
# SPVM/MyClass.spvm class MyClass { native static method sum : int ($num1 : int, $num2 : int); }
A native config file is needed for a native class. The name of the config file is the same as the SPVM class name, but the extension .spvm is replaced with .config.
.spvm
.config
# The name of a native config file SPVM/MyClass.config
A native config file is writen by Perl. It must end with a Builder::Config object.
A config file is executed by Perl's do function. The returned Builder::Config object is 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(file => __FILE__); $config;
C++:
my $config = SPVM::Builder::Config->new_cpp(file => __FILE__); $config;
The name of the native class file is the same as the SPVM class name, but the extension .spvm is replaced with . and the extension of the native class.
.
SPVM/MyClass.c
The file extension of a native class is defined by the ext field in the Builder::Config class in a config file.
Builder::Config
$config->ext('c'); $config->ext('cpp'); $config->ext('cc'); $config->ext('cu');
If the ext is defined, but its corresponding config file does not exist, an exception is thrown.
A native function is a function defined in a native class.
// 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; }
A native function must have the name following the rules below.
1. Starts with SPVM__.
SPVM__
2. Followed by the SPVM class name, but :: is replaced with __.
::
__
3. Followed by __.
3. Followed by the name of the method.
If the name of a native function is invalid, an exception is thrown.
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.
MyClass::Math
sum_value
SPVM__MyClass__Math__sum_value
# SPVM class class MyClass::Math { native method sum_value : void (); } // Native class SPVM__MyClass__Math__sum_value(SPVM_ENV* env, SPVM_VALUE* stack) { }
#include "spvm_native.h"
spvm_native.h is the header file for SPVM Native APIs.
spvm_native.h
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 the SPVM_ENV type.
env
SPVM_ENV
The second argument stack is the current runtime stack. This is is the pointer to the values of SPVM_VALUE type.
stack
The arguments given to this native function have been stored in the runtime stack.
See "Getting Argument" to get the values of the arguments.
A native function must return a value of the int32_t type.
int32_t
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.
See "Exception" for exception handling in native classes.
SPVM_VALUE type is an union type in the C language.
SPVM_VALUE
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; };
Arguments given to a native function have been stored in the runtime stack stack.
Consider the following method definition.
method foo ($args0 : int, $args1 : Point, $arg2 : Complex_2d);
Do the following using the ival field of the SPVM_VALUE type to get the value of $args0 which type is the int type.
ival
int32_t args0 = stack[0].ival;
Do the following using the oval field of the SPVM_VALUE type to get the value of $args1 which type is the Point type.
oval
int64_t args1 = stack[1].oval;
Do the following to get the values of $args2 which type is the 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.
Use the bval field of the SPVM_VALUE type to get the value of the SPVM byte type from an argument.
bval
byte
int8_t args0 = stack[0].bval;
Use the sval field of the SPVM_VALUE type to get the value of the SPVM short type from an argument.
sval
short
int16_t args0 = stack[0].sval;
Use the ival field of the SPVM_VALUE type to get the value of the SPVM int type from an argument.
int
Use the lval field of the SPVM_VALUE type to get the value of the SPVM long type from an argument.
lval
long
int64_t args0 = stack[0].lval;
Use the fval field of the SPVM_VALUE type to get the value of the SPVM float type from an argument.
fval
float
float args0 = stack[0].fval;
Use the dval field of the SPVM_VALUE type to get the value of the SPVM double type from an argument.
dval
double
double args0 = stack[0].dval;
Use the oval field of the SPVM_VALUE type to get the value of an SPVM object type from an argument.
void* args0 = stack[0].oval;
Use the bref field of the SPVM_VALUE type to get the value of an SPVM byte reference type from an argument.
bref
int8_t* args0 = stack[0].bref;
Use the sref field of the SPVM_VALUE type to get the value of an SPVM short reference type from an argument.
sref
int16_t* args0 = stack[0].sref;
Use the iref field of the SPVM_VALUE type to get the value of an SPVM int reference type from an argument.
iref
int32_t* args0 = stack[0].iref;
Use the lref field of the SPVM_VALUE type to get the value of an SPVM long reference type from an argument.
lref
int64_t* args0 = stack[0].lref;
Use the fref field of the SPVM_VALUE type to get the value of an SPVM float reference type from an argument.
fref
float* args0 = stack[0].fref;
Use the dref field of the SPVM_VALUE type to get the value of an SPVM double reference type from an argument.
dref
double* args0 = stack[0].dref;
The values of an SPVM multi-numeric type from an argument have been stored to the multiple values in the 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.
For example, if the argument type is the 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;
If the reutrn type of an SPVM method is not the void type, the first argument of the runtime stack must be set to a return value.
void
int32_t return_value = 5; stack[0].ival = return_value;
Use the bval field of the SPVM_VALUE type to set a return value of the SPVM byte type.
stack[0].bval = return_value;
Use the sval field of the SPVM_VALUE type to set a return value of the SPVM short type.
stack[0].sval = return_value;
Use the ival field of the SPVM_VALUE type to set a return value of the SPVM int type.
stack[0].ival = return_value;
Use the lval field of the SPVM_VALUE type to set a return value of the SPVM long type.
stack[0].lval = return_value;
Use the fval field of the SPVM_VALUE type to set a return value of the SPVM float type.
stack[0].fval = return_value;
Use the dval field of the SPVM_VALUE type to set a return value of the SPVM double type.
stack[0].dval = return_value;
Use the oval field of the SPVM_VALUE type to set a return value of an SPVM object type.
stack[0].oval = return_value;
Multiple values in the 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 the Complex_2d.
double return_value_x; double return_value_y; stack[0].dval = return_value_x; stack[1].dval = return_value_y;
Native APIs are the APIs written by the C language for SPVM operations. Native APIs can be called in native classes.
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; }
Get the characters of a string.
const char* chars = env->get_chars(env, stack, obj_string);
Get the elements of an array of the int type.
int32_t* values = env->get_elems_int(env, stack, obj_array);
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.
A message can be set to the exception variable. If no message is set to the exception variable, a default exception message is set to it.
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 can be used to throw an exception easily.
return env->die("The value must be %d.", 3, __func__, FILE_NAME, __LINE__);
An SPVM object can store a pointer to a native data. The class that have a pointer to a native data is called the pointer class.
The set_pointer native API sets a pointer to a native data.
The get_pointer native API gets a pointer to a native data.
The pointer class attribute indicates this class is a pointer class.
The following class is an example of a pointer class.
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; }
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.
.native
# The name of a native directory SPVM/MyClass.native
A native class can include native header files in the include directory under the native directory.
include
# Native header files SPVM/MyClass.native/include/foo.h /dir/bar.h // Native class #include "foo.h" #include "dir/bar.h"
A native class can compile native source files in the src directory under the native directory using the add_source_file in the SPVM::Builder::Config class.
src
SPVM::Builder::Config
# Native source files SPVM/MyClass.native/src/foo.c /dir/bar.c # Native config file my @source_files = ("foo.c", "dir/bar.c"); $config->add_source_file(@source_files);
A native function has its scope.
The enter_scope native API is called before the call of the native function.
The leave_scope native API is called after the call of the native function.
You can create a scope and push objects to the 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);
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.
A runtime environement is created for an SPVM runtime.
This is the pointer to the value of the SPVM_ENV type, normally named env.
SPVM_ENV* env;
A runtime environement is given to the first argument of a native function.
A runtime stack is created for a native thread. An SPVM runtime creates a runtime stack for the main thread.
A runtime stack is used to get values of arguments and return a value, and it also stored its own data such as the exception variable.
This is the pointer to the values of the SPVM_VALUE type, normally named stack.
SPVM_VALUE* stack;
A runtime stack is given to the second argument of a native function.
A runtime stack can be created and freed using the new_stack native API and the free_stack native API.
The width of the arguments is the length in units of the 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.
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.
A native mortal stack is a stack that is used by the enter_scope native API and the 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/UNIX, .dylib in Mac, .dll in Windows.
.so
.dylib
.dll
The 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.
A string of non-zero length must be set to the SPVM_BUILD_DIR environment variable. Otherwise, an exception is thrown.
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 compiliation 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);
A native class can use native header files and native source files writen by native langauges 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.
A distribution for a native class can be generated by the spvmdist command.
# C spvmdist --native c --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass # C++ spvmdist --native c++ --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass
A native class file and a config file only can be added to an existing distribution.
# C spvmdist --only-lib-files --native c --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass lib # C++ spvmdist --only-lib-files --native c++ --user-name="Yuki Kimoto" --user-email="kimoto.yuki@gmail.com" MyNativeClass lib
spvmdist
SPVM::Document::Language::Class
SPVM::Builder::Config::Exe
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
SPVM::Document::Resource
Copyright (c) 2023 Yuki Kimoto
MIT License
To install SPVM, copy and paste the appropriate command in to your terminal.
cpanm
cpanm SPVM
CPAN shell
perl -MCPAN -e shell install SPVM
For more information on module installation, please visit the detailed CPAN module installation guide.