A Detailed Inspection of the Native Maps - The C part

The idea of Native Maps allows ActionScript functions to call into C and Forth. This really means .abc code can call into C and vice versa. Again, this functionality is implemented via Macros, so sit down and drink some coffee! There was a report at the BBC which says that daily caffeine protects the brain. Now let's take a look at the native method maps. There are two big files which do this: Shell and Builtin. Since both are basically implemented in ActionScript this makes sense, and applies to both. We'll be using the shell files for this example, specifically the C portion. Another post will deal with the Forth interaction. Looking in shell_min.cpp:

shell_min.cpp
BEGIN_NATIVE_MAP(shell)
NATIVE_METHOD(flash_utils_ByteArray_private__readFile)
// Even more native method declarations
END_NATIVE_MAP()
These macros are defined in NativeFunction.cpp:
NativeFunction.h
#define BEGIN_NATIVE_MAP(n) const NativeMethodInfo NATIVE_METHODS(n)[] = {
#define END_NATIVE_MAP() { 0, 0, 0, 0 } };
#define NATIVE_METHOD(m) { uintptr_t(m), false, k_NativeRetType_##m, 0 },
However, these macros don't make a lot of sense until you look at what a NativeMethodInfo structure is:
NativeFunction.h
struct NativeMethodInfo
{
uintptr_t body; // forth wcodep or c++ function ptr
// note, could easily be bit-packed if we need space... byte-packed for now for (probably)
// cheaper access / better codegen.
uint8_t isForth; // set if body is forth, clr if native code (1 bit needed)
uint8_t retType; // NativeRetType used by native method (unused for forth) (4 bits needed)
uint16_t pad;
};
This is still kind of confusing. Let's expand one of these structures for our native method _readFile:
NativeFunction.h
NATIVE_METHOD(flash_utils_ByteArray_private__readFile)

After Expansion of Native_Method:
{ uintptr_t(flash_utils_ByteArray_private_readFile), false, k_NativeRetType_flash_utils_ByteArray_private__readFile, 0 }

After the expansion of uintptr:
{ address of flash_utils_ByteArray_private_readFile,false, k_NativeRetType_flash_utils_ByteArray_private__readFile, 0 }

We can then find the actual method declaration in ByteArrayGlue.cpp. So now we have one big array of C native Methods, and their addresses. However, what is the k_NativeRetType:
NativeFunction.h
#define AVMPLUS_NATIVE_METHOD_DECL(ret, name) \
enum { k_NativeRetType_##name = NativeRetType_##ret } ; \
extern ret FASTCALL name (const name##_args* args); /* semi required here */
Finally, we can look at the NativeRetType:
NativeFunction.h
// a little syntactic sugar for mapping C++ ret types into a useful enum
enum NativeRetType
{
NativeRetType_ScriptObjectp,
NativeRetType_bool,
NativeRetType_int32_t,
NativeRetType_uint32_t,
NativeRetType_Namespacep,
NativeRetType_double,
NativeRetType_BoxReturnType,
NativeRetType_Stringp,
NativeRetType_void
};

Where is the return type defined for this method though? Looking at shell_min.h (Header File):
shell_min.h
struct flash_utils_ByteArray_private__readFile_args
{
public: ScriptObjectp /*ByteArray*/ self; private: int32_t self_pad;
public: Stringp filename; private: int32_t filename_pad;
public: StatusOut* status_out;
};
AVMPLUS_NATIVE_METHOD_DECL(bool, flash_utils_ByteArray_private__readFile)
So now we finally know that the return type of this method is a boolean. We also see that the native method declaration REQUIRES every method have a certain name: methodName__args in a struct. Therefore, every function has as a parameter a structure of arguments, instead of the arguments themselves. We now have a C function and the address of the function required for a call to the native C method. How do we actually call the function? It's pretty complicated so I'll leave it to another post.