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.
@@ -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