WebVM

Intended audience: Native WebVM module developers.

  1. WebVM
  2. Introduction
    1. C stub generation
    2. Native implementation
    3. Entry points
    4. Example implementation of WVMModuleFuncs
  3. Class specification
    1. Top-down class design
    2. Method implementations
    3. Property support

Introduction

Imagine we want to implement the following JavaScript API as a native WebVM module:

var addressbook = webvm.load('addressbook');

var filter = {firstname:"Peter"};

// Finds all Peters in the addressbook and returns all matching contact ids
// as an array of numbers
var contactIDs = addressbook.findContacts(filter);

for(i = 0; i < contactIDs.length; i++) {

    // Retrieves the contact object for the given contact id
    var contact = addressbook.getContactByID(contactIDs[i]);

    // Displays the value of the lastname property of this contact
    // in a popup dialog
    alert(contact.get('lastname'));
}

var newcontact = {};
newcontact.firstname = "Peter";
newcontact.lastname  = "Miller";
newcontact.city      = "York";
newcontact.phone     = "01234567";

// Creates a new contact with the given properties
var id = addressbook.createContact(newcontact);

// Retrieves the contact object for the given id
var contact = addressbook.getContactByID(id);

// Sets the country property on the address object
contact.set('country', 'United Kingdom');

// Deletes the contact again
addressbook.deleteContactByID(id);

The given JavaScript API consists of two classes

Contact

AddressBook

In this example we further require that a contact object supports the following properties:

C stub generation

First you define your interfaces in Web IDL. Then you apply widlproc over the widls in order to generate WebIDL XML files.

Next to generate C stubs for the above defined API, WebIDL interfaces can be used in conjunction with the widl2c tool (part of the WebVM SDK since version 0.13)

To generate native module implementation stubs for given WebIDL interfaces, it is assumed that the WebIDL XML files of these interfaces are relative to the current directory. The usage is:

java -jar widl2c.jar <output directory> <module>.wxml [<module>.wxml [ .. ]]

To implement a class specification as a native module, all you need is to include all files in the output directory into your project and to implement all the _impl.c file(s).

Native implementation

Each WebVM module is a dynamic library with the following entry points:

WVMStatus WVMAttach(WVM              /* [in]  */  *instance,
                    WVMCallbackFuncs /* [in]  */  *wvcFuncs,
                    WVMModuleFuncs   /* [out] */ **wvmFuncs);

void WVMDetach(WVM /* [in] */ *instance);

WVMAttach

When WebVM loads a native module it calls the WVMAttach entry point. This call supplies two input arguments, an instance handle which can be used to store private data in (see webvm.h for details) and the structure of functions exposed by WebVM which are essential for interacting with WebVM. This call also expects the module function set to be stored in the output argument.

WVMDetach

The WVMDetach entry point is called when WebVM is going to unload the native module. It is the last chance to free any private data of the module stored in the instance handle, because the instance handle will be destroyed when this call returns.

Entry points

An example implementation of these entry points is generated by widl2c. It’ll look similiar to the following example.

static WVMCallbackFuncs  *wvcf;

WVMStatus WVMAttach(WVM              /* [in]  */  *instance,
            WVMCallbackFuncs /* [in]  */  *wvcFuncs,
            WVMModuleFuncs   /* [out] */ **wvmFuncs,
                    const WVMFeature /* [out] */ **pfeatures)
{
    static WVMModuleFuncs wvmf = {
        sizeof(WVMModuleFuncs),
        { WEBVM_VERSION_MAJOR, WEBVM_VERSION_MINOR },
        init,
        deinit,
        start,
        stop,
        release,
    };

    if(wvcFuncs->version.major < WEBVM_VERSION_MAJOR
    || (wvcFuncs->version.major == WEBVM_VERSION_MAJOR
        && wvcFuncs->version.minor < WEBVM_VERSION_MINOR))
    {
        return Error_Unsupported;
    }
    wvcf               = wvcFuncs;
    *wvmFuncs          = &wvmf;
    *pfeatures         = NULL;
    return Error_NoError;
}

void WVMDetach(WVM /* [in] */ *instance) {

}

In this example private data is not stored in the instance handle for simplicity. A real world module may need to keep private data that cannot be in static variables in case the module is loaded multiple times by the same WebVM environment. A pointer to such private data can be stored in the mdata field of the instance handle:

