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 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);