=encoding utf-8
=head1 NAME
JavaScript::Duktape - Perl interface to Duktape embeddable javascript engine
=head1 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);
}
})();
});
=head1 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.
=head1 JavaScript::Duktape->new(%options)
initiate JavaScript::Duktape with options
=head2 options
=over 4
=item max_memory
Set maximum memory allowed for the excuted javascript code to consume, not setting this option is the default, which means no restricts on the maximum memory that can be consumed.
Minumum value to set for the C
max_memory => 256 * 1024 * 2
You can resize the memory allowed to consume on different executions by calling
C
=item timout
Set maximum time javascript code can run, this value represented in seconds and is not 100% guranteed that the javascript code will fail after the exact value passed, but it will eventually fail on first tick checking.
Not setting this option is the default, which means no timeout checking at all
timeout => 5
You can override this value later on another code evaluation by calling C
$js->set_timeout(25);
See L section below
=back
=head1 methods
=over 4
=item 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
=item 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');
=item 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
=item get_object('name');
Same as C
$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 C
=back
=head1 Sandboxing
As of version C<2.2.0> C
C
# prevent javascript code to consume memory more
# than max_memory option
my $js = JavaScript::Duktape->new( max_memory => 256 * 1024 );
# this will fail with "Error: alloc failed" message
# when running, because it will consume more memory
# than the allowed max_memory
$js->eval(q{
var str = '';
while(1){ str += 'XXXX' }
});
=head2 C<set_timout(t)>
Enable/Disable timeout checking, to disable set the value to 0 this value is in seconds
my $js = JavaScript::Duktape->new();
# throw 'time out' Error if executed
# js code does not finish after 5 seconds
$js->set_timeout(5);
eval {
$js->eval(q{
while(1){}
});
};
print $@, "\n"; #RangeError: execution timeout
# disable timeout checking
$js->set_timeout(0);
# now will run infinitely
$js->eval(q{
while(1){}
});
This method can be used with duktape VM instance too
my $js = JavaScript::Duktape->new();
my $duk = $js->duk();
$duk->set_timeout(3);
$duk->peva_stringl(q{
while (1){}
});
print $duk->safe_to_string(-1); # Error: execution 'time out'
=head2 C<resize_memory(m)>
This method will have effect only if you intiated with max_memory option
my $js = JavaScript::Duktape->new( max_memory => 1024 * 256 );
eval {
$js->eval(q{
var buf = Buffer(( 1024 * 256 ) + 1000 );
print('does not reach');
});
};
print $@, "\n"; # Error: 'alloc failed'
$js->resize_memory( 1024 * 256 * 2 );
# now it will not throw
$js->eval(q{
var buf = Buffer(( 1024 * 256 ) + 1000 );
print('ok');
});
=head1 VM API
vm api corresponds to Duktape Engine API see Lhttp://duktape.org/api.html
To access vm create new context then call C
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 C
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): ]
=head1 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 Lhttp://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 C
Besides duktape api, C
=over 4
=item 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;
});
=item 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;
});
=item push_perl( ... );
Push given perl data into the duktape stack.
=item to_perl(index);
Get the value at index and return it as perl data
=item 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
=item reset_top
resets duktape stack top
=back
=head1 EXPORTS
C
=over 4
=item true
=item false
=item null
=item _ (underscore)
This can be used to indicate that we are calling an object function without arguments, see L
=item this
This can be called from pushed perl sub
$duk->push_perl_function(sub{
my $this = this;
});
See C<examples/this.pl>
=back
=head1 DEAFULT JAVASCRIPT FUNCTIONS
JavaScript::Duktape has C
=head1 CAVEATS
=head2 VM methods
C
=head2 JavaScript::Duktape::Object
C
# 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 C
$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
=head1 AUTHOR
Mamod Mehyar C<< mamod.mehyar@gmail.com >>
=head1 CONTRIBUTORS
Thanks for everyone who contributed to this module, either by code, bug reports, API design or suggestions
=over 4
=item * Rodrigo de Oliveira L<@rodrigolive|https://github.com/rodrigolive>
=item * jomo666 L<@jomo666|https://github.com/jomo666>
=item * Viacheslav Tykhanovskyi L<@vti|https://github.com/vti>
=item * Slaven Rezić L<@eserte|https://github.com/eserte>
=item * Max Maischein L<@Corion|https://github.com/Corion>
=back
=head1 APPRECIATION
Credits should go to L<< Duktape Javascript embeddable engine|http://duktape.org >> and it's creator L<< Sami Vaarala|https://github.com/svaarala >>
=head1 LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.