isomorfeus-speednode 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f788c1d82c38e1e5a842398250da97e42d3e03a8cee5ab7c4f7930e6bf988728
4
- data.tar.gz: 84c32ebeaaf0a77dd977af21934e29f1fdfd4d0491ccb2a9dc38710148520bb5
3
+ metadata.gz: 5a1b8da4a29439d529c0645ee7d9e6c324bd0d586331555798f98e8cf45edc46
4
+ data.tar.gz: 9a86b8a8f18b96b0a0f4f8ac4ad7991ada4e5833ff59ba6b18fdc99a4ccc38cc
5
5
  SHA512:
6
- metadata.gz: 3019227cb051d16b25886c6e597ef69dd34f6382a1b6ec4a52c84249b2c71848073cdd60bdb23dfafffde0c03c90cf87ca2df74674feeea5d71903ea731804ed
7
- data.tar.gz: 4b0884b00f062d3d2f8f6579d2d72368f2cc045c3ec352e50b45f43300f4e7b02b2ce8098d79b338951f1d26eb9eda48de681bc755e299c046de356f0f0184c3
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 2 runtimes:
17
- - `CompatibleSpeednode` - is compatible with the minimalistic execjs approach. Mainly intended for production/development usage.
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::PermissiveSpeednode
20
+ ExecJS.runtime = ExecJS::Runtimes::Speednode
25
21
  ```
26
- If node cant find node modules, uts possible to set the load path before assigning the runtime:
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 benchmark:
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
- - `ruby bench.rb`
80
+ - `bundle exec rake test`
@@ -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/speednode'
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
- if (stack && stack.indexOf("SyntaxError") == 0) {
138
- return "(execjs):1\n" + stack;
139
- } else {
140
- return stack;
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 getContext(uuid) {
145
- return contexts[uuid] || (contexts[uuid] = vm.createContext({ require, setTimeout }));
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
- deleteContext: function(uuid) {
150
- delete contexts[uuid];
151
- return [1];
152
- },
153
- exit: function(code) {
154
- process_exit = code;
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
- } else {
169
+ },
170
+ execp: function execJS(input) {
171
+ var context = getPermissiveContext(input.context);
168
172
  try {
169
- return ['ok', result];
170
- } catch (err) {
171
- return ['err', '' + err, err.stack];
172
- }
173
- }
174
- } catch (err) {
175
- return ['err', '' + err, massageStackTrace(err.stack)];
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
- var received_data = '';
197
+ var received_data = '';
182
198
 
183
- s.on('data', function (data) {
184
- received_data += data;
199
+ s.on('data', function (data) {
200
+ received_data += data;
185
201
 
186
- if (received_data[received_data.length - 1] !== "\n") { return; }
202
+ if (received_data[received_data.length - 1] !== "\n") { return; }
187
203
 
188
- var request = received_data;
189
- received_data = '';
204
+ var request = received_data;
205
+ received_data = '';
190
206
 
191
- var input = JSON.parse(request);
192
- var result = commands[input.cmd].apply(null, input.args);
193
- var outputJSON = '';
207
+ var input = JSON.parse(request);
208
+ var result = commands[input.cmd].apply(null, input.args);
209
+ var outputJSON = '';
194
210
 
195
- try {
196
- outputJSON = JSON.stringify(result);
197
- } catch(err) {
198
- if (err.message.includes('circular')) {
199
- outputJSON = CircularJSON.stringify(result);
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 raw_exec(source, options = {})
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 call(identifier, *args)
122
- eval "#{identifier}.apply(this, #{::Oj.dump(args)})"
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
@@ -1,5 +1,5 @@
1
1
  module Isomorfeus
2
2
  module Speednode
3
- VERSION = '0.1.5'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
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.1.5
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-06 00:00:00.000000000 Z
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/speednode.rb
93
- - lib/isomorfeus/speednode/compatible_runner.js
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/permissive_runner.js
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
@@ -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);