isomorfeus-speednode 0.2.12 → 0.3.1

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: 47b388ff399cb8efb905e876ff5a6b48ea538633db1fbe9a8602f1083f11858b
4
- data.tar.gz: ac6bd1e76e66ab17df66a6d92c1d3aa92f333427bcff0b6d41a3f9b27e967c45
3
+ metadata.gz: 1e0315df4f6d4730a62a4189ef7f12fcaf01e187d323ca999aa27cd0254ee7ea
4
+ data.tar.gz: '0711819b4c9280ff1dace7f9f7432f961383bb82cf543a0b3981d01e1a83b2be'
5
5
  SHA512:
6
- metadata.gz: 1cb15b4d6b5ff6251a5286d3aa8612fe4ab107b010f61546e4ce186a9f2969d58b5533368e1ae064755d512af73716f3f3e3dddf30592048095de494d3a7749a
7
- data.tar.gz: b758f28b4c24a186eb4ea1fccacd37e48ff4b27306c49e8038375b59199f91e8fd1c8dec8a6a7783385c4f0b1a819ee8be42570689cccc65e0a74a040b119e63
6
+ metadata.gz: 706dded2560521f21e3e130cfa266bb725f9891b88cca7331ad5f33e3ea054186a3832a4d6aa7144428085b77bfb1fcd1a4ce09b3fd2dcfc7872e3930b3c974b
7
+ data.tar.gz: '018b37989b54160cdb3e89ece63dd6feb9a423dd845229472e5d5b75864487d86c0fbcd46369d0ec70e8521afa7ae0f7b91d909344475d3e8b5cb2c8b4692b0b'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 John Hawthorn
4
+ Copyright (c) 2019 Jan Biedermann
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # isomorfeus-speednode
2
2
 
