NAME
JavaScript::Duktape - Perl interface to Duktape embeddable javascript engine
SYNOPSIS
use JavaScript::Duktape;
##create new js context
my $js = JavaScript::Duktape->new();
#set function to be used from javascript land
$js->set('write' => sub {
print $_[0], "\n";
});
$js->eval(qq~
(function(){
for (var i = 0; i < 100; i++){
write(i);
}
})();
~);
DESCRIPTION
JavaScript::Duktape implements almost all duktape javascript engine api, the c code is just a thin layer that maps duktape api to perl, and all other functions implemented in perl it self, so maintaing and contributing to the base code should be easy.
methods
- set('name', data);
-
Creates property 'name' and sets it's value to the given perl data
$js->set('something', {}); #set something $js->set('something.name', 'Joe'); $js->set('number', 1234); ...
this method will die if you try to set a property on undfined base value
$js->set('notHere.name', 'Joe'); ## will die ## so first set "notHere" $js->set('notHere', {}); $js->set('notHere.name', 'Joe'); ## good
- get('name');
-
Gets property 'name' value from javascript and return it as perl data, this method will die if you try to get value of undefined base value
my $print_sub = $js->get('print');
- eval('javascript');
-
Evaluates javascript string and return the results or croak if error
my $ret = $js->eval(q{ var t = 1+2; t; // return value from eval }); print $ret, "\n"; # 3
- get_object('name');
-
Same as
get
method but instead of returning a raw value of the property name, it will return aJavaScript::Duktape::Object
this method will die if you try to get a property that is not of type 'object'$js->eval(q{ function Person (name){ this.name = name; } }); my $personObject = $js->get('Person'); # $personObject is a blessed 'JavaScript::Duktape::Object' object # so you can call internal my $person = $personObject->new('Joe'); print $person->name, "\n"; # Joe
For more on how you can use
JavaScript::Duktape::Object
please see examples provided with this distribution
VM API
vm api corresponds to Duktape Engine API see http://duktape.org/api.html To access vm create new context then call vm
my $js = JavaScript::Duktape->new();
my $duk = $js->vm;
#now you can call Duktape API from perl
$duk->push_string('print');
$duk->eval();
$duk->push_string('hi');
$duk->call(1);
$duk->pop();
Also you may find it useful to use dump
function regularly to get a better idea where you're in the stack, the following code is the same example above but with using dump
function to get a glance of stack top
my $js = JavaScript::Duktape->new();
my $duk = $js->duk;
#push "print" string
$duk->push_string('print');
$duk->dump(); #-> [ Duktape (top=1): print ]
#since print is a native function we need to evaluate it
$duk->eval();
$duk->dump(); #-> [ Duktape (top=1): function print() {/* native */} ]
#push one argument to print function
$duk->push_string('hi');
$duk->dump(); #-> [ Duktape (top=2): function print() {/* native */} hi ]
#now call print function and pass "hi" as one argument
$duk->call(1);
#since print function doesn't return any value, it will push undefined to the stack
$duk->dump(); #-> [ Duktape (top=1): undefined ]
#pop to remove undefined from stack top
$duk->pop();
#Bingo
$duk->dump(); #-> [ Duktape (top=0): ]
VM methods
As a general rule all duktape api supported, but I haven't had the chance to test them all, so please report any missing or failure api call and I'll try to fix
For the list of duktape engine API please see http://duktape.org/api.html, and here is how you can translate duktape api to perl
my $js = JavaScript::Duktape->new();
my $duk = $js->duk;
# -- C example
# duk_push_c_function(func, 2);
# duk_push_int(ctx, 2);
# duk_push_int(ctx, 3);
# duk_call(ctx, 2); /* [ ... func 2 3 ] -> [ 5 ] */
# printf("2+3=%ld\n", (long) duk_get_int(ctx, -1));
# duk_pop(ctx);
#and here is how we can implement it in JavaScript::Duktape
$duk->push_c_function(sub {
my $num1 = $duk->get_int(0);
my $num2 = $duk->get_int(1);
my $total = $num1+$num2;
$duk->push_number($total);
return 1;
}, 2);
$duk->push_int(2);
$duk->push_int(3);
$duk->call(2); # [ ... func 2 3 ] -> [ 5 ]
printf("2+3=%ld\n", $duk->get_int(-1));
$duk->pop();
As you can see all you need to do is replacing duk_
with $duk->
and remove ctx
from the function call, this may sounds crazy but api tests have been generated by copying duktape tests and using search and replace tool :)
Besides duktape api, JavaScript::Duktape::Vm
implements the following methods
- push_function ( code_ref );
-
push perl sub into duktape stack, this is the same as push_perl_function except it will handle both passed arguments and return data for you
$duk->push_function(sub { my ($arg1, $arg2, ...) = @_; return $something; });
- push_perl_function ( code_ref, num_of_args );
-
an alias to push_c_function, same as push_perl_function except you will be responsible for extracting arguments and pushing returning data
$duk->push_perl_function(sub { my $arg1 = $duk->get_int(-1); my $somthing_to_return = ".."; $duk->push_string($somthing_to_return); return 1; });
- push_perl( ... );
-
Push given perl data into the duktape stack.
- to_perl(index);
-
Get the value at index and return it as perl data
- to_perl_object(index);
-
Get object at index and return it as 'JavaScript::Duktape::Object', this function will die if javascript data at index is not of type object
- reset_top
-
resets duktape stack top
EXPORTS
JavaScript::Duktape
exports the following by default
- true
- false
- null
- _ (underscore)
-
This can be used to indicate that we are calling an object function without arguments, see "CAVEATS"
- this
-
This can be called from pushed perl sub
$duk->push_perl_function(sub{ my $this = this; });
See
examples/this.pl
CAVEATS
VM methods
JavaScript::Duktape
vm methods is a direct low level calls to duktape c library, so stepping outside of the stack will result in a program termination without a useful error message, so you need to be careful when using these methods and always check your stack with $duk->dump()
method
JavaScript::Duktape::Object
JavaScript::Duktape::Object
use overload and AUTOLOAD internally, so there is no way to guess if you're trying to get a property type of function or executing it, this is the same as javascript behaviour
# js
$js->eval(q{
function test () {
return 'Hi';
}
print(test); // function(){ ... }
print(test()) // Hi
});
## same thing when we do it in perl
my $test = $js->get_object('test');
print $test, "\n"; #JavaScript::Duktape::Function=CODE(...)
print $test->(), "\n"; #Hi
This may sound ok with simple function calls but gets ugly not perlish when you're trying to call deep object properties
So JavaScript::Duktape
exports a special variable underscore '_' by default this to indicate that we are calling the function with no arguments
$js->eval(q{
function Person (name){
this.name = name;
}
Person.prototype.getName = function(){
print(this.name);
};
var me = new Person('Joe');
print(me.getName); // function(){ ... }
print(me.getName()); // Joe
});
# Now let's do it in perl
my $Person = $js->get_object('Person');
my $me = $Person->new('Joe');
print $me->getName, "\n"; #JavaScript::Duktape::Function=CODE(...)
print $me->getName(), "\n"; #JavaScript::Duktape::Function=CODE(...)
print $me->getName->(), "\n"; # Joe
#however if you pass any argument with the function it will work
print $me->getName(0), "\n"; #Joe
# or you can use special null argument _ which we export by default
print $me->getName(_), "\n"; #Joe
AUTHOR
Mamod Mehyar <mamod.mehyar@gmail.com>
CONTRIBUTORS
Big thanks for the much appreciated contributors
Rodrigo de Oliveira @rodrigolive
jomo666 @jomo666
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.