instance->mdata = yourdata;

This example WVMAttach implementation uses two global static variables to deal with the WebVM and module function structures.

The most important aspect of WVMAttach is the WVMModuleFuncs initialisation. The module function structure supplied to WebVM is used by WebVM for further calls into the native module. The sample code contains the functions init, deinit, start, stop and getProperty. These functions must be implemented by each native module.

Example implementation of WVMModuleFuncs

static WVMObjectReference scriptableObjectRef;

static WVMStatus init(WVM          /* [in]  */   *instance,
              WVMClassSpec /* [out] */ ***classSpecs,
                      const char   /* [out] */  **defaultRootName)
{
    *classSpecs     = classes;
    *defaultRootName= NULL;
    return Error_NoError;
}

static void deinit(WVM /* [in] */ *instance) {
    /* TODO: cleanup all your resources */
}

static WVMStatus start(WVM /* [in] */ *instance) {
    WVMStatus result;

    result = wvcf->reateObjectRef(instance, classes[0], CallType_ByReference,
                                     NULL, NULL, NULL, NULL, &scriptableObjectRef);
    if(result == Error_NoError) {
        result = wvcf->setScriptableObject(instance, scriptableObjectRef);
    }
    return result;
}


static WVMStatus stop(WVM /* [in] */ *instance) {
    return wvcf->derefObjectRef(instance, scriptableObjectRef);
}

init

When WVMAttach returns successfully, WebVM will call the module’s init function. This function has to initialise the native module and to return the class specification it supports.

start

After init returns, WebVM will process the given API class description internally and call the start module function when it is ready.

In this example the native module creates an object reference for the top-level class its API consists of. In the WebVM and browser terminology this is called a scriptable object. WebVM expects the native module to create an object reference for its top-level class during the start function and to use this reference to set its scriptable object. The scriptable object cannot be overwritten at a later point; it is fixed until stop is called.

In the case of an addressbook API the top-level class exported by a native module would be the AddressBook class described above.

Note: Object references cannot be created prior to the start function call by WebVM, because WebVM needs to know the exported classes before any referencing.

stop

Stop is called at any time after start. This tells the module to stop its activity and that no further API calls into the module will be made. WebVM expects the module to destroy the scriptable object reference during this call.

release

The release function is called when the reference count of a resource initially created by a native module reaches 0. The native module must free the resource if release is called.

deinit

This call occurs at any time after stop returned. A native module should clean up its object instance resources here.

Class specification

When WebVM calls the start module function, it expects to retrieve the class specification of a native module. Native WebVM modules only support static APIs described by an OO-like class model which is inspired from the basic principles of WebIDL interfaces. The class model cannot be changed after init has been called.

webvm.h defines a C data structure to represent this OO class interface design. The module sets up this C data structure to specify the class and its methods and fields.

Top-down class design

During the init call, the module sets an array of WVMClassSpec pointers, one for each interface being defined. This array and its size are returned to WebVM. To understand the native module class design from a top-down perspective, we start with the WVMClassSpec structure defined in webvm.h:

WVMClassSpec

struct _WVMClassSpec {
    const char      *name;
    WVMFieldSpec    *fields;
    WVMMethodSpec  **methods;
    [...]
};

The definition of the Contact and AddressBook classes in a native module would look like:

static WVMClassSpec contactClass = {
    /* name        */ "Contact",
    /* fields      */ NULL,
    /* methods     */ contactMethods,
    [...]
};

static WVMClassSpec addressbookClass = {
    /* name        */ "AddressBook",
    /* fields      */ NULL,
    /* methods     */ addressbookMethods,
    [...]
};

static WVMClassSpec *classes[] = { &addressbookClass, &contactClass };

Each WVMClassSpecs name must be unique throughout all classes which are exported.

Each class consists of a fixed number of fields and methods. Please see the generated decl.c files for examples.

A class method specification is a composition of three different types: the method specification container, the method signature, and the argument specifications of the signature.

The findContacts method, for instance, can be specified as follows:

static WVMArgSpec asContactIDArr = { AtaInt, NULL };
static WVMArgSpec asMap          = { AtoMap, NULL };

