/*
Copyright (C) 2006-2008, The Perl Foundation.
$Id: /mirror/trunk/languages/WMLScript/pmc/wmlsbytecode.pmc 33043 2008-11-22T22:22:34.195258Z fperrad  $

=head1 NAME

pmc/wmlsbytecode - WMLScript bytecode loader

=head1 DESCRIPTION

This singleton PMC holds a factory.

=head2 Methods

=over 4

=cut

*/

#include "parrot/embed.h"

static PMC * WmlsBytecode_PMC;
static PMC * script;
static const char * bytecode;
static UINTVAL maxi;
static const char *fp;

static void _init(Interp *interp, STRING *str)
{
    bytecode = Parrot_string_cstring(interp, str);
    maxi     = string_length(interp, str);
    fp       = (const char *)bytecode;
}

#define _info_get()     (UINTVAL)(&bytecode[maxi] - fp)
#define _get_pos()      (fp)
#define _get_c()        (*(fp++))

static unsigned short _get_mb16(void)
{
    unsigned short value;
    unsigned char c;

    value = 0;
    for (;;) {
        c = _get_c();
        value <<= 7;
        value += (c & 0x7F);
        if ((c & 0x80) == 0)
            break;
    }
    return value;
}

static unsigned int _get_mb32(void)
{
    unsigned int value;
    unsigned char c;

    value = 0;
    for (;;) {
        c = _get_c();
        value <<= 7;
        value += (c & 0x7F);
        if ((c & 0x80) == 0)
            break;
    }
    return value;
}

static unsigned char _get_uint8(void)
{
    return _get_c();
}

static unsigned short _get_uint16(void)
{
    unsigned short value;
    unsigned char c;

    c = _get_c();
    value = c & 0xFF;
    c = _get_c();
    value <<= 8;
    value += (c & 0xFF);
    return value;
}

static char _get_int8(void)
{
    return (char)_get_c();
}

static short _get_int16(void)
{
    short value;
    unsigned char c;

    c = _get_c();
    value = c & 0xFF;
    c = _get_c();
    value <<= 8;
    value += (c & 0xFF);
    return value;
}

static int _get_int32(void)
{
    short value;
    unsigned char c;

    c = _get_c();
    value = c & 0xFF;
    c = _get_c();
    value <<= 8;
    value += (c & 0xFF);
    c = _get_c();
    value <<= 8;
    value += (c & 0xFF);
    c = _get_c();
    value <<= 8;
    value += (c & 0xFF);
    return value;
}

static float _get_float32(void)
{
    float value;
    unsigned char *ptr = (unsigned char *)&value;

#if PARROT_BIGENDIAN
    ptr[3] = _get_c();
    ptr[2] = _get_c();
    ptr[1] = _get_c();
    ptr[0] = _get_c();
#else
    ptr[0] = _get_c();
    ptr[1] = _get_c();
    ptr[2] = _get_c();
    ptr[3] = _get_c();
#endif

    return value;
}

static int _load_header(Interp *interp)
{
    UINTVAL VersionNumber;
    UINTVAL CodeSize;

    VersionNumber = _get_uint8();

    if (VersionNumber != 0x01)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
            "incorrect version");

    VTABLE_set_integer_keyed_str(interp, script,
        const_string(interp, "VersionNumber"), VersionNumber);

    CodeSize = _get_mb32();

    if (CodeSize != _info_get())
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
            "incorrect code size");

    VTABLE_set_integer_keyed_str(interp, script,
        const_string(interp, "CodeSize"), CodeSize);

    return 0;
}

static PMC * new_wmls(Interp *interp, const char *name)
{
    PMC * key = key_append(interp,
          key_new_cstring(interp, "Wmls"),
          key_new_cstring(interp, name));
    PMC * class_object = Parrot_oo_get_class(interp, key);
    if (class_object == PMCNULL)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
              "Wmls::%s not loaded", name);
    return VTABLE_instantiate(interp, class_object, PMCNULL);
}

