execjs-xtrn 1.1.3 → 1.1.4
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/lib/execjs/node/node_modules/split/index.js +63 -0
- data/lib/execjs/node/node_modules/through/index.js +108 -0
- data/lib/execjs/xtrn/version.rb +1 -1
- metadata +3 -24
- data/.gitignore +0 -18
- data/.travis.yml +0 -19
- data/FarMenu.ini +0 -11
- data/Gemfile +0 -6
- data/LICENSE.txt +0 -22
- data/README.md +0 -235
- data/Rakefile +0 -17
- data/appveyor.yml +0 -36
- data/execjs-xtrn.gemspec +0 -30
- data/test/child.rb +0 -32
- data/test/engine.rb +0 -127
- data/test/load.js +0 -5
- data/test/nvm.rb +0 -33
- data/test/shagi.rb +0 -81
- data/test/top.rb +0 -97
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c96df8410557171dfaf3c5943b31f5b098b2f8a8
|
4
|
+
data.tar.gz: 24ca85d50e1bdf4c7945385849bef1532091ae3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffb757e3b1d4f256916646dfa1139186da502e43ac277fc0b03342bd65dc2ad16c5ccc3367dec0b52d46fe78626e4cf6113eac5049ba7cd375093febdf62da56
|
7
|
+
data.tar.gz: 12426082a20e85dd543483f50d2535696bbb2ea072217f7b42d12aae45640bd9d647d72886478cca17d22985d54f60c9c9985b14e74b8911ea1f8500c9a629bb
|
@@ -0,0 +1,63 @@
|
|
1
|
+
//filter will reemit the data if cb(err,pass) pass is truthy
|
2
|
+
|
3
|
+
// reduce is more tricky
|
4
|
+
// maybe we want to group the reductions or emit progress updates occasionally
|
5
|
+
// the most basic reduce just emits one 'data' event after it has recieved 'end'
|
6
|
+
|
7
|
+
|
8
|
+
var through = require('through')
|
9
|
+
var Decoder = require('string_decoder').StringDecoder
|
10
|
+
|
11
|
+
module.exports = split
|
12
|
+
|
13
|
+
//TODO pass in a function to map across the lines.
|
14
|
+
|
15
|
+
function split (matcher, mapper, options) {
|
16
|
+
var decoder = new Decoder()
|
17
|
+
var soFar = ''
|
18
|
+
var maxLength = options && options.maxLength;
|
19
|
+
if('function' === typeof matcher)
|
20
|
+
mapper = matcher, matcher = null
|
21
|
+
if (!matcher)
|
22
|
+
matcher = /\r?\n/
|
23
|
+
|
24
|
+
function emit(stream, piece) {
|
25
|
+
if(mapper) {
|
26
|
+
try {
|
27
|
+
piece = mapper(piece)
|
28
|
+
}
|
29
|
+
catch (err) {
|
30
|
+
return stream.emit('error', err)
|
31
|
+
}
|
32
|
+
if('undefined' !== typeof piece)
|
33
|
+
stream.queue(piece)
|
34
|
+
}
|
35
|
+
else
|
36
|
+
stream.queue(piece)
|
37
|
+
}
|
38
|
+
|
39
|
+
function next (stream, buffer) {
|
40
|
+
var pieces = ((soFar != null ? soFar : '') + buffer).split(matcher)
|
41
|
+
soFar = pieces.pop()
|
42
|
+
|
43
|
+
if (maxLength && soFar.length > maxLength)
|
44
|
+
stream.emit('error', new Error('maximum buffer reached'))
|
45
|
+
|
46
|
+
for (var i = 0; i < pieces.length; i++) {
|
47
|
+
var piece = pieces[i]
|
48
|
+
emit(stream, piece)
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
return through(function (b) {
|
53
|
+
next(this, decoder.write(b))
|
54
|
+
},
|
55
|
+
function () {
|
56
|
+
if(decoder.end)
|
57
|
+
next(this, decoder.end())
|
58
|
+
if(soFar != null)
|
59
|
+
emit(this, soFar)
|
60
|
+
this.queue(null)
|
61
|
+
})
|
62
|
+
}
|
63
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
var Stream = require('stream')
|
2
|
+
|
3
|
+
// through
|
4
|
+
//
|
5
|
+
// a stream that does nothing but re-emit the input.
|
6
|
+
// useful for aggregating a series of changing but not ending streams into one stream)
|
7
|
+
|
8
|
+
exports = module.exports = through
|
9
|
+
through.through = through
|
10
|
+
|
11
|
+
//create a readable writable stream.
|
12
|
+
|
13
|
+
function through (write, end, opts) {
|
14
|
+
write = write || function (data) { this.queue(data) }
|
15
|
+
end = end || function () { this.queue(null) }
|
16
|
+
|
17
|
+
var ended = false, destroyed = false, buffer = [], _ended = false
|
18
|
+
var stream = new Stream()
|
19
|
+
stream.readable = stream.writable = true
|
20
|
+
stream.paused = false
|
21
|
+
|
22
|
+
// stream.autoPause = !(opts && opts.autoPause === false)
|
23
|
+
stream.autoDestroy = !(opts && opts.autoDestroy === false)
|
24
|
+
|
25
|
+
stream.write = function (data) {
|
26
|
+
write.call(this, data)
|
27
|
+
return !stream.paused
|
28
|
+
}
|
29
|
+
|
30
|
+
function drain() {
|
31
|
+
while(buffer.length && !stream.paused) {
|
32
|
+
var data = buffer.shift()
|
33
|
+
if(null === data)
|
34
|
+
return stream.emit('end')
|
35
|
+
else
|
36
|
+
stream.emit('data', data)
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
stream.queue = stream.push = function (data) {
|
41
|
+
// console.error(ended)
|
42
|
+
if(_ended) return stream
|
43
|
+
if(data === null) _ended = true
|
44
|
+
buffer.push(data)
|
45
|
+
drain()
|
46
|
+
return stream
|
47
|
+
}
|
48
|
+
|
49
|
+
//this will be registered as the first 'end' listener
|
50
|
+
//must call destroy next tick, to make sure we're after any
|
51
|
+
//stream piped from here.
|
52
|
+
//this is only a problem if end is not emitted synchronously.
|
53
|
+
//a nicer way to do this is to make sure this is the last listener for 'end'
|
54
|
+
|
55
|
+
stream.on('end', function () {
|
56
|
+
stream.readable = false
|
57
|
+
if(!stream.writable && stream.autoDestroy)
|
58
|
+
process.nextTick(function () {
|
59
|
+
stream.destroy()
|
60
|
+
})
|
61
|
+
})
|
62
|
+
|
63
|
+
function _end () {
|
64
|
+
stream.writable = false
|
65
|
+
end.call(stream)
|
66
|
+
if(!stream.readable && stream.autoDestroy)
|
67
|
+
stream.destroy()
|
68
|
+
}
|
69
|
+
|
70
|
+
stream.end = function (data) {
|
71
|
+
if(ended) return
|
72
|
+
ended = true
|
73
|
+
if(arguments.length) stream.write(data)
|
74
|
+
_end() // will emit or queue
|
75
|
+
return stream
|
76
|
+
}
|
77
|
+
|
78
|
+
stream.destroy = function () {
|
79
|
+
if(destroyed) return
|
80
|
+
destroyed = true
|
81
|
+
ended = true
|
82
|
+
buffer.length = 0
|
83
|
+
stream.writable = stream.readable = false
|
84
|
+
stream.emit('close')
|
85
|
+
return stream
|
86
|
+
}
|
87
|
+
|
88
|
+
stream.pause = function () {
|
89
|
+
if(stream.paused) return
|
90
|
+
stream.paused = true
|
91
|
+
return stream
|
92
|
+
}
|
93
|
+
|
94
|
+
stream.resume = function () {
|
95
|
+
if(stream.paused) {
|
96
|
+
stream.paused = false
|
97
|
+
stream.emit('resume')
|
98
|
+
}
|
99
|
+
drain()
|
100
|
+
//may have become paused again,
|
101
|
+
//as drain emits 'data'.
|
102
|
+
if(!stream.paused)
|
103
|
+
stream.emit('drain')
|
104
|
+
return stream
|
105
|
+
}
|
106
|
+
return stream
|
107
|
+
}
|
108
|
+
|
data/lib/execjs/xtrn/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: execjs-xtrn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stas Ukolov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -115,15 +115,6 @@ executables: []
|
|
115
115
|
extensions: []
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
|
-
- ".gitignore"
|
119
|
-
- ".travis.yml"
|
120
|
-
- FarMenu.ini
|
121
|
-
- Gemfile
|
122
|
-
- LICENSE.txt
|
123
|
-
- README.md
|
124
|
-
- Rakefile
|
125
|
-
- appveyor.yml
|
126
|
-
- execjs-xtrn.gemspec
|
127
118
|
- lib/execjs/node/index.js
|
128
119
|
- lib/execjs/node/node_modules/split/index.js
|
129
120
|
- lib/execjs/node/node_modules/through/index.js
|
@@ -141,12 +132,6 @@ files:
|
|
141
132
|
- lib/execjs/xtrn/routing.rb
|
142
133
|
- lib/execjs/xtrn/version.rb
|
143
134
|
- lib/execjs/xtrn/wsh.rb
|
144
|
-
- test/child.rb
|
145
|
-
- test/engine.rb
|
146
|
-
- test/load.js
|
147
|
-
- test/nvm.rb
|
148
|
-
- test/shagi.rb
|
149
|
-
- test/top.rb
|
150
135
|
homepage: https://github.com/ukoloff/execjs-xtrn
|
151
136
|
licenses:
|
152
137
|
- MIT
|
@@ -171,10 +156,4 @@ rubygems_version: 2.2.3
|
|
171
156
|
signing_key:
|
172
157
|
specification_version: 4
|
173
158
|
summary: 'Proof-of-concept: make ExecJS fast even without therubyracer'
|
174
|
-
test_files:
|
175
|
-
- test/child.rb
|
176
|
-
- test/engine.rb
|
177
|
-
- test/load.js
|
178
|
-
- test/nvm.rb
|
179
|
-
- test/shagi.rb
|
180
|
-
- test/top.rb
|
159
|
+
test_files: []
|
data/.gitignore
DELETED
data/.travis.yml
DELETED
data/FarMenu.ini
DELETED
data/Gemfile
DELETED
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2014 Stas Ukolov
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
DELETED
@@ -1,235 +0,0 @@
|
|
1
|
-
# ExecJS::Xtrn
|
2
|
-
|
3
|
-
[](https://travis-ci.org/ukoloff/execjs-xtrn)
|
4
|
-
[](https://ci.appveyor.com/project/ukoloff/execjs-xtrn)
|
5
|
-
[](http://badge.fury.io/rb/execjs-xtrn)
|
6
|
-
|
7
|
-
Drop-in replacement for ExecJS. The latter spawns separate process for every JavaScript compilation
|
8
|
-
(when using external runtime),
|
9
|
-
while ExecJS::Xtrn spawn long running JavaScript process and communicates with it via stdin & stderr.
|
10
|
-
|
11
|
-
This is just proof of concept, not suitable for production.
|
12
|
-
|
13
|
-
When not on MS Windows, one definitely should use ExecJS with excellent `therubyracer` gem instead.
|
14
|
-
|
15
|
-
## Installation
|
16
|
-
|
17
|
-
Add this line to your application's Gemfile:
|
18
|
-
|
19
|
-
gem 'execjs-xtrn'
|
20
|
-
|
21
|
-
And then execute:
|
22
|
-
|
23
|
-
$ bundle
|
24
|
-
|
25
|
-
Or install it yourself as:
|
26
|
-
|
27
|
-
$ gem install execjs-xtrn
|
28
|
-
|
29
|
-
## Usage
|
30
|
-
|
31
|
-
Just add/require this gem after/instead (implicit) `execjs` gem.
|
32
|
-
The latter will be monkey-patched.
|
33
|
-
|
34
|
-
## Engines
|
35
|
-
|
36
|
-
ExecJS::Xtrn uses two external JavaScript runners:
|
37
|
-
|
38
|
-
* Windows Script Host
|
39
|
-
* Node.js in two modes:
|
40
|
-
- Simple (1 execution context = 1 external process)
|
41
|
-
- Nvm (all execution contexts share single external process using [vm API](http://nodejs.org/api/vm.html))
|
42
|
-
|
43
|
-
Nvm engine has nothing common with [nvm](https://github.com/creationix/nvm).
|
44
|
-
|
45
|
-
So, there exist *four* engines:
|
46
|
-
|
47
|
-
* Engine - absctract engine (smart enough to execute blank lines)
|
48
|
-
* Wsh - engine using WSH (CScript)
|
49
|
-
* Node - engine using Node.js (separate process for every execution context)
|
50
|
-
* Nvm - engine using Node.js and vm API (single process)
|
51
|
-
|
52
|
-
All engines autodetect their availability at startup (on `require 'execjs/xtrn'`) and sets `Valid` constants.
|
53
|
-
Eg on MS Windows ExecJS::Xtrn::Wsh::Valid = true, on Linux - false
|
54
|
-
|
55
|
-
One of available engines is made default engine for ExecJS.
|
56
|
-
If Node.js is available it is Nvm.
|
57
|
-
Else, if running on Windows it is Wsh.
|
58
|
-
Else it is Engine, so ExecJS is made unusable.
|
59
|
-
|
60
|
-
Default engine can be shown/changed at any moment with `ExecJS::Xtrn.engine` accessor, eg
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
ExecJS::Xtrn.engine=ExecJS::Xtrn::Node
|
64
|
-
```
|
65
|
-
|
66
|
-
## API
|
67
|
-
|
68
|
-
ExecJS::Xtrn is primarily designed to power other gems that use popular ExecJS.
|
69
|
-
|
70
|
-
But it has his own API (similar to ExecJS' API) and can be used itself.
|
71
|
-
|
72
|
-
In general one should create instance of an Engine and then feed it with JavaScript code:
|
73
|
-
|
74
|
-
```ruby
|
75
|
-
ctx=ExecJS::Xtrn::Wsh.new
|
76
|
-
ctx.exec 'fact = function(n){return n>1 ? n*fact(n-1) : 1}'
|
77
|
-
puts "10! = #{ctx.call 'fact', 10}"
|
78
|
-
```
|
79
|
-
Every execution context has four methods:
|
80
|
-
* exec(`code`) - executes arbitrary JavaScript code. To get result `return` must be called
|
81
|
-
* load(`code`) or load(`path`) - exec that can load its code from file
|
82
|
-
* eval(`expression`) - evaluate JavaScript expression. `return` is not needed
|
83
|
-
* call(`function`, arguments...) - special form of eval for function call
|
84
|
-
|
85
|
-
There are `exec` and `eval` methods in Engine class,
|
86
|
-
they just create brand new execution context,
|
87
|
-
pass argument to it, destroy that context and return its result.
|
88
|
-
Using these class methods is not recommended, since it's just what ExecJS does
|
89
|
-
(except for Nvm engine).
|
90
|
-
|
91
|
-
Engine class also has `compile` method that combines `new` and `exec`
|
92
|
-
and returns execution context.
|
93
|
-
This is how ExecJS is used in most cases.
|
94
|
-
|
95
|
-
```ruby
|
96
|
-
ctx=ExecJS::Xtrn::Wsh.compile 'fact = function(n){return n>1 ? n*fact(n-1) : 1}'
|
97
|
-
puts "10! = #{ctx.call 'fact', 10}"
|
98
|
-
```
|
99
|
-
And `load` methods is likewise combination of `new`+`load`,
|
100
|
-
it is `compile` that can load its code from file.
|
101
|
-
|
102
|
-
`load` method (class' or instance's) detects whether its argument
|
103
|
-
is code or path by first symbols of it. So, start path with `/`, `./`
|
104
|
-
or `../` (but not from `//`). On Windows `\` and `C:` can be also used.
|
105
|
-
|
106
|
-
Finally ExecJS::Xtrn patches ExecJS and installs those 4 class methods
|
107
|
-
(`exec`, `eval`, `compile` and `load`) in it.
|
108
|
-
So, `ExecJS.compile` is `ExecJS::Xtrn::Nvm.compile` if Nvm engine is available.
|
109
|
-
|
110
|
-
## Preloading
|
111
|
-
|
112
|
-
Sometimes it's neccesary to initialize all execution contexts before passing
|
113
|
-
code to them.
|
114
|
-
For instance, add some standard JavaScript methods missing in Wsh engine.
|
115
|
-
|
116
|
-
It can be done by setting Preload constant on engine class.
|
117
|
-
|
118
|
-
```ruby
|
119
|
-
ExecJS::Xtrn::Wsh::Preload='./lib/js/map.js'
|
120
|
-
```
|
121
|
-
or maybe
|
122
|
-
|
123
|
-
```ruby
|
124
|
-
ExecJS::Xtrn::Wsh::Preload=[
|
125
|
-
'./lib/js/map.js',
|
126
|
-
'./lib/js/keys.js',
|
127
|
-
'console={log: function(){WScript.Echo([].slice.call(arguments).join(" "))}}'
|
128
|
-
]
|
129
|
-
# Yes, console.log is avaialable in Wsh now!
|
130
|
-
# And yes, console.log can be used in ExecJS::Xtrn!
|
131
|
-
```
|
132
|
-
You can add preload scripts to any engine or to Engine base class.
|
133
|
-
They will be loaded according to inheritance:
|
134
|
-
Engine::Preload will be used by all engines,
|
135
|
-
Node::Preload is for Node and Nvm, while Nvm::Preload is for Nvm only.
|
136
|
-
|
137
|
-
## Overriding ExecJS
|
138
|
-
|
139
|
-
Sometimes ExecJS is required after ExecJS::Xtrn. In that case you can call ExecJS::Xtrn.init and
|
140
|
-
it will overwrite ExecJS' methods again.
|
141
|
-
|
142
|
-
To test whether JavaScript is served by ExecJS::Xtrn, it's convenient to look at ExecJS::Xtrn statistics.
|
143
|
-
|
144
|
-
## Statistics
|
145
|
-
|
146
|
-
Every engine gathers it's own usage statistics. Eg:
|
147
|
-
|
148
|
-
```ruby
|
149
|
-
> ExecJS::Xtrn::Node.stats # or ExecJS::Xtrn::Nvm.stats or ExecJS::Xtrn::Wsh.stats
|
150
|
-
=> {:c=>2, :n=>2, :o=>8, :i=>6, :t=>0.131013}
|
151
|
-
```
|
152
|
-
Here:
|
153
|
-
* c = number of child processes spawned (for Nvm c should always be 1)
|
154
|
-
* n = number of request made
|
155
|
-
* o = bytes sent to child process(es)
|
156
|
-
* i = bytes got from child process(es)
|
157
|
-
* t = seconds spent communicating with child process(es)
|
158
|
-
* m = number of VMs created (Nvm only)
|
159
|
-
* x = number of VMs destroyed (Nvm only)
|
160
|
-
|
161
|
-
ExecJS::Xtrn.stats combines statistics for all its engines, even unused.
|
162
|
-
|
163
|
-
ExecJS.stats shows statistics for current engine only.
|
164
|
-
|
165
|
-
Every execution context has his own statistics too. Eg
|
166
|
-
|
167
|
-
```ruby
|
168
|
-
s=ExecJS::Xtrn::Nvm.compile '...'
|
169
|
-
s.exec '...'
|
170
|
-
s.stats
|
171
|
-
```
|
172
|
-
but c (and m, x) fields are omitted there.
|
173
|
-
|
174
|
-
If ExecJS::Xtrn detects it is run under Ruby on Rails,
|
175
|
-
it installs additional path `/rails/jsx' to display its statistics
|
176
|
-
(you can see that route in sextant, for example).
|
177
|
-
|
178
|
-
It is one more reason not to use ExecJS::Xtrn in production mode ;-)
|
179
|
-
|
180
|
-
By default statistics is output in YAML format, but you can
|
181
|
-
get `/rails/jsx.json` or `/rails/jsx.html`.
|
182
|
-
|
183
|
-
## Compatibilty
|
184
|
-
|
185
|
-
Not every JavaScript code behaves identically in ExecJS and ExecJS::Xtrn. In most cases it depends on how
|
186
|
-
global JavaScript variables are used. For most modern code it is the same though.
|
187
|
-
|
188
|
-
As a rule of thumb, JavaScript code must survive after wrapping in anonymous function (`(function(){...})()`).
|
189
|
-
|
190
|
-
For instance, old versions of `handlebars_assets` didn't work
|
191
|
-
in ExecJS::Xtrn (and worked in ExecJS).
|
192
|
-
|
193
|
-
The following packages have been tested to run under ExecJS::Xtrn out-of-box:
|
194
|
-
|
195
|
-
* [CoffeeScript](http://coffeescript.org/) via [coffee-script](https://rubygems.org/gems/coffee-script) and [coffee-rails](https://rubygems.org/gems/coffee-rails) gems
|
196
|
-
* [UglifyJS2](https://github.com/mishoo/UglifyJS2) via [uglifier](https://github.com/lautis/uglifier)
|
197
|
-
* [Handlebars](http://handlebarsjs.com/) via [handlebars_assets](https://github.com/leshill/handlebars_assets) gem
|
198
|
-
|
199
|
-
CoffeeScript since v1.9.0 introduces new incompatibility:
|
200
|
-
it uses `Object.create` that is missing from WSH.
|
201
|
-
To fix it, `Object.create` was manually defined in ExecJS::Xtrn::Wsh
|
202
|
-
(sort of [ExecJS::Xtrn::Wsh::Preload](#preloading)).
|
203
|
-
Path to this polyfill is available as `ExecJS::Xtrn::Wsh::ES5` constant.
|
204
|
-
|
205
|
-
Gem [coffee-script](https://rubygems.org/gems/coffee-script) since v2.4.0 introduces another incompatibility:
|
206
|
-
it silently creates global function. This approach works in regular ExecJS but fails in ExecJS::Xtrn.
|
207
|
-
As a workaround pin `coffee-script` gem version to 2.3.0.
|
208
|
-
|
209
|
-
Later [uglifier](https://github.com/lautis/uglifier) from v3 started to use globals either. Pin it to '~> 2'.
|
210
|
-
|
211
|
-
## Testing
|
212
|
-
|
213
|
-
After git checkout, required NPM modules must be installed. Simply run:
|
214
|
-
|
215
|
-
```
|
216
|
-
bundle install
|
217
|
-
bundle exec rake npm
|
218
|
-
```
|
219
|
-
|
220
|
-
The testing itself is
|
221
|
-
|
222
|
-
```
|
223
|
-
bundle exec rake
|
224
|
-
```
|
225
|
-
|
226
|
-
And `bundle exec` may be ommited in most cases.
|
227
|
-
|
228
|
-
## Credits
|
229
|
-
|
230
|
-
* [ExecJS](https://github.com/sstephenson/execjs)
|
231
|
-
* [therubyracer](https://github.com/cowboyd/therubyracer)
|
232
|
-
* [Node.js](http://nodejs.org/)
|
233
|
-
* [Windows Script Host](http://en.wikipedia.org/wiki/Windows_Script_Host)
|
234
|
-
* [Travis CI](https://travis-ci.org/)
|
235
|
-
* [AppVeyor](http://www.appveyor.com/)
|
data/Rakefile
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
|
-
|
3
|
-
desc 'Install NPM modules'
|
4
|
-
task :npm do
|
5
|
-
system "npm", "install", chdir: "lib/execjs/node"
|
6
|
-
end
|
7
|
-
|
8
|
-
desc 'Run tests'
|
9
|
-
task :test do
|
10
|
-
require "minitest/autorun"
|
11
|
-
|
12
|
-
require 'execjs/xtrn'
|
13
|
-
|
14
|
-
Dir.glob('./test/*.rb'){|f| require f}
|
15
|
-
end
|
16
|
-
|
17
|
-
task default: :test
|
data/appveyor.yml
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
version: 1.0.{build}-{branch}
|
2
|
-
|
3
|
-
cache:
|
4
|
-
- vendor/bundle
|
5
|
-
|
6
|
-
environment:
|
7
|
-
matrix:
|
8
|
-
- RUBY_VERSION: 23
|
9
|
-
- RUBY_VERSION: 22
|
10
|
-
- RUBY_VERSION: 21
|
11
|
-
- RUBY_VERSION: 200
|
12
|
-
- RUBY_VERSION: 193
|
13
|
-
|
14
|
-
install:
|
15
|
-
- set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
|
16
|
-
- bundle config --local path vendor/bundle
|
17
|
-
- bundle install
|
18
|
-
- bundle exec rake npm
|
19
|
-
|
20
|
-
build: off
|
21
|
-
|
22
|
-
before_test:
|
23
|
-
- ruby -v
|
24
|
-
- gem -v
|
25
|
-
- bundle -v
|
26
|
-
- node -v
|
27
|
-
|
28
|
-
test_script:
|
29
|
-
- bundle exec rake
|
30
|
-
|
31
|
-
after_test:
|
32
|
-
- bundle exec rake build
|
33
|
-
|
34
|
-
artifacts:
|
35
|
-
- path: pkg/*.gem
|
36
|
-
name: Gem
|
data/execjs-xtrn.gemspec
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'execjs/xtrn/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "execjs-xtrn"
|
8
|
-
spec.version = ExecJS::Xtrn::VERSION
|
9
|
-
spec.authors = ["Stas Ukolov"]
|
10
|
-
spec.email = ["ukoloff@gmail.com"]
|
11
|
-
spec.description = "Drop-in replacement for ExecJS with persistent external runtime"
|
12
|
-
spec.summary = "Proof-of-concept: make ExecJS fast even without therubyracer"
|
13
|
-
spec.homepage = "https://github.com/ukoloff/execjs-xtrn"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files`.split($/)+
|
17
|
-
Dir.glob('**/node_modules/*/*.js')
|
18
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
-
spec.require_paths = ["lib"]
|
21
|
-
|
22
|
-
spec.add_dependency "json"
|
23
|
-
|
24
|
-
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
-
spec.add_development_dependency "rake"
|
26
|
-
spec.add_development_dependency "minitest"
|
27
|
-
spec.add_development_dependency "coffee-script", '2.3.0'
|
28
|
-
spec.add_development_dependency "uglifier", '~> 2'
|
29
|
-
spec.add_development_dependency "appveyor-worker"
|
30
|
-
end
|
data/test/child.rb
DELETED
@@ -1,32 +0,0 @@
|
|
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
DELETED
@@ -1,127 +0,0 @@
|
|
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
|
-
Engines.each{|e| e::Preload=[]}
|
10
|
-
M::Engine::Preload=[]
|
11
|
-
|
12
|
-
def shag_methods
|
13
|
-
refute @engine.exec <<-EOJ
|
14
|
-
fib = function(n)
|
15
|
-
{
|
16
|
-
return n<2 ? 1 : fib(n-1)+fib(n-2)
|
17
|
-
}
|
18
|
-
EOJ
|
19
|
-
assert_equal 89, @engine.eval('fib(10)')
|
20
|
-
assert_equal 8, @engine.call('fib', 5)
|
21
|
-
assert_equal 79, @engine.call('Math.max', 44, 27, 79, 73, 42, 4, 23, 24, 36, 13)
|
22
|
-
|
23
|
-
assert_raises(M::Error){ @engine.eval '_load' }
|
24
|
-
@engine.load File.expand_path '../load.js', __FILE__
|
25
|
-
assert_equal ({}), @engine.eval('_load')
|
26
|
-
@engine.load '_load.a=108'
|
27
|
-
assert_equal ({"a"=>108}), @engine.eval('_load')
|
28
|
-
end
|
29
|
-
|
30
|
-
def shag_vars
|
31
|
-
assert_raises(M::Error){ @engine.eval 'localVar' }
|
32
|
-
refute @engine.exec 'var localVar=1'
|
33
|
-
assert_raises(M::Error){ @engine.eval 'localVar' }
|
34
|
-
|
35
|
-
assert_raises(M::Error){ @engine.eval 'globalVar' }
|
36
|
-
refute @engine.exec "globalVar=#{v=rand 1000}"
|
37
|
-
assert_equal v, @engine.eval('globalVar')
|
38
|
-
end
|
39
|
-
|
40
|
-
def shag_coffee
|
41
|
-
@engine.exec File.read CoffeeScript::Source.bundled_path
|
42
|
-
assert_equal 3, @engine.call('CoffeeScript.compile', "->").split(/\Wfunction\W/).length
|
43
|
-
r=rand 100
|
44
|
-
assert_equal [r], @engine.eval(@engine.call 'CoffeeScript.compile', "do->[#{r}]", bare: true)
|
45
|
-
assert_equal 3, @engine.call('CoffeeScript.eval', 'Math.round Math.PI')
|
46
|
-
end
|
47
|
-
|
48
|
-
def shag_stats
|
49
|
-
@engine.exec '//'
|
50
|
-
s=@engine.stats
|
51
|
-
@engine.exec ' '
|
52
|
-
assert_equal s[:n], @engine.stats[:n]
|
53
|
-
@engine.exec '[]'
|
54
|
-
assert_equal s[:n]+1, @engine.stats[:n]
|
55
|
-
assert_equal 1, M::Nvm.stats[:c]||1
|
56
|
-
end
|
57
|
-
|
58
|
-
def klas_methods
|
59
|
-
assert_equal 4, @class.exec('return 2*2')
|
60
|
-
assert_equal 6, @class.eval('({x: 1+2+3}).x')
|
61
|
-
end
|
62
|
-
|
63
|
-
def klas_compile
|
64
|
-
x=@class.compile <<-EOJ
|
65
|
-
inc = function(x)
|
66
|
-
{
|
67
|
-
return x+1
|
68
|
-
}
|
69
|
-
EOJ
|
70
|
-
assert_equal 6, x.call('inc', 5)
|
71
|
-
end
|
72
|
-
|
73
|
-
def klas_load
|
74
|
-
z=@class.load File.expand_path '../load.js', __FILE__
|
75
|
-
assert_equal ({}), z.eval('_load')
|
76
|
-
end
|
77
|
-
|
78
|
-
def klas_preload
|
79
|
-
res=@class.name[-1]
|
80
|
-
res='em' if 'm'==res
|
81
|
-
|
82
|
-
Spawn.times do
|
83
|
-
begin
|
84
|
-
Engines.shuffle.each{|e| e::Preload << "_preload.#{e.name[-1]}=1"}
|
85
|
-
M::Engine::Preload << '!function(){this._preload={}}()'
|
86
|
-
|
87
|
-
assert_equal res, @class.eval('_preload').keys*''
|
88
|
-
|
89
|
-
ensure
|
90
|
-
Engines.each{|e| e::Preload.clear}
|
91
|
-
M::Engine::Preload.clear
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def engines
|
97
|
-
(1..Spawn).map do
|
98
|
-
Engines.map{|k| k::Valid ? k.compile : nil }
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.build
|
103
|
-
instance_methods(false).grep(/^shag_/).each do |m|
|
104
|
-
Engines.each_with_index do |klass, idx|
|
105
|
-
(1..Spawn).each do |n|
|
106
|
-
define_method("test_#{m.to_s.sub(/.*?_/, '')}_#{klass.name.split(/\W+/).last}_#{n}")do
|
107
|
-
skip unless @engine=(@@engines||=engines)[n-1][idx]
|
108
|
-
send m
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
instance_methods(false).grep(/^klas_/).each do |m|
|
115
|
-
Engines.each do |klass|
|
116
|
-
define_method("test_#{m.to_s.sub(/.*?_/, '')}_#{klass.name.split(/\W+/).last}_class")do
|
117
|
-
skip unless klass::Valid
|
118
|
-
@class=klass
|
119
|
-
send m
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
build
|
126
|
-
|
127
|
-
end
|
data/test/load.js
DELETED
data/test/nvm.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require_relative 'shagi'
|
2
|
-
|
3
|
-
class TestNvm < Shagi
|
4
|
-
|
5
|
-
Child=M::Nvm
|
6
|
-
|
7
|
-
def say(code)
|
8
|
-
@child.say vm: @vm, js: code
|
9
|
-
end
|
10
|
-
|
11
|
-
def buildVMs
|
12
|
-
@@child=child=M::Child.new(Child::Run)
|
13
|
-
(1..Spawn).map do
|
14
|
-
child.say(vm: 0)['vm']
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.build
|
19
|
-
ancestors[1].instance_methods(false).grep(/^shag_/).each do |m|
|
20
|
-
(1..Spawn).each do |n|
|
21
|
-
define_method("test_#{m.to_s.sub(/.*?_/, '')}_#{n}")do
|
22
|
-
skip unless Child::Valid
|
23
|
-
@vm=(@@vms||=buildVMs)[n-1]
|
24
|
-
@child=@@child
|
25
|
-
send m
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
build
|
32
|
-
|
33
|
-
end
|
data/test/shagi.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
class Shagi < Minitest::Test
|
3
|
-
|
4
|
-
Spawn=5
|
5
|
-
M=ExecJS::Xtrn
|
6
|
-
|
7
|
-
def say(code)
|
8
|
-
raise NotImplementedError, self.class.name
|
9
|
-
end
|
10
|
-
|
11
|
-
def assert_ok(result, code)
|
12
|
-
r=say code
|
13
|
-
refute r.key? 'err'
|
14
|
-
assert_equal r, {'ok'=>result}
|
15
|
-
end
|
16
|
-
|
17
|
-
def assert_err(code)
|
18
|
-
assert say(code)['err']
|
19
|
-
end
|
20
|
-
|
21
|
-
def shag_math
|
22
|
-
assert_ok 42, 'return 6*7'
|
23
|
-
assert_ok 3, 'return Math.round(Math.PI)'
|
24
|
-
end
|
25
|
-
|
26
|
-
Chars='Япония, 中华, Russia'
|
27
|
-
Codes=[1071, 1087, 1086, 1085, 1080, 1103, 44, 32, 20013, 21326, 44, 32, 82, 117, 115, 115, 105, 97]
|
28
|
-
|
29
|
-
def shag_intl
|
30
|
-
assert_ok Codes, <<-EOJ
|
31
|
-
var s='#{Chars}'
|
32
|
-
var r=[]
|
33
|
-
for(var i=0; i<s.length; i++) r.push(s.charCodeAt(i))
|
34
|
-
return r
|
35
|
-
EOJ
|
36
|
-
|
37
|
-
assert_ok Chars, <<-EOJ
|
38
|
-
var c=#{Codes}
|
39
|
-
var s=''
|
40
|
-
for(var i=0; i<c.length; i++) s+=String.fromCharCode(c[i])
|
41
|
-
return s
|
42
|
-
EOJ
|
43
|
-
end
|
44
|
-
|
45
|
-
def shag_error
|
46
|
-
assert_err '#' # Syntax
|
47
|
-
assert_err 'none' # Runtime
|
48
|
-
assert_err false # Argument
|
49
|
-
assert_err key: 2 # the same
|
50
|
-
end
|
51
|
-
|
52
|
-
def shag_null
|
53
|
-
assert_equal say(''), {}
|
54
|
-
assert_ok nil, 'return null'
|
55
|
-
end
|
56
|
-
|
57
|
-
def shag_vars
|
58
|
-
assert_err 'localVar'
|
59
|
-
say 'var localVar=1'
|
60
|
-
assert_err 'localVar'
|
61
|
-
|
62
|
-
assert_err 'globalVar'
|
63
|
-
say "globalVar=#{v=rand 1000}"
|
64
|
-
assert_ok v, 'return globalVar'
|
65
|
-
end
|
66
|
-
|
67
|
-
def shag_stats
|
68
|
-
@child.stats r={}
|
69
|
-
say '//'
|
70
|
-
assert_equal r[:n], 1
|
71
|
-
ri=@child.stats
|
72
|
-
rc=@child.class.stats
|
73
|
-
say ''
|
74
|
-
assert_equal r[:n], 2
|
75
|
-
assert_equal ri[:n]+1, @child.stats[:n]
|
76
|
-
assert_equal rc[:n]+1, @child.class.stats[:n]
|
77
|
-
[r, ri, rc].each{|rec| %w(i o t).each{|sym| assert rec[sym.to_sym]}}
|
78
|
-
assert_operator rc[:c], :>, 0
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
data/test/top.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
require 'coffee-script'
|
2
|
-
require 'uglifier'
|
3
|
-
|
4
|
-
ExecJS::Xtrn.init
|
5
|
-
|
6
|
-
class TestTop < Minitest::Test
|
7
|
-
|
8
|
-
def test_exec
|
9
|
-
assert_equal 42, ExecJS.exec('return 6*7')
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_eval
|
13
|
-
assert_equal 42, ExecJS.eval('6*7')
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_compile
|
17
|
-
ctx=ExecJS.compile <<-EOJ
|
18
|
-
dec = function(i)
|
19
|
-
{
|
20
|
-
return i-1
|
21
|
-
}
|
22
|
-
EOJ
|
23
|
-
assert_equal 8, ctx.call('dec', 9)
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_stats
|
27
|
-
ctx=ExecJS.compile <<-EOJ
|
28
|
-
summa = function(n)
|
29
|
-
{
|
30
|
-
var r=0
|
31
|
-
for(var i = 1; i<=n; i++)
|
32
|
-
{
|
33
|
-
r+=i
|
34
|
-
for(var t = ms() ; t>=ms(); );
|
35
|
-
}
|
36
|
-
return r
|
37
|
-
}
|
38
|
-
function ms()
|
39
|
-
{
|
40
|
-
return new Date().getTime()
|
41
|
-
}
|
42
|
-
EOJ
|
43
|
-
s=ctx.stats
|
44
|
-
%w(i o n t).each{|sym| assert s[sym.to_sym]}
|
45
|
-
ctx.exec ' '
|
46
|
-
assert_equal s, ctx.stats
|
47
|
-
assert_equal 5050, ctx.call('summa', 100)
|
48
|
-
assert_equal s[:n]+1, ctx.stats[:n]
|
49
|
-
%w(i o t).each{|sym| assert_operator s[sym.to_sym], :<, ctx.stats[sym.to_sym]}
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_coffee
|
53
|
-
assert CoffeeScript.compile('->', header: true)[CoffeeScript.version]
|
54
|
-
assert CoffeeScript.compile('a b c', bare: true)['a(b(c))']
|
55
|
-
end
|
56
|
-
|
57
|
-
def test_uglify
|
58
|
-
u=Uglifier.new
|
59
|
-
assert u.compile('a( 1 + 2 * 3 )')['a(7)']
|
60
|
-
assert u.compile('b( 1 ? x : y )')['b(x)']
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_switch
|
64
|
-
assert_equal 42, ExecJS.eval('7*6')
|
65
|
-
e=ExecJS::Xtrn.engine
|
66
|
-
ExecJS::Xtrn.engine=FakeEngine
|
67
|
-
assert_equal 1, ExecJS.exec('A')
|
68
|
-
assert_equal 2, ExecJS.eval('B')
|
69
|
-
assert_equal 4, ExecJS.compile.eval('C')
|
70
|
-
ExecJS::Xtrn.engine=e
|
71
|
-
assert_equal 42, ExecJS.eval('21*2')
|
72
|
-
end
|
73
|
-
|
74
|
-
def test_info
|
75
|
-
at_exit do
|
76
|
-
puts "Statistics:"
|
77
|
-
s=ExecJS::Xtrn.stats
|
78
|
-
len=s.keys.map(&:length).max+1
|
79
|
-
z = s.map do |k, v|
|
80
|
-
"#{' '*(len-k.length)}#{k}: "+
|
81
|
-
v.map do |kx, vx|
|
82
|
-
"#{kx}=#{vx.round(3).to_s.sub(/[.]?0*$/, '')}"
|
83
|
-
end * ', '
|
84
|
-
end * "\n"
|
85
|
-
puts z
|
86
|
-
AppVeyor::Worker.message "Compilations: #{s['Engine'][:n]}", z
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
class FakeEngine < ExecJS::Xtrn::Engine
|
93
|
-
def exec(s='')
|
94
|
-
@@n||=0
|
95
|
-
@@n+=1
|
96
|
-
end
|
97
|
-
end
|