execjs-xtrn 1.0.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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +171 -0
- data/Rakefile +17 -0
- data/execjs-xtrn.gemspec +27 -0
- data/lib/execjs/node/index.js +71 -0
- data/lib/execjs/node/package.json +6 -0
- data/lib/execjs/wsh/json2.js +481 -0
- data/lib/execjs/wsh/repl.js +41 -0
- data/lib/execjs/xtrn.rb +9 -0
- data/lib/execjs/xtrn/child.rb +57 -0
- data/lib/execjs/xtrn/engine.rb +61 -0
- data/lib/execjs/xtrn/init.rb +25 -0
- data/lib/execjs/xtrn/node.rb +21 -0
- data/lib/execjs/xtrn/nvm.rb +43 -0
- data/lib/execjs/xtrn/version.rb +5 -0
- data/lib/execjs/xtrn/wsh.rb +12 -0
- data/test/child.rb +32 -0
- data/test/engine.rb +103 -0
- data/test/nvm.rb +36 -0
- data/test/shagi.rb +81 -0
- data/test/top.rb +80 -0
- metadata +145 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
!function(){
|
2
|
+
|
3
|
+
var
|
4
|
+
w = WScript,
|
5
|
+
i = w.StdIn,
|
6
|
+
o = w.StdErr
|
7
|
+
|
8
|
+
!function()
|
9
|
+
{
|
10
|
+
var
|
11
|
+
fso = new ActiveXObject("Scripting.FileSystemObject"),
|
12
|
+
j2 = fso.GetParentFolderName(w.ScriptFullName)+"/json2.js"
|
13
|
+
new Function(fso.OpenTextFile(j2).ReadAll())()
|
14
|
+
}()
|
15
|
+
|
16
|
+
while(!i.AtEndOfStream)
|
17
|
+
o.WriteLine(wrap(i.ReadLine()))
|
18
|
+
|
19
|
+
function wrap(s)
|
20
|
+
{
|
21
|
+
try { s=compile(s) }
|
22
|
+
catch(e) { s={err: e.message || 'General error'} }
|
23
|
+
|
24
|
+
try { return JSON.stringify(s) }
|
25
|
+
catch(e) { return '{"err":"JSON.stringify error"}' }
|
26
|
+
}
|
27
|
+
|
28
|
+
function compile(s)
|
29
|
+
{
|
30
|
+
s = JSON.parse(s)
|
31
|
+
if('string'!=typeof s)
|
32
|
+
throw Error('String expected!')
|
33
|
+
return ok(new Function(s)())
|
34
|
+
}
|
35
|
+
|
36
|
+
function ok(v)
|
37
|
+
{
|
38
|
+
return 'undefined'==typeof v ? {} : {ok: v}
|
39
|
+
}
|
40
|
+
|
41
|
+
}()
|
data/lib/execjs/xtrn.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class ExecJS::Xtrn::Child
|
4
|
+
|
5
|
+
@@stats={c: 0} # children
|
6
|
+
|
7
|
+
def say(obj)
|
8
|
+
delta={
|
9
|
+
n: 1, # say calls
|
10
|
+
o: 0, # out bytes
|
11
|
+
i: 0, # in bytes
|
12
|
+
t: Time.now # time spent
|
13
|
+
}
|
14
|
+
@stdin.puts delta[:o]=JSON.generate([obj])[1...-1]
|
15
|
+
i=@stdout.gets
|
16
|
+
|
17
|
+
delta[:t]=Time.now-delta[:t]
|
18
|
+
delta[:i]=i.length
|
19
|
+
delta[:o]=delta[:o].length
|
20
|
+
@stats.each{|var| delta.each{|k, v| var[k]||=0; var[k]+=v}}
|
21
|
+
@stats.reject!{|var| var.delete :once }
|
22
|
+
|
23
|
+
JSON.load i
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.stats
|
27
|
+
@@stats.dup
|
28
|
+
end
|
29
|
+
|
30
|
+
def stats(*recs)
|
31
|
+
@stats+=recs
|
32
|
+
@stats[0].dup
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def initialize(options)
|
38
|
+
i=IO.pipe
|
39
|
+
o=IO.pipe
|
40
|
+
|
41
|
+
@pid=spawn *options[:args],
|
42
|
+
chdir: File.expand_path("../../#{options[:path]}", __FILE__),
|
43
|
+
in: i[0],
|
44
|
+
err: o[1]
|
45
|
+
i[0].close
|
46
|
+
o[1].close
|
47
|
+
@stdin=i[1]
|
48
|
+
@stdout=o[0]
|
49
|
+
if options[:encoding]
|
50
|
+
@stdin.set_encoding options[:encoding]
|
51
|
+
@stdout.set_encoding options[:encoding]
|
52
|
+
end
|
53
|
+
@@stats[:c]+=1
|
54
|
+
@stats=[{}, @@stats]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class ExecJS::Xtrn::Error < RuntimeError
|
2
|
+
end
|
3
|
+
|
4
|
+
class ExecJS::Xtrn::Engine
|
5
|
+
|
6
|
+
Error = ExecJS::Xtrn::Error
|
7
|
+
|
8
|
+
Run=nil # Abstract class
|
9
|
+
|
10
|
+
@stats=@@stats={c: 0}
|
11
|
+
|
12
|
+
def exec(code)
|
13
|
+
return if (code=code.to_s.strip).length==0
|
14
|
+
result=child.say code
|
15
|
+
result={'err'=>'Invalid JS result'} unless Hash===result
|
16
|
+
raise Error, result['err'] if result['err']
|
17
|
+
result['ok']
|
18
|
+
end
|
19
|
+
|
20
|
+
def eval(code)
|
21
|
+
return if (code=code.to_s.strip).length==0
|
22
|
+
exec "return eval(#{JSON.generate([code])[1...-1]})"
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(fn, *args)
|
26
|
+
eval "(#{fn}).apply(this, #{JSON.dump args})"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.exec(code)
|
30
|
+
new.exec code
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.eval(code)
|
34
|
+
new.eval code
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.compile(code='')
|
38
|
+
new.tap{|ctx| ctx.exec code}
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.stats
|
42
|
+
(@stats||{}).dup
|
43
|
+
end
|
44
|
+
|
45
|
+
def stats
|
46
|
+
(@stats||{}).dup
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def child
|
52
|
+
return @child if @child
|
53
|
+
raise NotImplementedError, self.class unless self.class::Run
|
54
|
+
@child=child=ExecJS::Xtrn::Child.new(self.class::Run)
|
55
|
+
child.stats @stats={}, @@stats, classStats=self.class.class_eval{@stats||={c: 0}}
|
56
|
+
@@stats[:c]+=1
|
57
|
+
classStats[:c]+=1
|
58
|
+
child
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ExecJS::Xtrn
|
2
|
+
|
3
|
+
Engines=[Nvm, Node, Wsh]
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :engine
|
7
|
+
end
|
8
|
+
|
9
|
+
# Find best available Engine
|
10
|
+
self.engine=Engines.find{|k| k::Valid} || Engine
|
11
|
+
|
12
|
+
# Install into ExecJS
|
13
|
+
def self.init
|
14
|
+
slf=self
|
15
|
+
sc=(class<<ExecJS;self;end)
|
16
|
+
Engine.methods(false).each{|m| sc.instance_eval{define_method(m){|*args| slf.engine.send m, *args }}}
|
17
|
+
end
|
18
|
+
|
19
|
+
init
|
20
|
+
|
21
|
+
def self.stats
|
22
|
+
Hash[([Child, Engine]+Engines).map{|k| [k.name.sub(/.*\W/, ''), k.stats]}]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class ExecJS::Xtrn::Node < ExecJS::Xtrn::Engine
|
2
|
+
|
3
|
+
Run={
|
4
|
+
args: %w(? .),
|
5
|
+
path: 'node',
|
6
|
+
}
|
7
|
+
|
8
|
+
Names=%w(nodejs node)
|
9
|
+
|
10
|
+
def self.valid?
|
11
|
+
i=Names.index do |n|
|
12
|
+
Run[:args][0]=n
|
13
|
+
{"ok"=>42}==ExecJS::Xtrn::Child.new(Run).say('return 7*6') rescue nil
|
14
|
+
end
|
15
|
+
Run[:args][0]='!' unless i
|
16
|
+
!!i
|
17
|
+
end
|
18
|
+
|
19
|
+
Valid=valid?
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Interface to Node's vm
|
2
|
+
class ExecJS::Xtrn::Nvm < ExecJS::Xtrn::Node
|
3
|
+
|
4
|
+
def exec(code)
|
5
|
+
return if (code=code.to_s.strip).length==0
|
6
|
+
result=say vm: vm, js: code
|
7
|
+
result={'err'=>'Invalid JS result'} unless Hash===result
|
8
|
+
raise Error, result['err'] if result['err']
|
9
|
+
result['ok']
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def child
|
15
|
+
@@child||=super
|
16
|
+
end
|
17
|
+
|
18
|
+
def say(code)
|
19
|
+
ch=@@child
|
20
|
+
@stats[:once]=1
|
21
|
+
ch.stats @stats
|
22
|
+
ch.say code
|
23
|
+
end
|
24
|
+
|
25
|
+
def vm
|
26
|
+
return @vm if @vm
|
27
|
+
c=child
|
28
|
+
@stats[:once]=1 if @stats # Remove @stats, added by Engine
|
29
|
+
@stats={} # Our new stats
|
30
|
+
vm=say({vm: 0})['vm']
|
31
|
+
raise Error, 'Cannot create VM' unless vm
|
32
|
+
cs=self.class.class_eval{@stats}
|
33
|
+
cs[:m]||=0
|
34
|
+
cs[:m]+=1
|
35
|
+
ObjectSpace.define_finalizer(self)do
|
36
|
+
cs[:x]||=0
|
37
|
+
cs[:x]+=1
|
38
|
+
c.say vm: vm rescue nil
|
39
|
+
end
|
40
|
+
@vm=vm
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/test/child.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'shagi'
|
2
|
+
|
3
|
+
class TestChild < Shagi
|
4
|
+
|
5
|
+
Children=[M::Node, M::Wsh]
|
6
|
+
|
7
|
+
def say(code)
|
8
|
+
@child.say code
|
9
|
+
end
|
10
|
+
|
11
|
+
def children
|
12
|
+
(1..Spawn).map do
|
13
|
+
Children.map{|k| k::Valid ? M::Child.new(k::Run) : nil }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.build
|
18
|
+
ancestors[1].instance_methods(false).grep(/^shag_/).each do |m|
|
19
|
+
Children.each_with_index do |klass, idx|
|
20
|
+
(1..Spawn).each do |n|
|
21
|
+
define_method("test_#{m.to_s.sub /.*?_/, ''}_#{klass.name.split(/\W+/).last}_#{n}")do
|
22
|
+
skip unless @child=(@@children||=children)[n-1][idx]
|
23
|
+
send m
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
build
|
31
|
+
|
32
|
+
end
|
data/test/engine.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'coffee_script/source'
|
2
|
+
|
3
|
+
class TestEngine < Minitest::Test
|
4
|
+
|
5
|
+
M=ExecJS::Xtrn
|
6
|
+
|
7
|
+
Spawn=5
|
8
|
+
Engines=M::Engines
|
9
|
+
|
10
|
+
def shag_methods
|
11
|
+
refute @engine.exec <<-EOJ
|
12
|
+
fib = function(n)
|
13
|
+
{
|
14
|
+
return n<2 ? 1 : fib(n-1)+fib(n-2)
|
15
|
+
}
|
16
|
+
EOJ
|
17
|
+
assert_equal 89, @engine.eval('fib(10)')
|
18
|
+
assert_equal 8, @engine.call('fib', 5)
|
19
|
+
assert_equal 79, @engine.call('Math.max', 44, 27, 79, 73, 42, 4, 23, 24, 36, 13)
|
20
|
+
end
|
21
|
+
|
22
|
+
def shag_vars
|
23
|
+
assert_raises(M::Error){ @engine.eval 'localVar' }
|
24
|
+
refute @engine.exec 'var localVar=1'
|
25
|
+
assert_raises(M::Error){ @engine.eval 'localVar' }
|
26
|
+
|
27
|
+
assert_raises(M::Error){ @engine.eval 'globalVar' }
|
28
|
+
refute @engine.exec "globalVar=#{v=rand 1000}"
|
29
|
+
assert_equal v, @engine.eval('globalVar')
|
30
|
+
end
|
31
|
+
|
32
|
+
def shag_coffee
|
33
|
+
@engine.exec File.read CoffeeScript::Source.bundled_path
|
34
|
+
assert_equal 3, @engine.call('CoffeeScript.compile', "->").split(/\Wfunction\W/).length
|
35
|
+
r=rand 100
|
36
|
+
assert_equal [r], @engine.eval(@engine.call 'CoffeeScript.compile', "do->[#{r}]", bare: true)
|
37
|
+
assert_equal 3, @engine.call('CoffeeScript.eval', 'Math.round Math.PI')
|
38
|
+
end
|
39
|
+
|
40
|
+
def shag_stats
|
41
|
+
@engine.exec '//'
|
42
|
+
s=@engine.stats
|
43
|
+
@engine.exec ' '
|
44
|
+
assert_equal s[:n], @engine.stats[:n]
|
45
|
+
@engine.exec '[]'
|
46
|
+
assert_equal s[:n]+1, @engine.stats[:n]
|
47
|
+
assert_equal 1, M::Nvm.stats[:c]||1
|
48
|
+
end
|
49
|
+
|
50
|
+
def klas_methods
|
51
|
+
assert_equal 4, @class.exec('return 2*2')
|
52
|
+
assert_equal 6, @class.eval('({x: 1+2+3}).x')
|
53
|
+
end
|
54
|
+
|
55
|
+
def klas_compile
|
56
|
+
x=@class.compile <<-EOJ
|
57
|
+
inc = function(x)
|
58
|
+
{
|
59
|
+
return x+1
|
60
|
+
}
|
61
|
+
EOJ
|
62
|
+
assert_equal 6, x.call('inc', 5)
|
63
|
+
end
|
64
|
+
|
65
|
+
def engines
|
66
|
+
(1..Spawn).map do
|
67
|
+
Engines.map{|k| k::Valid ? k.compile : nil }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.build
|
72
|
+
instance_methods(false).grep(/^shag_/).each do |m|
|
73
|
+
Engines.each_with_index do |klass, idx|
|
74
|
+
(1..Spawn).each do |n|
|
75
|
+
define_method("test_#{m.to_s.sub /.*?_/, ''}_#{klass.name.split(/\W+/).last}_#{n}")do
|
76
|
+
skip unless @engine=(@@engines||=engines)[n-1][idx]
|
77
|
+
send m
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
instance_methods(false).grep(/^klas_/).each do |m|
|
84
|
+
Engines.each do |klass|
|
85
|
+
define_method("test_#{m.to_s.sub /.*?_/, ''}_#{klass.name.split(/\W+/).last}_class")do
|
86
|
+
skip unless klass::Valid
|
87
|
+
@class=klass
|
88
|
+
send m
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
build
|
95
|
+
|
96
|
+
ObjectSpace.define_finalizer(M)do
|
97
|
+
puts "Statistics:"
|
98
|
+
s=M.stats
|
99
|
+
len=s.keys.map(&:length).max+1
|
100
|
+
s.each{|k, v| puts "#{' '*(len-k.length)}#{k}: "+v.map{|k, v| "#{k}=#{v.round(3).to_s.sub /[.]?0*$/, ''}"}*', ' }
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
data/test/nvm.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'shagi'
|
2
|
+
|
3
|
+
class TestNvm < Shagi
|
4
|
+
|
5
|
+
Child=M::Nvm
|
6
|
+
|
7
|
+
def say(code)
|
8
|
+
# puts "s: child=#{@child} vm=#{@vm}, code=#{code}"
|
9
|
+
r=@child.say vm: @vm, js: code
|
10
|
+
# puts "r: #{r}"
|
11
|
+
r
|
12
|
+
end
|
13
|
+
|
14
|
+
def buildVMs
|
15
|
+
@@child=child=M::Child.new(Child::Run)
|
16
|
+
(1..Spawn).map do
|
17
|
+
child.say(vm: 0)['vm']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.build
|
22
|
+
ancestors[1].instance_methods(false).grep(/^shag_/).each do |m|
|
23
|
+
(1..Spawn).each do |n|
|
24
|
+
define_method("test_#{m.to_s.sub /.*?_/, ''}_#{n}")do
|
25
|
+
skip unless Child::Valid
|
26
|
+
@vm=(@@vms||=buildVMs)[n-1]
|
27
|
+
@child=@@child
|
28
|
+
send m
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
build
|
35
|
+
|
36
|
+
end
|