libinterp/mkfile
, module/runt.m
, emu/Linux/emu
and emu/Linux/emu-g
. And since each new module tries to be built into the same files in the same places, and the user may want to add several such modules, moreover, in an order not known in advance, the standard patch
command will not be able to make the necessary changes. She will add one or two modules, but with the following she will have a problem. the editable location in these files will start to differ too much from what she expected to see. my $MODNAME = 'CJSON';
-R
parameter to rollback the changes made.$INFERNO_ROOT
, rename it to $INFERNO_ROOT
, change the module name to “Example” in it, and launch it. Now the (non-existent for now) Module Example is connected to the kernel, it remains to create it and rebuild Inferno with it.module/example.m
Example: module { PATH: con "$Example"; };
libinterp/example.c
#include <lib9.h> #include <isa.h> #include <interp.h> #include "runt.h" #include "examplemod.h" void examplemodinit(void) { builtinmod("$Example", Examplemodtab, Examplemodlen); }
$ (cd libinterp/; mk nuke) $ rm Linux/386/bin/emu # work around "text file busy" error $ mk install
testexample.b
implement TestExample; include "sys.m"; include "draw.m"; include "example.m"; TestExample: module { init: fn(nil: ref Draw->Context, nil: list of string); }; init(nil: ref Draw->Context, nil: list of string) { sys := load Sys Sys->PATH; example := load Example Example->PATH; if(example == nil) sys->print("fail to load Example: %r\n"); else sys->print("Example module loaded\n"); }
$ emu ; limbo testexample.b ; testexample Example module loaded ;
module/example.m
file is analyzed, and the necessary C-shny structures describing this module are generated — in a separate libinterp/examplemod.h
— and its entire public interface (constants, adt-shki, functions) are added to the libinterp/runt.h
file libinterp/runt.h
containing information on all C-modules. These two .h files are already connected to our libinterp/example.c
.examplemodinit()
will be called once, which should initialize the global data of our module (if any) and connect it (by calling builtinmod(…)
) to the Inferno core. The call to builtinmod()
establishes a connection between our module and the pseudo-path to it $ Example specified in the PATH
constant used from Limbo when this module is loaded with the load command.module/example.m
Example: module { ... increment: fn(i: int): int; };
libinterp/example.c
... void Example_increment(void *fp) { F_Example_increment *f; int i; f = fp; i = f->i; *f->ret = i + 1; }
testexample.b
... init(nil: ref Draw->Context, nil: list of string) { ... sys->print("increment(5) = %d\n", example->increment(5)); }
emu
before running our example, sinceemu
does not contain a modified C-module. $ emu ; limbo testexample.b ; testexample Example module loaded increment(5) = 6 ;
increment()
function found in module/example.m
, a description of this function, its parameters and return values was automatically added to the libinterp/runt.h
file: void Example_increment(void*); typedef struct F_Example_increment F_Example_increment; struct F_Example_increment { WORD regs[NREG-1]; WORD* ret; uchar temps[12]; WORD i; };
regs
; temps
are explicitly added for alignment; ret
is a pointer to the return value; i
is our parameter.module/example.m
Example: module { ... say: fn(s: string); };
libinterp/example.c
... void Example_say(void *fp) { F_Example_say *f; String *s; char *str; f = fp; s = f->s; str = string2c(s); print("%s\n", str); }
testexample.b
... init(nil: ref Draw->Context, nil: list of string) { ... example->say("Hello!"); }
$ emu ; limbo testexample.b ; testexample Example module loaded increment(5) = 6 Hello! ;
libinterp/runt.h
: void Example_say(void*); typedef struct F_Example_say F_Example_say; struct F_Example_say { WORD regs[NREG-1]; WORD noret; uchar temps[12]; String* s; };
noret
instead of ret
everything is clear, the say()
function returns nothing. The String*
type is a C-implementation of Limbo strings. You can find the struct String
in include/interp.h
, functions for working with strings (such as used in our example, string2c()
) are in libinterp/string.c
.Array*
, List*
, etc. Not all structures have ready auxiliary functions as for working with strings, but you can find enough examples in the implementation of opcodes of the virtual machine libinterp/xec.c
(for example, how to work with array slices).module/example.m
converted to the usual C-shny struct (and pick adt to union). Tuples are also converted to regular struct.module/example.m
you have to start the build (which fails by mistake) to libinterp/runt.h
and see exactly which structures were created for your data and understand how to implement working with them in libinterp/example.c
.error()
function. You can connect raise.h
to return standard errors described in libinterp/raise.c
or declare your own libinterp/example.c
in libinterp/example.c
in the same way.malloc()
, then before calling error()
you need to free this memory, otherwise there will be a leak. Objects allocated in a standard way via heap (like String*
and Array*
) need not be released, anyway, they will be found and deleted by the garbage collector a little later. (In more detail about work of heap and the collector of garbage in part 2. )*f->ret
physically points to the memory cell where the result of the function execution should be after its successful completion. Two consequences follow from this:*f->ret
, and then decide that an error occurred and throw an exception, then something impossible will happen in terms of Limbo: the AND function will return the value AND cause an exception.increment()
like this:libinterp/example.c
... void Example_increment(void *fp) { ... *f->ret = i + 1; error("some error"); }
testexample.b
... init(nil: ref Draw->Context, nil: list of string) { ... i := 0; { i = example->increment(5); } exception e { "*" => sys->print("catched: %s\n", e); } sys->print("i = %d\n", i); }
; testexample ... catched: some error i = 6 ;
*f->ret
you must release the current value. This is usually done either like this: destroy(*f->ret); *f->ret = new_value;
H
is the C-shny analogue of Limbo-vskogo nil
): void *tmp; ... tmp = *f->ret; *f->ret = H; destroy(tmp); ... *f->ret = new_value;
release()
before this operation so that Dis continues to run in another thread in parallel with your function, and then again to acquire()
(otherwise it will be impossible to return the result and return to the code that caused this function in Limbo). An example can be found in the sys->read()
implementation in the emu/port/inferno.c
: void Sys_read(void *fp) { ... release(); *f->ret = kread(fdchk(f->fd), f->buf->data, n); acquire(); }
Source: https://habr.com/ru/post/118665/
All Articles