isomorfeus-speednode 0.5.2 → 0.5.3

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: 676da9269c67e955ccfe546dbbbc82bf079e796d1070dd8d699c6523dbc537ba
4
- data.tar.gz: b78918192805e28a455e9c5d45594fdc3b683d93ddc6f0a38f13207afecb808a
3
+ metadata.gz: 11a836ac72dbce40b8926276342a91f3c7d3999b562f5be8670102e9b2196751
4
+ data.tar.gz: 1110a497795537221829ea361959a1b9c00e1570447744141b047efedecf2685
5
5
  SHA512:
6
- metadata.gz: b933335621ed51658ade944312fea14e1a8b89ec71585e4b99a46fe3a2dc6c94ccc073f6013281ed19ad07d889e91a09bfad75784c9f5004c8a35e3124b4dd2f
7
- data.tar.gz: 17c4dd51349c07013c961d714daee25f06c524edae7bb876cc0c78f7eda9519ea8be8c966840b86a08f019ef6b44540b31af09d52e5e8fd36c2576a84b702c44
6
+ metadata.gz: 0c4f86cf44a01774584782e12c94cb8bd4677c348f914aca3740c697297be2f843da12642683932bb5bd7470c7c1e87009373429644cfe871c11b50082d594df
7
+ data.tar.gz: 3ef559f6eecf237c5fb4a46ef3eadd243d8f482fef64170391fcaeeaed373a900401dfe4259d6d7bebf62200529ad54e8efc69f4c6cd887c022b02f701d91092
data/README.md CHANGED
@@ -53,7 +53,7 @@ Evaluation in a permissive context:
53
53
  ExecJS.permissive_eval('1+1')
54
54
  ```
55
55
 
56
- ### Storing scripts for repeated execution
56
+ ### Precompiling and Storing scripts for repeated execution
57
57
 
58
58
  Scripts can be precompiled and stored for repeated execution, which leads to a significant performance improvement, especially for larger scripts:
59
59
  ```ruby
@@ -61,6 +61,7 @@ context = ExecJS.compile('Test = "test"')
61
61
  context.add_script(key: 'super', source: some_large_javascript) # will compile and store the script
62
62
  context.eval_script(:key: 'super') # will run the precompiled script in the context
63
63
  ```
64
+ For the actual performance benefit see below.
64
65
 
65
66
  ### Async function support
66
67
 
@@ -95,21 +96,24 @@ Attaching and calling ruby methods to/from permissive contexts is not that fast.
95
96
 
96
97
  Highly scientific, maybe.
97
98
 
98
- 1000 rounds using node 16.6.2.:
99
+ 1000 rounds using node 16.14.0 on linux (ctx = using context, scsc = using precompiled scripts):
99
100
  ```
100
101
  standard ExecJS CoffeeScript eval benchmark:
101
- user system total real
102
- Isomorfeus Speednode Node.js (V8) Windows 0.079000 0.031000 0.110000 ( 0.518821)
103
- Isomorfeus Speednode Node.js (V8) Linux 0.092049 0.039669 0.131718 ( 0.546846)
104
- mini_racer (V8) 0.4.0 Linux only 0.776317 0.062923 0.839240 ( 0.480490)
105
- Node.js (V8) Linux 0.330156 0.354536 52.523972 ( 51.203355)
106
-
107
- evel overhead benchmark:
108
- user system total real
109
- Isomorfeus Speednode Node.js (V8) Windows 0.032000 0.031000 0.063000 ( 0.227634)
110
- Isomorfeus Speednode Node.js (V8) Linux 0.085135 0.022002 0.107137 ( 0.153055)
111
- mini_racer (V8) 0.4.0 Linux only 0.083562 0.115612 0.199174 ( 0.128455)
112
- Node.js (V8) Linux 0.273238 0.265101 30.169976 ( 29.636668)
102
+ user system total real
103
+ Isomorfeus Speednode Node.js (V8): 0.086443 0.061089 0.147532 ( 0.567715)
104
+ Isomorfeus Speednode Node.js (V8) ctx: 0.080821 0.047469 0.128290 ( 0.468664)
105
+ Isomorfeus Speednode Node.js (V8) scsc: 0.089423 0.046541 0.135964 ( 0.381198) <- fastest
106
+ mini_racer (V8): 0.723352 0.179944 0.903296 ( 0.513696)
107
+ mini_racer (V8) ctx: 0.475681 0.226101 0.701782 ( 0.429463)
108
+
109
+ eval overhead benchmark:
110
+ user system total real
111
+ Isomorfeus Speednode Node.js (V8): 0.028312 0.062895 0.091207 ( 0.199176)
112
+ Isomorfeus Speednode Node.js (V8) ctx: 0.061245 0.027093 0.088338 ( 0.183092)
113
+ Isomorfeus Speednode Node.js (V8) scsc: 0.061062 0.019203 0.080265 ( 0.107898) <- fastest
114
+ mini_racer (V8): 0.103914 0.092251 0.196165 ( 0.125081)
115
+ mini_racer (V8) ctx: 0.120440 0.060329 0.180769 ( 0.112695)
116
+
113
117
  ```
114
118
  minify discourse benchmark from mini_racer:
115
119
  ```