static int _load_constant_pool(Interp *interp)
{
    UINTVAL NumberOfConstants;
    UINTVAL CharacterSet;
    UINTVAL idx;
    PMC * Constants;

    NumberOfConstants = _get_mb16();
    Constants = new_wmls(interp, "Constants");
    VTABLE_set_integer_native(interp, Constants, NumberOfConstants);
    VTABLE_set_pmc_keyed_str(interp, script,
        const_string(interp, "Constants"), Constants);

    CharacterSet = _get_mb16();
    VTABLE_set_integer_keyed_str(interp, script,
        const_string(interp, "CharacterSet"), CharacterSet);

    for (idx = 0; idx < NumberOfConstants; idx++) {
        PMC * Constant;
        UINTVAL ConstantType = _get_uint8();
        switch (ConstantType) {
        case 0:
        {
            INTVAL ConstantInteger8 = _get_int8();
            Constant = new_wmls(interp, "ConstantInteger");
            VTABLE_set_integer_native(interp, Constant, ConstantInteger8);
            break;
        }
        case 1:
        {
            INTVAL ConstantInteger16 = _get_int16();
            Constant = new_wmls(interp, "ConstantInteger");
            VTABLE_set_integer_native(interp, Constant, ConstantInteger16);
            break;
        }
        case 2:
        {
            INTVAL ConstantInteger32 = _get_int32();
            Constant = new_wmls(interp, "ConstantInteger");
            VTABLE_set_integer_native(interp, Constant, ConstantInteger32);
            break;
        }
        case 3:
        {
            FLOATVAL ConstantFloat32 = _get_float32();
            Constant = new_wmls(interp, "ConstantFloat");
            VTABLE_set_number_native(interp, Constant, ConstantFloat32);
            break;
        }
        case 4:
        {
            UINTVAL StringSize = _get_mb32();
            STRING * String = string_make(interp, _get_pos(),
              StringSize, "unicode", 0);
            while (StringSize --) {
                (void)_get_uint8();
            }
            Constant = new_wmls(interp, "ConstantUTF8String");
            VTABLE_set_string_native(interp, Constant, String);
            break;
        }
        case 5:
        {
            Constant = new_wmls(interp, "ConstantEmptyString");
            break;
        }
        case 6:
        {
            UINTVAL StringSize = _get_mb32();
            STRING * String = string_from_cstring(interp,
              _get_pos(), StringSize);
            while (StringSize --) {
                (void)_get_uint8();
            }
            Constant = new_wmls(interp, "ConstantString");
            VTABLE_set_string_native(interp, Constant, String);
            break;
        }
        default:
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
                "invalid ConstantType (%u).", ConstantType);
        }
        VTABLE_set_pmc_keyed_int(interp, Constants, idx, Constant);
    }

    return 0;
}

static int _load_pragma_pool(Interp *interp)
{
    UINTVAL NumberOfPragmas;
    UINTVAL idx;
    PMC * Pragmas;

    NumberOfPragmas = _get_mb16();
    Pragmas = new_wmls(interp, "Pragmas");
    VTABLE_set_integer_native(interp, Pragmas, NumberOfPragmas);
    VTABLE_set_pmc_keyed_str(interp, script,
        const_string(interp, "Pragmas"), Pragmas);

    for (idx = 0; idx < NumberOfPragmas; idx++) {
        PMC * Pragma;
        UINTVAL PragmaType = _get_uint8();
        switch (PragmaType) {
        case 0:
        {
            UINTVAL AccessDomainIndex = _get_mb16();
            Pragma = new_wmls(interp, "AccessDomain");
            VTABLE_set_integer_keyed_str(interp, Pragma,
              const_string(interp, "AccessDomainIndex"), AccessDomainIndex);
            break;
        }
        case 1:
        {
            UINTVAL AccessPathIndex = _get_mb16();
            Pragma = new_wmls(interp, "AccessPath");
            VTABLE_set_integer_keyed_str(interp, Pragma,
              const_string(interp, "AccessPathIndex"), AccessPathIndex);
            break;
        }
        case 2:
        {
            UINTVAL PropertyNameIndex = _get_mb16();
            UINTVAL ContentIndex = _get_mb16();
            Pragma = new_wmls(interp, "UserAgentProperty");
            VTABLE_set_integer_keyed_str(interp, Pragma,
              const_string(interp, "PropertyNameIndex"), PropertyNameIndex);
            VTABLE_set_integer_keyed_str(interp, Pragma,
              const_string(interp, "ContentIndex"), ContentIndex);
            break;
        }
        case 3:
        {
            UINTVAL PropertyNameIndex = _get_mb16();
            UINTVAL ContentIndex = _get_mb16();
            UINTVAL SchemeIndex = _get_mb16();
            Pragma = new_wmls(interp, "UserAgentProperty&Scheme");
            VTABLE_set_integer_keyed_str(interp, Pragma,
              const_string(interp, "PropertyNameIndex"), PropertyNameIndex);
            VTABLE_set_integer_keyed_str(interp, Pragma,
              const_string(interp, "ContentIndex"), ContentIndex);
            VTABLE_set_integer_keyed_str(interp, Pragma,
              const_string(interp, "SchemeIndex"), SchemeIndex);
            break;
        }
        default:
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
                "invalid PragmaType (%u).", PragmaType);
        }
        VTABLE_set_pmc_keyed_int(interp, Pragmas, idx, Pragma);
    }

    return 0;
}

