Skip to content

Commit

Permalink
Prepare for release
Browse files Browse the repository at this point in the history
  • Loading branch information
Devin Torres committed Nov 1, 2013
1 parent 232c748 commit 3204fc5
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 42 deletions.
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# 2 space indentation
[*.{ex,exs,eex}]
indent_style = space
indent_size = 2
20 changes: 20 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright © 2013 Devin Torres <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
37 changes: 28 additions & 9 deletions lib/execjs.ex
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
defmodule Execjs do
import Execjs.Escape, only: [escape: 1]

defexception Error, message: nil
defexception RuntimeError, message: nil
defexception RuntimeUnavailable, message: "Could not find JavaScript runtime"

defexception RuntimeUnavailable,
message: "Could not find a JavaScript runtime"
@spec eval(String.t) :: any
def eval(source) when is_binary(source) do
exec %s[eval("#{escape(source)}")]
end

def compile(source) do
@spec compile(String.t) :: (String.t -> String.t)
def compile(source) when is_binary(source) do
{ pre, post } = { "(function(){\n#{source};\n", ";\n})()" }
fn (source) ->
pre <> source <> post
end
end

def call(context, thing, args) do
source = "return #{thing}.apply(this, #{JSON.encode!(args)})"
eval(context.(source))
@spec call((String.t -> String.t), String.t, list(any)) :: any
def call(context, identifier, args) when is_binary(identifier) and is_list(args) do
source = "return #{identifier}.apply(this, #{JSON.encode!(args)})"
exec context.(source)
end

def eval(source) do
defp exec(source) do
runtime = Execjs.Runtimes.best_available
program = runtime.template(escape(source))
program = runtime.template(source)
command = runtime.command |> System.find_executable
tmpfile = compile_to_tempfile(program)

try do
port = Port.open({ :spawn_executable, command },
[:binary, :eof, :hide, { :args, [tmpfile] }])

loop(port)
extract_result(loop(port))
after
File.rm! tmpfile
end
Expand All @@ -55,4 +61,17 @@ defmodule Execjs do
File.write! path, program
path
end

defp extract_result(output) do
case JSON.decode!(output) do
[ "ok", value ] ->
value
[ "ok" ] ->
:undefined
[ "err", message ] ->
raise Execjs.RuntimeError, message: message
[ "err" ] ->
raise Execjs.Error
end
end
end
11 changes: 4 additions & 7 deletions lib/execjs/escape.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
defmodule Execjs.Escape do
@compile :native

def escape(""), do: ""

def escape(string) do
iolist_to_binary(escape(string, ""))
end
def escape(""), do: ""
def escape(string), do: escape(string, "")

escape_map = [
{ ?\\, "\\\\" },
Expand All @@ -19,12 +16,12 @@ defmodule Execjs.Escape do

lc { char, escaped } inlist escape_map do
defp escape(<< unquote(char), rest :: binary >>, acc) do
escape(rest, [acc, unquote(escaped)])
escape(rest, << acc :: binary, unquote(escaped) >>)
end
end

defp escape(<< char :: utf8, rest :: binary >>, acc) do
escape(rest, [acc, char])
escape(rest, << acc :: binary, char :: utf8 >>)
end

defp escape(<<>>, acc) do
Expand Down
22 changes: 21 additions & 1 deletion lib/execjs/runtimes.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
defmodule Execjs.Runtimes do
import Execjs.Runtime

alias Execjs.RuntimeUnavailable

Module.register_attribute __MODULE__, :runtimes, accumulate: true

defruntime Node,
command: "node",
runner: "node_runner.js.eex"

defruntime SpiderMonkey,
command: "js",
runner: "spidermonkey_runner.js.eex"

defruntime JavaScriptCore,
command: "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc",
runner: "jsc_runner.js.eex"

defruntime Rhino,
command: "rhino",
runner: "rhino_runner.js.eex"

def runtimes do
unquote(Enum.reverse(@runtimes))
end
Expand All @@ -20,7 +30,17 @@ defmodule Execjs.Runtimes do
{ :ok, runtime } ->
runtime
:undefined ->
runtime = Enum.find(runtimes, &(&1.available?)) || raise RuntimeUnavailable
runtime = case System.get_env("EXECJS_RUNTIME") do
nil ->
Enum.find(runtimes, &(&1.available?)) || raise RuntimeUnavailable
name ->
runtime = Module.concat(__MODULE__, name)
Code.ensure_loaded?(runtime)
&& function_exported?(runtime, :available?, 0)
&& runtime.available?
|| raise RuntimeUnavailable
runtime
end
:application.set_env(:execjs, :runtime, runtime)
runtime
end
Expand Down
8 changes: 6 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Execjs.Mixfile do
[ app: :execjs,
version: "0.0.1",
elixir: "~> 0.10.3",
deps: deps ]
deps: deps(Mix.env) ]
end

