Skip to content
joshperry edited this page Jan 21, 2012 · 30 revisions

One of the most interesting features of erlv8 is being able to supply functionality to JavaScript VM using modules that are implemented in Erlang but are exposed through JavaScript API.

First start erlv8.

application:start(erlv8).

Lets start with a simple example. For example, you want to expose a primitive value through your module (lets say, a greeting). For this, you need to register 'greeting' module (currently only atoms are supported for naming modules) and associate it with a function that returns its value.

{ok, VM} = erlv8_vm:start(),
Global = erlv8_vm:global(VM),
Global:set_value(VM,"greeting", "Hello!"),
erlv8_vm:run(VM,"greeting").

The result of this code will be:

{ok,"Hello!"}

Now, a more sophisticated example:

{ok, VM} = erlv8_vm:start(),
Global = erlv8_vm:global(VM),
Global:set_value("greeting", erlv8_object:new([{"English", "Hello!"}])),
erlv8_vm:run(VM,"greeting.English").

The result of this code will be:

{ok,"Hello!"}

(Again!)

But what about functions? That's the most interesting part! Basically, instead of returning a primitive value, just return a fun of this specification:

-spec (Invocation::#erlv8_fun_invocation,Arguments::list(term())) -> term().
rr("/path/to/erlv8/include/erlv8.hrl"), %% for use in shell only
{ok, VM} = erlv8_vm:start(),
Global = erlv8_vm:global(VM),
Global:set_value("greeting",  erlv8_object:new([{"English", fun (#erlv8_fun_invocation{}, []) ->
                                                                                                  "Hello!" end}])),
erlv8_vm:run(VM,"greeting.English()").

The result of this code will be (surprise!):

{ok,"Hello!"}

You can use the same approach to pass arguments into your function. Lets, for example, implement a string reversal function:

rr("/path/to/erlv8/include/erlv8.hrl"), %% for use in shell only
{ok, VM} = erlv8_vm:start(),
Global = erlv8_vm:global(VM),
Global:set_value("string",erlv8_object:new([{"reverse", fun (#erlv8_fun_invocation{}, [String]) -> lists:reverse(String) end}])),
erlv8_vm:run(VM,"string.reverse('Hello!')").

And the result will be:

{ok,"!olleH"}

Easy, isn't it? But that's not all. Remember that Invocation record? It is defined as:

-record(erlv8_fun_invocation, {
		  is_construct_call = false,
		  holder,
		  this,
		  ref,
		  server,
                  ctx
		 }).

ref, server and *ctx are only available through the record itself but what's more interesting is that the rest can be operated through a transparent Invocation API. For instance, you can check whether you're in a constructor and update your newly created object:

rr("/path/to/erlv8/include/erlv8.hrl"), %% for use in shell only
{ok, VM} = erlv8_vm:start(),
Global = erlv8_vm:global(VM),
Global:set_value("greeting", erlv8_object:new([{"English", 
                                               fun  (#erlv8_fun_invocation{}=Invocation, []) -> 
                                                    case Invocation:is_construct_call() of  
                                                            true ->
                                                                 This = Invocation:this(),
                                                                 This:set_value("greeting","Hello!");
                                                            false ->
                                                                 {throw, {error, "This should always be used as a constructor!"}}
                                                    end
                                                   end}])),
erlv8_vm:run(VM,"greeting.English()"), %% first run
{ok, Obj} = erlv8_vm:run(VM,"new greeting.English()"), Obj:proplist(). %% second run

The first run will return an exception (because we threw it):

{exception,[{"message",
             "This should always be used as a constructor!"},
            {"stack",
             "Error: This should always be used as a constructor!\n    at unknown source"},
            {"name","Error"}]}

And the second one will return this:

[{"greeting","Hello!"}]

(which is an Erlang representation of {greeting: 'Hello!'}).

You can also access VM's global object the same way you access this by using Invocation:global/0

Clone this wiki locally