isomorfeus-speednode 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|