# Configuration for the OTP application
Expand All @@ -15,7 +15,11 @@ defmodule Execjs.Mixfile do

# Returns the list of dependencies in the format:
# { :foobar, "~> 0.1", git: "https://github.com/elixir-lang/foobar.git" }
defp deps do
defp deps(:dev) do
deps(:prod) ++ [{ :benchmark, github: "meh/elixir-benchmark" }]
end

defp deps(_) do
[ { :jazz, github: "meh/jazz", tag: "v0.0.1" } ]
end
end
3 changes: 2 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
[ "jazz": {:git, "git://github.com/meh/jazz.git", "5b3fd0e3efd2c8445289bdbda2f93bb0d96ad169", [{:tag, "v0.0.1"}]} ]
[ "benchmark": {:git, "git://github.com/meh/elixir-benchmark.git", "d5e15aabff417f1ffc9ecf8e5966f5c338b10c1e", []},
"jazz": {:git, "git://github.com/meh/jazz.git", "5b3fd0e3efd2c8445289bdbda2f93bb0d96ad169", [{:tag, "v0.0.1"}]} ]
16 changes: 8 additions & 8 deletions priv/jsc_runner.js.eex
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
(function(program, execJS) { execJS(program); })(function() {
return eval("<%= source %>");
(function(program, runner) { runner(program); })(function() {
return <%= source %>;
}, function(program) {
var result;
try {
result = program();
if (reult === undefined) {
print('["ok"]');
} else {
try {
try {
if (result === undefined) {
print('["ok"]');
} else {
print(JSON.stringify(['ok', result]));
} catch (err) {
print('["err"]');
}
} catch (err) {
print('["err"]');
}
} catch (err) {
print(JSON.stringify(['err', '' + err]));
Expand Down
16 changes: 8 additions & 8 deletions priv/node_runner.js.eex
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
(function(program, execJS) { execJS(program); })(function() {
return eval("<%= source %>");
(function(program, runner) { runner(program); })(function() {
return <%= source %>;
}, function(program) {
var result;
try {
result = program();
if (result === undefined) {
process.stdout.write('["ok"]');
} else {
try {
try {
if (result === undefined) {
process.stdout.write('["ok"]');
} else {
process.stdout.write(JSON.stringify(['ok', result]));
} catch (err) {
process.stdout.write('["err"]');
}
} catch (err) {
process.stdout.write('["err"]');
}
} catch (err) {
process.stdout.write(JSON.stringify(['err', '' + err]));
Expand Down
19 changes: 19 additions & 0 deletions priv/rhino_runner.js.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(function(program, runner) { runner(program); })(function() {
return <%= source %>;
}, function(program) {
var result;
try {
result = program();
try {
if (result === undefined) {
process.stdout.write('["ok"]');
} else {
java.lang.System.out.println(JSON.stringify(['ok', result]));
}
} catch (err) {
java.lang.System.out.println('["err"]');
}
} catch (err) {
java.lang.System.out.println(JSON.stringify(['err', '' + err]));
}
});
19 changes: 19 additions & 0 deletions priv/spidermonkey_runner.js.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(function(program, runner) { runner(program); })(function() {
return <%= source %>;
}, function(program) {
var result;
try {
result = program();
try {
if (result === undefined) {
print('["ok"]');
} else {
print(JSON.stringify(['ok', result]));
}
} catch (err) {
print('["err"]');
}
} catch (err) {
print(JSON.stringify(['err', '' + err]));
}
});
15 changes: 9 additions & 6 deletions test/execjs_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ defmodule ExecjsTest do
use ExUnit.Case

test "eval" do
# assert Execjs.eval("(function() { return 1 + 1; })();") == ["ok", 2]
assert Execjs.eval("1 + 1") == 2
assert Execjs.eval(%s{var a = "a"; a + "b"}) == "ab"
end

test "call" do
context = Execjs.compile(%S""")
function addOne(n) {
return n + 1;
}
"""
function addOne(n) {
return n + 1;
}
"""

IO.inspect Execjs.call(context, "addOne", [3])
assert Execjs.call(context, "addOne", [3]) == 4
end
end

0 comments on commit 3204fc5

Please sign in to comment.