execjs-fastnode 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.gitmodules +3 -0
- data/.travis.yml +13 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +98 -0
- data/Rakefile +17 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docs/example_new.mscgen +21 -0
- data/docs/example_new.png +0 -0
- data/docs/example_old.mscgen +25 -0
- data/docs/example_old.png +0 -0
- data/execjs-fastnode.gemspec +28 -0
- data/lib/execjs/fastnode.rb +8 -0
- data/lib/execjs/fastnode/command.rb +47 -0
- data/lib/execjs/fastnode/external_piped_runtime.rb +155 -0
- data/lib/execjs/fastnode/node_piped_runner.js +72 -0
- data/lib/execjs/fastnode/runtimes.rb +18 -0
- data/lib/execjs/fastnode/version.rb +5 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 13794e0f836dc1cc7c7eb16a23391df25a556e99
|
4
|
+
data.tar.gz: 66e77b96d13a3bd2be1fd42e249f89e9c832aafb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 017acad49287c35d0cf4c573939ddbdad3af1e55413d041ec024a71d76b7444286fa9794515fa18b395934e7f75e033100b393b52d3ade132b2e9ec1c85f8b0e
|
7
|
+
data.tar.gz: af736abba04aeb15cae84a798f2556d9ebb052ae12c76329de34e2c3f65073c26bec5f4e9616a0420b8cb3cd109ffe6e73c0b8e2560f01bcdc15a26084d13f8f
|
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 John Hawthorn
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# ExecJS FastNode
|
2
|
+
|
3
|
+
## What's this?
|
4
|
+
|
5
|
+
An alternative implementation of ExecJS's Node.js integration. This aims to speed up sprockets compilation without needing to embed a javascript interpreter inside of ruby like [therubyracer](cowboyd/therubyracer).
|
6
|
+
|
7
|
+
## How much faster is it?
|
8
|
+
|
9
|
+
Much.
|
10
|
+
|
11
|
+
```
|
12
|
+
$ rake bench
|
13
|
+
...
|
14
|
+
user system total real
|
15
|
+
Node.js (V8) fast 0.000000 0.000000 0.000000 ( 0.069723)
|
16
|
+
therubyracer (V8) 0.020000 0.000000 0.020000 ( 0.018010)
|
17
|
+
Node.js (V8) 0.000000 0.010000 1.470000 ( 1.487579)
|
18
|
+
```
|
19
|
+
|
20
|
+
Okay, so it's not as fast as `therubyracer`, but it's 20x faster than the standard ExecJS Node.js implementation.
|
21
|
+
|
22
|
+
The benchmark measures the time to compile the javascript CoffeeScript compiler and then eval a small snippet 10 times.
|
23
|
+
|
24
|
+
## How?
|
25
|
+
|
26
|
+
The existing ExecJS runtime has to run a new Node.js process each time any JS is to be run. This means that if you are loading up the CoffeeScript compiler in order to compile some sprockets assets, it needs to reload the node executable and the entire coffeescript compiler for each file it is compiling.
|
27
|
+
|
28
|
+
This implementation avoids this by starting an always running Node.js process connected by pipes to STDIN and STDOUT. The JS to execute is piped into the process and the results are recieved from its output. Isolation between different ExecJS contexts is achieved through Node's [vm.Script](https://nodejs.org/api/vm.html).
|
29
|
+
|
30
|
+
| Old | New |
|
31
|
+
| --- | --- |
|
32
|
+
| ![](docs/example_old.png) | ![](docs/example_new.png) |
|
33
|
+
|
34
|
+
## Is this production ready?
|
35
|
+
|
36
|
+
Maybe? It needs more testing to be labeled as such. If you encounter any troubles please [file an issue](https://github.com/jhawthorn/execjs-fastnode/issues/new).
|
37
|
+
|
38
|
+
Currently minimal effort is made to handle catastrophic errors: Node.js crashing, running out of memory, being killed. All of which result in `Errno::EPIPE: Broken pipe` for future ExecJS calls.
|
39
|
+
|
40
|
+
It's probably fine for development.
|
41
|
+
|
42
|
+
## Installation
|
43
|
+
|
44
|
+
Add this line to your application's Gemfile:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
gem 'execjs-fastnode'
|
48
|
+
```
|
49
|
+
|
50
|
+
And then `bundle install`. You know the drill.
|
51
|
+
|
52
|
+
You can verify that this runtime is being autodetected and used by checking `ExecJS.runtime` in a console.
|
53
|
+
|
54
|
+
You can force a certain runtime to be used using `EXECJS_RUNTIME=FastNode`
|
55
|
+
|
56
|
+
```
|
57
|
+
$ EXECJS_RUNTIME=FastNode bin/console
|
58
|
+
> ExecJS.runtime
|
59
|
+
=> #<ExecJS::FastNode::ExternalPipedRuntime:0x005599c0d38740 @name="Node.js (V8) fast"...
|
60
|
+
|
61
|
+
$ EXECJS_RUNTIME=Node bin/console
|
62
|
+
> ExecJS.runtime
|
63
|
+
=> #<ExecJS::ExternalRuntime:0x00559c440347c0 @name="Node.js (V8)" ...
|
64
|
+
```
|
65
|
+
|
66
|
+
## Why not upstream this to execjs?
|
67
|
+
|
68
|
+
I'd like to, but it will take time. The goal of this endeavour is to give users a fast ExecJS runtime without needing any additional dependencies, just a Node.js installation. However most users don't (and shouldn't have to) think about ExecJS and need it to Just Work. This is a pretty significant change and it probably won't "Just Work" right now, and more time is needed to tell if it can.
|
69
|
+
|
70
|
+
I have sent a pull request upstream to [use vm.runInContext](https://github.com/rails/execjs/pull/55), which would be the first step to merging this upstream.
|
71
|
+
|
72
|
+
## Why not just use therubyracer?
|
73
|
+
|
74
|
+
Maybe you should, if you need the speed.
|
75
|
+
|
76
|
+
Heroku [recommends againts it](https://devcenter.heroku.com/articles/rails-asset-pipeline#therubyracer) as do various stack overflow answers with vague mentions of high memory usage.
|
77
|
+
I haven't seen any benchmarks or bug reports that demonstrate this, so I consider these claims somewhat suspect.
|
78
|
+
|
79
|
+
[mini_racer](https://github.com/discourse/mini_racer) is another option.
|
80
|
+
|
81
|
+
The ExecJS Node runtime has its benefits as well. It should works on jRuby and other non-MRI runtimes.
|
82
|
+
If this were merged upstream it would give developers fast javascript execution without needing an extra gem or configuration, just a working `node` somewhere in `$PATH`.
|
83
|
+
|
84
|
+
## Development
|
85
|
+
|
86
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
87
|
+
|
88
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
89
|
+
|
90
|
+
## Contributing
|
91
|
+
|
92
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jhawthorn/execjs-fastnode.
|
93
|
+
|
94
|
+
|
95
|
+
## License
|
96
|
+
|
97
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
98
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
task :bench do
|
5
|
+
sh 'ruby -Ilib -r./test/shim test/bench_execjs.rb'
|
6
|
+
end
|
7
|
+
|
8
|
+
task :test do
|
9
|
+
ENV["EXECJS_RUNTIME"] = 'FastNode'
|
10
|
+
end
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << "test"
|
13
|
+
t.libs << "lib"
|
14
|
+
t.test_files = FileList['test/shim.rb', 'test/**/*_test.rb', 'test/test_execjs.rb']
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => :test
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "execjs/fastnode"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
msc {
|
2
|
+
hscale = "1";
|
3
|
+
|
4
|
+
ruby,node;
|
5
|
+
|
6
|
+
node=>node [ label = "start" ] ;
|
7
|
+
ruby=>ruby [ label = "c = ExecJS.compile(...)" ];
|
8
|
+
ruby->node [ label = "CoffeeScript source code" ] ;
|
9
|
+
node=>node [ label = "Evaluate CoffeeScript source" ] ;
|
10
|
+
||| ;
|
11
|
+
ruby=>ruby [ label = "c.call('CoffeeScript', 'compile', ...)" ];
|
12
|
+
ruby->node [ label = "CoffeeScript.compile('(x) -> x * x')" ] ;
|
13
|
+
node=>node [ label = "CoffeeScript.compile(...)" ] ;
|
14
|
+
node->ruby [ label = "\"(function(x){ return x * x })\"" ] ;
|
15
|
+
||| ;
|
16
|
+
ruby=>ruby [ label = "c.call('CoffeeScript', 'compile', ...)" ];
|
17
|
+
ruby->node [ label = "CoffeeScript.compile('(x) -> x * 2')" ] ;
|
18
|
+
node=>node [ label = "CoffeeScript.compile(...)" ] ;
|
19
|
+
node->ruby [ label = "\"(function(x){ return x * 2 })\"" ] ;
|
20
|
+
}
|
21
|
+
|
Binary file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
msc {
|
2
|
+
hscale = "1";
|
3
|
+
|
4
|
+
ruby,node;
|
5
|
+
|
6
|
+
ruby=>ruby [ label = "c = ExecJS.compile(...)" ];
|
7
|
+
ruby=>ruby [ label = "c.call('CoffeeScript', 'compile', ...)" ];
|
8
|
+
node=>node [ label = "start" ] ;
|
9
|
+
ruby->node [ label = "CoffeeScript source code" ] ;
|
10
|
+
ruby->node [ label = "CoffeeScript.compile('(x) -> x * x')" ] ;
|
11
|
+
node=>node [ label = "Evaluate CoffeeScript source" ] ;
|
12
|
+
node=>node [ label = "CoffeeScript.compile(...)" ] ;
|
13
|
+
node->ruby [ label = "\"(function(x){ return x * x })\"" ] ;
|
14
|
+
node=>node [ label = "exit" ] ;
|
15
|
+
||| ;
|
16
|
+
ruby=>ruby [ label = "c.call('CoffeeScript', 'compile', ...)" ];
|
17
|
+
node=>node [ label = "start" ] ;
|
18
|
+
ruby->node [ label = "CoffeeScript source code" ] ;
|
19
|
+
ruby->node [ label = "CoffeeScript.compile('(x) -> x * 2')" ] ;
|
20
|
+
node=>node [ label = "Evaluate CoffeeScript source" ] ;
|
21
|
+
node=>node [ label = "CoffeeScript.compile(...)" ] ;
|
22
|
+
node->ruby [ label = "\"(function(x){ return x * 2 })\"" ] ;
|
23
|
+
node=>node [ label = "exit" ] ;
|
24
|
+
}
|
25
|
+
|
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'execjs/fastnode/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "execjs-fastnode"
|
8
|
+
spec.version = ExecJS::FastNode::VERSION
|
9
|
+
spec.authors = ["John Hawthorn"]
|
10
|
+
spec.email = ["john.hawthorn@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A faster implementation of ExecJS's node runtime}
|
13
|
+
spec.description = %q{An ExecJS node runtime which uses pipes to avoid repeated startup costs}
|
14
|
+
spec.homepage = "https://github.com/jhawthorn/execjs-fastnode"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "execjs", "~> 2.0"
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ExecJS
|
2
|
+
module FastNode
|
3
|
+
class Command
|
4
|
+
def initialize(command)
|
5
|
+
@command = command
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_cmd
|
9
|
+
which
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def locate_executable(command)
|
15
|
+
commands = Array(command)
|
16
|
+
if ExecJS.windows? && File.extname(command) == ""
|
17
|
+
ENV['PATHEXT'].split(File::PATH_SEPARATOR).each { |p|
|
18
|
+
commands << (command + p)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
commands.find { |cmd|
|
23
|
+
if File.executable? cmd
|
24
|
+
cmd
|
25
|
+
else
|
26
|
+
path = ENV['PATH'].split(File::PATH_SEPARATOR).find { |p|
|
27
|
+
full_path = File.join(p, cmd)
|
28
|
+
File.executable?(full_path) && File.file?(full_path)
|
29
|
+
}
|
30
|
+
path && File.expand_path(cmd, path)
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def which
|
36
|
+
Array(@command).find do |name|
|
37
|
+
name, args = name.split(/\s+/, 2)
|
38
|
+
path = locate_executable(name)
|
39
|
+
|
40
|
+
next unless path
|
41
|
+
|
42
|
+
args ? "#{path} #{args}" : path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require "tmpdir"
|
2
|
+
require 'json'
|
3
|
+
require "open3"
|
4
|
+
require "thread"
|
5
|
+
require "execjs/runtime"
|
6
|
+
require "execjs/fastnode/command"
|
7
|
+
|
8
|
+
module ExecJS
|
9
|
+
module FastNode
|
10
|
+
class ExternalPipedRuntime < ExecJS::Runtime
|
11
|
+
class VM
|
12
|
+
attr_reader :stdin, :stdout
|
13
|
+
|
14
|
+
def initialize(options)
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@stdin = @stdout = @wait_thr = nil
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def started?
|
21
|
+
!!@stdin
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.finalize(stdin)
|
25
|
+
proc { stdin.puts('{"cmd": "exit", "arguments": [0]}') }
|
26
|
+
end
|
27
|
+
|
28
|
+
def exec(context, source)
|
29
|
+
command("exec", {context: context, source: source})
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete_context(context)
|
33
|
+
command("deleteContext", context)
|
34
|
+
end
|
35
|
+
|
36
|
+
def start
|
37
|
+
return if started?
|
38
|
+
@mutex.synchronize do
|
39
|
+
# will double check started? with the lock held
|
40
|
+
start_without_synchronization
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def start_without_synchronization
|
47
|
+
return if started?
|
48
|
+
@stdin, @stdout, @wait_thr = Open3.popen2(@options[:binary], @options[:runner_path])
|
49
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@stdin))
|
50
|
+
end
|
51
|
+
|
52
|
+
def command(cmd, *arguments)
|
53
|
+
start
|
54
|
+
@mutex.synchronize do
|
55
|
+
@stdin.puts(::JSON.generate({cmd: cmd, args: arguments}))
|
56
|
+
result = ::JSON.parse(@stdout.gets, create_additions: false)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Context < Runtime::Context
|
62
|
+
def initialize(runtime, source = "", options = {})
|
63
|
+
@runtime = runtime
|
64
|
+
@uuid = SecureRandom.uuid
|
65
|
+
|
66
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@runtime, @uuid))
|
67
|
+
|
68
|
+
source = encode(source)
|
69
|
+
|
70
|
+
raw_exec(source)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.finalize(runtime, uuid)
|
74
|
+
proc { runtime.vm.delete_context(uuid) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def eval(source, options = {})
|
78
|
+
if /\S/ =~ source
|
79
|
+
raw_exec("(#{source})")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def exec(source, options = {})
|
84
|
+
raw_exec("(function(){#{source}})()")
|
85
|
+
end
|
86
|
+
|
87
|
+
def raw_exec(source, options = {})
|
88
|
+
source = encode(source)
|
89
|
+
|
90
|
+
result = @runtime.vm.exec(@uuid, source)
|
91
|
+
extract_result(result)
|
92
|
+
end
|
93
|
+
|
94
|
+
def call(identifier, *args)
|
95
|
+
eval "#{identifier}.apply(this, #{::JSON.generate(args)})"
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def extract_result(output)
|
101
|
+
status, value, stack = output
|
102
|
+
if status == "ok"
|
103
|
+
value
|
104
|
+
else
|
105
|
+
stack ||= ""
|
106
|
+
stack = stack.split("\n").map do |line|
|
107
|
+
line.sub(" at ", "").strip
|
108
|
+
end
|
109
|
+
stack.reject! { |line| ["eval code", "eval@[native code]"].include?(line) }
|
110
|
+
stack.shift unless stack[0].to_s.include?("(execjs)")
|
111
|
+
error_class = value =~ /SyntaxError:/ ? RuntimeError : ProgramError
|
112
|
+
error = error_class.new(value)
|
113
|
+
error.set_backtrace(stack + caller)
|
114
|
+
raise error
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
attr_reader :name, :vm
|
120
|
+
|
121
|
+
def initialize(options)
|
122
|
+
@name = options[:name]
|
123
|
+
@command = Command.new(options[:command])
|
124
|
+
@runner_path = options[:runner_path]
|
125
|
+
@encoding = options[:encoding]
|
126
|
+
@deprecated = !!options[:deprecated]
|
127
|
+
@binary = nil
|
128
|
+
|
129
|
+
@popen_options = {}
|
130
|
+
@popen_options[:external_encoding] = @encoding if @encoding
|
131
|
+
@popen_options[:internal_encoding] = ::Encoding.default_internal || 'UTF-8'
|
132
|
+
end
|
133
|
+
|
134
|
+
def vm
|
135
|
+
@vm ||= VM.new(
|
136
|
+
binary: @binary,
|
137
|
+
runner_path: @runner_path
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
def available?
|
142
|
+
binary ? true : false
|
143
|
+
end
|
144
|
+
|
145
|
+
def deprecated?
|
146
|
+
@deprecated
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def binary
|
151
|
+
@binary ||= @command.to_cmd
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
(function(){
|
2
|
+
var stdin = process.stdin
|
3
|
+
var stdout = process.stdout
|
4
|
+
var buf = ""
|
5
|
+
|
6
|
+
stdin.setEncoding('utf8')
|
7
|
+
|
8
|
+
var vm = require('vm');
|
9
|
+
var contexts = {};
|
10
|
+
|
11
|
+
function getContext(uuid) {
|
12
|
+
return contexts[uuid] || (contexts[uuid] = vm.createContext())
|
13
|
+
}
|
14
|
+
|
15
|
+
var commands = {
|
16
|
+
deleteContext: function(uuid) {
|
17
|
+
delete contexts[uuid];
|
18
|
+
return 1;
|
19
|
+
},
|
20
|
+
exit: function(code) {
|
21
|
+
process.exit(code)
|
22
|
+
},
|
23
|
+
exec: function execJS(input) {
|
24
|
+
var context = getContext(input.context);
|
25
|
+
var source = input.source;
|
26
|
+
try {
|
27
|
+
var program = function(){
|
28
|
+
return vm.runInContext(source, context, "(execjs)");
|
29
|
+
}
|
30
|
+
result = program();
|
31
|
+
if (typeof result == 'undefined' && result !== null) {
|
32
|
+
return ['ok'];
|
33
|
+
} else {
|
34
|
+
try {
|
35
|
+
return ['ok', result];
|
36
|
+
} catch (err) {
|
37
|
+
return ['err', '' + err, err.stack];
|
38
|
+
}
|
39
|
+
}
|
40
|
+
} catch (err) {
|
41
|
+
return ['err', '' + err, err.stack];
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
function processLine(line){
|
47
|
+
var input = JSON.parse(line)
|
48
|
+
var result = commands[input.cmd].apply(null, input.args)
|
49
|
+
|
50
|
+
var outputJSON = JSON.stringify(result)
|
51
|
+
stdout.write(outputJSON)
|
52
|
+
stdout.write('\n')
|
53
|
+
}
|
54
|
+
|
55
|
+
function processBuffer(){
|
56
|
+
if(buf.indexOf('\n') >= 0){
|
57
|
+
var lines = buf.split('\n')
|
58
|
+
for(var i = 0; i < lines.length - 1; i++) {
|
59
|
+
processLine(lines[i]);
|
60
|
+
}
|
61
|
+
buf = lines[lines.length - 1]
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
process.stdin.on('readable', function(){
|
66
|
+
var chunk = process.stdin.read();
|
67
|
+
if (chunk !== null) {
|
68
|
+
buf += chunk
|
69
|
+
processBuffer()
|
70
|
+
}
|
71
|
+
});
|
72
|
+
})();
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "execjs/fastnode/version"
|
2
|
+
require "execjs/fastnode/external_piped_runtime"
|
3
|
+
require "execjs/runtimes"
|
4
|
+
|
5
|
+
module ExecJS
|
6
|
+
module Runtimes
|
7
|
+
# re-opening the runtimes class
|
8
|
+
FastNode = FastNode::ExternalPipedRuntime.new(
|
9
|
+
name: "Node.js (V8) fast",
|
10
|
+
command: ["nodejs", "node"],
|
11
|
+
runner_path: File.expand_path('../../fastnode/node_piped_runner.js', __FILE__),
|
12
|
+
encoding: 'UTF-8'
|
13
|
+
)
|
14
|
+
|
15
|
+
# Place FastNode runtime first
|
16
|
+
runtimes.unshift(FastNode)
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: execjs-fastnode
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Hawthorn
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: execjs
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
description: An ExecJS node runtime which uses pipes to avoid repeated startup costs
|
70
|
+
email:
|
71
|
+
- john.hawthorn@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".gitmodules"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
85
|
+
- docs/example_new.mscgen
|
86
|
+
- docs/example_new.png
|
87
|
+
- docs/example_old.mscgen
|
88
|
+
- docs/example_old.png
|
89
|
+
- execjs-fastnode.gemspec
|
90
|
+
- lib/execjs/fastnode.rb
|
91
|
+
- lib/execjs/fastnode/command.rb
|
92
|
+
- lib/execjs/fastnode/external_piped_runtime.rb
|
93
|
+
- lib/execjs/fastnode/node_piped_runner.js
|
94
|
+
- lib/execjs/fastnode/runtimes.rb
|
95
|
+
- lib/execjs/fastnode/version.rb
|
96
|
+
homepage: https://github.com/jhawthorn/execjs-fastnode
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.5.1
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: A faster implementation of ExecJS's node runtime
|
120
|
+
test_files: []
|