h8 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/benchmark/big.txt +121706 -0
- data/benchmark/knightsmove.coffee +48 -0
- data/benchmark/knigthsmove.rb +70 -0
- data/benchmark/process_text.coffee +12 -0
- data/benchmark/text_process.rb +51 -0
- data/benchmark/tools.rb +21 -0
- data/bin/h8 +12 -0
- data/ext/h8/h8.cpp +4 -2
- data/ext/h8/h8.h +8 -3
- data/ext/h8/js_gate.h +25 -1
- data/ext/h8/main.cpp +12 -5
- data/ext/h8/object_wrap.h +96 -102
- data/ext/h8/ruby_gate.cpp +0 -1
- data/ext/h8/ruby_gate.h +5 -3
- data/lib/h8/coffee.rb +19 -3
- data/lib/h8/command.rb +128 -0
- data/lib/h8/context.rb +14 -21
- data/lib/h8/errors.rb +9 -8
- data/lib/h8/pargser.rb +133 -0
- data/lib/h8/version.rb +1 -1
- data/lib/{h8 → scripts}/coffee-script.js +0 -0
- data/lib/scripts/globals.coffee +4 -0
- data/spec/coffee/cli_tests.coffee +50 -0
- data/spec/coffee_spec.rb +50 -0
- data/spec/command_spec.rb +72 -0
- data/spec/context_spec.rb +21 -1
- data/spec/js_gate_spec.rb +53 -1
- data/spec/pargser_spec.rb +67 -0
- data/spec/ruby_gate_spec.rb +17 -0
- metadata +21 -4
data/ext/h8/ruby_gate.cpp
CHANGED
@@ -14,7 +14,6 @@ static void* unblock_caller(void *param) {
|
|
14
14
|
}
|
15
15
|
|
16
16
|
static void with_gvl(RubyGate *gate, const std::function<void(void)> &block) {
|
17
|
-
// v8::Unlocker unlock(gate->isolate());
|
18
17
|
H8 *h8 = gate->getH8();
|
19
18
|
if (h8->isGvlReleased()) {
|
20
19
|
h8->setGvlReleased(false);
|
data/ext/h8/ruby_gate.h
CHANGED
@@ -41,9 +41,7 @@ public:
|
|
41
41
|
}
|
42
42
|
|
43
43
|
virtual void free() {
|
44
|
-
|
45
|
-
persistent().ClearWeak();
|
46
|
-
persistent().Reset();
|
44
|
+
// printf("RG::FREE(%p)\n", this);
|
47
45
|
delete this;
|
48
46
|
}
|
49
47
|
|
@@ -52,6 +50,10 @@ public:
|
|
52
50
|
}
|
53
51
|
|
54
52
|
virtual ~RubyGate() {
|
53
|
+
// puts("~RG()");
|
54
|
+
persistent().ClearWeak();
|
55
|
+
persistent().Reset();
|
56
|
+
// The rest is done by the base classes
|
55
57
|
}
|
56
58
|
|
57
59
|
Isolate* isolate() const noexcept {
|
data/lib/h8/coffee.rb
CHANGED
@@ -36,13 +36,15 @@ module H8
|
|
36
36
|
# Create compiler instance.
|
37
37
|
def initialize
|
38
38
|
@context = H8::Context.new
|
39
|
-
@context.eval
|
39
|
+
@context.eval read_script 'coffee-script.js'
|
40
|
+
eval read_script('globals.coffee')
|
40
41
|
end
|
41
42
|
|
42
43
|
# compile coffeescript source and return compiled javascript
|
43
|
-
def compile src, **kwargs
|
44
|
+
def compile src, file_name: nil, **kwargs
|
44
45
|
@context[:cs] = src
|
45
|
-
|
46
|
+
@context[:filename] = file_name
|
47
|
+
res = @context.eval('CoffeeScript.compile(cs,{filename: filename})')
|
46
48
|
@context[:cs] = nil # Sources can be big...
|
47
49
|
res
|
48
50
|
end
|
@@ -52,6 +54,20 @@ module H8
|
|
52
54
|
def eval src, **kwargs
|
53
55
|
@context.eval compile(src), **kwargs
|
54
56
|
end
|
57
|
+
|
58
|
+
# Provide context with CoffeeScrip compiler loaded
|
59
|
+
def context
|
60
|
+
@context
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
@@base = File.expand_path File.join(File.dirname(__FILE__), '../scripts')
|
66
|
+
@@cache = {}
|
67
|
+
|
68
|
+
def read_script name
|
69
|
+
@@cache[name] ||= open(File.join(@@base, name), 'r').read
|
70
|
+
end
|
55
71
|
end
|
56
72
|
|
57
73
|
|
data/lib/h8/command.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
require 'pargser'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'h8'
|
5
|
+
|
6
|
+
module H8
|
7
|
+
|
8
|
+
class Command
|
9
|
+
|
10
|
+
def initialize *args, out: STDOUT, err: STDERR
|
11
|
+
@out = out
|
12
|
+
@err = err
|
13
|
+
|
14
|
+
run *args if args.length > 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def run *args
|
18
|
+
count = 0
|
19
|
+
|
20
|
+
@parser = Pargser.new(args)
|
21
|
+
|
22
|
+
@parser.key('-e', default: false, doc: 'inline coffee code to execute') { |script|
|
23
|
+
if script
|
24
|
+
@file = '-e'
|
25
|
+
context.coffee (@script=script)
|
26
|
+
count += 1
|
27
|
+
end
|
28
|
+
}
|
29
|
+
.key('-x', default: nil, doc: 'inline js code to execute') { |script|
|
30
|
+
if script
|
31
|
+
@file = '-e'
|
32
|
+
context.eval (@script=script)
|
33
|
+
count += 1
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
@parser.parse { |file|
|
38
|
+
count += 1
|
39
|
+
@script = open(file, 'r').read
|
40
|
+
file.downcase.end_with?('.coffee') and @script = H8::Coffee.compile(@script)
|
41
|
+
@file = file
|
42
|
+
context.eval @script
|
43
|
+
}
|
44
|
+
|
45
|
+
count > 0 or raise 'Must provide at least one file'
|
46
|
+
end
|
47
|
+
|
48
|
+
def context
|
49
|
+
@context ||= begin
|
50
|
+
cxt = H8::Context.new
|
51
|
+
console = Console.new out: @out, err: @err
|
52
|
+
cxt[:console] = console
|
53
|
+
print = -> (*args) { console.debug *args }
|
54
|
+
cxt[:print] = print
|
55
|
+
cxt[:puts] = print
|
56
|
+
cxt[:open] = -> (name, mode='r', block=nil) { Stream.new(name, mode, block) }
|
57
|
+
cxt['__FILE__'] = @file ? @file.to_s : '<inline>'
|
58
|
+
cxt[:File] = FileProxy.new
|
59
|
+
cxt
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def usage
|
64
|
+
"\nh8 #{H8::VERSION} CLI inteface\n\n" +
|
65
|
+
"Usage: h8 <file.js/file.coffe>\n\n" +
|
66
|
+
@parser.keys_doc
|
67
|
+
end
|
68
|
+
|
69
|
+
class FileProxy
|
70
|
+
def dirname str
|
71
|
+
File.dirname str
|
72
|
+
end
|
73
|
+
|
74
|
+
def extname str
|
75
|
+
File.extname str
|
76
|
+
end
|
77
|
+
|
78
|
+
def basename str
|
79
|
+
File.basename str
|
80
|
+
end
|
81
|
+
|
82
|
+
def expand_path str
|
83
|
+
File.expand_path str
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
class Stream
|
89
|
+
def initialize name, mode, block=nil
|
90
|
+
@file = open(name, mode)
|
91
|
+
if block
|
92
|
+
block.call self
|
93
|
+
@file.close
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def read(count=nil)
|
98
|
+
if count
|
99
|
+
@file.read(count)
|
100
|
+
else
|
101
|
+
@file.read
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
class Console
|
108
|
+
def initialize out: STDOUT, err: STDERR
|
109
|
+
@out, @err = out, err
|
110
|
+
end
|
111
|
+
|
112
|
+
def debug *args
|
113
|
+
@out.puts args.join(' ')
|
114
|
+
end
|
115
|
+
|
116
|
+
def log *args
|
117
|
+
debug *args
|
118
|
+
end
|
119
|
+
|
120
|
+
def error *args
|
121
|
+
@err.puts args.join(' ')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
data/lib/h8/context.rb
CHANGED
@@ -25,7 +25,7 @@ module H8
|
|
25
25
|
# * ruby Class - creating a javascript constructor function that creates ruby
|
26
26
|
# class instance (any arguments) and gates it to use with js.
|
27
27
|
def []= name, value
|
28
|
-
set_all name => value
|
28
|
+
set_all name.to_sym => value
|
29
29
|
end
|
30
30
|
|
31
31
|
# Execute a given script on the current context with optionally limited execution time.
|
@@ -37,16 +37,25 @@ module H8
|
|
37
37
|
#
|
38
38
|
# @return [Value] wrapped object returned by the script
|
39
39
|
# @raise [H8::TimeoutError] if the timeout was set and expired
|
40
|
-
def eval script, max_time: 0, timeout: 0
|
40
|
+
def eval script, max_time: 0, timeout: 0, file_name: nil
|
41
41
|
timeout = max_time * 1000 if max_time > 0
|
42
42
|
yield(self) if block_given?
|
43
|
-
_eval script, timeout.to_i
|
43
|
+
_eval script, timeout.to_i, file_name
|
44
44
|
end
|
45
45
|
|
46
|
+
# Compile and execute coffeescript, taking same arguments as #eval.
|
47
|
+
#
|
48
|
+
# If you need to execute same script more than once consider first H8::Coffee.compile
|
49
|
+
# and cache compiled script.
|
50
|
+
def coffee script, ** kwargs
|
51
|
+
eval Coffee.compile script, **kwargs
|
52
|
+
end
|
53
|
+
|
54
|
+
|
46
55
|
# Execute script in a new context with optionally set vars. @see H8#set_all
|
47
56
|
# @return [Value] wrapped object returned by the script
|
48
|
-
def self.eval script, **kwargs
|
49
|
-
Context.new(** kwargs).eval script
|
57
|
+
def self.eval script, file_name: nil, **kwargs
|
58
|
+
Context.new(** kwargs).eval script, file_name: file_name
|
50
59
|
end
|
51
60
|
|
52
61
|
# Secure gate for JS to securely access ruby class properties (methods with no args)
|
@@ -111,20 +120,4 @@ module H8
|
|
111
120
|
end
|
112
121
|
end
|
113
122
|
|
114
|
-
# class RacerContext < Context
|
115
|
-
# protected
|
116
|
-
# def set_var name, value
|
117
|
-
# if value.is_a?(Proc)
|
118
|
-
# n1 = "___compat_#{name}"
|
119
|
-
# _eval "function #{name}() { return #{n1}( this, arguments); }", 0
|
120
|
-
# super n1, -> (this, arguments) {
|
121
|
-
# args = [this] + H8::arguments_to_a(arguments)
|
122
|
-
# value.call *args
|
123
|
-
# }
|
124
|
-
# else
|
125
|
-
# super name, value
|
126
|
-
# end
|
127
|
-
# end
|
128
|
-
# end
|
129
|
-
|
130
123
|
end
|
data/lib/h8/errors.rb
CHANGED
@@ -8,26 +8,27 @@ module H8
|
|
8
8
|
# The general error caused by the script execution, e.g. uncaught javascript exceptinos and like.
|
9
9
|
# Check #message to see the cause.
|
10
10
|
class JsError < Error
|
11
|
-
# Error message
|
12
|
-
attr :message
|
13
11
|
|
14
12
|
# Javascript Error object. May be nil
|
15
13
|
attr :javascript_error
|
16
14
|
|
17
|
-
def to_s
|
18
|
-
message
|
19
|
-
end
|
20
|
-
|
21
15
|
# Error name
|
22
16
|
def name
|
23
|
-
@javascript_error.name ? @javascript_error.name : message
|
17
|
+
@javascript_error.name ? @javascript_error.name : @message
|
24
18
|
end
|
25
19
|
|
26
20
|
# String that represents stack trace if any as multiline string (\n separated)
|
27
21
|
def javascript_backtrace
|
28
|
-
@javascript_error ? @javascript_error.stack : message
|
22
|
+
@javascript_error ? @javascript_error.stack : @message
|
29
23
|
end
|
30
24
|
|
25
|
+
def to_s
|
26
|
+
javascript_backtrace
|
27
|
+
end
|
28
|
+
|
29
|
+
def message
|
30
|
+
to_s
|
31
|
+
end
|
31
32
|
end
|
32
33
|
|
33
34
|
# Script execution is timed out (see H8::Context#eval timeout parameter)
|
data/lib/h8/pargser.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module H8
|
4
|
+
class Pargser
|
5
|
+
|
6
|
+
class Error < ArgumentError; end
|
7
|
+
|
8
|
+
# Create parser instance with a list of arguments. Otherwise, arguments can
|
9
|
+
# be passed to #parse call.
|
10
|
+
#
|
11
|
+
# @param [Array] args arguments
|
12
|
+
def initialize args=[]
|
13
|
+
@args = args
|
14
|
+
@keys = {}
|
15
|
+
@required = Set.new
|
16
|
+
@docs = []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Register key handler.
|
20
|
+
# When #parse handler blocks will be called in order of appearance in
|
21
|
+
# arguments array
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# @param [String] name key name
|
25
|
+
#
|
26
|
+
# @param [Array(String)] aliases for the key
|
27
|
+
#
|
28
|
+
# @param [Boolean] :needs_value if set then the parser wants a value argument after the
|
29
|
+
# key which will be passed to block as an argument. if default param is not set and
|
30
|
+
# the key will not be detected, Pargser::Error will be raised
|
31
|
+
#
|
32
|
+
# @param [String] :default value. if set, needs_value parameter can be omitted - the handler
|
33
|
+
# block will be passed either with this value or with one specified in args.
|
34
|
+
#
|
35
|
+
# @param [String] :doc optional documentation string that will be used in #keys_doc
|
36
|
+
#
|
37
|
+
# @yield block if the key is found with optional value argument
|
38
|
+
#
|
39
|
+
def key name, *aliases, needs_value: false, doc: nil, **kwargs, &block
|
40
|
+
k = name.to_s
|
41
|
+
|
42
|
+
default_set = false
|
43
|
+
default = nil
|
44
|
+
if kwargs.include?(:default)
|
45
|
+
default = kwargs[:default]
|
46
|
+
needs_value = true
|
47
|
+
default_set = true
|
48
|
+
end
|
49
|
+
|
50
|
+
@keys.include?(k) and raise Error, "Duplicate key registration #{k}"
|
51
|
+
data = @keys[k] = OpenStruct.new required: false,
|
52
|
+
needs_value: needs_value,
|
53
|
+
block: block,
|
54
|
+
doc: doc,
|
55
|
+
key: k,
|
56
|
+
aliases: aliases,
|
57
|
+
default: default,
|
58
|
+
default_set: default_set
|
59
|
+
@docs << data
|
60
|
+
aliases.each { |a| @keys[a.to_s] = data }
|
61
|
+
@required.add(data) if needs_value
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Process command line and call key handlers in the order of
|
66
|
+
# appearance. Then call handlers that keys which need values
|
67
|
+
# and were not called and have defaults, or raise error.
|
68
|
+
#
|
69
|
+
# The rest of arguments (non-keys) are either yielded or returned
|
70
|
+
# as an array.
|
71
|
+
#
|
72
|
+
# You can optionally set other arguments than specified in constructor
|
73
|
+
#
|
74
|
+
# @param [Array] args to parse. If specified, arguments passed to constructor
|
75
|
+
# will be ignored and lost
|
76
|
+
# @return [Array] non-keys arguments (keys afer '--' or other arguments)
|
77
|
+
# @yield [String] non keys argumenrs (same as returned)
|
78
|
+
def parse args=nil
|
79
|
+
@args = args if args
|
80
|
+
no_more_keys = false
|
81
|
+
rest = []
|
82
|
+
while !@args.empty?
|
83
|
+
a = @args.shift
|
84
|
+
case
|
85
|
+
when no_more_keys
|
86
|
+
rest << a
|
87
|
+
when (data = @keys[a])
|
88
|
+
@required.delete data
|
89
|
+
if data.needs_value
|
90
|
+
value = @args.shift or raise "Value needed for key #{a}"
|
91
|
+
data.block.call value
|
92
|
+
else
|
93
|
+
data.block.call
|
94
|
+
end
|
95
|
+
when a == '--'
|
96
|
+
no_more_keys = true
|
97
|
+
when a[0] == '-'
|
98
|
+
raise Error, "Unknown key #{a}"
|
99
|
+
else
|
100
|
+
rest << a
|
101
|
+
end
|
102
|
+
end
|
103
|
+
@required.each { |data|
|
104
|
+
raise Error, "Required key is missing: #{data.key}" if !data.default_set
|
105
|
+
data.block.call data.default
|
106
|
+
}
|
107
|
+
block_given? and rest.each { |a| yield a }
|
108
|
+
rest
|
109
|
+
end
|
110
|
+
|
111
|
+
# Generate keys documentation multiline text
|
112
|
+
def keys_doc
|
113
|
+
res = []
|
114
|
+
@docs.each { |d|
|
115
|
+
keys = [d.key] + d.aliases
|
116
|
+
str = "\t#{keys.join(',')}"
|
117
|
+
if d.needs_value
|
118
|
+
str += " value"
|
119
|
+
if d.default
|
120
|
+
str += " (default: #{d.default})" if d.default
|
121
|
+
else
|
122
|
+
str += ' (optional)'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
res << str
|
126
|
+
d.doc and d.doc.split("\n").each{ |l| res << "\t\t#{l}" }
|
127
|
+
}
|
128
|
+
res.join("\n")
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
data/lib/h8/version.rb
CHANGED
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
errorsCount = 0
|
2
|
+
|
3
|
+
class TestFailed extends Error
|
4
|
+
|
5
|
+
fail = (msg) ->
|
6
|
+
msg = "Failed: #{msg}\n"
|
7
|
+
errorsCount++
|
8
|
+
try
|
9
|
+
throw Error()
|
10
|
+
catch e
|
11
|
+
msg += e.stack.split("\n")[3]+' '+__FILE__
|
12
|
+
console.error msg
|
13
|
+
|
14
|
+
ok = (condition, message='') ->
|
15
|
+
condition or fail "condition is not true"
|
16
|
+
|
17
|
+
gt = (a, b, message = '') ->
|
18
|
+
unless a > b
|
19
|
+
fail "#{a} > #{b} #{message}"
|
20
|
+
|
21
|
+
ge = (a, b, message = '') ->
|
22
|
+
unless a >= b
|
23
|
+
fail "#{a} >= #{b} #{message}"
|
24
|
+
|
25
|
+
lt = (a, b, message = '') ->
|
26
|
+
unless a < b
|
27
|
+
fail "#{a} < #{b} #{message}"
|
28
|
+
|
29
|
+
le = (a, b, message = '') ->
|
30
|
+
unless a <= b
|
31
|
+
fail "#{a} <= #{b} #{message}"
|
32
|
+
|
33
|
+
eq = (a, b, message = '') ->
|
34
|
+
unless a == b
|
35
|
+
fail "#{a} == #{b} #{message}"
|
36
|
+
|
37
|
+
ne = (a, b, message = '') ->
|
38
|
+
unless a != b
|
39
|
+
fail "#{a} != #{b} #{message}"
|
40
|
+
|
41
|
+
|
42
|
+
ok File.dirname(__FILE__).match(/spec\/coffee$/)
|
43
|
+
eq File.extname(__FILE__), '.coffee'
|
44
|
+
|
45
|
+
eq File.basename(__FILE__), 'cli_tests.coffee'
|
46
|
+
|
47
|
+
if errorsCount > 0
|
48
|
+
print "#{errorsCount} tests failed"
|
49
|
+
else
|
50
|
+
print "All tests passed"
|