3
- A fast runtime for execjs using node js.
3
+ A fast runtime for execjs using node js. Works on Linux, BSDs, MacOS and Windows.
4
4
  Inspired by [execjs-fastnode](https://github.com/jhawthorn/execjs-fastnode).
5
5
 
6
6
  ### Community and Support
@@ -26,6 +26,7 @@ If node cant find node modules for the permissive contexts (see below), its poss
26
26
  ```ruby
27
27
  ENV['NODE_PATH'] = './node_modules'
28
28
  ```
29
+
29
30
  ### Contexts
30
31
 
31
32
  Each ExecJS context runs in a node vm. Speednode offers two kinds of contexts:
@@ -33,8 +34,7 @@ Each ExecJS context runs in a node vm. Speednode offers two kinds of contexts:
33
34
  - a permissive context, which is more permissive and allows to `require` node modules.
34
35
 
35
36
  #### Compatible
36
- A compatible context can be created with the standard `ExecJS.compile` or code can be executed within a compatible context by using the standard
37
- `ExecJS.eval` or `ExecJS.exec`.
37
+ A compatible context can be created with the standard `ExecJS.compile` or code can be executed within a compatible context by using the standard `ExecJS.eval` or `ExecJS.exec`.
38
38
  Example for a compatible context:
39
39
  ```ruby
40
40
  compat_context = ExecJS.compile('Test = "test"')
@@ -56,19 +56,35 @@ ExecJS.permissive_eval('1+1')
56
56
  ### Benchmarks
57
57
 
58
58
  Highly scientific, maybe.
59
+
60
+ 1000 rounds on Linux using node 16.1.0:
61
+ ```
62
+ standard ExecJS CoffeeScript call benchmark:
63
+ user system total real
64
+ Node.js (V8) fast 0.234199 0.093775 0.327974 ( 0.877142)
65
+ Isomorfeus Speednode Node.js (V8) 0.108882 0.048014 0.156896 ( 0.587261)
66
+ mini_racer (V8) 0.801764 0.105020 0.906784 ( 0.515463)
67
+ Node.js (V8) 0.420842 0.293893 52.471348 ( 50.990930)
68
+
69
+ call overhead benchmark:
70
+ user system total real
71
+ Node.js (V8) fast 0.192230 0.086253 0.278483 ( 0.372702)
72
+ Isomorfeus Speednode Node.js (V8) 0.101266 0.034985 0.136251 ( 0.214831)
73
+ mini_racer (V8) 0.163080 0.052209 0.215289 ( 0.141151)
74
+ Node.js (V8) 0.354932 0.218117 29.016030 ( 28.410343)
75
+ ```
76
+
77
+ 1000 rounds on Windows 10 using node 16.1.0:
59
78
  ```
60
- standard ExecJS CoffeeScript call benchmark, but 1000 rounds:
61
- user system total real
62
- Isomorfeus Speednode Compatible Node.js (V8) 0.042263 0.017215 0.059478 ( 0.442855)
63
- Node.js (V8) fast 0.222875 0.087109 0.309984 ( 0.806736)
64
- mini_racer (V8) 0.425273 0.013478 0.438751 ( 0.304434)
65
-
66
-
67
- call overhead benchmark, 1000 rounds:
68
- user system total real
69
- Isomorfeus Speednode Compatible Node.js (V8) 0.023060 0.010358 0.033418 ( 0.059640)
70
- Node.js (V8) fast 0.191454 0.081396 0.272850 ( 0.368568)
71
- mini_racer (V8) 0.017091 0.002494 0.019585 ( 0.019584)
79
+ standard ExecJS CoffeeScript call benchmark:
80
+ user system total real
81
+ Isomorfeus Speednode Node.js (V8) 0.031000 0.016000 0.047000 ( 0.548920)
82
+ Node.js (V8) 1.172000 2.594000 3.766000 ( 91.619422)
83
+
84
+ call overhead benchmark:
85
+ user system total real
86
+ Isomorfeus Speednode Node.js (V8) 0.063000 0.000000 0.063000 ( 0.162426)
87
+ Node.js (V8) 0.656000 2.516000 3.172000 ( 63.766556)
72
88
  ```
73
89
 
74
90
  To run benchmarks:
@@ -2,6 +2,7 @@
2
2
 
3
3
  const vm = require('vm');
4
4
  const net = require('net');
5
+ const os = require('os');
5
6
  let contexts = {};
6
7
  let process_exit = false;
7
8
 
@@ -172,27 +173,21 @@ let commands = {
172
173
  },
173
174
  execp: function execJS(input) {
174
175
  let context = getPermissiveContext(input.context);
175
- try {
176
- let program = function(){ return vm.runInContext(input.source, context, "(execjs)"); };
177
- let result = program();
178
- if (typeof result == 'undefined' && result !== null) { return ['ok']; }
179
- else {
180
- try { return ['ok', result]; }
181
- catch (err) { return ['err', '' + err, err.stack]; }
182
- }
183
- } catch (err) { return ['err', '' + err, massageStackTrace(err.stack)]; }
176
+ let result = vm.runInContext(input.source, context, "(execjs)");
177
+ if (typeof result === 'undefined' && result !== null) { return ['ok']; }
178
+ else {
179
+ try { return ['ok', result]; }
180
+ catch (err) { return ['err', ['', err].join(''), err.stack]; }
181
+ }
184
182
  },
185
183
  exec: function execJS(input) {
186
184
  let context = getCompatibleContext(input.context);
187
- try {
188
- let program = function(){ return vm.runInContext(input.source, context, "(execjs)"); };
189
- let result = program();
190
- if (typeof result == 'undefined' && result !== null) { return ['ok']; }
191
- else {
192
- try { return ['ok', result]; }
193
- catch (err) { return ['err', '' + err, err.stack]; }
194
- }
195
- } catch (err) { return ['err', '' + err, massageStackTrace(err.stack)]; }
185
+ let result = vm.runInContext(input.source, context, "(execjs)");
186
+ if (typeof result === 'undefined' && result !== null) { return ['ok']; }
187
+ else {
188
+ try { return ['ok', result]; }
189
+ catch (err) { return ['err', ['', err].join(''), err.stack]; }
190
+ }
196
191
  }
197
192
  };
198
193
 
@@ -202,23 +197,43 @@ let server = net.createServer(function(s) {
202
197
  s.on('data', function (data) {
203
198
  received_data.push(data);
204
199
  if (data[data.length - 1] !== 4) { return; }
200
+
205
201
  let request = received_data.join('').toString('utf8');
206
202
  request = request.substr(0, request.length - 1);
207
203
  received_data = [];
208
- let input = JSON.parse(request);
209
- let result = commands[input.cmd].apply(null, input.args);
204
+
205
+ let input, result;
210
206
  let outputJSON = '';
211
207
 
208
+ try { input = JSON.parse(request); }
209
+ catch(err) {
210
+ outputJSON = JSON.stringify(['err', ['', err].join(''), err.stack]);
211
+ s.write([outputJSON, "\x04"].join(''));
212
+ return;
213
+ }
214
+
215
+ try { result = commands[input.cmd].apply(null, input.args); }
216
+ catch (err) {
217
+ outputJSON = JSON.stringify(['err', ['', err].join(''), massageStackTrace(err.stack)]);
218
+ s.write([outputJSON, "\x04"].join(''));
219
+ return;
220
+ }
221
+
212
222
  try { outputJSON = JSON.stringify(result); }
213
223
  catch(err) {
214
224
  if (err.message.includes('circular')) { outputJSON = CircularJSON.stringify(result); }
215
- else { outputJSON = JSON.stringify(['err', '' + err, err.stack]); }
225
+ else { outputJSON = JSON.stringify([['', err].join(''), err.stack]); }
226
+ s.write([outputJSON, "\x04"].join(''));
227
+ return;
216
228
  }
217
- s.write(outputJSON + '\x04');
229
+
230
+ s.write([outputJSON, "\x04"].join(''));
218
231
  if (process_exit !== false) { process.exit(process_exit); }
219
232
  });
220
233
  });
221
234
 
222
235
  let socket_path = process.env.SOCKET_PATH;
223
236
  if (!socket_path) { throw 'No SOCKET_PATH given!'; };
224
- server.listen(socket_path);
237
+ if (os.platform().indexOf('win') > -1) { server.listen('\\\\.\\pipe\\' + socket_path); }
238
+ else { server.listen(socket_path); }
239
+
@@ -1,3 +1,42 @@
1
+ require 'securerandom'
2
+ if Gem.win_platform?
3
+ require 'win32/pipe'
4
+
5
+ module Win32
6
+ class Pipe
7
+ def write(data)
8
+ bytes = FFI::MemoryPointer.new(:ulong)
9
+
10
+ raise Error, "no pipe created" unless @pipe
11
+
12
+ if @asynchronous
13
+ bool = WriteFile(@pipe, data, data.bytesize, bytes, @overlapped)
14
+ bytes_written = bytes.read_ulong
15
+
16
+ if bool && bytes_written > 0
17
+ @pending_io = false
18
+ return true
19
+ end
20
+
21
+ error = GetLastError()
22
+ if !bool && error == ERROR_IO_PENDING
23
+ @pending_io = true
24
+ return true
25
+ end
26
+
27
+ return false
28
+ else
29
+ unless WriteFile(@pipe, data, data.bytesize, bytes, nil)
30
+ raise SystemCallError.new("WriteFile", FFI.errno)
31
+ end
32
+
33
+ return true
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
1
40
  module Isomorfeus
2
41
  module Speednode
3
42
  class Runtime < ExecJS::Runtime
@@ -11,10 +50,27 @@ module Isomorfeus
11
50
  def execute
12
51
  result = ''
13
52
  message = ::Oj.dump({ 'cmd' => @cmd, 'args' => @arguments }, mode: :strict)
14
- @socket.sendmsg(message + "\x04")
15
- begin
16
- result << @socket.recvmsg()[0]
17
- end until result.end_with?("\x04")
53
+ message = message + "\x04"
54
+ bytes_to_send = message.bytesize
55
+ sent_bytes = 0
56
+
57
+ if ExecJS.windows?
58
+ @socket.write(message)
59
+ begin
60
+ result << @socket.read
61
+ end until result.end_with?("\x04")
62
+ else
63
+ sent_bytes = @socket.sendmsg(message)
64
+ if sent_bytes < bytes_to_send
65
+ while sent_bytes < bytes_to_send
66
+ sent_bytes += @socket.sendmsg(message.byteslice((sent_bytes)..-1))
67
+ end
68
+ end
69
+
70
+ begin
71
+ result << @socket.recvmsg()[0]
72
+ end until result.end_with?("\x04")
73
+ end
18
74
  ::Oj.load(result.chop!, create_additions: false)
19
75
  end
20
76
  end
@@ -66,21 +122,40 @@ module Isomorfeus
66
122
 
67
123
  def start_without_synchronization
68
124
  return if @started
69
- @socket_dir = Dir.mktmpdir("isomorfeus-speednode-")
70
- @socket_path = File.join(@socket_dir, "socket")
125
+ if ExecJS.windows?
126
+ @socket_dir = nil
127
+ @socket_path = SecureRandom.uuid
128
+ else
129
+ @socket_dir = Dir.mktmpdir("isomorfeus-speednode-")
130
+ @socket_path = File.join(@socket_dir, "socket")
131
+ end
71
132
  @pid = Process.spawn({"SOCKET_PATH" => @socket_path}, @options[:binary], @options[:runner_path])
72
133
 
73
134
  retries = 20
74
- while !File.exist?(@socket_path)
75
- sleep 0.05
76
- retries -= 1
77
135
 
78
- if retries == 0
79
- raise "Unable to start nodejs process in time"
136
+ if ExecJS.windows?
137
+ timeout_or_connected = false
138
+ begin
139
+ retries -= 1
140
+ begin
141
+ @socket = Win32::Pipe::Client.new(@socket_path, Win32::Pipe::ACCESS_DUPLEX)
142
+ rescue
143
+ sleep 0.1
144
+ raise "Unable to start nodejs process in time" if retries == 0
145
+ next
146
+ end
147
+ timeout_or_connected = true
148
+ end until timeout_or_connected
149
+ else
150
+ while !File.exist?(@socket_path)
151
+ sleep 0.1
152
+ retries -= 1
153
+ raise "Unable to start nodejs process in time" if retries == 0
80
154
  end
155
+
156
+ @socket = UNIXSocket.new(@socket_path)
81
157
  end
82
158
 
83
- @socket = UNIXSocket.new(@socket_path)
84
159
  @started = true
85
160
 
86
161
  at_exit do
@@ -1,5 +1,5 @@
1
1
  module Isomorfeus
2
2
  module Speednode
3
- VERSION = '0.2.12'
3
+ VERSION = '0.3.1'
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.2.12
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Biedermann
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-20 00:00:00.000000000 Z
11
+ date: 2021-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs
@@ -16,28 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.7.0
19
+ version: 2.8.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.7.0
26
+ version: 2.8.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: oj
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 3.6.0
33
+ version: 3.11.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 3.6.0
40
+ version: 3.11.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: win32-pipe
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.4.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.4.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +72,14 @@ dependencies:
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '5.0'
75
+ version: 5.14.4
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '5.0'
82
+ version: 5.14.4
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -101,7 +115,7 @@ licenses:
101
115
  - MIT
102
116
  metadata:
103
117
  github_repo: ssh://github.com/isomorfeus/gems
104
- post_install_message:
118
+ post_install_message:
105
119
  rdoc_options: []
106
120
  require_paths:
107
121
  - lib
@@ -116,8 +130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
130
  - !ruby/object:Gem::Version
117
131
  version: '0'
118
132
  requirements: []
119
- rubygems_version: 3.0.6
120
- signing_key:
133
+ rubygems_version: 3.2.15
134
+ signing_key:
121
135
  specification_version: 4
122
136
  summary: A fast ExecJS runtime based on nodejs, tuned for Isomorfeus.
123
137
  test_files: []
data/LICENSE DELETED
@@ -1 +0,0 @@
1
- ../LICENSE