static WVMArgSpec *addressbookFindContactsInputArgs[] = { &asMap, 0 };
static WVMSignature addressbookFindContactsSig = { addressbookFindContactsInputArgs, &asContactIDArr };
static WVMStatus addressbookFindContactsInvoke(WVM *instance, WVMObjectReference ref, WVMVariant *vArgs, WVMVariant *vResultArgs) {
    /* implementation */
    return Error_NoError;
}
static WVMMethodSpec addressbookFindContactsMethod = { "findContacts", &addressbookFindContactsSig, addressbookFindContactsInvoke };

WVMInvokePtr

The method specification of findContacts defines the method name, and contains a pointer to the method’s signature and a function pointer to its implementation. A pointer to any method implementation function matches the following prototype defined in webvm.h:

typedef WVMStatus (*WVMInvokePtr) (WVM                /* [in]  */ *instance,
                   WVMObjectReference /* [in]  */  ref,
                   WVMVariant         /* [in]  */ *vArgs,
                   WVMVariant         /* [out] */ *vResultArgs);

This means that any native method implementation must follow this prototype definition. In the given findContacts example, the addressbookFindContactsInvoke function implements this prototype. We will have a more detailed look to function implementations later.

WVMSignature

The method specification also defines the signature of the exposed method. The given addressbookFindContactsSig signature defines that it takes an input argument of the type asMap and returns an argument of the type asContactIDArr.

Note: Multiple functions with the same signature may share the same signature definition.

WVMArgSpec

An argument specification defines the argument type. Here is a list of the most common argument specs:

static WVMArgSpec asInt    = { VariantType_Int32, NULL };    // defines an integer argument
static WVMArgSpec aslong   = { VariantType_Int64, NULL };    // defines a long argument
static WVMArgSpec asDouble = { VariantType_Double, NULL };     // defines a double argument
static WVMArgSpec asString = { VariantType_String, NULL }; // defines a string argument

If you wanted to specify an argument of the type Contact, representing an object of the Contact class, this is done using the second argument in an WVMArgSpec:

static WVMArgSpec asContact = { VariantType_ObjectRef, &contactClass };

WVMArgSpecs depend on the WVMVariantType enum. For the C data types you only need to use the following WVMVariantType values:

C typeWVMVariantType
voidVariantType_Void
charVariantType_Char
unsigned charVariantType_Byte
shortVariantType_Int32
intVariantType_Int32
long longVariantType_Int64
floatVariantType_Double
doubleVariantType_Double
char *VariantType_String
unsigned char[]VariantType_ByteArray
int[]VariantType_Int32Array
long long[]VariantType_Int64Array
double[]VariantType_DoubleArray
WVMVariant[]VariantType_VariantArray
WVMVariant (as a map)VariantType_Map
WVMErrorVariantType_Error
other object referenceVariantType_ObjectRef

Method implementations

To understand how methods are implemented, the WVMVariant type is crucial, because it is used for the argument processing in native modules. Each WVMVariant is a container of the WVMValue type.

typedef union {
    WVMBool             boolValue;
    Int32               intValue;
    Int64               longValue;
    float               floatValue;
    double              doubleValue;
    WVMObjectReference  refValue;
    WVMVariant         *varValue;
    WVMError           *errValue;
    WVMObject           objValue;

    struct {
        WVMObject handle;
        union {
            char        *utf8Elements;
            byte        *byteElements;
            Int32       *intElements;
            Int64       *longElements;
            double      *doubleElements;
            WVMMapEntry *mapElements;
        } elements;
    } arr;
} WVMValue;

struct _WVMVariant {
    WVMVariantType type;
    UInt32       count;
    WVMValue     value;
    void       (*release)(WVMVariant *);
    void        *userdata;
};

struct _WVMMapEntry {
    WVMAtom     id;
    WVMVariant  varValue;
};

getContactByID

First of all consider the function addressbookGetContactByID again, which is based on a helper function:

typedef struct _Contact Contact;
struct _Contact {
    Int32               id;
    WVMObjectReference  ref;
    Contact            *next;
};

