SPVMネイティブAPI仕様がこのドキュメントには記述されています。SPVMは、1.0のリリースに向けて、ベータテスト中です。SPVMネイティブAPI仕様は、警告なく変更されることがあります。
最終更新日 2019年6月5日
SPVMネイティブAPIとは、SPVMのネイティブサブルーチンの中で呼び出すことのできるC言語のAPIのことです。SPVMのネイティブサブルーチンの仕様についてもここに記述されます。
作成したネイティブサブルーチンは、SPVMから呼び出すことができます。
SPVMネイティブサブルーチンの宣言は、サブルーチンのディスクリプタ「native」を使うことによって行います。サブルーチンのブロックを書かずに、セミコロンで終わります。以下は、Foo::Barというモジュールの中で宣言する場合の例です。
# Foo/Bar.spvm package Foo::Bar { native sub sum : int ($num1 : int, $num2 : int); }
ネイティブサブルーチンの定義は、ネイティブソースファイルの中で行います。ネイティブソースファイルは、SPVMのモジュールの拡張子を、対象の言語の拡張子に変えたものになります。たとえばC言語であれば「.c」に変えたものです。C++であれば、「.cpp」に変えたものです。これは、ネイティブサブルーチンの設定において、変更することができます。
# モジュール名がFoo::Barの場合のネイティブソースファイル Foo/Bar.c
ネイティブソースファイルを作成するときは、合わせてネイティブ設定ファイルを作成する必要があります。ネイティブ設定ファイルは、SPVMのモジュールの拡張子を「.config」に変えたものになります。ネイティブ設定ファイルが存在しない場合は、例外が発生します。
# モジュール名がFoo::Barの場合のネイティブ設定ファイル Foo/Bar.config
ネイティブ設定ファイルはPerlのソースコードです。SPVM::Builder::Configオブジェクトをソースコードの最後に記述して、返却しなければなりません。そうでない場合は、例外が発生します。
SPVMと同じC99で、C言語を書きたい場合は、以下のように記述します。
use strict; use warnings; use SPVM::Builder::Config; my $bconf = SPVM::Builder::Config->new_c99; $bconf;
C99以外でコンパイルしたい場合や、ライブラリを追加したい場合はSPVM::Builder::Configのドキュメントのサンプルを見てください。
ネイティブサブルーチンの定義は、ネイティブソースファイルの中で行います。この場合の例は「Foo/Bar.c」です。
#include "spvm_native.h" int32_t SPNATIVE__Foo__Bar__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 SPVM_SUCCESS; }
ネイティブソースファイルの先頭で「spvm_native.h」をインクルードしてください。このヘッダファイルには、SPVMネイティブAPIと必要な構造体が定義されています。
ネイティブサブルーチンの定義は、簡単なC言語の関数です。
ネイティブサブルーチンのCにおける関数名は「SPNATIVE__」で始まり、モジュール名の「::」を「__」に変換したものが続き、「__」が続き、サブルーチン名で終わります。SPVMにおけるサブルーチン名との対応が正しくない場合は、コンパイルエラーが発生します。
引数は二つで、第一引数は、実行環境の情報を持った「SPVM_ENV* env」で、第二引数は引数と戻り値に利用される「SPVM_VALUE* stack」です。
戻り値の型は「int32_t」です。サブルーチンが例外を発生させる場合は「SPVM_EXCEPTION」、それ以外の場合は「SPVM_SUCCESS」を返却します。
上記のサンプルでは、SPVMのint型の引数を二つ受け取り、合計を計算し、戻り値を返すということを行っています。
ネイティブサブルーチンは、Perlをコンパイルしたコンパイラで、コンパイルされて、OSに応じた、動的に読み可能な共有ライブラリにコンパイルされます。Unix/Linuxにおいては、共有ライブラリ(.so)、Windowsにおいてはダイナミックリンクライブラリ(.dll)などです。
動的に読み可能な共有ライブラリへのコンパイルは、SPVMのコンパイル時に行われます。コンパイルのときに、ビルドディレクトリが存在していなければなりません。ビルドディレクトリが存在しない場合は、例外が発生します。
ビルドディレクトリのデフォルトは、実行したPerlスクリプトが存在するディレクトリの「spvm_build」ディレクトリで、環境変数「SPVM_BUILD_DIR」で変更することができます。
PerlからSPVMネイティブサブルーチンを利用したい場合は実行したPerlスクリプトが存在するディレクトリに「spvm_build」ディレクトリ作成します。
script.pl spvm_build/
中間的に生成されるオブジェクトファイルは、ビルドディレクトリの下の「work/object」の下に生成されます。オブジェクトファイル名は、SPVMのモジュールの拡張子を「.o」に変えたものになります。
spvm_build/work/object/Foo/Bar.o
動的に読み可能な共有ライブラリは、ビルドディレクトリの下の「work/lib」の下に生成されます。動的に読み可能な共有ライブラリのファイル名は、SPVMのモジュールの拡張子を、OSに応じた、動的に読み可能な共有ライブラリの拡張子に変えたものになります。
# Unix/Linux spvm_build/work/object/Foo/Bar.so # Windows spvm_build/work/object/Foo/Bar.dll
スタックとは、ネイティブサブルーチンの定義における第二引数で渡される「SPVM_VALUE* stack」のことで、この中に引数が格納されています。
int32_t SPNATIVE__Foo__Bar__sum(SPVM_ENV* env, SPVM_VALUE* stack) { }
SPVM_VALUEは、SPVMの値を格納するためのC言語の共用体です。数値型、オブジェクト型、リファレンス型の値を保存できます。
「SPVM_VALUE* stack」の「SPVM_VALUE型の配列」の先頭のポインタです。SPVM側からコールされたネイティブサブルーチンの引数の値がこの配列に設定されます。
たとえば、int型の第一引数の値を取得する場合は、以下のように書きます。
int32_t args1 = stack[0].ival;
たとえば、long型の第二引数の値を取得する場合は、以下のように書きます。
int64_t args2 = stack[0].lval;
SPVMのbyte型の引数を取得するには、bvalフィールドにアクセスします。C言語のint8_t型に代入します。
int8_t args1 = stack[0].bval;
SPVMのshort型の引数を取得するには、svalフィールドにアクセスします。C言語のint16_t型に代入します。
int16_t args1 = stack[0].sval;
SPVMのint型の引数を取得するには、ivalフィールドにアクセスします。C言語のint32_t型に代入します。
int32_t args1 = stack[0].ival;
SPVMのlong型の引数を取得するには、lvalフィールドにアクセスします。C言語のint64_t型に代入します。
int64_t args1 = stack[0].lval;
SPVMのfloat型の引数を取得するには、fvalフィールドにアクセスします。C言語のfloat型に代入します。
float args1 = stack[0].fval;
SPVMのdouble型の引数を取得するには、dvalフィールドにアクセスします。C言語のdouble型に代入します。
double args1 = stack[0].dval;
SPVMのobject型の引数を取得するには、ovalフィールドにアクセスします。C言語のvoid*型に代入します。
void* args1 = stack[0].oval;
SPVMのbyteのリファレンス型の引数を取得するには、brefフィールドにアクセスします。C言語のint8_t*型に代入します。
int8_t* args1 = stack[0].bref;
SPVMのshortのリファレンス型の引数を取得するには、srefフィールドにアクセスします。C言語のint16_t*型に代入します。
int16_t* args1 = stack[0].sref;
SPVMのintのリファレンス型の引数を取得するには、irefフィールドにアクセスします。C言語のint32_t*型に代入します。
int32_t* args1 = stack[0].iref;
SPVMのlongのリファレンス型の引数を取得するには、lrefフィールドにアクセスします。C言語のint64_t*型に代入します。
int64_t* args1 = stack[0].lref;
SPVMのfloatのリファレンス型の引数を取得するには、frefフィールドにアクセスします。C言語のfloat*型に代入します。
float* args1 = stack[0].fref;
SPVMのdoubleのリファレンス型の引数を取得するには、drefフィールドにアクセスします。C言語のdouble*型に代入します。
double* args1 = stack[0].dref;
ネイティブサブルーチンにおいては、複数数値型の引数は、複数の引数に代入されます。
たとえば、SPVM::Complex_2d型の引数の場合は、二つの引数から取得します。フィールド名からはアクセスできないことに注意してください。
double args_re = stack[0].dval; double args_im = stack[1].dval;
ネイティブサブルーチンは、C言語のreturn文によって戻り値を返すのではなく、スタックを使って戻り値を設定します。
たとえば、int型の戻り値を返す場合は、以下のように書きます。
stack[0].ival = 3;
たとえば、long型の第二引数の値を取得する場合は、以下のように書きます。
stack[0].lval = 56;
SPVMのbyte型の戻り値を設定するには、bvalフィールドに代入します。C言語のint8_t型の値を代入します。
int8_t retval; stack[0].bval = retval;
SPVMのshort型の戻り値を設定するには、svalフィールドに代入します。C言語のint16_t型の値を代入します。
int16_t retval; stack[0].sval = retval;
SPVMのint型の戻り値を設定するには、ivalフィールドに代入します。C言語のint32_t型の値を代入します。
int32_t retval; stack[0].ival = retval;
SPVMのlong型の戻り値を設定するには、lvalフィールドに代入します。C言語のint64_t型の値を代入します。
int64_t retval; stack[0].lval = retval;
SPVMのfloat型の戻り値を設定するには、fvalフィールドに代入します。C言語のfloat型の値を代入します。
float retval; stack[0].fval = retval;
SPVMのdouble型の戻り値を設定するには、dvalフィールドに代入します。C言語のdouble型の値を代入します。
double retval; stack[0].dval = retval;
SPVMのobject型の戻り値を設定するには、ovalフィールドに代入します。C言語のvoid*型の値を代入します。
void* retval; stack[0].oval = retval;
ネイティブサブルーチンにおいては、複数数値型の戻り値は、複数の戻り値に代入します。
たとえば、SPVM::Complex_2d型の戻り値の場合は、二つの戻り値を設定します。
double retval_re; double retval_im; stack[0].dval = retval_re; stack[1].dval = retval_im;
ネイティブAPIは、名前に対応するインデックスを持っています。この番号は、ネイティブサブルーチンののバイナリ互換性を保つために、永続的に維持されます。新しいAPIの追加の場合は、末尾に追加されます。
0 runtime_package_vars_heap_offset 1 object_header_byte_size 2 weaken_backref_head 3 object_ref_count_offset 4 object_basic_type_id_offset 5 object_type_dimension_offset 6 object_runtime_type_offset 7 object_flag_offset 8 object_length_offset 9 byte_object_basic_type_id 10 short_object_basic_type_id 11 int_object_basic_type_id 12 long_object_basic_type_id 13 float_object_basic_type_id 14 double_object_basic_type_id 15 runtime 16 exception_object 17 native_mortal_stack 18 native_mortal_stack_top 19 native_mortal_stack_capacity 20 basic_type_id 21 field_id 22 field_offset 23 pkgvar_id 24 sub_id 25 method_sub_id 26 new_obj_raw 27 new_obj 28 new_barray_raw 29 new_barray 30 new_sarray_raw 31 new_sarray 32 new_iarray_raw 33 new_iarray 34 new_larray_raw 35 new_larray 36 new_farray_raw 37 new_farray 38 new_darray_raw 39 new_darray 40 new_oarray_raw 41 new_oarray 42 new_marray_raw 43 new_marray 44 new_varray_raw 45 new_varray 46 new_str_raw 47 new_str 48 new_str_len_raw 49 new_str_len 50 new_pointer_raw 51 new_pointer 52 concat_raw 53 concat 54 new_stack_trace_raw 55 new_stack_trace 56 len 57 belems 58 selems 59 ielems 60 lelems 61 felems 62 delems 63 oelems 64 set_oelems 65 bfield 66 sfield 67 ifield 68 lfield 69 ffield 70 dfield 71 ofield 72 set_bfield 73 set_sfield 74 set_ifield 75 set_lfield 76 set_ffield 77 set_dfield 78 set_ofield 79 bpkgvar 80 spkgvar 81 ipkgvar 82 lpkgvar 83 fpkgvar 84 dpkgvar 85 opkgvar 86 set_bpkgvar 87 set_spkgvar 88 set_ipkgvar 89 set_lpkgvar 90 set_fpkgvar 91 set_dpkgvar 92 set_opkgvar 93 pointer 94 set_pointer 95 call_sub 96 exception 97 set_exception 98 ref_count 99 inc_ref_count 100 dec_ref_count 101 enter_scope 102 push_mortal 103 leave_scope 104 remove_mortal 105 is_type 106 has_callback 107 object_basic_type_id 108 object_type_dimension 109 weaken 110 isweak 111 unweaken 112 alloc_memory_block_zero 113 free_memory_block 114 memory_blocks_count 115 type_name_raw 116 type_name 117 new_env 118 free_env