isomorfeus-speednode 0.2.12 → 0.3.1

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: 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