static Contact *getContactByID(WVM *instance, Int32 id) {
    Contact *c;
    IContact *ic;

    for(c = contacts; c; c = c->next) {
        if(c->id == id) {
            return c;
        }
    }
    if((ic = GetAddressBook()->FindContactByID(idmap[id]))) {
        c = malloc(sizeof(Contact));
        c->next = contacts;
        c->id = id;
        wvcf->createObjectRef(instance, &contactClass, c, &c->ref);
        contacts = c;
        return c;
    }
    return NULL;
}

static WVMStatus addressbookGetContactByIDInvoke(WVM *instance, WVMObjectReference ref, WVMVariant *vArgs, WVMVariant *vResultArgs) {
    static WVMError contactNotFoundError = { "Error", "Contact not found.", NULL };
    Contact *c = getContactByID(instance, vArgs[0].value.intValue);

    if(c) {
        vResultArgs->type = VariantType_ObjectRef;
        vResultArgs->value.refValue = c->ref;
        return Error_NoError;
    }
    else {
        vResultArgs->type = VariantType_Error;
        vResultArgs->value.errValue = &contactNotFoundError;
        return Error_Native;
    }
}

In this example the only input argument to addressbookGetContactByIDInvoke is an integer representing the contact id. It is accessed using:

vArgs[0].value.intValue

WebVM ensures that you always retrieve the correct amount of input arguments, so you do not need to check if your input arguments are of the expected form. In this example the first argument is used to retrieve or instantiate a new Contact object, whose object reference (of the specified type VariantType_ObjectRef) is returned.

This makes sure that any subsequent call on this object handle in JavaScript will be directed into the specified Contact class methods, with the ref argument set to the pointer reference used in wvcf->createObjectRef, which, in the example above, is the variable c (of type Contact *). Here is a JavaScript example of the call that creates the object handle, and one that then calls a method on it:

var contact = addressbook.getContactByID(id);
var name = contact.get('name');

If the contact could not be found an error result is returned instead.

Note: Whenever you return an Error_ result, it is expected by WebVM that the result variant points to a proper WVMError struct. An erroneous result will throw an exception in the JavaScript context which retrieves the exception strings from the WVMError structure.

Contact.get

Now consider the following implementation of Contact.get(), which has been specified earlier:

static void releaseString(WVMVariant *v) {
    if(v->type == VariantType_String) {
        free(v->value.arr.elements.utf8Elements);
    }
}

static WVMArgSpec *contactGetInputArgs[] = { &asString };
static WVMSignature contactGetSig = { 1, contactGetInputArgs, &asString };
static WVMStatus contactGetInvoke(WVM *instance, WVMObjectReference ref, WVMVariant *vArgs, WVMVariant *vResultArgs) {
    static WVMError propNotFoundError = { "Error", "Property not found.", NULL };
    Contact *c = getContactByRef(ref);
    IContact *ic;
    const Field *f;

    if(c) {
        ic = GetAddressBook()->FindContactByID(idmap[c->id]);
    }
    if(ic && vArgs[0].type == VariantType_String && (f = getFieldByKey(vArgs[0].value.arr.elements.utf8Elements))) {
        vResultArgs->type = VariantType_String;
        vResultArgs->value.arr.elements.utf8Elements = (char *)f->get(ic);
        vResultArgs->count = strlen(vResultArgs->value.arr.elements.utf8Elements);
        vResultArgs->release = releaseString;
        return Error_NoError;
    }
    else {
        vResultArgs->type = VariantType_String;
        vResultArgs->value.errValue = &propNotFoundError;
        return Error_Native;
    }
}
static WVMMethodSpec contactGetMethod = { "get", &contactGetSig, contactGetInvoke };

This method takes a string as argument and returns a string as result. More importantly, note how the ref argument to this method is used. When the underlying contact object has been instantiated (in getContactByID using wvmf->createObjectRef), an object reference has been retrieved. This object reference is supplied as input argument on any call on this object. This means you can use WebVM to keep track of your objects.

Note that you need to release allocated strings if they aren’t static. In this example the

f->get(ic);

call returns the value of the given property as a string duplicate using strdup. That’s why the release callback on the WVMVariant is set to releaseString.

createContact

Now let’s consider the implementation of createContact, which takes a map as argument and has been introduced earlier. This example depends on some preliminary code which needs to be implemented in init:

