therubyracer-freebsd 0.10.1
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.
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/.yardopts +1 -0
- data/Changelog.md +231 -0
- data/Gemfile +3 -0
- data/README.md +167 -0
- data/Rakefile +26 -0
- data/bin/therubyracer +11 -0
- data/ext/v8/extconf.rb +26 -0
- data/ext/v8/rr.cpp +189 -0
- data/ext/v8/rr.h +41 -0
- data/ext/v8/v8.cpp +48 -0
- data/ext/v8/v8_array.cpp +48 -0
- data/ext/v8/v8_array.h +8 -0
- data/ext/v8/v8_callbacks.cpp +81 -0
- data/ext/v8/v8_callbacks.h +8 -0
- data/ext/v8/v8_context.cpp +92 -0
- data/ext/v8/v8_context.h +6 -0
- data/ext/v8/v8_date.cpp +40 -0
- data/ext/v8/v8_date.h +6 -0
- data/ext/v8/v8_debug.cpp +17 -0
- data/ext/v8/v8_debug.h +6 -0
- data/ext/v8/v8_exception.cpp +133 -0
- data/ext/v8/v8_exception.h +11 -0
- data/ext/v8/v8_external.cpp +70 -0
- data/ext/v8/v8_external.h +8 -0
- data/ext/v8/v8_function.cpp +69 -0
- data/ext/v8/v8_function.h +11 -0
- data/ext/v8/v8_handle.cpp +186 -0
- data/ext/v8/v8_handle.h +48 -0
- data/ext/v8/v8_locker.cpp +139 -0
- data/ext/v8/v8_locker.h +6 -0
- data/ext/v8/v8_message.cpp +67 -0
- data/ext/v8/v8_message.h +10 -0
- data/ext/v8/v8_object.cpp +122 -0
- data/ext/v8/v8_object.h +10 -0
- data/ext/v8/v8_script.cpp +36 -0
- data/ext/v8/v8_script.h +8 -0
- data/ext/v8/v8_string.cpp +52 -0
- data/ext/v8/v8_string.h +9 -0
- data/ext/v8/v8_template.cpp +344 -0
- data/ext/v8/v8_template.h +8 -0
- data/ext/v8/v8_try_catch.cpp +70 -0
- data/ext/v8/v8_try_catch.h +5 -0
- data/ext/v8/v8_v8.cpp +34 -0
- data/ext/v8/v8_v8.h +6 -0
- data/ext/v8/v8_value.cpp +175 -0
- data/ext/v8/v8_value.h +10 -0
- data/ext/v8/v8_weakref.cpp +61 -0
- data/ext/v8/v8_weakref.h +29 -0
- data/lib/v8.rb +23 -0
- data/lib/v8/access.rb +92 -0
- data/lib/v8/array.rb +17 -0
- data/lib/v8/c/locker.rb +18 -0
- data/lib/v8/cli.rb +133 -0
- data/lib/v8/context.rb +111 -0
- data/lib/v8/error.rb +130 -0
- data/lib/v8/function.rb +44 -0
- data/lib/v8/object.rb +69 -0
- data/lib/v8/portal.rb +86 -0
- data/lib/v8/portal/caller.rb +37 -0
- data/lib/v8/portal/constructor.rb +98 -0
- data/lib/v8/portal/function.rb +63 -0
- data/lib/v8/portal/interceptors.rb +152 -0
- data/lib/v8/portal/proxies.rb +151 -0
- data/lib/v8/portal/templates.rb +73 -0
- data/lib/v8/stack.rb +66 -0
- data/lib/v8/tap.rb +9 -0
- data/lib/v8/version.rb +3 -0
- data/spec/ext/array_spec.rb +15 -0
- data/spec/ext/cxt_spec.rb +57 -0
- data/spec/ext/ext_spec_helper.rb +27 -0
- data/spec/ext/func_spec.rb +64 -0
- data/spec/ext/object_spec.rb +10 -0
- data/spec/ext/string_spec.rb +11 -0
- data/spec/ext/try_catch_spec.rb +60 -0
- data/spec/redjs_spec.rb +9 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/v8/error_spec.rb +131 -0
- data/spec/v8/portal/proxies_spec.rb +106 -0
- data/specmem/handle_memspec.rb +41 -0
- data/specmem/object_memspec.rb +14 -0
- data/specmem/proxies_memspec.rb +49 -0
- data/specmem/spec_helper.rb +24 -0
- data/specthread/spec_helper.rb +2 -0
- data/specthread/threading_spec.rb +13 -0
- data/thefrontside.png +0 -0
- data/therubyracer.gemspec +27 -0
- metadata +183 -0
data/lib/v8/array.rb
ADDED
data/lib/v8/c/locker.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module V8
|
2
|
+
module C
|
3
|
+
# Shim to support the old style locking syntax. We don't currently
|
4
|
+
# deprecate this because it might make a comeback at some point.
|
5
|
+
#
|
6
|
+
# to synchronize access to V8, and to associate V8 with this thread:
|
7
|
+
#
|
8
|
+
# Locker() do
|
9
|
+
# #... interact with v8
|
10
|
+
# end
|
11
|
+
def self.Locker
|
12
|
+
lock = Locker.new
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
lock.delete
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/v8/cli.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module V8
|
5
|
+
module CLI
|
6
|
+
def self.run(exename = 'v8', args = [])
|
7
|
+
options = OpenStruct.new
|
8
|
+
options.libs = []
|
9
|
+
options.libdirs = []
|
10
|
+
parser = OptionParser.new
|
11
|
+
parser.banner = "Usage: #{exename} [options]"
|
12
|
+
parser.on("-r", "--require FILE", "load and execute FILE as JavaScript source") {|r| options.libs << r}
|
13
|
+
parser.on("-i", "--interactive", "interactive mode") {options.interactive = true}
|
14
|
+
parser.on("-e", "--execute JAVASCRIPT", String, "evaluate JAVASCRIPT and exit") {|src| options.execute = src}
|
15
|
+
parser.on("--selftest", "check that therubyracer is functioning properly") {options.selftest = true}
|
16
|
+
parser.on_tail("-v", "--version", "Show version and exit") {options.version_info = true}
|
17
|
+
parser.on_tail("-h", "--help", "Show this message") do
|
18
|
+
puts parser
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
parser.parse!(args.dup)
|
23
|
+
rescue OptionParser::InvalidOption => e
|
24
|
+
puts "#{exename}: #{e.message}"
|
25
|
+
exit(1)
|
26
|
+
end
|
27
|
+
if options.version_info
|
28
|
+
require 'libv8'
|
29
|
+
puts "The Ruby Racer #{V8::VERSION}"
|
30
|
+
puts "V8 Version #{Libv8::V8_VERSION}"
|
31
|
+
exit
|
32
|
+
elsif options.selftest
|
33
|
+
self.test
|
34
|
+
end
|
35
|
+
Context.new(:with => Shell.new) do |cxt|
|
36
|
+
for libfile in options.libs do
|
37
|
+
load(cxt,libfile)
|
38
|
+
end
|
39
|
+
if options.interactive
|
40
|
+
repl(cxt, exename)
|
41
|
+
elsif options.execute
|
42
|
+
cxt.eval(options.execute, '<execute>')
|
43
|
+
else
|
44
|
+
begin
|
45
|
+
cxt.eval($stdin, '<stdin>')
|
46
|
+
rescue Interrupt => e
|
47
|
+
puts; exit
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.load(cxt, libfile)
|
54
|
+
begin
|
55
|
+
cxt.load(libfile)
|
56
|
+
rescue V8::JSError => e
|
57
|
+
puts e.message
|
58
|
+
puts e.backtrace(:javascript)
|
59
|
+
rescue StandardError => e
|
60
|
+
puts e
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.test
|
65
|
+
begin
|
66
|
+
require 'rspec'
|
67
|
+
specs = File.expand_path('../../../spec', __FILE__)
|
68
|
+
$:.unshift specs
|
69
|
+
ARGV.clear
|
70
|
+
ARGV << specs
|
71
|
+
::RSpec::Core::Runner.autorun
|
72
|
+
exit(0)
|
73
|
+
rescue LoadError => e
|
74
|
+
puts "selftest requires rspec to be installed (gem install rspec)"
|
75
|
+
exit(1)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.repl(cxt, exename)
|
81
|
+
require 'readline'
|
82
|
+
puts "help() for help. quit() to quit."
|
83
|
+
puts "The Ruby Racer #{V8::VERSION}"
|
84
|
+
puts "Vroom Vroom!"
|
85
|
+
trap("SIGINT") do
|
86
|
+
puts "^C"
|
87
|
+
end
|
88
|
+
loop do
|
89
|
+
line = Readline.readline("#{exename}> ", true)
|
90
|
+
begin
|
91
|
+
result = cxt.eval(line, '<shell>')
|
92
|
+
puts(result) unless result.nil?
|
93
|
+
rescue V8::JSError => e
|
94
|
+
puts e.message
|
95
|
+
puts e.backtrace(:javascript)
|
96
|
+
rescue StandardError => e
|
97
|
+
puts e
|
98
|
+
puts e.backtrace.join("\n")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Shell
|
104
|
+
def to_s
|
105
|
+
"[object Shell]"
|
106
|
+
end
|
107
|
+
|
108
|
+
def print(string)
|
109
|
+
puts string
|
110
|
+
end
|
111
|
+
|
112
|
+
def exit(status = 0)
|
113
|
+
Kernel.exit(status)
|
114
|
+
end
|
115
|
+
|
116
|
+
alias_method :quit, :exit
|
117
|
+
|
118
|
+
def help(*args)
|
119
|
+
<<-HELP
|
120
|
+
print(msg)
|
121
|
+
print msg to STDOUT
|
122
|
+
|
123
|
+
exit(status = 0)
|
124
|
+
exit the shell
|
125
|
+
also: quit()
|
126
|
+
|
127
|
+
evalrb(source)
|
128
|
+
evaluate some ruby source
|
129
|
+
HELP
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/v8/context.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module V8
|
4
|
+
class Context
|
5
|
+
attr_reader :native, :scope, :access
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
@access = Access.new
|
9
|
+
@to = Portal.new(self, @access)
|
10
|
+
@to.lock do
|
11
|
+
with = opts[:with]
|
12
|
+
constructor = nil
|
13
|
+
template = if with
|
14
|
+
constructor = @to.templates.to_constructor(with.class)
|
15
|
+
constructor.disable()
|
16
|
+
constructor.template.InstanceTemplate()
|
17
|
+
else
|
18
|
+
C::ObjectTemplate::New()
|
19
|
+
end
|
20
|
+
@native = opts[:with] ? C::Context::New(template) : C::Context::New()
|
21
|
+
@native.enter do
|
22
|
+
@global = @native.Global()
|
23
|
+
@to.proxies.register_javascript_proxy @global, :for => with if with
|
24
|
+
constructor.enable() if constructor
|
25
|
+
@scope = @to.rb(@global)
|
26
|
+
@global.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext"), C::External::New(self))
|
27
|
+
end
|
28
|
+
yield(self) if block_given?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def eval(javascript, filename = "<eval>", line = 1)
|
33
|
+
if IO === javascript || StringIO === javascript
|
34
|
+
javascript = javascript.read()
|
35
|
+
end
|
36
|
+
err = nil
|
37
|
+
value = nil
|
38
|
+
@to.lock do
|
39
|
+
C::TryCatch.try do |try|
|
40
|
+
@native.enter do
|
41
|
+
script = C::Script::Compile(@to.v8(javascript.to_s), @to.v8(filename.to_s))
|
42
|
+
if try.HasCaught()
|
43
|
+
err = JSError.new(try, @to)
|
44
|
+
else
|
45
|
+
result = script.Run()
|
46
|
+
if try.HasCaught()
|
47
|
+
err = JSError.new(try, @to)
|
48
|
+
else
|
49
|
+
value = @to.rb(result)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
raise err if err
|
56
|
+
return value
|
57
|
+
end
|
58
|
+
|
59
|
+
def load(filename)
|
60
|
+
File.open(filename) do |file|
|
61
|
+
self.eval file, filename, 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def [](key)
|
66
|
+
@to.open do
|
67
|
+
@to.rb @global.Get(@to.v8(key))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def []=(key, value)
|
72
|
+
@to.open do
|
73
|
+
@global.Set(@to.v8(key), @to.v8(value))
|
74
|
+
end
|
75
|
+
return value
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.stack(limit = 99)
|
79
|
+
if native = C::Context::GetEntered()
|
80
|
+
global = native.Global()
|
81
|
+
cxt = global.GetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext")).Value()
|
82
|
+
cxt.instance_eval {@to.rb(C::StackTrace::CurrentStackTrace(limit))}
|
83
|
+
else
|
84
|
+
[]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.passes_this_argument?
|
89
|
+
true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module C
|
94
|
+
class Context
|
95
|
+
def enter
|
96
|
+
if block_given?
|
97
|
+
if IsEntered()
|
98
|
+
yield(self)
|
99
|
+
else
|
100
|
+
Enter()
|
101
|
+
begin
|
102
|
+
yield(self)
|
103
|
+
ensure
|
104
|
+
Exit()
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/v8/error.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
|
2
|
+
module V8
|
3
|
+
class JSError < StandardError
|
4
|
+
attr_reader :value, :boundaries
|
5
|
+
|
6
|
+
def initialize(try, to)
|
7
|
+
@to = to
|
8
|
+
super(initialize_unsafe(try))
|
9
|
+
rescue Exception => e
|
10
|
+
@boundaries = [Boundary.new(:rbframes => e.backtrace)]
|
11
|
+
@value = e
|
12
|
+
super(<<-EOMSG)
|
13
|
+
You have uncovered what is probably a BUG in therubyracer exception code. An error report would be very helpful.
|
14
|
+
JSError#initialize failed!: #{e.message}"
|
15
|
+
EOMSG
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize_unsafe(try)
|
19
|
+
message = nil
|
20
|
+
ex = @to.rb(try.Exception())
|
21
|
+
@boundaries = [Boundary.new(:rbframes => caller(3), :jsframes => parse_js_frames(try))]
|
22
|
+
if V8::Object === ex
|
23
|
+
if msg = ex['message']
|
24
|
+
message = msg
|
25
|
+
else
|
26
|
+
message = ex.to_s
|
27
|
+
end
|
28
|
+
if cause = ex.instance_variable_get(:@native).GetHiddenValue("TheRubyRacer::Cause")
|
29
|
+
if !cause.IsEmpty()
|
30
|
+
prev = cause.Value()
|
31
|
+
if prev.kind_of?(JSError)
|
32
|
+
@boundaries.concat prev.boundaries
|
33
|
+
@value = prev.value
|
34
|
+
else
|
35
|
+
@value = prev
|
36
|
+
@boundaries.concat [Boundary.new(:rbframes => prev.backtrace)]
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@value = ex
|
40
|
+
end
|
41
|
+
end
|
42
|
+
else
|
43
|
+
@value = ex
|
44
|
+
message = ex.to_s
|
45
|
+
end
|
46
|
+
return message
|
47
|
+
end
|
48
|
+
|
49
|
+
def in_ruby?
|
50
|
+
@value.kind_of?(Exception)
|
51
|
+
end
|
52
|
+
|
53
|
+
def in_javascript?
|
54
|
+
!in_ruby?
|
55
|
+
end
|
56
|
+
|
57
|
+
def backtrace(*modifiers)
|
58
|
+
trace_framework = modifiers.include?(:framework)
|
59
|
+
trace_ruby = modifiers.length == 0 || modifiers.include?(:ruby)
|
60
|
+
trace_javascript = modifiers.length == 0 || modifiers.include?(:javascript)
|
61
|
+
mixed = []
|
62
|
+
rbcontext = []
|
63
|
+
jscontext = []
|
64
|
+
@boundaries.each do |b|
|
65
|
+
rbframes = b.rbframes.dup
|
66
|
+
rbcontext.reverse_each do |frame|
|
67
|
+
if frame == rbframes.last
|
68
|
+
rbframes.pop
|
69
|
+
else
|
70
|
+
break
|
71
|
+
end
|
72
|
+
end if trace_ruby
|
73
|
+
jsframes = b.jsframes.dup
|
74
|
+
jscontext.reverse_each do |frame|
|
75
|
+
if frame == jsframes.last
|
76
|
+
jsframes.pop
|
77
|
+
else
|
78
|
+
break
|
79
|
+
end
|
80
|
+
end if trace_javascript
|
81
|
+
rbcontext = b.rbframes
|
82
|
+
jscontext = b.jsframes
|
83
|
+
rbframes.reject! {|f| f =~ /lib\/v8\/.*\.rb/} unless trace_framework
|
84
|
+
mixed.unshift(*rbframes) if trace_ruby
|
85
|
+
mixed.unshift(*jsframes) if trace_javascript
|
86
|
+
end
|
87
|
+
return mixed
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_js_frames(try)
|
91
|
+
#I can't figure out why V8 is not capturing the stacktrace here
|
92
|
+
#in terms of StackTrace and StackFrame objects, so we have to
|
93
|
+
#parse the string.
|
94
|
+
raw = @to.rb(try.StackTrace())
|
95
|
+
if raw && !raw.empty? && !syntax_error?(try)
|
96
|
+
raw.split("\n")[1..-1].tap do |frames|
|
97
|
+
frames.each {|frame| frame.strip!; frame.chomp!(",")}
|
98
|
+
end.reject(&:empty?)
|
99
|
+
else
|
100
|
+
msg = try.Message()
|
101
|
+
["at #{@to.rb(msg.GetScriptResourceName())}:#{msg.GetLineNumber()}:#{msg.GetStartColumn() + 1}"]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
#Syntax errors are weird in that they have a non-empty stack trace
|
106
|
+
#but it does not contain any source location information, so
|
107
|
+
#in these instances, we have to pull it out of the Message object
|
108
|
+
#in the TryCatch. Is there a better way to detect a syntax error
|
109
|
+
def syntax_error?(try)
|
110
|
+
ex = @to.rb(try.Exception())
|
111
|
+
if ex && ex.kind_of?(V8::Object)
|
112
|
+
type = ex["constructor"]
|
113
|
+
type && type.kind_of?(V8::Function) && type.name == "SyntaxError"
|
114
|
+
else
|
115
|
+
false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Boundary
|
120
|
+
attr_reader :rbframes, :jsframes
|
121
|
+
|
122
|
+
def initialize(frames = {})
|
123
|
+
@rbframes = frames[:rbframes] || []
|
124
|
+
@jsframes = frames[:jsframes] || []
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
#deprecated -- use JSError
|
129
|
+
JavasriptError = JSError
|
130
|
+
end
|
data/lib/v8/function.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module V8
|
2
|
+
class Function < V8::Object
|
3
|
+
|
4
|
+
def methodcall(thisObject, *args)
|
5
|
+
err = nil
|
6
|
+
return_value = nil
|
7
|
+
@portal.open do |to|
|
8
|
+
C::TryCatch.try do |try|
|
9
|
+
this = to.v8(thisObject)
|
10
|
+
return_value = to.rb(@native.Call(this, to.v8(args)))
|
11
|
+
err = JSError.new(try, to) if try.HasCaught()
|
12
|
+
end
|
13
|
+
end
|
14
|
+
raise err if err
|
15
|
+
return return_value
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(*args)
|
19
|
+
@portal.open do
|
20
|
+
self.methodcall(@portal.context.native.Global(), *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def new(*args)
|
25
|
+
@portal.open do |to|
|
26
|
+
to.rb(@native.NewInstance(to.v8(args)))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
@portal.open do |to|
|
32
|
+
to.rb(@native.GetName())
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def name=(name)
|
37
|
+
name.tap do
|
38
|
+
@portal.open do |to|
|
39
|
+
@native.SetName(to.v8(name))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|