@@ -10,9 +10,9 @@ module Isomorfeus
10
10
  ERROR_IO_PENDING = 997
11
11
  ERROR_PIPE_CONNECTED = 535
12
12
  ERROR_SUCCESS = 0
13
-
13
+
14
14
  FILE_FLAG_OVERLAPPED = 0x40000000
15
-
15
+
16
16
  INFINITE = 0xFFFFFFFF
17
17
  INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
18
18
 
@@ -91,7 +91,7 @@ module Isomorfeus
91
91
  overlap[:hEvent] = @events[0]
92
92
 
93
93
  @pipe = { overlap: overlap, instance: nil, request: FFI::Buffer.new(1, BUFFER_SIZE), bytes_read: 0, reply: FFI::Buffer.new(1, BUFFER_SIZE), bytes_to_write: 0, state: nil, pending_io: false }
94
- @pipe[:instance] = CreateNamedPipe(@full_pipe_name,
94
+ @pipe[:instance] = CreateNamedPipe(@full_pipe_name,
95
95
  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
96
96
  PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
97
97
  4,
@@ -102,7 +102,7 @@ module Isomorfeus
102
102
  raise "CreateNamedPipe failed with #{GetLastError()}" if @pipe[:instance] == INVALID_HANDLE_VALUE
103
103
  @pipe[:pending_io] = connect_to_new_client
104
104
  @pipe[:state] = @pipe[:pending_io] ? CONNECTING_STATE : READING_STATE
105
-
105
+
106
106
  @events_pointer.write_array_of_ulong_long(@events)
107
107
  nil
108
108
  end
@@ -194,9 +194,9 @@ module Isomorfeus
194
194
  def disconnect_and_reconnect
195
195
  FlushFileBuffers(@pipe[:instance])
196
196
  STDERR.puts("DisconnectNamedPipe failed with #{GetLastError()}") if !DisconnectNamedPipe(@pipe[:instance])
197
-
197
+
198
198
  @pipe[:pending_io] = connect_to_new_client
199
-
199
+
200
200
  @pipe[:state] = @pipe[:pending_io] ? CONNECTING_STATE : READING_STATE
201
201
  end
202
202
 
@@ -207,7 +207,7 @@ module Isomorfeus
207
207
  connected = ConnectNamedPipe(@pipe[:instance], @pipe[:overlap].to_ptr)
208
208
  last_error = GetLastError()
209
209
  raise "ConnectNamedPipe failed with #{last_error} - #{connected}" if connected != 0
210
-
210
+
211
211
  case last_error
212
212
  when ERROR_IO_PENDING
213
213
  pending_io = true
@@ -29,9 +29,8 @@ module Isomorfeus
29
29
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
30
30
  end
31
31
  end
32
- sleep 0.005
33
32
  ret = begin
34
- IO.select([@socket], nil, nil, 1) || next
33
+ IO.select([@socket], nil, nil, nil) || next
35
34
  rescue Errno::EBADF
36
35
  end
37
36
  end
@@ -43,4 +42,4 @@ module Isomorfeus
43
42
  end
44
43
  end
45
44
  end
46
- end
45
+ end
@@ -243,6 +243,18 @@ function massageStackTrace(stack) {
243
243
 
244
244
  let socket_path = process.env.SOCKET_PATH;
245
245
  if (!socket_path) { throw 'No SOCKET_PATH given!'; };
246
+ let debug_contexts = [];
247
+ let commands_swapped = false;
248
+
249
+ function swap_commands(c) {
250
+ c.oexec = c.exec;
251
+ c.exec = c.execd;
252
+ c.oeval = c.eval;
253
+ c.eval = c.evald;
254
+ c.oevsc = c.evsc;
255
+ c.evsc = c.evscd;
256
+ commands_swapped = true;
257
+ }
246
258
 
247
259
  let commands = {
248
260
  attach: function(input) {
@@ -258,6 +270,14 @@ let commands = {
258
270
  let result = vm.runInContext(input.source, context, getContextOptions(input.context));
259
271
  return formatResult(result);
260
272
  },
273
+ created: function (input) {
274
+ debug_contexts.push(input.context);
275
+ if (!commands_swapped) {
276
+ swap_commands(commands);
277
+ let result = eval(input.source);
278
+ return formatResult(result);
279
+ } else { return formatResult(true) }
280
+ },
261
281
  createp: function (input) {
262
282
  let context = createPermissiveContext(input.context, input.options);
263
283
  let result = vm.runInContext(input.source, context, getContextOptions(input.context));
@@ -276,12 +296,30 @@ let commands = {
276
296
  let result = vm.runInContext(input.source, getContext(input.context), getContextOptions(input.context));
277
297
  return formatResult(result);
278
298
  },
299
+ execd: function(input) {
300
+ if (debug_contexts.includes(input.context)) {
301
+ let result = eval(input.source);
302
+ return formatResult(result);
303
+ } else {
304
+ return commands.oexec(input);
305
+ }
306
+ },
279
307
  eval: function (input) {
280
308
  if (input.source.match(/^\s*{/)) { input.source = "(" + input.source + ")"; }
281
309
  else if (input.source.match(/^\s*function\s*\(/)) { input.source = "(" + input.source + ")"; }
282
310
  let result = vm.runInContext(input.source, getContext(input.context), getContextOptions(input.context));
283
311
  return formatResult(result);
284
312
  },
313
+ evald: function(input) {
314
+ if (debug_contexts.includes(input.context)) {
315
+ if (input.source.match(/^\s*{/)) { input.source = "(" + input.source + ")"; }
316
+ else if (input.source.match(/^\s*function\s*\(/)) { input.source = "(" + input.source + ")"; }
317
+ let result = eval(input.source);
318
+ return formatResult(result);
319
+ } else {
320
+ return commands.oeval(input);
321
+ }
322
+ },
285
323
  scsc: function (input) {
286
324
  if (!scripts[input.context]) { scripts[input.context] = {}; }
287
325
  scripts[input.context][input.key] = new vm.Script(input.source);
@@ -291,6 +329,14 @@ let commands = {
291
329
  let result = scripts[input.context][input.key].runInContext(getContext(input.context));
292
330
  return formatResult(result);
293
331
  },
332
+ evscd: function(input) {
333
+ if (debug_contexts.includes(input.context)) {
334
+ let result = scripts[input.context][input.key].runInThisContext();
335
+ return formatResult(result);
336
+ } else {
337
+ return commands.oevsc(input);
338
+ }
339
+ }
294
340
  // ctxo: function (input) {
295
341
  // return formatResult(getContextOptions(input.context));
296
342
  // },
@@ -304,7 +350,7 @@ let server = net.createServer(function(s) {
304
350
  if (data[data.length - 1] !== 4) { return; }
305
351
 
306
352
  let request = received_data.join('').toString('utf8');
307
- request = request.substr(0, request.length - 1);
353
+ request = request.substring(0, request.length - 1);
308
354
  received_data = [];
309
355
 
310
356
  let input, result;
@@ -317,7 +363,7 @@ let server = net.createServer(function(s) {
317
363
  return;
318
364
  }
319
365
 
320
- try { result = commands[input.cmd].apply(null, input.args); }
366
+ try { result = commands[input.cmd](input.args); }
321
367
  catch (err) {
322
368
  outputJSON = JSON.stringify(['err', ['', err].join(''), massageStackTrace(err.stack)]);
323
369
  s.write([outputJSON, "\x04"].join(''));
@@ -3,13 +3,17 @@ module Isomorfeus
3
3
  class Runtime < ExecJS::Runtime
4
4
  class Context < ::ExecJS::Runtime::Context
5
5
  def self.finalize(runtime, uuid)
6
- proc { runtime.vm.delete_context(uuid) }
6
+ proc do
7
+ runtime.vm.delete_context(uuid) rescue nil # if delete_context fails, the vm exited before probably
8
+ end
7
9
  end
8
10
 
9
11
  def initialize(runtime, source = "", options = {})
10
12
  @runtime = runtime
11
13
  @uuid = SecureRandom.uuid
12
14
  @permissive = !!options.delete(:permissive)
15
+ @debug = @permissive ? !!options.delete(:debug) { false } : false
16
+ @debug = false unless ENV['NODE_OPTIONS']&.include?('--inspect')
13
17
  @vm = @runtime.vm
14
18
  @context = nil
15
19
  @timeout = options[:timeout] ? options[:timeout]/1000 : 600
@@ -24,8 +28,14 @@ module Isomorfeus
24
28
  rescue
25
29
  source = source.force_encoding('UTF-8')
26
30
  end
27
-
28
- @permissive ? raw_createp(source, options) : raw_create(source, options)
31
+ if @debug && @permissive
32
+ raw_created(source, options)
33
+ elsif @permissive
34
+ raw_createp(source, options)
35
+ else
36
+ raw_create(source, options)
37
+ end
38
+ self.add_script(key: :_internal_exec_fin, source: '!global.__LastExecutionFinished')
29
39
  end
30
40
 
31
41
  # def options
@@ -108,6 +118,14 @@ module Isomorfeus
108
118
  extract_result(result)
109
119
  end
110
120
 
121
+ def raw_created(source, options)
122
+ return if @context
123
+ source = encode(source)
124
+ result = @vm.created(@uuid, source, options)
125
+ @context = true
126
+ extract_result(result)
127
+ end
128
+
111
129
  def raw_createp(source, options)
112
130
  return if @context
113
131
  source = encode(source)
@@ -140,7 +158,7 @@ module Isomorfeus
140
158
 
141
159
  def await_result
142
160
  start_time = Time.now
143
- while !execution_finished? && !timed_out?(start_time)
161
+ while eval_script(key: :_internal_exec_fin) && !timed_out?(start_time)
144
162
  sleep 0.005
145
163
  end
146
164
  result = exec <<~JAVASCRIPT
@@ -166,13 +184,9 @@ module Isomorfeus
166
184
  extract_result(result)
167
185
  end
168
186
 
169
- def execution_finished?
170
- eval 'global.__LastExecutionFinished'
171
- end
172
-
173
187
  def timed_out?(start_time)
174
188
  if (Time.now - start_time) > @timeout
175
- raise "Command Execution timed out!"
189
+ raise "IsomorfeusSpeednode: Command Execution timed out!"
176
190
  end
177
191
  false
178
192
  end
@@ -63,7 +63,7 @@ module Isomorfeus
63
63
  end
64
64
 
65
65
  def self.exit_node(socket, socket_dir, socket_path, pid)
66
- VMCommand.new(socket, "exit", [0]).execute
66
+ VMCommand.new(socket, "exit", 0).execute rescue nil
67
67
  socket.close
68
68
  File.unlink(socket_path) if File.exist?(socket_path)
69
69
  Dir.rmdir(socket_dir) if socket_dir && Dir.exist?(socket_dir)
@@ -99,6 +99,10 @@ module Isomorfeus
99
99
  command('create', {'context' => context, 'source' => source, 'options' => options})
100
100
  end
101
101
 
102
+ def created(context, source, options)
103
+ command('created', {'context' => context, 'source' => source, 'options' => options})
104
+ end
105
+
102
106
  def createp(context, source, options)
103
107
  command('createp', {'context' => context, 'source' => source, 'options' => options})
104
108
  end
@@ -110,7 +114,7 @@ module Isomorfeus
110
114
 
111
115
  def delete_context(context)
112
116
  @mutex.synchronize do
113
- VMCommand.new(@socket, "deleteContext", [context]).execute rescue nil
117
+ VMCommand.new(@socket, "deleteContext", context).execute rescue nil
114
118
  end
115
119
  rescue ThreadError
116
120
  nil
@@ -139,7 +143,7 @@ module Isomorfeus
139
143
  end
140
144
  @pid = Process.spawn({"SOCKET_PATH" => @socket_path}, @options[:binary], @options[:source_maps], @options[:runner_path])
141
145
 
142
- retries = 100
146
+ retries = 500
143
147
 
144
148
  if ExecJS.windows?
145
149
  timeout_or_connected = false
@@ -166,15 +170,6 @@ module Isomorfeus
166
170
 
167
171
  @started = true
168
172
 
169
- at_exit do
170
- begin
171
- self.class.exit_node(@socket, @socket_dir, @socket_path, @pid) unless @socket.closed?
172
- rescue
173
- # do nothing
174
- end
175
- end
176
-
177
- ObjectSpace.define_finalizer(self, self.class.finalize(@socket, @socket_dir, @socket_path, @pid))
178
173
  Kernel.at_exit { self.class.finalize(@socket, @socket_dir, @socket_path, @pid).call }
179
174
  end
180
175
 
@@ -204,10 +199,10 @@ module Isomorfeus
204
199
  @responder.run
205
200
  end
206
201
 
207
- def command(cmd, *arguments)
202
+ def command(cmd, argument)
208
203
  @mutex.synchronize do
209
204
  start_without_synchronization unless @started
210
- VMCommand.new(@socket, cmd, arguments).execute
205
+ VMCommand.new(@socket, cmd, argument).execute
211
206
  end
212
207
  end
213
208
  end
@@ -1,5 +1,5 @@
1
1
  module Isomorfeus
2
2
  module Speednode
3
- VERSION = '0.5.2'
3
+ VERSION = '0.5.3'
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.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Biedermann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-19 00:00:00.000000000 Z
11
+ date: 2022-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs
@@ -151,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
153
  requirements: []
154
- rubygems_version: 3.3.3
154
+ rubygems_version: 3.3.7
155
155
  signing_key:
156
156
  specification_version: 4
157
157
  summary: A fast ExecJS runtime based on nodejs, tuned for Isomorfeus.