static const char kfirstname[] = "firstname";
static const char klastname[]  = "lastname";
static const char kphone[]     = "phone";
static const char kemail[]     = "email";
static const char kcellphone[] = "cellphone";
static const char kstreet[]    = "street";
static const char kcity[]      = "city";
static const char kpostalcode[]= "postalcode";
static const char kcountry[]   = "country";

static WVMAtom firstnameAtom;
static WVMAtom lastnameAtom;
static WVMAtom phoneAtom;
static WVMAtom emailAtom;
static WVMAtom cellphoneAtom;
static WVMAtom streetAtom;
static WVMAtom cityAtom;
static WVMAtom postalcodeAtom;
static WVMAtom countryAtom;

static WVMStatus init(WVM          /* [in]  */   *instance,
              WVMClassSpec /* [out] */ ***classSpecs
[..]
{
    wvcf->getAtomForString(instance, kfirstname, sizeof kfirstname, &firstnameAtom);
    wvcf->getAtomForString(instance, klastname, sizeof klastname, &lastnameAtom);
    wvcf->getAtomForString(instance, kphone, sizeof kphone, &phoneAtom);
    wvcf->getAtomForString(instance, kemail, sizeof kemail, &emailAtom);
    wvcf->getAtomForString(instance, kcellphone, sizeof kcellphone, &cellphoneAtom);
    wvcf->getAtomForString(instance, kstreet, sizeof kstreet, &streetAtom);
    wvcf->getAtomForString(instance, kpostalcode, sizeof kpostalcode, &postalcodeAtom);
    wvcf->getAtomForString(instance, kpostalcode, sizeof kcountry, &countryAtom);
    *classSpecCount = 2;
    *classSpecs     = classes;
    return Error_NoError;
}

In this preliminary code several WVMAtoms are initialized. This is because maps in WebVM are arrays of WVMMapEntry structs:

struct _WVMMapEntry {
    WVMAtom     id;
    WVMVariant  varValue;
};

An atom is an opaque structure for a given string which is unique throughout the WebVM environment. In the given example, firstnameAtom is a unique representation of the string “firstname” (kfirstname).

To understand a map as input to the createContact method, look at the earlier introduced JavaScript code:

var newcontact = {};
newcontact.firstname = "Peter";
newcontact.lastname  = "Miller";
newcontact.city      = "York";
newcontact.phone     = "01234567";

This input argument will be translated by WebVM into a WVMVariant of the type AtoMap and with the following array entries:

WVMVariant.value.arr.mapElements[0].id == firstnameAtom
WVMVariant.value.arr.mapElements[0].value.type == VariantType_String
WVMVariant.value.arr.mapElements[0].value.arr.elements.utf8Elements == "Peter"

WVMVariant.value.arr.mapElements[1].id == lastnameAtom
WVMVariant.value.arr.mapElements[1].value.type == VariantType_String
WVMVariant.value.arr.mapElements[1].value.arr.elements.utf8Elements == "Miller"

WVMVariant.value.arr.mapElements[2].id == cityAtom
WVMVariant.value.arr.mapElements[2].value.type == VariantType_String
WVMVariant.value.arr.mapElements[2].value.arr.elements.utf8Elements == "York"

WVMVariant.value.arr.mapElements[3].id == phoneAtom
WVMVariant.value.arr.mapElements[3].value.type == VariantType_String
WVMVariant.value.arr.mapElements[3].value.arr.elements.utf8Elements == "01234567"

WebVM provides a convenience function for dealing with maps however, which is used in the real implementation as follows:

static WVMStatus addressbookCreateContactInvoke(WVM *instance, WVMObjectReference ref, WVMVariant *vArgs, WVMVariant *vResultArgs) {
    static WVMError couldntCreateError = { "Error", "Could not create contact.", NULL };
    int result;
    IContact *c;
    WVMVariant v;
    WCHAR *wfirstname = NULL;
    WCHAR *wlastname = NULL;
    WCHAR *wphone = NULL;
    WCHAR *wcellphone = NULL;
    WCHAR *wemail = NULL;
    WCHAR *wstreet = NULL;
    WCHAR *wpostalcode = NULL;
    WCHAR *wcity = NULL;
    WCHAR *wcountry = NULL;

    if((result = wvcf->getMapEntry(instance, vArgs, firstnameAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wfirstname);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, lastnameAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wlastname);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, phoneAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wphone);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, cellphoneAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wcellphone);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, emailAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wemail);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, streetAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wstreet);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, postalcodeAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wpostalcode);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, cityAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wcity);
    }
    if((result = wvcf->getMapEntry(instance, vArgs, countryAtom, VariantType_String, &v)) == Error_NoError) {
        utf82wchar(v.value.arr.elements.utf8Elements, (void **)&wcountry);
    }

    wstring firstname(wfirstname ? wfirstname : L"");
    wstring lastname(wlastname ? wlastname : L"");
    wstring phone(wphone ? wphone : L"");
    wstring cellphone(wcellphone ? wcellphone : L"");
    wstring email(wemail ? wemail : L"");
    wstring street(wstreet ? wstreet : L"");
    wstring postalcode(wpostalcode ? wpostalcode : L"");
    wstring city(wcity ? wcity : L"");
    wstring country(wcountry ? wcountry : L"");

    c = GetAddressBook()->AddContact(firstname, lastname, phone, cellphone, email, street, postalcode, city, country);

    free(wfirstname);
    free(wlastname);
    free(wphone);
    free(wcellphone);
    free(wemail);
    free(wstreet);
    free(wpostalcode);
    free(wcity);
    free(wcountry);


    if(c) {
        vResultArgs->type = VariantType_Int32Array;
        vResultArgs->value.intValue = getIndexForID(c->GetID());

        return Error_NoError;
    }
    else {
        vResultArgs->type = VariantType_Error;
        vResultArgs->value.errValue = &couldntCreateError;
        return Error_Native;
    }
}
static WVMMethodSpec addressbookCreateContactMethod = { "createContact", &addressbookCreateContactSig, addressbookCreateContactInvoke };

