therubyracer-xcode 0.12.2
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 +7 -0
- data/.gitignore +23 -0
- data/.travis.yml +14 -0
- data/Changelog.md +263 -0
- data/Gemfile +12 -0
- data/README.md +227 -0
- data/Rakefile +42 -0
- data/benchmarks.rb +218 -0
- data/ext/v8/accessor.cc +181 -0
- data/ext/v8/array.cc +26 -0
- data/ext/v8/backref.cc +45 -0
- data/ext/v8/constants.cc +34 -0
- data/ext/v8/constraints.cc +52 -0
- data/ext/v8/context.cc +130 -0
- data/ext/v8/date.cc +18 -0
- data/ext/v8/exception.cc +38 -0
- data/ext/v8/extconf.rb +34 -0
- data/ext/v8/external.cc +43 -0
- data/ext/v8/function.cc +58 -0
- data/ext/v8/gc.cc +43 -0
- data/ext/v8/handles.cc +34 -0
- data/ext/v8/heap.cc +35 -0
- data/ext/v8/init.cc +39 -0
- data/ext/v8/invocation.cc +86 -0
- data/ext/v8/locker.cc +77 -0
- data/ext/v8/message.cc +51 -0
- data/ext/v8/object.cc +335 -0
- data/ext/v8/primitive.cc +8 -0
- data/ext/v8/rr.cc +83 -0
- data/ext/v8/rr.h +934 -0
- data/ext/v8/script.cc +115 -0
- data/ext/v8/signature.cc +18 -0
- data/ext/v8/stack.cc +76 -0
- data/ext/v8/string.cc +47 -0
- data/ext/v8/template.cc +175 -0
- data/ext/v8/trycatch.cc +87 -0
- data/ext/v8/v8.cc +87 -0
- data/ext/v8/value.cc +239 -0
- data/lib/therubyracer.rb +1 -0
- data/lib/v8/access/indices.rb +40 -0
- data/lib/v8/access/invocation.rb +47 -0
- data/lib/v8/access/names.rb +65 -0
- data/lib/v8/access.rb +5 -0
- data/lib/v8/array.rb +26 -0
- data/lib/v8/context.rb +258 -0
- data/lib/v8/conversion/array.rb +11 -0
- data/lib/v8/conversion/class.rb +119 -0
- data/lib/v8/conversion/code.rb +38 -0
- data/lib/v8/conversion/fixnum.rb +11 -0
- data/lib/v8/conversion/fundamental.rb +11 -0
- data/lib/v8/conversion/hash.rb +11 -0
- data/lib/v8/conversion/indentity.rb +31 -0
- data/lib/v8/conversion/method.rb +26 -0
- data/lib/v8/conversion/object.rb +28 -0
- data/lib/v8/conversion/primitive.rb +7 -0
- data/lib/v8/conversion/proc.rb +5 -0
- data/lib/v8/conversion/reference.rb +16 -0
- data/lib/v8/conversion/string.rb +12 -0
- data/lib/v8/conversion/symbol.rb +7 -0
- data/lib/v8/conversion/time.rb +13 -0
- data/lib/v8/conversion.rb +36 -0
- data/lib/v8/error.rb +169 -0
- data/lib/v8/function.rb +28 -0
- data/lib/v8/object.rb +79 -0
- data/lib/v8/stack.rb +85 -0
- data/lib/v8/version.rb +3 -0
- data/lib/v8/weak.rb +82 -0
- data/lib/v8.rb +30 -0
- data/spec/c/array_spec.rb +19 -0
- data/spec/c/constants_spec.rb +22 -0
- data/spec/c/exception_spec.rb +28 -0
- data/spec/c/external_spec.rb +11 -0
- data/spec/c/function_spec.rb +48 -0
- data/spec/c/handles_spec.rb +31 -0
- data/spec/c/locker_spec.rb +36 -0
- data/spec/c/object_spec.rb +47 -0
- data/spec/c/script_spec.rb +30 -0
- data/spec/c/string_spec.rb +18 -0
- data/spec/c/template_spec.rb +31 -0
- data/spec/c/trycatch_spec.rb +52 -0
- data/spec/mem/blunt_spec.rb +42 -0
- data/spec/redjs_spec.rb +10 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/threading_spec.rb +64 -0
- data/spec/v8/context_spec.rb +19 -0
- data/spec/v8/conversion_spec.rb +52 -0
- data/spec/v8/error_spec.rb +167 -0
- data/spec/v8/function_spec.rb +9 -0
- data/spec/v8/object_spec.rb +15 -0
- data/thefrontside.png +0 -0
- data/therubyracer.gemspec +22 -0
- metadata +186 -0
data/lib/v8/stack.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
|
2
|
+
module V8
|
3
|
+
|
4
|
+
class StackTrace
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(native)
|
8
|
+
@context = V8::Context.current
|
9
|
+
@native = native
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
@context.enter do
|
14
|
+
@native ? @native.GetFrameCount() : 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
return unless @native
|
20
|
+
@context.enter do
|
21
|
+
for i in 0..length - 1
|
22
|
+
yield V8::StackFrame.new(@native.GetFrame(i), @context)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@native ? map(&:to_s).join("\n") : ""
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class StackFrame
|
33
|
+
|
34
|
+
def initialize(native, context)
|
35
|
+
@context = context
|
36
|
+
@native = native
|
37
|
+
end
|
38
|
+
|
39
|
+
def script_name
|
40
|
+
@context.enter do
|
41
|
+
@context.to_ruby(@native.GetScriptName())
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def function_name
|
46
|
+
@context.enter do
|
47
|
+
@context.to_ruby(@native.GetFunctionName())
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def line_number
|
52
|
+
@context.enter do
|
53
|
+
@native.GetLineNumber()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def column
|
58
|
+
@context.enter do
|
59
|
+
@native.GetColumn()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def eval?
|
64
|
+
@context.enter do
|
65
|
+
@native.IsEval()
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def constructor?
|
70
|
+
@context.enter do
|
71
|
+
@native.IsConstructor()
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
@context.enter do
|
77
|
+
"at " + if !function_name.empty?
|
78
|
+
"#{function_name} (#{script_name}:#{line_number}:#{column})"
|
79
|
+
else
|
80
|
+
"#{script_name}:#{line_number}:#{column}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/v8/version.rb
ADDED
data/lib/v8/weak.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module V8
|
2
|
+
module Weak
|
3
|
+
# weak refs are broken on MRI 1.9 and merely slow on 1.8
|
4
|
+
# so we mitigate this by using the 'ref' gem. However, this
|
5
|
+
# only mitigates the problem. Under heavy load, you will still
|
6
|
+
# get different or terminated objects being returned. bad stuff.
|
7
|
+
#
|
8
|
+
# If you are having problems with this, an upgrade to 2.0 is *highly*
|
9
|
+
# recommended.
|
10
|
+
#
|
11
|
+
# for other platforms we just use the stdlib 'weakref'
|
12
|
+
# implementation
|
13
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.0.0'
|
14
|
+
require 'ref'
|
15
|
+
Ref = ::Ref::WeakReference
|
16
|
+
WeakValueMap = ::Ref::WeakValueMap
|
17
|
+
else
|
18
|
+
require 'weakref'
|
19
|
+
class Ref
|
20
|
+
def initialize(object)
|
21
|
+
@ref = ::WeakRef.new(object)
|
22
|
+
end
|
23
|
+
def object
|
24
|
+
@ref.__getobj__
|
25
|
+
rescue ::WeakRef::RefError
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class WeakValueMap
|
31
|
+
def initialize
|
32
|
+
@values = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](key)
|
36
|
+
if ref = @values[key]
|
37
|
+
ref.object
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def []=(key, value)
|
42
|
+
ref = V8::Weak::Ref.new(value)
|
43
|
+
ObjectSpace.define_finalizer(value, self.class.ensure_cleanup(@values, key, ref))
|
44
|
+
|
45
|
+
@values[key] = ref
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.ensure_cleanup(values,key,ref)
|
49
|
+
proc {
|
50
|
+
values.delete(key) if values[key] == ref
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module Cell
|
57
|
+
def weakcell(name, &block)
|
58
|
+
unless storage = instance_variable_get("@#{name}")
|
59
|
+
storage = instance_variable_set("@#{name}", Storage.new)
|
60
|
+
end
|
61
|
+
storage.access(&block)
|
62
|
+
end
|
63
|
+
class Storage
|
64
|
+
def access(&block)
|
65
|
+
if @ref
|
66
|
+
@ref.object || populate(block)
|
67
|
+
else
|
68
|
+
populate(block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def populate(block)
|
75
|
+
occupant = block.call()
|
76
|
+
@ref = V8::Weak::Ref.new(occupant)
|
77
|
+
return occupant
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/v8.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "v8/version"
|
2
|
+
|
3
|
+
require 'v8/weak'
|
4
|
+
require 'v8/init'
|
5
|
+
require 'v8/error'
|
6
|
+
require 'v8/stack'
|
7
|
+
require 'v8/conversion/fundamental'
|
8
|
+
require 'v8/conversion/indentity'
|
9
|
+
require 'v8/conversion/reference'
|
10
|
+
require 'v8/conversion/primitive'
|
11
|
+
require 'v8/conversion/code'
|
12
|
+
require 'v8/conversion/class'
|
13
|
+
require 'v8/conversion/object'
|
14
|
+
require 'v8/conversion/time'
|
15
|
+
require 'v8/conversion/hash'
|
16
|
+
require 'v8/conversion/array'
|
17
|
+
require 'v8/conversion/proc'
|
18
|
+
require 'v8/conversion/method'
|
19
|
+
require 'v8/conversion/symbol'
|
20
|
+
require 'v8/conversion/string'
|
21
|
+
require 'v8/conversion/fixnum'
|
22
|
+
require 'v8/conversion'
|
23
|
+
require 'v8/access/names'
|
24
|
+
require 'v8/access/indices'
|
25
|
+
require 'v8/access/invocation'
|
26
|
+
require 'v8/access'
|
27
|
+
require 'v8/context'
|
28
|
+
require 'v8/object'
|
29
|
+
require 'v8/array'
|
30
|
+
require 'v8/function'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::Array do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
it "can store and retrieve a value" do
|
7
|
+
o = V8::C::Object::New()
|
8
|
+
a = V8::C::Array::New()
|
9
|
+
a.Length().should eql 0
|
10
|
+
a.Set(0, o)
|
11
|
+
a.Length().should eql 1
|
12
|
+
a.Get(0).Equals(o).should be_truthy
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can be initialized with a length" do
|
16
|
+
a = V8::C::Array::New(5)
|
17
|
+
a.Length().should eql 5
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
it "has constant methods for Undefined, Null, True and False" do
|
7
|
+
[:Undefined, :Null, :True, :False].each do |name|
|
8
|
+
constant = V8::C.send(name)
|
9
|
+
constant.should_not be_nil
|
10
|
+
V8::C.send(name).should be constant
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a value for the Empty handle" do
|
15
|
+
V8::C::Value::Empty.should_not be_nil
|
16
|
+
V8::C::Value::Empty.should be V8::C::Value::Empty
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can access the V8 version" do
|
20
|
+
V8::C::V8::GetVersion().should match /^3\.16/
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::Exception do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
it "can be thrown from Ruby" do
|
7
|
+
t = V8::C::FunctionTemplate::New(method(:explode))
|
8
|
+
@cxt.Global().Set("explode", t.GetFunction())
|
9
|
+
script = V8::C::Script::New(<<-JS, '<eval>')
|
10
|
+
(function() {
|
11
|
+
try {
|
12
|
+
explode()
|
13
|
+
} catch (e) {
|
14
|
+
return e.message
|
15
|
+
}
|
16
|
+
})()
|
17
|
+
JS
|
18
|
+
result = script.Run()
|
19
|
+
result.should_not be_nil
|
20
|
+
result.should be_kind_of(V8::C::String)
|
21
|
+
result.Utf8Value().should eql 'did not pay syntax'
|
22
|
+
end
|
23
|
+
|
24
|
+
def explode(arguments)
|
25
|
+
error = V8::C::Exception::SyntaxError('did not pay syntax')
|
26
|
+
V8::C::ThrowException(error)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::Function do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
it "can be called" do
|
7
|
+
fn = run '(function() {return "foo"})'
|
8
|
+
fn.Call(@cxt.Global(), []).Utf8Value().should eql "foo"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "can be called with arguments and context" do
|
12
|
+
fn = run '(function(one, two, three) {this.one = one; this.two = two; this.three = three})'
|
13
|
+
one = V8::C::Object::New()
|
14
|
+
two = V8::C::Object::New()
|
15
|
+
fn.Call(@cxt.Global(), [one, two, 3])
|
16
|
+
@cxt.Global().Get("one").should eql one
|
17
|
+
@cxt.Global().Get("two").should eql two
|
18
|
+
@cxt.Global().Get("three").should eql 3
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can be called as a constructor" do
|
22
|
+
fn = run '(function() {this.foo = "foo"})'
|
23
|
+
fn.NewInstance().Get(V8::C::String::New('foo')).Utf8Value().should eql "foo"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can be called as a constructor with arguments" do
|
27
|
+
fn = run '(function(foo) {this.foo = foo})'
|
28
|
+
object = fn.NewInstance([V8::C::String::New("bar")])
|
29
|
+
object.Get(V8::C::String::New('foo')).Utf8Value().should eql "bar"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "doesn't kill the world if invoking it throws a javascript exception" do
|
33
|
+
V8::C::TryCatch() do
|
34
|
+
fn = run '(function() { throw new Error("boom!")})'
|
35
|
+
fn.Call(@cxt.Global(), [])
|
36
|
+
fn.NewInstance([])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def run(source)
|
42
|
+
source = V8::C::String::New(source.to_s)
|
43
|
+
filename = V8::C::String::New("<eval>")
|
44
|
+
script = V8::C::Script::New(source, filename)
|
45
|
+
result = script.Run()
|
46
|
+
result.kind_of?(V8::C::String) ? result.Utf8Value() : result
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "setting up handles scopes" do
|
4
|
+
around(:each) do |example|
|
5
|
+
V8::C::Locker() do
|
6
|
+
cxt = V8::C::Context::New()
|
7
|
+
begin
|
8
|
+
cxt.Enter()
|
9
|
+
example.run
|
10
|
+
ensure
|
11
|
+
cxt.Exit()
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can allocate handle scopes" do
|
17
|
+
V8::C::HandleScope() do
|
18
|
+
V8::C::Object::New()
|
19
|
+
end.class.should eql V8::C::Object
|
20
|
+
end
|
21
|
+
|
22
|
+
it "isn't the end of the world if a ruby exception is raised inside a HandleScope" do
|
23
|
+
begin
|
24
|
+
V8::C::HandleScope() do
|
25
|
+
raise "boom!"
|
26
|
+
end
|
27
|
+
rescue StandardError => e
|
28
|
+
e.message.should eql "boom!"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::Locker do
|
4
|
+
it "can lock and unlock the VM" do
|
5
|
+
V8::C::Locker::IsLocked().should be_falsey
|
6
|
+
V8::C::Locker() do
|
7
|
+
V8::C::Locker::IsLocked().should be_truthy
|
8
|
+
V8::C::Unlocker() do
|
9
|
+
V8::C::Locker::IsLocked().should be_falsey
|
10
|
+
end
|
11
|
+
end
|
12
|
+
V8::C::Locker::IsLocked().should be_falsey
|
13
|
+
end
|
14
|
+
|
15
|
+
it "properly unlocks if an exception is thrown inside a lock block" do
|
16
|
+
begin
|
17
|
+
V8::C::Locker() do
|
18
|
+
raise "boom!"
|
19
|
+
end
|
20
|
+
rescue
|
21
|
+
V8::C::Locker::IsLocked().should be_falsey
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "properly re-locks if an exception is thrown inside an un-lock block" do
|
26
|
+
V8::C::Locker() do
|
27
|
+
begin
|
28
|
+
V8::C::Unlocker() do
|
29
|
+
raise "boom!"
|
30
|
+
end
|
31
|
+
rescue
|
32
|
+
V8::C::Locker::IsLocked().should be_truthy
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::Object do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
it "can store and retrieve a value" do
|
7
|
+
o = V8::C::Object::New()
|
8
|
+
key = V8::C::String::New("foo")
|
9
|
+
value = V8::C::String::New("bar")
|
10
|
+
o.Set(key, value)
|
11
|
+
o.Get(key).Utf8Value().should eql "bar"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can retrieve all property names" do
|
15
|
+
o = V8::C::Object::New()
|
16
|
+
o.Set(V8::C::String::New("foo"), V8::C::String::New("bar"))
|
17
|
+
o.Set(V8::C::String::New("baz"), V8::C::String::New("bang"))
|
18
|
+
names = o.GetPropertyNames()
|
19
|
+
names.Length().should eql 2
|
20
|
+
names.Get(0).Utf8Value().should eql "foo"
|
21
|
+
names.Get(1).Utf8Value().should eql "baz"
|
22
|
+
end
|
23
|
+
it "can set an accessor from ruby" do
|
24
|
+
o = V8::C::Object::New()
|
25
|
+
property = V8::C::String::New("statement")
|
26
|
+
callback_data = V8::C::String::New("I am Legend")
|
27
|
+
left = V8::C::String::New("Yo! ")
|
28
|
+
getter = proc do |name, info|
|
29
|
+
info.This().StrictEquals(o).should be_truthy
|
30
|
+
info.Holder().StrictEquals(o).should be_truthy
|
31
|
+
V8::C::String::Concat(left, info.Data())
|
32
|
+
end
|
33
|
+
setter = proc do |name, value, info|
|
34
|
+
left = value
|
35
|
+
end
|
36
|
+
o.SetAccessor(property, getter, setter, callback_data)
|
37
|
+
o.Get(property).Utf8Value().should eql "Yo! I am Legend"
|
38
|
+
o.Set(property, V8::C::String::New("Bro! "))
|
39
|
+
o.Get(property).Utf8Value().should eql "Bro! I am Legend"
|
40
|
+
end
|
41
|
+
it "always returns the same ruby object for the same V8 object" do
|
42
|
+
one = V8::C::Object::New()
|
43
|
+
two = V8::C::Object::New()
|
44
|
+
one.Set("two", two)
|
45
|
+
one.Get("two").should be two
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe V8::C::Script do
|
5
|
+
requires_v8_context
|
6
|
+
|
7
|
+
it "can run a script and return a polymorphic result" do
|
8
|
+
source = V8::C::String::New("(new Array())")
|
9
|
+
filename = V8::C::String::New("<eval>")
|
10
|
+
script = V8::C::Script::New(source, filename)
|
11
|
+
result = script.Run()
|
12
|
+
result.should be_kind_of V8::C::Array
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can accept precompiled script data" do
|
16
|
+
source = "7 * 6"
|
17
|
+
name = V8::C::String::New("<spec>")
|
18
|
+
origin = V8::C::ScriptOrigin.new(name)
|
19
|
+
data = V8::C::ScriptData::PreCompile(source, source.length)
|
20
|
+
data.HasError().should be_falsey
|
21
|
+
script = V8::C::Script::New(V8::C::String::New(source), origin, data)
|
22
|
+
script.Run().should eql 42
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can detect errors in the script data" do
|
26
|
+
source = "^ = ;"
|
27
|
+
data = V8::C::ScriptData::PreCompile(source, source.length)
|
28
|
+
data.HasError().should be_truthy
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::String do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
it "can hold Unicode values outside the Basic Multilingual Plane" do
|
7
|
+
string = V8::C::String::New("\u{100000}")
|
8
|
+
string.Utf8Value().should eql "\u{100000}"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "can naturally translate ruby strings into v8 strings" do
|
12
|
+
V8::C::String::Concat(V8::C::String::New("Hello "), "World").Utf8Value().should eql "Hello World"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can naturally translate ruby objects into v8 strings" do
|
16
|
+
V8::C::String::Concat(V8::C::String::New("forty two is "), 42).Utf8Value().should eql "forty two is 42"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::Template do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
describe V8::C::FunctionTemplate do
|
7
|
+
it "can be created with no arguments" do
|
8
|
+
t = V8::C::FunctionTemplate::New()
|
9
|
+
t.GetFunction().Call(@cxt.Global(),[]).StrictEquals(@cxt.Global()).should be_truthy
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can be created with a callback" do
|
13
|
+
receiver = V8::C::Object::New()
|
14
|
+
f = nil
|
15
|
+
callback = lambda do |arguments|
|
16
|
+
arguments.Length().should be(2)
|
17
|
+
arguments[0].Utf8Value().should eql 'one'
|
18
|
+
arguments[1].Utf8Value().should eql 'two'
|
19
|
+
arguments.Callee().StrictEquals(f).should be_truthy
|
20
|
+
arguments.This().StrictEquals(receiver).should be_truthy
|
21
|
+
arguments.Holder().StrictEquals(receiver).should be_truthy
|
22
|
+
arguments.IsConstructCall().should be_falsey
|
23
|
+
arguments.Data().Value().should be(42)
|
24
|
+
V8::C::String::New("result")
|
25
|
+
end
|
26
|
+
t = V8::C::FunctionTemplate::New(callback, V8::C::External::New(42))
|
27
|
+
f = t.GetFunction()
|
28
|
+
f.Call(receiver, [V8::C::String::New('one'), V8::C::String::New('two')]).Utf8Value().should eql "result"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe V8::C::External do
|
4
|
+
requires_v8_context
|
5
|
+
|
6
|
+
it "can catch javascript exceptions" do
|
7
|
+
V8::C::V8::SetCaptureStackTraceForUncaughtExceptions(true, 99, V8::C::StackTrace::kDetailed)
|
8
|
+
V8::C::TryCatch() do |trycatch|
|
9
|
+
source = V8::C::String::New(<<-JS)
|
10
|
+
function one() {
|
11
|
+
two()
|
12
|
+
}
|
13
|
+
function two() {
|
14
|
+
three()
|
15
|
+
}
|
16
|
+
function three() {
|
17
|
+
boom()
|
18
|
+
}
|
19
|
+
function boom() {
|
20
|
+
throw new Error('boom!')
|
21
|
+
}
|
22
|
+
eval('one()')
|
23
|
+
JS
|
24
|
+
filename = V8::C::String::New("<eval>")
|
25
|
+
script = V8::C::Script::New(source, filename)
|
26
|
+
result = script.Run()
|
27
|
+
trycatch.HasCaught().should be_truthy
|
28
|
+
trycatch.CanContinue().should be_truthy
|
29
|
+
exception = trycatch.Exception()
|
30
|
+
exception.should_not be_nil
|
31
|
+
exception.IsNativeError().should be_truthy
|
32
|
+
trycatch.StackTrace().Utf8Value().should match /boom.*three.*two.*one/m
|
33
|
+
message = trycatch.Message();
|
34
|
+
message.should_not be_nil
|
35
|
+
message.Get().Utf8Value().should eql "Uncaught Error: boom!"
|
36
|
+
message.GetSourceLine().Utf8Value().should eql " throw new Error('boom!')"
|
37
|
+
message.GetScriptResourceName().Utf8Value().should eql "<eval>"
|
38
|
+
message.GetLineNumber().should eql 11
|
39
|
+
stack = message.GetStackTrace()
|
40
|
+
stack.should_not be_nil
|
41
|
+
stack.GetFrameCount().should eql 6
|
42
|
+
frame = stack.GetFrame(0)
|
43
|
+
frame.GetLineNumber().should eql 11
|
44
|
+
frame.GetColumn().should eql 15
|
45
|
+
frame.GetScriptName().Utf8Value().should eql "<eval>"
|
46
|
+
frame.GetScriptNameOrSourceURL().Utf8Value().should eql "<eval>"
|
47
|
+
frame.IsEval().should be_falsey
|
48
|
+
stack.GetFrame(4).IsEval().should be_truthy
|
49
|
+
frame.IsConstructor().should be_falsey
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "A Very blunt test to make sure that we aren't doing stupid leaks", :memory => true do
|
4
|
+
before do
|
5
|
+
if Object.const_defined?(:RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
6
|
+
pending 'need to figure out how to do memory sanity checks on rbx'
|
7
|
+
end
|
8
|
+
#allocate a single context to make sure that v8 loads its snapshot and
|
9
|
+
#we pay the overhead.
|
10
|
+
V8::Context.new
|
11
|
+
@start_memory = process_memory
|
12
|
+
GC.stress = true
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
GC.stress = false
|
17
|
+
end
|
18
|
+
it "won't increase process memory by more than 50% no matter how many contexts we create" do
|
19
|
+
500.times do
|
20
|
+
V8::Context.new
|
21
|
+
run_v8_gc
|
22
|
+
end
|
23
|
+
process_memory.should <= @start_memory * 1.5
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can eval simple value passing statements repeatedly without significantly increasing memory" do
|
27
|
+
V8::C::Locker() do
|
28
|
+
cxt = V8::Context.new
|
29
|
+
500.times do
|
30
|
+
cxt.eval('7 * 6')
|
31
|
+
run_v8_gc
|
32
|
+
end
|
33
|
+
end
|
34
|
+
process_memory.should <= @start_memory * 1.1
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_memory
|
38
|
+
/\w*[ ]*#{Process.pid}[ ]*([.,\d]*)[ ]*([.,\d]*)[ ]*([\d]*)[ ]*([\d]*)/.match(`ps aux`)[4].to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
data/spec/redjs_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'v8'
|
2
|
+
|
3
|
+
def run_v8_gc
|
4
|
+
V8::C::V8::LowMemoryNotification()
|
5
|
+
while !V8::C::V8::IdleNotification() do
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def rputs(msg)
|
10
|
+
puts "<pre>#{ERB::Util.h(msg)}</pre>"
|
11
|
+
$stdout.flush
|
12
|
+
end
|
13
|
+
|
14
|
+
module V8ContextHelpers
|
15
|
+
module GroupMethods
|
16
|
+
def requires_v8_context
|
17
|
+
around(:each) do |example|
|
18
|
+
bootstrap_v8_context(&example)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def bootstrap_v8_context
|
24
|
+
V8::C::Locker() do
|
25
|
+
V8::C::HandleScope() do
|
26
|
+
@cxt = V8::C::Context::New()
|
27
|
+
begin
|
28
|
+
@cxt.Enter()
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
@cxt.Exit()
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
RSpec.configure do |c|
|
39
|
+
c.include V8ContextHelpers
|
40
|
+
c.extend V8ContextHelpers::GroupMethods
|
41
|
+
end
|