isomorfeus-speednode 0.6.2 → 0.7.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 +4 -4
- data/README.md +7 -0
- data/lib/isomorfeus/speednode/runner.js +19 -10
- data/lib/isomorfeus/speednode/runtime/context.rb +14 -14
- data/lib/isomorfeus/speednode/runtime/vm.rb +25 -15
- data/lib/isomorfeus/speednode/runtime/vm_command.rb +1 -1
- data/lib/isomorfeus/speednode/runtime.rb +19 -3
- data/lib/isomorfeus/speednode/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91aa4cb54a3cb13afcf15476f841f711ce8de37c0573dfcdbd535aeda3a5dcbd
|
4
|
+
data.tar.gz: 4f1f9ccd06769d21ea4f25171cf128a69d8e4ea942f4417b10413f0df830b944
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae92dbfa6c44909c8d9b27392cab6ae86854d25343074441b5b5657c54d36bbc5bf332b19c25fd9694f2a58d75abfdc859982cf79a08a7128916943e40c04ae3
|
7
|
+
data.tar.gz: b68a5bd5ebc54b333c67918f6a0a51f8fe871d5607c6b36b987163dd195243b1cb21181e4f52f714528b381ea7a81f43160b5327ddbf4380472cfc58daece9f2
|
data/README.md
CHANGED
@@ -53,6 +53,13 @@ Evaluation in a permissive context:
|
|
53
53
|
ExecJS.permissive_eval('1+1')
|
54
54
|
```
|
55
55
|
|
56
|
+
#### Stopping Contexts
|
57
|
+
Contexts can be stopped programmatically. If all contexts of a VM are stopped, the VM itself will be shut down with Node exiting, freeing memory and resources.
|
58
|
+
```ruby
|
59
|
+
context = ExecJS.compile('Test = "test"') # will start a node process
|
60
|
+
ExecJS::Runtimes::Speednode.stop_context(context) # will kill the node process
|
61
|
+
```
|
62
|
+
|
56
63
|
### Precompiling and Storing scripts for repeated execution
|
57
64
|
|
58
65
|
Scripts can be precompiled and stored for repeated execution, which leads to a significant performance improvement, especially for larger scripts:
|
@@ -132,6 +132,13 @@ CircularJSON.stringify = function stringify(value, replacer, space, doNotResolve
|
|
132
132
|
};
|
133
133
|
/*** end of circular-json ***/
|
134
134
|
|
135
|
+
// serialize Map as Object, also below in attach
|
136
|
+
function simple_map_replacer(key, value) {
|
137
|
+
if (value && typeof value === 'object' && value.constructor.name === "Map")
|
138
|
+
return Object.fromEntries(value);
|
139
|
+
return value;
|
140
|
+
}
|
141
|
+
|
135
142
|
function attachFunctionSource(responder_path, context, func) {
|
136
143
|
return `${func} = async function(...method_args) {
|
137
144
|
let context = '${context}';
|
@@ -158,8 +165,13 @@ function attachFunctionSource(responder_path, context, func) {
|
|
158
165
|
}, 10)
|
159
166
|
});
|
160
167
|
}
|
168
|
+
function simple_map_replacer(key, value) {
|
169
|
+
if (value && typeof value === 'object' && value.constructor.name === "Map")
|
170
|
+
return Object.fromEntries(value);
|
171
|
+
return value;
|
172
|
+
}
|
161
173
|
return new Promise(function(resolve, reject) {
|
162
|
-
let request_json = JSON.stringify(request);
|
174
|
+
let request_json = JSON.stringify(request, simple_map_replacer);
|
163
175
|
let buffer = Buffer.alloc(0);
|
164
176
|
let socket = net.connect(responder_path);
|
165
177
|
socket.setTimeout(2000);
|
@@ -228,18 +240,15 @@ function getContext(uuid) {
|
|
228
240
|
|
229
241
|
function getContextOptions(uuid) {
|
230
242
|
let options = { filename: "(execjs)", displayErrors: true };
|
231
|
-
if (contexts[uuid].options.timeout)
|
243
|
+
if (contexts[uuid].options.timeout)
|
232
244
|
options.timeout = contexts[uuid].options.timeout;
|
233
|
-
}
|
234
245
|
return options;
|
235
246
|
}
|
236
247
|
|
237
248
|
function massageStackTrace(stack) {
|
238
|
-
if (stack && stack.indexOf("SyntaxError") == 0)
|
249
|
+
if (stack && stack.indexOf("SyntaxError") == 0)
|
239
250
|
return "(execjs):1\n" + stack;
|
240
|
-
|
241
|
-
return stack;
|
242
|
-
}
|
251
|
+
return stack;
|
243
252
|
}
|
244
253
|
|
245
254
|
let socket_path = process.env.SOCKET_PATH;
|
@@ -300,7 +309,7 @@ let commands = {
|
|
300
309
|
deleteContext: function(uuid) {
|
301
310
|
delete contexts[uuid];
|
302
311
|
delete scripts[uuid]
|
303
|
-
return [
|
312
|
+
return ['ok', Object.keys(contexts).length];
|
304
313
|
},
|
305
314
|
exit: function(code) {
|
306
315
|
process_exit = code;
|
@@ -384,9 +393,9 @@ let server = net.createServer(function(s) {
|
|
384
393
|
return;
|
385
394
|
}
|
386
395
|
|
387
|
-
try { outputJSON = JSON.stringify(result); }
|
396
|
+
try { outputJSON = JSON.stringify(result, simple_map_replacer); }
|
388
397
|
catch(err) {
|
389
|
-
if (err.message.includes('circular')) { outputJSON = CircularJSON.stringify(result); }
|
398
|
+
if (err.message.includes('circular')) { outputJSON = CircularJSON.stringify(result, simple_map_replacer); }
|
390
399
|
else { outputJSON = JSON.stringify([['', err].join(''), err.stack]); }
|
391
400
|
s.write([outputJSON, "\x04"].join(''));
|
392
401
|
if (process_exit !== false) { process.exit(process_exit); }
|
@@ -4,22 +4,20 @@ module Isomorfeus
|
|
4
4
|
class Context < ::ExecJS::Runtime::Context
|
5
5
|
def self.finalize(runtime, uuid)
|
6
6
|
proc do
|
7
|
-
runtime.
|
7
|
+
runtime.unregister_context(uuid)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(runtime, source = "", options = {})
|
12
12
|
@runtime = runtime
|
13
13
|
@uuid = SecureRandom.uuid
|
14
|
+
@runtime.register_context(@uuid, self)
|
14
15
|
@permissive = !!options.delete(:permissive)
|
15
16
|
@debug = @permissive ? !!options.delete(:debug) { false } : false
|
16
17
|
@debug = false unless ENV['NODE_OPTIONS']&.include?('--inspect')
|
17
18
|
@vm = @runtime.vm
|
18
|
-
@context = nil
|
19
19
|
@timeout = options[:timeout] ? options[:timeout]/1000 : 600
|
20
|
-
|
21
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(@runtime, @uuid))
|
22
|
-
|
20
|
+
|
23
21
|
filename = options.delete(:filename)
|
24
22
|
source = File.read(filename) if filename
|
25
23
|
|
@@ -28,6 +26,9 @@ module Isomorfeus
|
|
28
26
|
rescue
|
29
27
|
source = source.force_encoding('UTF-8')
|
30
28
|
end
|
29
|
+
|
30
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@runtime, @uuid))
|
31
|
+
|
31
32
|
if @debug && @permissive
|
32
33
|
raw_created(source, options)
|
33
34
|
elsif @permissive
|
@@ -35,7 +36,8 @@ module Isomorfeus
|
|
35
36
|
else
|
36
37
|
raw_create(source, options)
|
37
38
|
end
|
38
|
-
|
39
|
+
|
40
|
+
add_script(key: :_internal_exec_fin, source: '!global.__LastExecutionFinished')
|
39
41
|
end
|
40
42
|
|
41
43
|
# def options
|
@@ -104,6 +106,10 @@ module Isomorfeus
|
|
104
106
|
raw_exec("(function(){#{source}})()")
|
105
107
|
end
|
106
108
|
|
109
|
+
def stop
|
110
|
+
@runtime.unregister_context(@uuid)
|
111
|
+
end
|
112
|
+
|
107
113
|
protected
|
108
114
|
|
109
115
|
def raw_bench(source)
|
@@ -119,26 +125,20 @@ module Isomorfeus
|
|
119
125
|
end
|
120
126
|
|
121
127
|
def raw_create(source, options)
|
122
|
-
return if @context
|
123
128
|
source = encode(source)
|
124
129
|
result = @vm.create(@uuid, source, options)
|
125
|
-
@context = true
|
126
130
|
extract_result(result)
|
127
131
|
end
|
128
132
|
|
129
133
|
def raw_created(source, options)
|
130
|
-
return if @context
|
131
134
|
source = encode(source)
|
132
135
|
result = @vm.created(@uuid, source, options)
|
133
|
-
@context = true
|
134
136
|
extract_result(result)
|
135
137
|
end
|
136
138
|
|
137
139
|
def raw_createp(source, options)
|
138
|
-
return if @context
|
139
140
|
source = encode(source)
|
140
141
|
result = @vm.createp(@uuid, source, options)
|
141
|
-
@context = true
|
142
142
|
extract_result(result)
|
143
143
|
end
|
144
144
|
|
@@ -165,7 +165,7 @@ module Isomorfeus
|
|
165
165
|
end
|
166
166
|
|
167
167
|
def await_result
|
168
|
-
start_time = Time.now
|
168
|
+
start_time = ::Time.now
|
169
169
|
while eval_script(key: :_internal_exec_fin) && !timed_out?(start_time)
|
170
170
|
sleep 0.005
|
171
171
|
end
|
@@ -193,7 +193,7 @@ module Isomorfeus
|
|
193
193
|
end
|
194
194
|
|
195
195
|
def timed_out?(start_time)
|
196
|
-
if (Time.now - start_time) > @timeout
|
196
|
+
if (::Time.now - start_time) > @timeout
|
197
197
|
raise "IsomorfeusSpeednode: Command Execution timed out!"
|
198
198
|
end
|
199
199
|
false
|
@@ -5,7 +5,7 @@ if Gem.win_platform?
|
|
5
5
|
module Win32
|
6
6
|
class Pipe
|
7
7
|
def write(data)
|
8
|
-
bytes = FFI::MemoryPointer.new(:ulong)
|
8
|
+
bytes = ::FFI::MemoryPointer.new(:ulong)
|
9
9
|
|
10
10
|
raise Error, "no pipe created" unless @pipe
|
11
11
|
|
@@ -41,20 +41,6 @@ module Isomorfeus
|
|
41
41
|
module Speednode
|
42
42
|
class Runtime < ExecJS::Runtime
|
43
43
|
class VM
|
44
|
-
attr_reader :responder
|
45
|
-
|
46
|
-
def initialize(options)
|
47
|
-
@mutex = Thread::Mutex.new
|
48
|
-
@socket_path = nil
|
49
|
-
@options = options
|
50
|
-
@started = false
|
51
|
-
@socket = nil
|
52
|
-
end
|
53
|
-
|
54
|
-
def started?
|
55
|
-
@started
|
56
|
-
end
|
57
|
-
|
58
44
|
def self.finalize(socket, socket_dir, socket_path, pid)
|
59
45
|
proc do
|
60
46
|
Isomorfeus::Speednode::Runtime.responders[socket_path].kill if Isomorfeus::Speednode::Runtime.responders[socket_path]
|
@@ -79,6 +65,20 @@ module Isomorfeus
|
|
79
65
|
nil
|
80
66
|
end
|
81
67
|
|
68
|
+
attr_reader :responder
|
69
|
+
|
70
|
+
def initialize(options)
|
71
|
+
@mutex = ::Thread::Mutex.new
|
72
|
+
@socket_path = nil
|
73
|
+
@options = options
|
74
|
+
@started = false
|
75
|
+
@socket = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def started?
|
79
|
+
@started
|
80
|
+
end
|
81
|
+
|
82
82
|
def evsc(context, key)
|
83
83
|
command('evsc', { 'context' => context, 'key' => key })
|
84
84
|
end
|
@@ -134,6 +134,16 @@ module Isomorfeus
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
def stop
|
138
|
+
return unless @started
|
139
|
+
@mutex.synchronize do
|
140
|
+
self.class.exit_node(@socket, @socket_dir, @socket_path, @pid)
|
141
|
+
@socket_path = nil
|
142
|
+
@started = false
|
143
|
+
@socket = nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
137
147
|
private
|
138
148
|
|
139
149
|
def start_without_synchronization
|
@@ -19,14 +19,30 @@ module Isomorfeus
|
|
19
19
|
@name = options[:name]
|
20
20
|
@binary = Isomorfeus::Speednode::NodeCommand.cached(options[:command])
|
21
21
|
@runner_path = options[:runner_path]
|
22
|
+
@vm = VM.new(binary: @binary, source_maps: '--enable-source-maps', runner_path: @runner_path)
|
22
23
|
@encoding = options[:encoding]
|
23
24
|
@deprecated = !!options[:deprecated]
|
24
|
-
|
25
|
-
@vm = VM.new(binary: @binary, source_maps: '--enable-source-maps', runner_path: @runner_path)
|
26
|
-
|
27
25
|
@popen_options = {}
|
28
26
|
@popen_options[:external_encoding] = @encoding if @encoding
|
29
27
|
@popen_options[:internal_encoding] = ::Encoding.default_internal || 'UTF-8'
|
28
|
+
@contexts = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def register_context(uuid, context)
|
32
|
+
@contexts[uuid] = context
|
33
|
+
end
|
34
|
+
|
35
|
+
def unregister_context(uuid)
|
36
|
+
context = @contexts.delete(uuid)
|
37
|
+
if context && @vm
|
38
|
+
ObjectSpace.undefine_finalizer(context)
|
39
|
+
_ok, cc = @vm.delete_context(uuid) rescue nil # if delete_context fails, the vm exited before probably
|
40
|
+
end
|
41
|
+
@vm.stop if @contexts.size == 0 && @vm.started?
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop_context(context)
|
45
|
+
unregister_context(context.instance_variable_get(:@uuid))
|
30
46
|
end
|
31
47
|
|
32
48
|
def available?
|
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.
|
4
|
+
version: 0.7.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: 2023-
|
11
|
+
date: 2023-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|