In this implementation the wvcf->getMapEntry function is used, which has been defined in webvm.h as follows:

typedef WVMStatus (*WVCGetMapEntryPtr) (WVM        /* [in]  */ *instance,
                    WVMVariant /* [in]  */ *map,
                    WVMAtom    /* [in]  */  id,
                    WVMVariantType /* [in]  */  type,
                    WVMVariant /* [out] */ *result);

This function takes the map argument, the atom you are looking for, and the type you expect as input, and will return Error_NoError if that map entry exists, and set the result variant accordingly. Otherwise it will return with an Error code. Usually you only need this for existence tests.

A general limitation of maps is that they cannot be specified with type information up front. This is why it is highly recommended to use the WVCGetMapEntryPtr call into WebVM for dealing with maps, because WebVM deals best with argument conversions of the given map handle from JavaScript.

Note that there is no way to guarantee that the expected values of a map are actually supplied by the web developer.

Property support

WebVM has got a new feature to query properties such as the version information of WebVM. Imagine the following example code to query for some default WebVM properties using JavaScript:

var version     = webvm.getProperty("webvm.version");
var vendor      = webvm.getProperty("webvm.vendor");
var description = webvm.getProperty("webvm.description");

This new approach can also be used for querying native module properties. Imagine the simple native module as in the http://example.webvm.net/simple example:

var simpleVersion     = webvm.getProperty("simple.version");
var simpleVendor      = webvm.getProperty("simple.vendor");
var simpleDescription = webvm.getProperty("simple.description"):

We created a new example to demonstrate this property functionality which can be found at

http://example.webvm.net/info

In order to support the new property functionality in native modules, we changed the getProperty module function to be exported as a dynamic loadable symbol called WVMGetProperty. WebVM will load a native module by the supplied property prefix and call this WVMGetProperty function of the module to query for particular properties. The simple example in the SDK has been updated accordingly.

To upgrade your module you need to perform the following two steps:

Replace the old getProperty implementation with something like the following function:

const char *WVMGetProperty(const char /* [in]  */  *key) {
    if(!strcmp(key, "vendor")) {
    return "<YOUR COMPANY>";
    }
    else if(!strcmp(key, "copyright")) {
    return "<YOUR COPYRIGHT>";
    }
    else if(!strcmp(key, "version")) {
    return "<YOUR VERSION>";
    }
    else if(!strcmp(key, "description")) {
    return "<YOUR DESCRIPTION>";
    }
    return NULL;
}

Discussion

Please subscribe to dev@webvm.net through sending a mail to dev+subscribe@webvm.net.