duktape
Duktape.cr
Duktape.cr provides Crystal bindings to the Duktape javascript engine.
Installation
Duktape.cr is best installed using either Shards or the Crystal Package Manager.
Via Crystal Package Manager
Add this to your Projectfile
:
deps do
github "jessedoyle/duktape.cr", name: "duktape"
end
then execute:
crystal deps
Finally build the native Duktape library:
make -C libs/duktape/ext libduktape
Via Shards
Add this to your shard.yml
:
name: example # your project's name
version: 1.0.0 # your project's version
dependencies:
duktape:
github: jessedoyle/duktape.cr
version: ~> 0.6.0
then execute:
shards install
Note that Shards v0.4.0
or greater will automatically make the native library. Otherwise you will have to make the library manually by calling make libduktape
from libs/duktape/ext
.
Usage
You must first create a Duktape context:
require "duktape"
sbx = Duktape::Sandbox.new
sbx.eval! <<-JS
var birthYear = 1990;
function calcAge(birthYear){
var current = new Date();
var year = current.getFullYear();
return year - birthYear;
}
print("You are " + calcAge(birthYear) + " years old.");
JS
An overwhelming majority of the Duktape API has been implemented. You can call the API functions directly on a Duktape::Sandbox
or Duktape::Context
instance:
sbx = Duktape::Sandbox.new
sbx.push_global_object # [ global ]
sbx.push_string "Math" # [ global "Math" ]
sbx.get_prop -2 # [ global Math ]
sbx.push_string "PI" # [ global Math "PI" ]
sbx.get_prop -2 # [ global Math PI ]
pi = sbx.get_number -1
puts "PI: #{pi}" # => PI: 3.14159
sbx.pop_3
Eval vs Eval!
All of the evaluation API methods have a corresponding bang-method (!
). The bang method calls will raise when a javascript error occurs, the non-bang methods will not raise on invalid javascript.
For example:
sbx = Duktape::Context.new
sbx.eval <<-JS
var a =
JS
will not raise any errors, but will return a non-zero error code.
The following code:
sbx = Duktape::Context.new
sbx.eval! <<-JS
__invalid();
JS
will raise Duktape::Error "SyntaxError"
.
Sandbox vs Context
You should only execute untrusted javascript code from within a Duktape::Sandbox
instance. A sandbox isolates code from insecure operations such as Duktape's internal require
mechanism and the Duktape
global javascript object.
Creating a Duktape::Context
gives code access to internal Duktape properties:
ctx = Duktape::Context.new
ctx.eval! <<-JS
print(Duktape.version);
JS
Setting a Timeout
Duktape::Sandbox
instances may optionally take an execution timeout limit in milliseconds. This provides protection against infinite loops when executing untrusted code.
A Duktape::Error "RangeError"
exception is raised when the following code executes for longer than specified:
sbx = Duktape::Sandbox.new 500 # 500ms execution time limit
sbx.eval! "while (true) {}" # => RangeError
Calling Crystal Code from Javascript
Note: This functionality is considered experimental and syntax/functionality may change dramatically between releases.
It is possible to call Crystal code from your javascript:
sbx = Duktape::Sandbox.new
sbx.push_global_object
sbx.push_proc(2) do |ptr| # 2 stack arguments
env = Duktape::Sandbox.new ptr
a = env.get_number 0
b = env.get_number 1
env.push_number a + b
env.return 1 # return success
end
sbx.put_prop_string -2, "adder"
sbx.eval! "print(adder(2, 3));" # => 5
Contributing
I'll accept any pull requests that are well tested for bugs/features with Duktape.cr.
You should fork the main repo, create a feature branch, write tests and submit a pull request.
License
Duktape.cr is licensed under the MIT License. Please see LICENSE
for details.