execjs-xtrn 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|