Calling into a native method

So far we've gotten into a lot of detail of how a native method is created, and mapped into the forth VM. This post will finally glue the two together. All method calls get compiled into the JavaScript opcode CallProperty, which is implemented in forth as such:

vm.fs
EXTERN: OP_callproperty callprop NEXT ;

: callprop ( obj [ns] [name] args )
prepnameandbind callpropvec ;

CASE: callpropvec
( 0 BKIND_NONE ) callprop_none
( 1 BKIND_METHOD ) callprop_method
// Others

: callprop_method bind2method 2nip callenv ;

: callenv ( xobj xargs argc env -- result )
PUSHFRAME ( obj args argc env )
methodinfopc SETPC ( obj args argc )
ckint ckstk ckargc
coerce_actual_args ( obj args argc )
// param0 is "special"
prepparam0 ( obj args argc obj teobjtype )
coerce ( obj args argc obj' )
replacereceiver0 ( obj' args argc )
ENV getforthimpl ( obj args argc wimpl )
EXECUTE ( result )

We see this long chain until we get to callenv which is the important one. The other words such as bind, are used to lookup the method name and which object it is associated with. Both Forth words "ENV" and "getForthimpl" are C functions. If we look at getforthimpl:
Interpreter.cpp
wcodep FASTCALL getforthimpl(MethodEnv* env)
{
return env->getForthWord();
}
Which does the following:
MethodEnv.cpp
wcodep MethodEnv::getForthWord() const
{
if (NativeMethodInfop(m_body_pos)->isForth)
return wcodep(NativeMethodInfop(m_body_pos)->body);
return CODE_POS(w_enterimpl_native);
}
So here, if the "native" method is implemented in Forth, we jump into the Forth implementation which again is really the code_pool defined in vm_min.cpp. We are done explaining how Forth native methods are called. However, the weird part is that to call a C native method, we have to jump BACK into Forth, which jumps BACK into C. Hence it still makes sense to call "getForthWord", because we get the forth word which jumps into C code. Looking at w_enterimpl_native:
vm_min.h
extern_entry(w_enterimpl_native, 8045)
So let's look at the forth implementation of what happens here:
vm.fs
EXTERN: w_enterimpl_native ( obj args argc -- result )
PENDX CALLNATIVE_X ckpendingexception ; ( result )
Looking at CALLNATIVE_X:
Interpreter.cpp
// obj args argc pendx -- result
PRIM(void, CALLNATIVE_X)(Frame*& f, Boxp& sp)
{
const int32_t argc = sp[-1].i;
sp[-argc-2].q = callnative_x_glue(sp[0], f->env);
sp -= argc+2;
}

Finally, we can look at callnative_x_glue:
Interpreter.cpp
NativeMethodInfop nmi = env->getNativeMI();
(*NativeMethodProc_int32_t(f))(argv)
Which finally calls the native C function. WHEW.