Skip to content

Native Interoperability

Xanderplayz16 edited this page Jun 30, 2022 · 7 revisions

Because cish is an interpreted language, it is impossible to compile/work with statically linked libraries. Therefore all c libraries created for superforth, like C#, must be a DLL.

Writing a DLL for cish

Quick Setup

  1. If you haven't already, please download the external native-interoperation SDK. Once you have the folder on your computer, simply run make on the MakeFil in the folder. It should produce a superforth.o file immediately thereafter.

  2. Copy these two files, superforth.h and superforth.o to the folder you intend to develop the DLL in. You may freely reference superforth.h, but make sure that you link your program with superforth.o after compilation.

Some useful functionalities

Adding a foreign function

To add a foreign function to cish's FFI, simply call the following function defined in superforth.h:

int ffi_include_func(ffi_t* ffi_table, foreign_func func)

Where foreign_func is a function pointer defined like the following:

typedef int (*foreign_func)(machine_t* machine, machine_reg_t* input, machine_reg_t* output);

You can retrieve a cish vm's(machine_t) foreign function table via the ffi_t ffi_table property.

Making a cish Garbage-Collected heap allocation

You can interact with cish by providing heap allocations that can be interpreted as structures, arrays, or potentially other allocation types. In addition these heap allocations are garbage-collected as well, so do not use them to store static memory. Read this article about record layouts, and how to interact with their runtime representations.

The heap allocation function itself is defined like the following:

heap_alloc_t* machine_alloc(machine_t* machine, uint16_t req_size, gc_trace_mode_t trace_mode);
  • uint16_t req_size is the length(the amount of elements/properties).
  • gc_trace_mode_t trace_mode represents the trace coniguration. Read more here about heap allocations during runtime.
  • The return value is a memory safe, garbage collected SuperForth heap allocation.

Some Starter Code

Defining a Foreign Function

Fundamentally, interoperation with C is done through foreign functions. They are function pointer types defined as:

typedef int (*foreign_func)(machine_t* machine, machine_reg_t* input, machine_reg_t* output);

Put in non-C terms, all foreign functions take in the current instance's virtual machine,an input and an output SuperForth register, and return an integer status. They are called from within cish like this.

Take the dynamic-library-import foreign function for example:

static int std_import(machine_t* machine, machine_reg_t* in, machine_reg_t* out) {
	char* import_name = read_str_from_heap_alloc(in->heap_alloc);
	PANIC_ON_FAIL(import_name, machine, ERROR_MEMORY);
	out->long_int = machine->ffi_table.func_count;
	if (!dynamic_library_load(machine->dynamic_library_table, machine, import_name)) {
		out->long_int = -1;
	}
	return 1;
}

It takes in the vm-instance, and an input and output register. All output must be written to the output register. Although there aren't any guards against writing to the input registers location, it is highly unrecomended to do so.

Keep in mind that while each foreign function may only interact with a few parameters in a somewhat limited manner, you can also interact with static-memory/other more-complex functions to provide more powerful, feature-rich functionality. Read more about SuperForth's Foreign Function Interface's Design Philosophy here.

Defining cish's Entry Point

When cish imports a native dynamically linked library, the first think it'll do is search for it's SUPERFORTH_ENTRY function, and invoke it immediately. It is recomended that your initialization code be performed in this entry point. Initialization tasks include, but are not limited to: Adding foreign functions to a vm's foreign function table. Include superforth.h if you haven't already. Using the SUPERFORTH_ENTRY macro, define superforth's entry point as such:

SUPERFORTH_ENTRY({
	//your entry point code here
	return 1;
});

The SUPERFORTH_ENTRY macro expands into a function with the following parameters and return value, each provided by the calling cish instance:

int superforth_entry(machine_t* machine);

Note: The parameter machine_t* machine is a pointer to the calling cish instances virtual machine. Use this oppurtunity to add foreign functions to the vm's foreign function table, and perform other relevant initialization.

Note: that the cish entry function's return value: this signals cish if the initialization process has suceded or not. In addition the vm instance has an error_t last_err property defined; set this flag to the one of the following errors to provide a more detailed reason for faliure.

Clone this wiki locally