execjs-xtrn 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }()
@@ -0,0 +1,9 @@
1
+ %w(
2
+ version
3
+ engine
4
+ child
5
+ wsh
6
+ node
7
+ nvm
8
+ init
9
+ ).each{|x| require_relative "xtrn/#{x}"}
@@ -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
@@ -0,0 +1,5 @@
1
+ module ExecJS
2
+ module Xtrn
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ class ExecJS::Xtrn::Wsh < ExecJS::Xtrn::Engine
2
+
3
+ Run={
4
+ args: %w(cscript //Nologo //U repl.js),
5
+ path: 'wsh',
6
+ encoding: 'UTF-16LE:UTF-8',
7
+ }
8
+
9
+ Valid=Gem.win_platform?
10
+
11
+ exec '//' rescue nil if Valid # Warm up
12
+ 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