I thought others maybe interested in how marshalling support could be
implemented in Mono.

Paolo gave me permission to email his message to the Mono-List.

-----Original Message-----
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED]] On Behalf Of Paolo Molaro
Sent: Wednesday, May 29, 2002 7:11 AM
To: Mono Hackers
Subject: Re: [Mono-hackers-list] More implementation of the Marshal
class

On 05/28/02 Daniel Morgan wrote:
> I assume PtrToStructure() would be implemented as a internal call and
> placed in mono/mono/metadata/icall.c

Yes, but the logic needed to implement it needs to go into
metadata/marshal.c, because it's used when marshaling the structure
to P/Invoke methods.

> However, to gain a better understanding of what I may have to do,
> I would need to look at:
> /mono/mono/metadata/class.c and /mono/mono/metadata/class.h
> 
> Particularily, the functions are helpful:
> class_compute_field_layout ()
> mono_class_init ()
> mono_get_class()

Actually, you don't need to look at that, but only to the result of that
calls, i.e. the class->fields array.
The MonoClassField structure has some of the info you need: field type
and field offset inside the _object_. We still need to load the
marshaling info from the metadata tables, though.

> At first, I thought align was the same thing as the Pack described in
> the MSDN docs for StructLayoutAttribute, but Lupus pointed out that
pack
> is not the same thing as align.  I think Lupus recently committed
> something for a pack directive to cvs.  I don't know if it has
anything
> to do with the Pack field in StructLayoutAttribute.

Yes, it's the same thing, though it has no relevance to the
implementation of PtrToStructure().

> Any ideas on how to implement PtrToStructure() ?

I have some rough design ideas about how it should all work.
You can get away with a small hack in the mean time to get you started,
but I'd like the final code to look something like the following
proposal (if there are no objections).

Problem: implement the marshaling facilities (both for PtrToStructure()
and the reverse and for P/Invoke invocations).
There are a lot of details on how it should be implemented and some of
the details are quite messy: I want this code to be as generic as
possible so that it can be used by all the runtimes on all the
architectures if possible.

The basic idea is: for each type that we need to marshal we write the
code needed in a special custom bytecode. This bytecode can be either
'interpreted' or it can be easily translated into native code.
PtrToStructure() will likely interpret it, but the jit and the interp
will translate it to native code to implement P/Invoke: this way, all
the messy logic is in a single place and the arch or runtime specific
code only needs to do a simple translation.

Consider:
<C#>
struct S {
        [MarshalAs(UnmanagedType.LPStr)]
        string name;
        [MarshalAs(UnmanagedType.Bool)]
        bool val;
}
<C>
struct S {
        char* name;
        gboolean val;
}

Here the bool value takes one byte in the object, but it takes 4 bytes
in the C structure: this is only one of the several issues we need to be
aware of.
So, here is how the bytecode may look like to go from the C# object
("from") to the C struct ("dest"):

        ldobj dest
        ld.ptr c_name_offset
        free_if_needed
        ldobj dest
        add c_name_offset
        ldobj from
        ld.str name_offset
        conv.lpstr
        stind.ptr
        ldobj dest
        add c_val_offset
        ldobj from
        ld.bool
        conv.native_bool
        stind.native_bool

The code will have a small header to know in advance the stack size
needed. 

        ldobj   loads the address of either the source or destination.
        add     adds an integer offset to a pointer.
        ld.ptr  loads a pointer from the pointer on the stack
        free_if_needed calls g_free/free on the pointer on the stack if
                required
        ld.str  loads a MonoString* from the pointer on the stack
        conv.lpstr converts a MonoString* to a char*
        stdind.ptr indirect stores a pointer
        ld.bool loads a C# bool value
        stind.native_bool indirect stores a native bool

and so on...
There are about 50 or so opcodes needed, but they should be easy to
implement.
The same idea could be used for the P/Invoke calls with a couple more
special opcodes. For example when calling:

        double stof (string val);

we could have:

        localloc,ptr 0
        localloc.double 1
        local_addr 0
        ldarg.str 0
        conv.lpstr
        stind.ptr
        local_addr 0
        ld.ptr
        call func
        local_addr 1
        stind.double 1 // store retval from FP stack
        local_addr 0
        free
        local_addr 1
        ld.double

The bytecode->binary code translator could be the same for the interp
and the jit on each arch, because the only difference would be in how
the ldarg.* opcodes retreive the incoming arguments.

As you see, these are just rough ideas that still need much thinking.
Suggestions and comments are welcome.

lupus




_______________________________________________
Mono-list maillist  -  [EMAIL PROTECTED]
http://lists.ximian.com/mailman/listinfo/mono-list

Reply via email to