jscall 1.1.0 → 1.3.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 +59 -8
- data/Rakefile +5 -1
- data/examples/pdf-js.rb +56 -0
- data/lib/jscall/main.mjs +62 -25
- data/lib/jscall/synch.mjs +185 -0
- data/lib/jscall/version.rb +1 -1
- data/lib/jscall.rb +11 -8
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1df05fa2e6cee8575b7cdf9a95d35c264422d66c1c793f71e620462380a891bc
|
4
|
+
data.tar.gz: 4bae39ef37ec190d8c6b87fb4541c0ec4f074b7c5b09eeecc1d92a4a47dd570c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16c12783ff80fefb85e4453ca09b54faf4b614895157f4e57108379a51ef0ebbacc2fb38ddff7d87faef50a5c3880a6690cfeb03dc5e5d67be447e36236475af
|
7
|
+
data.tar.gz: a5b57ae8e5d7d891648d457ed32f481ea1b0d528d973e693e164f5053e39f6bfce235148fc22990a53e52e5d7ceaa5eb91dc3297b4b89b8a7af90aa47b88ff54
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@ Jscall.exec '1 + 1'
|
|
18
18
|
```
|
19
19
|
|
20
20
|
This returns `2`. The argument passed to `Jscall.exec` can be
|
21
|
-
|
21
|
+
multiple lines. It is executed as source code written in JavaScript.
|
22
22
|
|
23
23
|
`Jscall.exec` returns a resulting value. Numbers, character strings (and symbols), boolean values, and `nil` (and `null`)
|
24
24
|
are copied when passing between Ruby and JavaScript. An array is shallow-copied.
|
@@ -64,7 +64,7 @@ when this ruby object is passed to JavaScript as an argument,
|
|
64
64
|
a normal object `{ a: 2, b: 3 }` is created as its copy in JavaScript
|
65
65
|
and passed to a JavaScript method.
|
66
66
|
|
67
|
-
To call a JavaScript function from Ruby, call a
|
67
|
+
To call a JavaScript function from Ruby, call a method on `Jscall`.
|
68
68
|
For example,
|
69
69
|
|
70
70
|
```
|
@@ -90,7 +90,7 @@ Jscall.console.log('Hello')
|
|
90
90
|
```
|
91
91
|
|
92
92
|
This prints `Hello` on a JavaScript console. `Jscall.console` returns a remote
|
93
|
-
|
93
|
+
reference to the value of `console` in JavaScript. Then, `.log('Hello')`
|
94
94
|
calls the `log` method on `console` in JavaScript.
|
95
95
|
|
96
96
|
When a Ruby object is passed to a JavaScript function/method,
|
@@ -110,7 +110,7 @@ created in Ruby.
|
|
110
110
|
Note that you must `await` every call to Ruby object since it is
|
111
111
|
asynchronous call.
|
112
112
|
|
113
|
-
In JavaScript, `Ruby.exec` is
|
113
|
+
In JavaScript, `Ruby.exec` is available to run a program in Ruby.
|
114
114
|
For example,
|
115
115
|
|
116
116
|
```
|
@@ -261,7 +261,7 @@ fs_module = await load('fs')
|
|
261
261
|
## Promise
|
262
262
|
|
263
263
|
If a program attempts to pass a `Promise` object from JavaScript to Ruby,
|
264
|
-
it waits until the promise is
|
264
|
+
it waits until the promise is fulfilled. Then Jscall passes
|
265
265
|
the value of that promise from JavaScript to Ruby instead of that
|
266
266
|
promise itself (or a remote reference to that promise). When that promise
|
267
267
|
is rejected, an error object is passed to Ruby
|
@@ -270,7 +270,7 @@ This design reflects the fact that an `async` function in JavaScript
|
|
270
270
|
also returns a `Promise` object but this object must not be returned
|
271
271
|
to Ruby as is when that `async` function is called from Ruby.
|
272
272
|
Jscall cannot determine whether a promise should be passed as is to Ruby
|
273
|
-
or its value must be passed to Ruby after the promise is
|
273
|
+
or its value must be passed to Ruby after the promise is fulfilled.
|
274
274
|
|
275
275
|
When enforcing Jscall to pass a `Promise` object from JavaScript to Ruby,
|
276
276
|
`.async` must be inserted between a receiver and a method name.
|
@@ -288,6 +288,27 @@ prom = obj.async.a # promise
|
|
288
288
|
prom.then(->(r) { puts r }) # 7
|
289
289
|
```
|
290
290
|
|
291
|
+
## Synchronous calls
|
292
|
+
|
293
|
+
You might want to avoid writing `await` when you call a method on a Ruby
|
294
|
+
object or you execute Ruby code by `Ruby.exec` from JavaScript.
|
295
|
+
For example, that call is included in library code and you might not
|
296
|
+
be able to modify the library code so that `await` will be inserted.
|
297
|
+
|
298
|
+
Jscall supports synchronous calls from JavaScript to Ruby only when
|
299
|
+
the underlying JavaScript engine is node.js on Linux.
|
300
|
+
In the mode of synchronous calls, you do not have to `await` a method call
|
301
|
+
on a Ruby object or a call to `Ruby.exec`.
|
302
|
+
It blocks until the return value comes back from Ruby.
|
303
|
+
While it blocks, all calls from Ruby to JavaScript are
|
304
|
+
synchronously processed.
|
305
|
+
|
306
|
+
To change to the mode of synchronous calls,
|
307
|
+
call `Jscall.config`:
|
308
|
+
|
309
|
+
```
|
310
|
+
Jscall.config(sync: true)
|
311
|
+
```
|
291
312
|
|
292
313
|
## Configuration
|
293
314
|
|
@@ -348,15 +369,45 @@ Passing `true` for `browser:` switches the execution engine to a web browser.
|
|
348
369
|
The default engine is node.js.
|
349
370
|
To switch the engine back to node.js, pass `false` for `browser:`.
|
350
371
|
Call `Jscall.close` to detach the current execution engine.
|
351
|
-
A new
|
372
|
+
A new engine with a new configuration will be created.
|
352
373
|
|
353
374
|
`port:` specifies the port number of an http server. It is optional.
|
354
375
|
The example above specifies that Ruby receives http requests
|
355
376
|
sent to http://localhost:10082 from JavaScript on a web browser.
|
356
377
|
|
357
378
|
|
379
|
+
### Misc.
|
380
|
+
|
381
|
+
To change to the mode of synchronous calls,
|
382
|
+
|
383
|
+
```
|
384
|
+
Jscall.config(sync: true)
|
385
|
+
```
|
386
|
+
|
387
|
+
To set all the configurations to the default ones,
|
388
|
+
|
389
|
+
```
|
390
|
+
Jscall.config()
|
391
|
+
```
|
392
|
+
|
358
393
|
### Other configurations
|
359
394
|
|
395
|
+
To obtain more detailed error messages,
|
396
|
+
set a debugging level to 10.
|
397
|
+
In Ruby,
|
398
|
+
|
399
|
+
```
|
400
|
+
Jscall.debug = 10
|
401
|
+
```
|
402
|
+
|
403
|
+
In JavaScript,
|
404
|
+
|
405
|
+
```
|
406
|
+
Ruby.setDebugLevel(10)
|
407
|
+
```
|
408
|
+
|
409
|
+
The default debugging level is 0.
|
410
|
+
|
360
411
|
To change the name of the node command,
|
361
412
|
|
362
413
|
```
|
@@ -415,7 +466,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/csg-to
|
|
415
466
|
|
416
467
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
417
468
|
|
418
|
-
##
|
469
|
+
## Acknowledgment
|
419
470
|
|
420
471
|
The icon image for jscall was created by partly using the Ruby logo, which was obtained
|
421
472
|
from https://www.ruby-lang.org/en/about/logo/ under CC BY-SA 2.5.
|
data/Rakefile
CHANGED
@@ -6,7 +6,11 @@ require "rake/testtask"
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
7
7
|
t.libs << "test"
|
8
8
|
t.libs << "lib"
|
9
|
-
|
9
|
+
files = FileList["test/**/test_*.rb"]
|
10
|
+
unless /linux/ =~ RbConfig::CONFIG['host_os']
|
11
|
+
files.exclude "test/test_sync_io.rb"
|
12
|
+
end
|
13
|
+
t.test_files = files
|
10
14
|
end
|
11
15
|
|
12
16
|
task default: :test
|
data/examples/pdf-js.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Display a pdf file by using pdf.js
|
2
|
+
|
3
|
+
require 'jscall'
|
4
|
+
|
5
|
+
# Run a JavaScript program on a browser
|
6
|
+
Jscall.config browser: true
|
7
|
+
|
8
|
+
# Write HTML code on a blank web page.
|
9
|
+
Jscall.dom.append_to_body(<<CODE)
|
10
|
+
<h1>PDF.js 'Hello, world!' example</h1>
|
11
|
+
<canvas id="the-canvas"></canvas>
|
12
|
+
CODE
|
13
|
+
|
14
|
+
# import pdf.js
|
15
|
+
pdfjs = Jscall.dyn_import('https://mozilla.github.io/pdf.js/build/pdf.js')
|
16
|
+
|
17
|
+
# A pdf file
|
18
|
+
url = "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf"
|
19
|
+
|
20
|
+
pdf = Jscall.exec 'window["pdfjs-dist/build/pdf"]'
|
21
|
+
|
22
|
+
pdf.GlobalWorkerOptions.workerSrc = "https://mozilla.github.io/pdf.js/build/pdf.worker.js"
|
23
|
+
|
24
|
+
loadingTask = pdf.getDocument(url)
|
25
|
+
loadingTask.async.promise.then(-> (pdf) {
|
26
|
+
puts "PDF loaded"
|
27
|
+
|
28
|
+
# Fetch the first page
|
29
|
+
pageNumber = 1;
|
30
|
+
pdf.async.getPage(pageNumber).then(-> (page) {
|
31
|
+
puts "Page loaded"
|
32
|
+
|
33
|
+
scale = 1.5;
|
34
|
+
viewport = page.getViewport({ scale: scale })
|
35
|
+
|
36
|
+
canvas = Jscall.document.getElementById("the-canvas")
|
37
|
+
context = canvas.getContext("2d")
|
38
|
+
canvas.height = viewport.height
|
39
|
+
canvas.width = viewport.width
|
40
|
+
|
41
|
+
# Render the pdf page
|
42
|
+
renderContext = {
|
43
|
+
canvasContext: context,
|
44
|
+
viewport: viewport,
|
45
|
+
}
|
46
|
+
renderTask = page.render(renderContext)
|
47
|
+
renderTask.async.promise.then(-> (r) {
|
48
|
+
# Print a message when the rendering succeeds.
|
49
|
+
puts "Page rendered #{r}"
|
50
|
+
})
|
51
|
+
})
|
52
|
+
},
|
53
|
+
-> (reason) {
|
54
|
+
# an error occurs.
|
55
|
+
puts reason
|
56
|
+
})
|
data/lib/jscall/main.mjs
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
// Copyright (C) 2022- Shigeru Chiba. All rights reserved.
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
const
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
let debug_level = 0
|
4
|
+
|
5
|
+
// debug level is 0 (default) or 10.
|
6
|
+
export const setDebugLevel = d => {
|
7
|
+
debug_level = d
|
8
|
+
}
|
9
|
+
|
10
|
+
export const cmd_eval = 1
|
11
|
+
export const cmd_call = 2
|
12
|
+
export const cmd_reply = 3
|
13
|
+
export const cmd_async_call = 4
|
14
|
+
export const cmd_async_eval = 5
|
15
|
+
export const cmd_retry = 6
|
16
|
+
export const cmd_reject = 7
|
10
17
|
|
11
18
|
const param_array = 0
|
12
19
|
const param_object = 1
|
@@ -185,12 +192,23 @@ const decode_obj = obj => {
|
|
185
192
|
throw `decode_obj: unsupported value, ${obj}`
|
186
193
|
}
|
187
194
|
|
195
|
+
export const decode_obj_or_error = obj => {
|
196
|
+
const result = decode_obj(obj)
|
197
|
+
if (result instanceof RubyError)
|
198
|
+
return result.get()
|
199
|
+
else
|
200
|
+
return result
|
201
|
+
}
|
202
|
+
|
188
203
|
const js_eval = eval
|
189
204
|
|
190
|
-
const funcall_from_ruby = cmd => {
|
205
|
+
export const funcall_from_ruby = cmd => {
|
191
206
|
const receiver = decode_obj(cmd[2])
|
192
207
|
const name = cmd[3]
|
193
208
|
const args = cmd[4].map(e => decode_obj(e))
|
209
|
+
if (debug_level >= 10)
|
210
|
+
console.error(`RubyToJS> ${name} ${cmd[1]}`)
|
211
|
+
|
194
212
|
if (name.endsWith('=')) {
|
195
213
|
const name2 = name.substring(0, name.length - 1)
|
196
214
|
if (receiver === null)
|
@@ -211,8 +229,8 @@ const funcall_from_ruby = cmd => {
|
|
211
229
|
}
|
212
230
|
else {
|
213
231
|
const f = Reflect.get(receiver, name)
|
214
|
-
if (f)
|
215
|
-
if (typeof f === 'function')
|
232
|
+
if (f !== undefined)
|
233
|
+
if (typeof f === 'function' && !(f instanceof RemoteRef))
|
216
234
|
return Reflect.apply(f, receiver, args)
|
217
235
|
else if (args.length === 0)
|
218
236
|
return f // obtain a propety
|
@@ -221,7 +239,7 @@ const funcall_from_ruby = cmd => {
|
|
221
239
|
throw `unknown JS function/method was called: ${name} on <${receiver}>`
|
222
240
|
}
|
223
241
|
|
224
|
-
let stdout_puts = console.log
|
242
|
+
export let stdout_puts = console.log
|
225
243
|
let num_generated_ids = 0
|
226
244
|
|
227
245
|
const fresh_id = () => {
|
@@ -229,7 +247,7 @@ const fresh_id = () => {
|
|
229
247
|
return num_generated_ids
|
230
248
|
}
|
231
249
|
|
232
|
-
const reply = (message_id, value, sync_mode) => {
|
250
|
+
export const reply = (message_id, value, sync_mode) => {
|
233
251
|
if (sync_mode && value instanceof Promise)
|
234
252
|
value.then(result => { reply(message_id, result, true) })
|
235
253
|
.catch(err => reply_error(message_id, err))
|
@@ -244,8 +262,10 @@ const reply = (message_id, value, sync_mode) => {
|
|
244
262
|
}
|
245
263
|
}
|
246
264
|
|
247
|
-
const reply_error = (message_id,
|
248
|
-
const
|
265
|
+
export const reply_error = (message_id, error) => {
|
266
|
+
const msg = typeof error === 'string' ? error : error.toString() +
|
267
|
+
'\n ---\n' + error.stack
|
268
|
+
const cmd = reply_with_piggyback([cmd_reply, message_id, encode_error(msg)])
|
249
269
|
stdout_puts(JSON.stringify(cmd))
|
250
270
|
}
|
251
271
|
|
@@ -279,24 +299,39 @@ let reply_counter = 0
|
|
279
299
|
|
280
300
|
export const exec = src => {
|
281
301
|
return new Promise((resolve, reject) => {
|
282
|
-
const
|
283
|
-
const
|
302
|
+
const cmd = make_cmd_eval(src)
|
303
|
+
const message_id = cmd[1]
|
284
304
|
callback_stack.push([message_id, resolve, reject])
|
285
305
|
stdout_puts(JSON.stringify(cmd))
|
286
306
|
})
|
287
307
|
}
|
288
308
|
|
289
|
-
const
|
309
|
+
export const make_cmd_eval = src => {
|
310
|
+
const message_id = fresh_id()
|
311
|
+
return reply_with_piggyback([cmd_eval, message_id, src])
|
312
|
+
}
|
313
|
+
|
314
|
+
let funcall_to_ruby = (receiver_id, name, args) => {
|
290
315
|
return new Promise((resolve, reject) => {
|
291
|
-
const
|
292
|
-
const
|
293
|
-
const encoded_args = args.map(e => encode_obj(e))
|
294
|
-
const cmd = reply_with_piggyback([cmd_call, message_id, receiver, name, encoded_args])
|
316
|
+
const cmd = make_cmd_call(receiver_id, name, args)
|
317
|
+
const message_id = cmd[1]
|
295
318
|
callback_stack.push([message_id, resolve, reject])
|
319
|
+
if (debug_level >= 10)
|
320
|
+
console.error(`JStoRuby< ${name} ${message_id}`)
|
321
|
+
|
296
322
|
stdout_puts(JSON.stringify(cmd))
|
297
323
|
})
|
298
324
|
}
|
299
325
|
|
326
|
+
export const set_funcall_to_ruby = f => { funcall_to_ruby = f }
|
327
|
+
|
328
|
+
export const make_cmd_call = (receiver_id, name, args) => {
|
329
|
+
const message_id = fresh_id()
|
330
|
+
const receiver = [param_local_object, receiver_id]
|
331
|
+
const encoded_args = args.map(e => encode_obj(e))
|
332
|
+
return reply_with_piggyback([cmd_call, message_id, receiver, name, encoded_args])
|
333
|
+
}
|
334
|
+
|
300
335
|
const returned_from_callback = cmd => {
|
301
336
|
const message_id = cmd[1]
|
302
337
|
const result = decode_obj(cmd[2])
|
@@ -412,6 +447,10 @@ export class MessageReader {
|
|
412
447
|
}
|
413
448
|
}
|
414
449
|
|
450
|
+
let make_message_reader = (stdin) => new MessageReader(stdin)
|
451
|
+
|
452
|
+
export const set_make_message_reader = (f) => { make_message_reader = f }
|
453
|
+
|
415
454
|
export const start = async (stdin, use_stdout) => {
|
416
455
|
if (use_stdout)
|
417
456
|
console.log = console.error // on node.js
|
@@ -419,7 +458,7 @@ export const start = async (stdin, use_stdout) => {
|
|
419
458
|
stdout_puts = (m) => stdin.puts(m) // on browser
|
420
459
|
|
421
460
|
stdin.setEncoding('utf8')
|
422
|
-
for await (const json_data of
|
461
|
+
for await (const json_data of make_message_reader(stdin)) {
|
423
462
|
let cmd
|
424
463
|
try {
|
425
464
|
cmd = JSON.parse(json_data)
|
@@ -451,9 +490,7 @@ export const start = async (stdin, use_stdout) => {
|
|
451
490
|
else // cmd_retry and other unknown commands
|
452
491
|
reply_error(cmd[1], `invalid command ${cmd[0]}`)
|
453
492
|
} catch (error) {
|
454
|
-
|
455
|
-
'\n ---\n' + error.stack
|
456
|
-
reply_error(cmd[1], msg)
|
493
|
+
reply_error(cmd[1], error)
|
457
494
|
}
|
458
495
|
}
|
459
496
|
}
|
@@ -0,0 +1,185 @@
|
|
1
|
+
// Copyright (C) 2022- Shigeru Chiba. All rights reserved.
|
2
|
+
// This works only with node.js on Linux
|
3
|
+
|
4
|
+
import * as main from './main.mjs'
|
5
|
+
import { readSync, openSync } from 'fs'
|
6
|
+
|
7
|
+
class SynchronousStdin {
|
8
|
+
constructor() {
|
9
|
+
this.buf_size = 4096
|
10
|
+
this.buffer = Buffer.alloc(this.buf_size);
|
11
|
+
this.stdin = openSync('/dev/stdin', 'rs')
|
12
|
+
}
|
13
|
+
|
14
|
+
*[Symbol.iterator]() {
|
15
|
+
let str
|
16
|
+
while ((str = this.readOne()) !== null)
|
17
|
+
yield str
|
18
|
+
}
|
19
|
+
|
20
|
+
readOne() {
|
21
|
+
while (true) {
|
22
|
+
try {
|
23
|
+
const nbytes = readSync(this.stdin, this.buffer, 0, this.buf_size)
|
24
|
+
if (nbytes > 0)
|
25
|
+
return this.buffer.toString('utf-8', 0, nbytes)
|
26
|
+
else
|
27
|
+
return null // maybe EOF on macOS
|
28
|
+
}
|
29
|
+
catch (e) {
|
30
|
+
if (e.code === 'EOF')
|
31
|
+
return null
|
32
|
+
else if (e.code !== 'EAGAIN')
|
33
|
+
throw e
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
class SyncMessageReader extends main.MessageReader {
|
40
|
+
constructor(stream) {
|
41
|
+
super(stream)
|
42
|
+
this.stdin = new SynchronousStdin()
|
43
|
+
this.generator = null
|
44
|
+
}
|
45
|
+
|
46
|
+
gets_function() {
|
47
|
+
const iterator = this[Symbol.iterator]()
|
48
|
+
return () => {
|
49
|
+
const v = iterator.next()
|
50
|
+
if (v.done)
|
51
|
+
return null
|
52
|
+
else
|
53
|
+
return v.value
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
async *[Symbol.asyncIterator]() {
|
58
|
+
if (this.generator !== null) {
|
59
|
+
while (true) {
|
60
|
+
const v = this.generator.next()
|
61
|
+
if (v.done)
|
62
|
+
break
|
63
|
+
else
|
64
|
+
yield v.value
|
65
|
+
}
|
66
|
+
}
|
67
|
+
for await (const data of this.stream) {
|
68
|
+
this.acc += data
|
69
|
+
this.generator = this.generatorBody()
|
70
|
+
while (true) {
|
71
|
+
const v = this.generator.next()
|
72
|
+
if (v.done)
|
73
|
+
break
|
74
|
+
else
|
75
|
+
yield v.value
|
76
|
+
}
|
77
|
+
}
|
78
|
+
this.checkEOS()
|
79
|
+
}
|
80
|
+
|
81
|
+
*[Symbol.iterator]() {
|
82
|
+
if (this.generator !== null) {
|
83
|
+
while (true) {
|
84
|
+
const v = this.generator.next()
|
85
|
+
if (v.done)
|
86
|
+
break
|
87
|
+
else
|
88
|
+
yield v.value
|
89
|
+
}
|
90
|
+
}
|
91
|
+
for (const data of this.stdin) {
|
92
|
+
this.acc += data
|
93
|
+
this.generator = this.generatorBody()
|
94
|
+
while (true) {
|
95
|
+
const v = this.generator.next()
|
96
|
+
if (v.done)
|
97
|
+
break
|
98
|
+
else
|
99
|
+
yield v.value
|
100
|
+
}
|
101
|
+
}
|
102
|
+
this.checkEOS()
|
103
|
+
}
|
104
|
+
|
105
|
+
*generatorBody() {
|
106
|
+
let pos = 0
|
107
|
+
while (true) {
|
108
|
+
const result = this.iteratorBody(pos)
|
109
|
+
if (result[0] === false)
|
110
|
+
break
|
111
|
+
else if (result[0] !== true)
|
112
|
+
yield result[0] // result[0] is a string
|
113
|
+
|
114
|
+
pos = result[1]
|
115
|
+
if (this.checkEmptiness(pos))
|
116
|
+
break
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
export const start = main.start
|
122
|
+
|
123
|
+
let stdin_gets = null
|
124
|
+
|
125
|
+
main.set_make_message_reader((stdin) => {
|
126
|
+
const reader = new SyncMessageReader(stdin)
|
127
|
+
stdin_gets = reader.gets_function()
|
128
|
+
return reader
|
129
|
+
})
|
130
|
+
|
131
|
+
const js_eval = eval
|
132
|
+
const exported = main.get_exported_imported()[0]
|
133
|
+
|
134
|
+
const event_loop = () => {
|
135
|
+
let json_data
|
136
|
+
while ((json_data = stdin_gets()) !== null) {
|
137
|
+
let cmd
|
138
|
+
try {
|
139
|
+
cmd = JSON.parse(json_data)
|
140
|
+
|
141
|
+
// scavenge remote references
|
142
|
+
if (cmd.length > 5)
|
143
|
+
cmd[5].forEach(i => exported.remove(i))
|
144
|
+
|
145
|
+
if (cmd[0] == main.cmd_eval || cmd[0] == main.cmd_async_eval) {
|
146
|
+
const result = js_eval(cmd[2])
|
147
|
+
main.reply(cmd[1], result, false)
|
148
|
+
}
|
149
|
+
else if (cmd[0] == main.cmd_call || cmd[0] == main.cmd_async_call) {
|
150
|
+
const result = main.funcall_from_ruby(cmd)
|
151
|
+
main.reply(cmd[1], result, false)
|
152
|
+
}
|
153
|
+
else if (cmd[0] == main.cmd_reply)
|
154
|
+
return main.decode_obj_or_error(cmd[2])
|
155
|
+
else { // cmd_retry, cmd_reject, and other unknown commands
|
156
|
+
console.error(`*** node.js; bad message received: ${json_data}`)
|
157
|
+
break
|
158
|
+
}
|
159
|
+
} catch (error) {
|
160
|
+
const msg = typeof error === 'string' ? error : error.toString() +
|
161
|
+
'\n ---\n' + error.stack
|
162
|
+
main.reply_error(cmd[1], msg)
|
163
|
+
}
|
164
|
+
}
|
165
|
+
return undefined
|
166
|
+
}
|
167
|
+
|
168
|
+
export const exec = src => {
|
169
|
+
const cmd = main.make_cmd_eval(src)
|
170
|
+
main.stdout_puts(JSON.stringify(cmd))
|
171
|
+
return event_loop()
|
172
|
+
}
|
173
|
+
|
174
|
+
main.set_funcall_to_ruby((receiver_id, name, args) => {
|
175
|
+
const cmd = main.make_cmd_call(receiver_id, name, args)
|
176
|
+
main.stdout_puts(JSON.stringify(cmd))
|
177
|
+
return event_loop()
|
178
|
+
})
|
179
|
+
|
180
|
+
export const scavenge_references = main.scavenge_references
|
181
|
+
|
182
|
+
// for testing and debugging
|
183
|
+
export const get_exported_imported = main.get_exported_imported
|
184
|
+
|
185
|
+
export const dyn_import = main.dyn_import
|
data/lib/jscall/version.rb
CHANGED
data/lib/jscall.rb
CHANGED
@@ -231,7 +231,8 @@ module Jscall
|
|
231
231
|
script2 += "import * as m#{i + 2} from \"#{module_names[i][1]}#{module_names[i][2]}\"; globalThis.#{module_names[i][0]} = m#{i + 2}; "
|
232
232
|
end
|
233
233
|
script2 += "import { createRequire } from \"node:module\"; globalThis.require = createRequire(\"file://#{Dir.pwd}/\");"
|
234
|
-
|
234
|
+
main_js_file = if config[:sync] then "synch.mjs" else "main.mjs" end
|
235
|
+
script = "'import * as m1 from \"#{__dir__}/jscall/#{main_js_file}\"; globalThis.Ruby = m1; #{script2}; Ruby.start(process.stdin, true)'"
|
235
236
|
@pipe = IO.popen("#{@@node_cmd} #{options} --input-type 'module' -e #{script}", "r+t")
|
236
237
|
@pipe.autoclose = true
|
237
238
|
end
|
@@ -392,13 +393,15 @@ module Jscall
|
|
392
393
|
if reply[1] != message_id
|
393
394
|
send_reply(reply[1], nil, false, CMD_REJECT)
|
394
395
|
else
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
396
|
+
if @pending_replies.key?(message_id)
|
397
|
+
result = @pending_replies.delete(message_id)
|
398
|
+
if result.is_a?(JavaScriptError)
|
399
|
+
raise result
|
400
|
+
else
|
401
|
+
return result
|
402
|
+
end
|
400
403
|
else
|
401
|
-
|
404
|
+
raise RuntimeError.new("bad CMD_RETRY: #{reply}")
|
402
405
|
end
|
403
406
|
end
|
404
407
|
else
|
@@ -432,7 +435,7 @@ module Jscall
|
|
432
435
|
@configurations = {}
|
433
436
|
@pipeToJsClass = PipeToJs
|
434
437
|
|
435
|
-
#def self.config(module_names: [], options: '', browser: false)
|
438
|
+
#def self.config(module_names: [], options: '', browser: false, sync: false)
|
436
439
|
def self.config(**kw)
|
437
440
|
if kw.nil? || kw == {}
|
438
441
|
@configurations = {}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jscall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shigeru Chiba
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: webrick
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- LICENSE
|
36
36
|
- README.md
|
37
37
|
- Rakefile
|
38
|
+
- examples/pdf-js.rb
|
38
39
|
- jscall.gemspec
|
39
40
|
- lib/jscall.rb
|
40
41
|
- lib/jscall/apple-touch-icon.png
|
@@ -43,6 +44,7 @@ files:
|
|
43
44
|
- lib/jscall/favicon.ico
|
44
45
|
- lib/jscall/jscall.html
|
45
46
|
- lib/jscall/main.mjs
|
47
|
+
- lib/jscall/synch.mjs
|
46
48
|
- lib/jscall/version.rb
|
47
49
|
homepage: https://github.com/csg-tokyo/jscall
|
48
50
|
licenses:
|