static int _load_function_pool(Interp *interp)
{
    UINTVAL NumberOfFunctions;
    UINTVAL NumberOfFunctionNames;
    UINTVAL idx;
    PMC * Functions;
    PMC * FunctionNameTable;

    NumberOfFunctions = _get_uint8();
    Functions = new_wmls(interp, "Functions");
    VTABLE_set_integer_native(interp, Functions, NumberOfFunctions);
    VTABLE_set_pmc_keyed_str(interp, script,
        const_string(interp, "Functions"), Functions);

    NumberOfFunctionNames = _get_uint8();
    FunctionNameTable = new_wmls(interp, "FunctionNameTable");
    VTABLE_set_integer_native(interp, FunctionNameTable,
        NumberOfFunctionNames);
    VTABLE_set_pmc_keyed_str(interp, script,
        const_string(interp, "FunctionNameTable"), FunctionNameTable);

    for (idx = 0; idx < NumberOfFunctionNames; idx++) {
        PMC * Couple = pmc_new(interp, enum_class_Array);
        UINTVAL FunctionIndex = _get_uint8();
        UINTVAL FunctionNameSize = _get_uint8();
        STRING * FunctionName = string_make(interp, _get_pos(),
          FunctionNameSize, "unicode", 0);
        while (FunctionNameSize --) {
            (void)_get_uint8();
        }
        VTABLE_set_integer_native(interp, Couple, 2);
        VTABLE_set_integer_keyed_int(interp, Couple, 0, FunctionIndex);
        VTABLE_set_string_keyed_int(interp, Couple, 1, FunctionName);

        VTABLE_set_pmc_keyed_int(interp, FunctionNameTable, idx, Couple);
    }

    for (idx = 0; idx < NumberOfFunctions; idx++) {
        PMC * Function;
        UINTVAL NumberOfArguments = _get_uint8();
        UINTVAL NumberOfLocalVariables = _get_uint8();
        UINTVAL FunctionSize = _get_mb32();
        STRING * CodeArray = string_make(interp, _get_pos(),
          FunctionSize, "binary", 0);
        while (FunctionSize --) {
            (void)_get_uint8();
        }
        Function = new_wmls(interp, "Function");
        VTABLE_set_integer_keyed_str(interp, Function,
          const_string(interp, "NumberOfArguments"), NumberOfArguments);
        VTABLE_set_integer_keyed_str(interp, Function,
          const_string(interp, "NumberOfLocalVariables"),
          NumberOfLocalVariables);
        VTABLE_set_string_keyed_str(interp, Function,
          const_string(interp, "CodeArray"), CodeArray);

        VTABLE_set_pmc_keyed_int(interp, Functions, idx, Function);
    }

    if (_info_get() != 0)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
            "incorrect size");

    return 0;
}


pmclass WmlsBytecode
    singleton
    dynpmc
    group wmls_group {

/*

=item C<void* get_pointer()>

=item C<void set_pointer(void *ptr)>

These two functions are part of the singleton creation interface. For more
information see F<src/pmc.c>.

=cut

*/
    VTABLE void* get_pointer() {
        return WmlsBytecode_PMC;
    }

    VTABLE void set_pointer(void* ptr) {
        WmlsBytecode_PMC = (PMC*) ptr;
    }

/*

=item C<PMC* load(STRING* bytecode)>

Loads WMLScript bytecode.

=cut

*/
    METHOD PMC* load(STRING* bytecode) {
        script = new_wmls(interp, "Script");
        _init(INTERP, bytecode);
        if (_load_header(INTERP))
            goto err;
        if (_load_constant_pool(INTERP))
            goto err;
        if (_load_pragma_pool(INTERP))
            goto err;
        if (_load_function_pool(INTERP))
            goto err;
        RETURN(PMC *script);
err:
        RETURN(PMC *NULL);
    }

}

/*

=back

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
