execjs-xtrn 1.1.3 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://travis-ci.org/ukoloff/execjs-xtrn.svg?branch=master)](https://travis-ci.org/ukoloff/execjs-xtrn)
|
4
|
-
[![Build status](https://ci.appveyor.com/api/projects/status/tw7av89nj591fg8w?svg=true)](https://ci.appveyor.com/project/ukoloff/execjs-xtrn)
|
5
|
-
[![Gem Version](https://badge.fury.io/rb/execjs-xtrn.svg)](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
|