isomorfeus-speednode 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +39 -10
- data/lib/isomorfeus-speednode.rb +3 -1
- data/lib/isomorfeus/execjs_module.rb +15 -0
- data/lib/isomorfeus/execjs_runtime.rb +18 -0
- data/lib/isomorfeus/execjs_runtimes.rb +11 -0
- data/lib/isomorfeus/speednode/{permissive_runner.js → runner.js} +63 -50
- data/lib/isomorfeus/speednode/runtime.rb +30 -4
- data/lib/isomorfeus/speednode/version.rb +1 -1
- metadata +6 -5
- data/lib/isomorfeus/speednode.rb +0 -27
- data/lib/isomorfeus/speednode/compatible_runner.js +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a1b8da4a29439d529c0645ee7d9e6c324bd0d586331555798f98e8cf45edc46
|
4
|
+
data.tar.gz: 9a86b8a8f18b96b0a0f4f8ac4ad7991ada4e5833ff59ba6b18fdc99a4ccc38cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4d35cd84a7371e9e255af034e42f26b384fcc19a7e5b9210b6df74cab5193e1f136be71fcc3cb228479952c7ed085bd7a5848df338b0b695a942a79d760986f
|
7
|
+
data.tar.gz: f312e192470c867709f8b7f120f1979e949f330c9a83cf5433613bda55f5e51ad913762430700e67bd669d64e130bb9b155b195f44db06811e0af71aa5ec2981
|
data/README.md
CHANGED
@@ -13,20 +13,43 @@ In Gemfile:
|
|
13
13
|
|
14
14
|
### Configuration
|
15
15
|
|
16
|
-
Isomorfeus-speednode provides
|
17
|
-
|
18
|
-
- `PermissiveSpeednode` - allows for the usage of javascript commonjs 'require' and can handle circular objects from javascript world, to some extend.
|
19
|
-
Best used in spec/test environments.
|
20
|
-
|
21
|
-
`CompatibleSpeednode` is the default. The permissive runtime can be chosen by:
|
16
|
+
Isomorfeus-speednode provides one node based runtime `Speednode` which runs scripts in node vms.
|
17
|
+
The runtime can be chosen by:
|
22
18
|
|
23
19
|
```ruby
|
24
|
-
ExecJS.runtime = ExecJS::Runtimes::
|
20
|
+
ExecJS.runtime = ExecJS::Runtimes::Speednode
|
25
21
|
```
|
26
|
-
If node cant find node modules,
|
22
|
+
If node cant find node modules for the permissive contexts (see below), its possible to set the load path before assigning the runtime:
|
27
23
|
```ruby
|
28
24
|
ENV['NODE_PATH'] = './node_modules'
|
29
25
|
```
|
26
|
+
### Contexts
|
27
|
+
|
28
|
+
Each ExecJS context runs in a node vm. Speednode offers two kinds of contexts:
|
29
|
+
- a compatible context, which is compatible with default ExecJS behavior.
|
30
|
+
- a permissive context, which is more permissive and allows to `require` node modules.
|
31
|
+
|
32
|
+
#### Compatible
|
33
|
+
A compatible context can be created with the standard `ExecJS.compile` or code can be executed within a compatible context by using the standard
|
34
|
+
`ExecJS.eval` or `ExecJS.exec`.
|
35
|
+
Example for a compatible context:
|
36
|
+
```ruby
|
37
|
+
compat_context = ExecJS.compile('Test = "test"')
|
38
|
+
compat_context.eval('1+1')
|
39
|
+
```
|
40
|
+
#### Permissive
|
41
|
+
A permissive context can be created with `ExecJS.permissive_compile` or code can be executed within a permissive context by using
|
42
|
+
`ExecJS.permissive_eval` or `ExecJS.permissive_exec`.
|
43
|
+
Example for a permissive context:
|
44
|
+
```ruby
|
45
|
+
perm_context = ExecJS.permissive_compile('Test = "test"')
|
46
|
+
perm_context.eval('1+1')
|
47
|
+
```
|
48
|
+
Evaluation in a permissive context:
|
49
|
+
```ruby
|
50
|
+
ExecJS.permissive_eval('1+1')
|
51
|
+
```
|
52
|
+
|
30
53
|
### Benchmarks
|
31
54
|
|
32
55
|
Highly scientific, maybe.
|
@@ -45,7 +68,13 @@ Node.js (V8) fast 0.191454 0.081396 0.272850 (
|
|
45
68
|
mini_racer (V8) 0.017091 0.002494 0.019585 ( 0.019584)
|
46
69
|
```
|
47
70
|
|
48
|
-
To run
|
71
|
+
To run benchmarks:
|
72
|
+
- clone repo
|
73
|
+
- `bundle install`
|
74
|
+
- `bundle exec rake bench`
|
75
|
+
|
76
|
+
### Tests
|
77
|
+
To run tests:
|
49
78
|
- clone repo
|
50
79
|
- `bundle install`
|
51
|
-
- `
|
80
|
+
- `bundle exec rake test`
|
data/lib/isomorfeus-speednode.rb
CHANGED
@@ -6,5 +6,7 @@ require 'isomorfeus/speednode/node_command'
|
|
6
6
|
require 'execjs/runtime'
|
7
7
|
require 'isomorfeus/speednode/runtime'
|
8
8
|
require "execjs/runtimes"
|
9
|
-
require 'isomorfeus/
|
9
|
+
require 'isomorfeus/execjs_runtime'
|
10
|
+
require 'isomorfeus/execjs_module'
|
11
|
+
require 'isomorfeus/execjs_runtimes'
|
10
12
|
require 'execjs'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ExecJS
|
2
|
+
class << self
|
3
|
+
def permissive_exec(source, options = {})
|
4
|
+
runtime.permissive_exec(source, options)
|
5
|
+
end
|
6
|
+
|
7
|
+
def permissive_eval(source, options = {})
|
8
|
+
runtime.permissive_eval(source, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def permissive_compile(source, options = {})
|
12
|
+
runtime.permissive_compile(source, options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ExecJS
|
2
|
+
# Abstract base class for runtimes
|
3
|
+
class Runtime
|
4
|
+
def permissive_exec(source, options = {})
|
5
|
+
context = permissive_compile("", options)
|
6
|
+
context.exec(source, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def permissive_eval(source, options = {})
|
10
|
+
context = permissive_compile("", options)
|
11
|
+
context.eval(source, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def permissive_compile(source, options = {})
|
15
|
+
context_class.new(self, source, options.merge({permissive: true}))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ExecJS
|
2
|
+
module Runtimes
|
3
|
+
Speednode = Isomorfeus::Speednode::Runtime.new(
|
4
|
+
name: 'Isomorfeus Speednode Node.js (V8)',
|
5
|
+
command: %w[nodejs node],
|
6
|
+
runner_path: File.join(File.dirname(__FILE__), 'speednode', 'runner.js'),
|
7
|
+
encoding: 'UTF-8'
|
8
|
+
)
|
9
|
+
runtimes.unshift(Speednode)
|
10
|
+
end
|
11
|
+
end
|
@@ -134,72 +134,85 @@ CircularJSON.stringify = function stringify(value, replacer, space, doNotResolve
|
|
134
134
|
* these versions.
|
135
135
|
*/
|
136
136
|
function massageStackTrace(stack) {
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
if (stack && stack.indexOf("SyntaxError") == 0) {
|
138
|
+
return "(execjs):1\n" + stack;
|
139
|
+
} else {
|
140
|
+
return stack;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
function createCompatibleContext() {
|
145
|
+
var c = vm.createContext();
|
146
|
+
vm.runInContext('delete this.console', c, "(execjs)");
|
147
|
+
return c;
|
142
148
|
}
|
143
149
|
|
144
|
-
function
|
145
|
-
|
150
|
+
function createPermissiveContext() {
|
151
|
+
return vm.createContext({ require, setTimeout });
|
152
|
+
}
|
153
|
+
|
154
|
+
function getCompatibleContext(uuid) {
|
155
|
+
return contexts[uuid] || (contexts[uuid] = createCompatibleContext());
|
156
|
+
}
|
157
|
+
function getPermissiveContext(uuid) {
|
158
|
+
return contexts[uuid] || (contexts[uuid] = createPermissiveContext());
|
146
159
|
}
|
147
160
|
|
148
161
|
var commands = {
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
return ['ok'];
|
156
|
-
},
|
157
|
-
exec: function execJS(input) {
|
158
|
-
var context = getContext(input.context);
|
159
|
-
var source = input.source;
|
160
|
-
try {
|
161
|
-
var program = function(){
|
162
|
-
return vm.runInContext(source, context, "(execjs)");
|
163
|
-
};
|
164
|
-
var result = program();
|
165
|
-
if (typeof result == 'undefined' && result !== null) {
|
162
|
+
deleteContext: function(uuid) {
|
163
|
+
delete contexts[uuid];
|
164
|
+
return [1];
|
165
|
+
},
|
166
|
+
exit: function(code) {
|
167
|
+
process_exit = code;
|
166
168
|
return ['ok'];
|
167
|
-
|
169
|
+
},
|
170
|
+
execp: function execJS(input) {
|
171
|
+
var context = getPermissiveContext(input.context);
|
168
172
|
try {
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
173
|
+
var program = function(){ return vm.runInContext(input.source, context, "(execjs)"); };
|
174
|
+
var result = program();
|
175
|
+
if (typeof result == 'undefined' && result !== null) { return ['ok']; }
|
176
|
+
else {
|
177
|
+
try { return ['ok', result]; }
|
178
|
+
catch (err) { return ['err', '' + err, err.stack]; }
|
179
|
+
}
|
180
|
+
} catch (err) { return ['err', '' + err, massageStackTrace(err.stack)]; }
|
181
|
+
},
|
182
|
+
exec: function execJS(input) {
|
183
|
+
var context = getCompatibleContext(input.context);
|
184
|
+
try {
|
185
|
+
var program = function(){ return vm.runInContext(input.source, context, "(execjs)"); };
|
186
|
+
var result = program();
|
187
|
+
if (typeof result == 'undefined' && result !== null) { return ['ok']; }
|
188
|
+
else {
|
189
|
+
try { return ['ok', result]; }
|
190
|
+
catch (err) { return ['err', '' + err, err.stack]; }
|
191
|
+
}
|
192
|
+
} catch (err) { return ['err', '' + err, massageStackTrace(err.stack)]; }
|
176
193
|
}
|
177
|
-
}
|
178
194
|
};
|
179
195
|
|
180
196
|
var server = net.createServer(function(s) {
|
181
|
-
|
197
|
+
var received_data = '';
|
182
198
|
|
183
|
-
|
184
|
-
|
199
|
+
s.on('data', function (data) {
|
200
|
+
received_data += data;
|
185
201
|
|
186
|
-
|
202
|
+
if (received_data[received_data.length - 1] !== "\n") { return; }
|
187
203
|
|
188
|
-
|
189
|
-
|
204
|
+
var request = received_data;
|
205
|
+
received_data = '';
|
190
206
|
|
191
|
-
|
192
|
-
|
193
|
-
|
207
|
+
var input = JSON.parse(request);
|
208
|
+
var result = commands[input.cmd].apply(null, input.args);
|
209
|
+
var outputJSON = '';
|
194
210
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
outputJSON =
|
200
|
-
} else {
|
201
|
-
outputJSON = JSON.stringify(['err', '' + err, err.stack]);
|
202
|
-
}
|
211
|
+
try { outputJSON = JSON.stringify(result); }
|
212
|
+
catch(err) {
|
213
|
+
if (err.message.includes('circular')) {
|
214
|
+
outputJSON = CircularJSON.stringify(result);
|
215
|
+
} else { outputJSON = JSON.stringify(['err', '' + err, err.stack]); }
|
203
216
|
}
|
204
217
|
s.write(outputJSON + '\n');
|
205
218
|
if (process_exit !== false) { process.exit(process_exit); }
|
@@ -43,6 +43,10 @@ module Isomorfeus
|
|
43
43
|
command("exec", {'context' => context, 'source' => source})
|
44
44
|
end
|
45
45
|
|
46
|
+
def execp(context, source)
|
47
|
+
command("execp", {'context' => context, 'source' => source})
|
48
|
+
end
|
49
|
+
|
46
50
|
def delete_context(context)
|
47
51
|
command("deleteContext", context)
|
48
52
|
end
|
@@ -89,18 +93,23 @@ module Isomorfeus
|
|
89
93
|
def initialize(runtime, source = "", options = {})
|
90
94
|
@runtime = runtime
|
91
95
|
@uuid = SecureRandom.uuid
|
96
|
+
@permissive = !!options[:permissive]
|
92
97
|
|
93
98
|
ObjectSpace.define_finalizer(self, self.class.finalize(@runtime, @uuid))
|
94
99
|
|
95
100
|
source = encode(source)
|
96
101
|
|
97
|
-
raw_exec(source)
|
102
|
+
@permissive ? raw_execp(source) : raw_exec(source)
|
98
103
|
end
|
99
104
|
|
100
105
|
def self.finalize(runtime, uuid)
|
101
106
|
proc { runtime.vm.delete_context(uuid) }
|
102
107
|
end
|
103
108
|
|
109
|
+
def call(identifier, *args)
|
110
|
+
eval "#{identifier}.apply(this, #{::Oj.dump(args)})"
|
111
|
+
end
|
112
|
+
|
104
113
|
def eval(source, options = {})
|
105
114
|
if /\S/ =~ source
|
106
115
|
raw_exec("(#{source})")
|
@@ -111,15 +120,32 @@ module Isomorfeus
|
|
111
120
|
raw_exec("(function(){#{source}})()")
|
112
121
|
end
|
113
122
|
|
114
|
-
def
|
123
|
+
def permissive?
|
124
|
+
@permissive
|
125
|
+
end
|
126
|
+
|
127
|
+
def permissive_eval(source, options = {})
|
128
|
+
if /\S/ =~ source
|
129
|
+
raw_execp("(#{source})")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def permissive_exec(source, options = {})
|
134
|
+
raw_execp("(function(){#{source}})()")
|
135
|
+
end
|
136
|
+
|
137
|
+
def raw_exec(source)
|
115
138
|
source = encode(source)
|
116
139
|
|
117
140
|
result = @runtime.vm.exec(@uuid, source)
|
118
141
|
extract_result(result)
|
119
142
|
end
|
120
143
|
|
121
|
-
def
|
122
|
-
|
144
|
+
def raw_execp(source)
|
145
|
+
source = encode(source)
|
146
|
+
|
147
|
+
result = @runtime.vm.execp(@uuid, source)
|
148
|
+
extract_result(result)
|
123
149
|
end
|
124
150
|
|
125
151
|
protected
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: isomorfeus-speednode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Biedermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|
@@ -89,10 +89,11 @@ files:
|
|
89
89
|
- LICENSE
|
90
90
|
- README.md
|
91
91
|
- lib/isomorfeus-speednode.rb
|
92
|
-
- lib/isomorfeus/
|
93
|
-
- lib/isomorfeus/
|
92
|
+
- lib/isomorfeus/execjs_module.rb
|
93
|
+
- lib/isomorfeus/execjs_runtime.rb
|
94
|
+
- lib/isomorfeus/execjs_runtimes.rb
|
94
95
|
- lib/isomorfeus/speednode/node_command.rb
|
95
|
-
- lib/isomorfeus/speednode/
|
96
|
+
- lib/isomorfeus/speednode/runner.js
|
96
97
|
- lib/isomorfeus/speednode/runtime.rb
|
97
98
|
- lib/isomorfeus/speednode/version.rb
|
98
99
|
homepage: http://isomorfeus.com
|
data/lib/isomorfeus/speednode.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Isomorfeus
|
2
|
-
module Speednode
|
3
|
-
PermissiveRuntime = Isomorfeus::Speednode::Runtime.new(
|
4
|
-
name: 'Isomorfeus Speednode Permissive Node.js (V8)',
|
5
|
-
command: %w[nodejs node],
|
6
|
-
runner_path: File.join(File.dirname(__FILE__), 'speednode', 'permissive_runner.js'),
|
7
|
-
encoding: 'UTF-8'
|
8
|
-
)
|
9
|
-
CompatibleRuntime = Isomorfeus::Speednode::Runtime.new(
|
10
|
-
name: 'Isomorfeus Speednode Compatible Node.js (V8)',
|
11
|
-
command: %w[nodejs node],
|
12
|
-
runner_path: File.join(File.dirname(__FILE__), 'speednode', 'compatible_runner.js'),
|
13
|
-
encoding: 'UTF-8'
|
14
|
-
)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module ExecJS
|
19
|
-
module Runtimes
|
20
|
-
PermissiveSpeednode = Isomorfeus::Speednode::PermissiveRuntime
|
21
|
-
|
22
|
-
CompatibleSpeednode = Isomorfeus::Speednode::CompatibleRuntime
|
23
|
-
|
24
|
-
runtimes << PermissiveSpeednode
|
25
|
-
runtimes.unshift(CompatibleSpeednode)
|
26
|
-
end
|
27
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
var vm = require('vm');
|
2
|
-
const net = require('net');
|
3
|
-
var contexts = {};
|
4
|
-
var process_exit = false;
|
5
|
-
|
6
|
-
/*
|
7
|
-
* Versions of node before 0.12 (notably 0.10) didn't properly propagate
|
8
|
-
* syntax errors.
|
9
|
-
* This also regressed in the node 4.0 releases.
|
10
|
-
*
|
11
|
-
* To get around this, if it looks like we are missing the location of the
|
12
|
-
* error, we guess it is (execjs):1
|
13
|
-
*
|
14
|
-
* This is obviously not ideal, but only affects syntax errors, and only on
|
15
|
-
* these versions.
|
16
|
-
*/
|
17
|
-
function massageStackTrace(stack) {
|
18
|
-
if (stack && stack.indexOf("SyntaxError") == 0) {
|
19
|
-
return "(execjs):1\n" + stack;
|
20
|
-
} else {
|
21
|
-
return stack;
|
22
|
-
}
|
23
|
-
}
|
24
|
-
function createCompatibleContext() {
|
25
|
-
var c = vm.createContext();
|
26
|
-
vm.runInContext('delete this.console', c, "(execjs)");
|
27
|
-
return c;
|
28
|
-
}
|
29
|
-
function getContext(uuid) {
|
30
|
-
return contexts[uuid] || (contexts[uuid] = createCompatibleContext());
|
31
|
-
}
|
32
|
-
|
33
|
-
var commands = {
|
34
|
-
deleteContext: function(uuid) {
|
35
|
-
delete contexts[uuid];
|
36
|
-
return [1];
|
37
|
-
},
|
38
|
-
exit: function(code) {
|
39
|
-
process_exit = code;
|
40
|
-
return ['ok'];
|
41
|
-
},
|
42
|
-
exec: function execJS(input) {
|
43
|
-
var context = getContext(input.context);
|
44
|
-
var source = input.source;
|
45
|
-
try {
|
46
|
-
var program = function(){
|
47
|
-
return vm.runInContext(source, context, "(execjs)");
|
48
|
-
}
|
49
|
-
var result = program();
|
50
|
-
if (typeof result == 'undefined' && result !== null) {
|
51
|
-
return ['ok'];
|
52
|
-
} else {
|
53
|
-
try {
|
54
|
-
return ['ok', result];
|
55
|
-
} catch (err) {
|
56
|
-
return ['err', '' + err, err.stack];
|
57
|
-
}
|
58
|
-
}
|
59
|
-
} catch (err) {
|
60
|
-
return ['err', '' + err, massageStackTrace(err.stack)];
|
61
|
-
}
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
var server = net.createServer(function(s) {
|
66
|
-
var received_data = '';
|
67
|
-
|
68
|
-
s.on('data', function (data) {
|
69
|
-
received_data += data;
|
70
|
-
|
71
|
-
if (received_data[received_data.length - 1] !== "\n") { return; }
|
72
|
-
|
73
|
-
var request = received_data;
|
74
|
-
received_data = '';
|
75
|
-
|
76
|
-
var input = JSON.parse(request);
|
77
|
-
var result = commands[input.cmd].apply(null, input.args);
|
78
|
-
var outputJSON = '';
|
79
|
-
|
80
|
-
try {
|
81
|
-
outputJSON = JSON.stringify(result);
|
82
|
-
} catch(err) {
|
83
|
-
outputJSON = JSON.stringify(['err', '' + err, err.stack]);
|
84
|
-
}
|
85
|
-
s.write(outputJSON + '\n');
|
86
|
-
if (process_exit) { process.exit(process_exit); }
|
87
|
-
});
|
88
|
-
});
|
89
|
-
|
90
|
-
var socket_path = process.env.SOCKET_PATH;
|
91
|
-
if (!socket_path) { throw 'No SOCKET_PATH given!'; };
|
92
|
-
server.listen(socket_path);
|