therubyracer 0.7.0 → 0.7.1.pre
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of therubyracer might be problematic. Click here for more details.
- data/README.rdoc +1 -0
- data/Rakefile +2 -2
- data/contrib/v8/jasmine.rb +22 -0
- data/contrib/v8/jasmine/context.rb +13 -0
- data/contrib/v8/jasmine/jasmine-0.10.3.js +2331 -0
- data/contrib/v8/jasmine/window.js +11 -0
- data/ext/v8/extconf.rb +5 -2
- data/ext/v8/v8_func.cpp +23 -40
- data/ext/v8/v8_obj.cpp +5 -0
- data/ext/v8/v8_script.cpp +9 -0
- data/ext/v8/v8_str.cpp +2 -1
- data/ext/v8/v8_try_catch.cpp +1 -0
- data/ext/v8/v8_value.cpp +4 -0
- data/lib/v8.rb +1 -1
- data/lib/v8/array.rb +4 -2
- data/lib/v8/function.rb +12 -2
- data/lib/v8/object.rb +21 -5
- data/lib/v8/to.rb +1 -1
- data/spec/contrib/v8/jasmine_spec.rb +38 -0
- data/spec/ext/func_spec.rb +5 -5
- data/spec/redjs/jsapi_spec.rb +44 -10
- data/spec/spec_helper.rb +1 -0
- data/therubyracer.gemspec +5 -5
- metadata +18 -9
data/ext/v8/extconf.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mkmf'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
UPSTREAM = File.expand_path(File.dirname(__FILE__) + "/upstream")
|
4
5
|
BUILD = "#{UPSTREAM}/build/v8"
|
@@ -8,8 +9,7 @@ puts "Compiling V8"
|
|
8
9
|
|
9
10
|
system("cd #{UPSTREAM} && make") or raise "Error compiling V8"
|
10
11
|
|
11
|
-
|
12
|
-
have_library('v8') or raise "Unable to find libv8 in #{BUILD}, was there an error compiling it?"
|
12
|
+
find_header('v8.h', "#{BUILD}/include")
|
13
13
|
have_library('pthread')
|
14
14
|
have_library('objc') if RUBY_PLATFORM =~ /darwin/
|
15
15
|
|
@@ -17,6 +17,9 @@ $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
|
|
17
17
|
$CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
|
18
18
|
$CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
19
19
|
|
20
|
+
$DEFLIBPATH.unshift(BUILD)
|
21
|
+
$LIBS << ' -lv8'
|
22
|
+
|
20
23
|
CONFIG['LDSHARED'] = '$(CXX) -shared' unless RUBY_PLATFORM =~ /darwin/
|
21
24
|
|
22
25
|
create_makefile('v8')
|
data/ext/v8/v8_func.cpp
CHANGED
@@ -13,50 +13,33 @@ namespace {
|
|
13
13
|
Local<Function> unwrap(VALUE value) {
|
14
14
|
return V8_Ref_Get<Function>(value);
|
15
15
|
}
|
16
|
-
VALUE Call(
|
16
|
+
VALUE Call(VALUE self, VALUE recv, VALUE arguments) {
|
17
17
|
HandleScope handles;
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Local<Function> function = V8_Ref_Get<Function>(self);
|
22
|
-
Local<Object> thisObject;
|
23
|
-
if (NIL_P(recv)) {
|
24
|
-
if (Context::InContext()) {
|
25
|
-
thisObject = Context::GetEntered()->Global();
|
26
|
-
} else {
|
27
|
-
Persistent<Context> cxt = Context::New();
|
28
|
-
Context::Scope scope(cxt);
|
29
|
-
thisObject = Object::New();
|
30
|
-
cxt.Dispose();
|
31
|
-
}
|
32
|
-
} else {
|
33
|
-
if (!Context::InContext()) {
|
34
|
-
Persistent<Context> cxt = Context::New();
|
35
|
-
cxt->Enter();
|
36
|
-
thisObject = rr_rb2v8(recv)->ToObject();
|
37
|
-
cxt->Exit();
|
38
|
-
} else {
|
39
|
-
thisObject = rr_rb2v8(recv)->ToObject();
|
40
|
-
}
|
18
|
+
if (!Context::InContext()) {
|
19
|
+
rb_raise(rb_eScriptError, "no open V8 Context in V8::C::Function::Call()");
|
20
|
+
return Qnil;
|
41
21
|
}
|
42
|
-
|
43
|
-
Local<
|
44
|
-
|
45
|
-
|
22
|
+
Local<Function> function = unwrap(self);
|
23
|
+
Local<Object> thisObj = rr_rb2v8(recv)->ToObject();
|
24
|
+
Handle<Array> args = V8_Ref_Get<Array>(arguments);
|
25
|
+
int argc = args->Length();
|
26
|
+
Handle<Value> argv[argc];
|
27
|
+
for (int i = 0; i < argc; i++) {
|
28
|
+
argv[i] = args->Get(i);
|
46
29
|
}
|
47
|
-
|
48
|
-
return rr_v82rb(result);
|
30
|
+
return rr_v82rb(function->Call(thisObj, argc, argv));
|
49
31
|
}
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
32
|
+
|
33
|
+
VALUE NewInstance(VALUE self, VALUE arguments) {
|
34
|
+
HandleScope scope;
|
35
|
+
Local<Function> function = unwrap(self);
|
36
|
+
Handle<Array> args = V8_Ref_Get<Array>(arguments);
|
37
|
+
int argc = args->Length();
|
38
|
+
Handle<Value> argv[argc];
|
56
39
|
for (int i = 0; i < argc; i++) {
|
57
|
-
|
40
|
+
argv[i] = args->Get(i);
|
58
41
|
}
|
59
|
-
return rr_v82rb(function->NewInstance(argc,
|
42
|
+
return rr_v82rb(function->NewInstance(argc, argv));
|
60
43
|
}
|
61
44
|
VALUE GetName(VALUE self) {
|
62
45
|
return rr_v82rb(unwrap(self)->GetName());
|
@@ -73,8 +56,8 @@ namespace {
|
|
73
56
|
|
74
57
|
void rr_init_func() {
|
75
58
|
FunctionClass = rr_define_class("Function", rr_cV8_C_Object);
|
76
|
-
rr_define_method(FunctionClass, "Call", Call,
|
77
|
-
rr_define_method(FunctionClass, "NewInstance", NewInstance,
|
59
|
+
rr_define_method(FunctionClass, "Call", Call, 2);
|
60
|
+
rr_define_method(FunctionClass, "NewInstance", NewInstance, 1);
|
78
61
|
rr_define_method(FunctionClass, "GetName", GetName, 0);
|
79
62
|
rr_define_method(FunctionClass, "SetName", SetName, 1);
|
80
63
|
// rr_define_method(FunctionClass, "GetScriptOrigin", GetScriptOrigin, 0);
|
data/ext/v8/v8_obj.cpp
CHANGED
@@ -63,6 +63,10 @@ namespace {
|
|
63
63
|
// rr_v8_ref_setref(self, "RubyPeer", )
|
64
64
|
return Qnil;
|
65
65
|
}
|
66
|
+
VALUE GetHiddenValue(VALUE self, VALUE key) {
|
67
|
+
HandleScope scope;
|
68
|
+
return rr_v82rb(unwrap(self)->GetHiddenValue(rr_rb2v8(key)->ToString()));
|
69
|
+
}
|
66
70
|
}
|
67
71
|
|
68
72
|
void rr_init_obj() {
|
@@ -72,6 +76,7 @@ void rr_init_obj() {
|
|
72
76
|
rr_define_method(rr_cV8_C_Object, "Get", Get, 1);
|
73
77
|
rr_define_method(rr_cV8_C_Object, "Set", Set, 2);
|
74
78
|
rr_define_method(rr_cV8_C_Object, "GetPropertyNames", GetPropertyNames, 0);
|
79
|
+
rr_define_method(rr_cV8_C_Object, "GetHiddenValue", GetHiddenValue, 1);
|
75
80
|
rr_define_method(rr_cV8_C_Object, "SetHiddenValue", SetHiddenValue, 2);
|
76
81
|
}
|
77
82
|
|
data/ext/v8/v8_script.cpp
CHANGED
@@ -6,6 +6,14 @@
|
|
6
6
|
using namespace v8;
|
7
7
|
|
8
8
|
namespace {
|
9
|
+
|
10
|
+
VALUE New(VALUE self, VALUE source, VALUE source_name) {
|
11
|
+
HandleScope scope;
|
12
|
+
Local<String> src(rr_rb2v8(source)->ToString());
|
13
|
+
Local<String> src_name(rr_rb2v8(source_name)->ToString());
|
14
|
+
return rr_v8_ref_create(self, Script::Compile(src, src_name));
|
15
|
+
}
|
16
|
+
|
9
17
|
VALUE Compile(VALUE self, VALUE source, VALUE source_name) {
|
10
18
|
Local<String> src(rr_rb2v8(source)->ToString());
|
11
19
|
Local<String> src_name(rr_rb2v8(source_name)->ToString());
|
@@ -21,6 +29,7 @@ namespace {
|
|
21
29
|
|
22
30
|
void rr_init_script() {
|
23
31
|
VALUE ScriptClass = rr_define_class("Script");
|
32
|
+
rr_define_singleton_method(ScriptClass, "New", New, 2);
|
24
33
|
rr_define_singleton_method(ScriptClass, "Compile", Compile, 2);
|
25
34
|
rr_define_method(ScriptClass, "Run", Run, 0);
|
26
35
|
rb_funcall(ScriptClass, rb_intern("private_class_method"), 1, rb_str_new2("new"));
|
data/ext/v8/v8_str.cpp
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
#include "v8_str.h"
|
3
3
|
#include "v8.h"
|
4
4
|
#include "v8_ref.h"
|
5
|
+
#include "v8_value.h"
|
5
6
|
|
6
7
|
using namespace v8;
|
7
8
|
|
@@ -37,7 +38,7 @@ VALUE rr_reflect_v8_string(Handle<Value> value) {
|
|
37
38
|
}
|
38
39
|
|
39
40
|
void rr_init_str() {
|
40
|
-
StringClass = rr_define_class("String");
|
41
|
+
StringClass = rr_define_class("String", rr_cV8_C_Value);
|
41
42
|
rr_define_singleton_method(StringClass, "New", New, 1);
|
42
43
|
rr_define_method(StringClass, "Utf8Value", Utf8Value, 0);
|
43
44
|
rr_define_method(StringClass, "Utf16Value", Utf16Value, 0);
|
data/ext/v8/v8_try_catch.cpp
CHANGED
data/ext/v8/v8_value.cpp
CHANGED
@@ -7,6 +7,9 @@ namespace {
|
|
7
7
|
Local<Value> unwrap(VALUE value) {
|
8
8
|
return V8_Ref_Get<Value>(value);
|
9
9
|
}
|
10
|
+
VALUE IsEmpty(VALUE value) {
|
11
|
+
return value == rr_cV8_C_Empty ? Qtrue : Qfalse;
|
12
|
+
}
|
10
13
|
VALUE IsUndefined(VALUE self) {
|
11
14
|
HandleScope scope;
|
12
15
|
return rr_v82rb(unwrap(self)->IsUndefined());
|
@@ -128,6 +131,7 @@ void rr_init_value() {
|
|
128
131
|
rr_cV8_C_Value = rr_define_class("Value");
|
129
132
|
rr_cV8_C_Empty = rr_define_const("Empty", rr_v8_ref_create(rr_cV8_C_Value, Handle<Value>()));
|
130
133
|
|
134
|
+
rr_define_method(rr_cV8_C_Value, "IsEmpty", IsEmpty, 0);
|
131
135
|
rr_define_method(rr_cV8_C_Value, "IsUndefined", IsUndefined, 0);
|
132
136
|
rr_define_method(rr_cV8_C_Value, "IsNull", IsNull, 0);
|
133
137
|
rr_define_method(rr_cV8_C_Value, "IsTrue", IsTrue, 0);
|
data/lib/v8.rb
CHANGED
data/lib/v8/array.rb
CHANGED
data/lib/v8/function.rb
CHANGED
@@ -1,19 +1,29 @@
|
|
1
1
|
module V8
|
2
2
|
class Function < V8::Object
|
3
3
|
|
4
|
-
def
|
4
|
+
def methodcall(thisObject, *args)
|
5
5
|
err = nil
|
6
6
|
return_value = nil
|
7
7
|
C::TryCatch.try do |try|
|
8
8
|
@context.enter do
|
9
9
|
this = To.v8(thisObject)
|
10
|
-
return_value = To.ruby(@native.Call(this,
|
10
|
+
return_value = To.ruby(@native.Call(this, To.v8(args)))
|
11
11
|
err = JavascriptError.new(try) if try.HasCaught()
|
12
12
|
end
|
13
13
|
end
|
14
14
|
raise err if err
|
15
15
|
return return_value
|
16
16
|
end
|
17
|
+
|
18
|
+
def call(*args)
|
19
|
+
self.methodcall(@context.Global(), *args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def new(*args)
|
23
|
+
@context.enter do
|
24
|
+
To.rb(@native.NewInstance(To.v8(args)))
|
25
|
+
end
|
26
|
+
end
|
17
27
|
|
18
28
|
def self.rubycall(rubycode, *args)
|
19
29
|
begin
|
data/lib/v8/object.rb
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
module V8
|
3
3
|
class Object
|
4
4
|
include Enumerable
|
5
|
-
|
5
|
+
|
6
6
|
def initialize(native, context = nil)
|
7
7
|
@native = native
|
8
8
|
@context = context || C::Context::GetEntered()
|
9
9
|
raise ScriptError, "V8::Object.new called without an open V8 context" unless @context
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def [](key)
|
13
13
|
@context.enter do
|
14
14
|
To.ruby(@native.Get(To.v8(key)))
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def []=(key, value)
|
19
19
|
value.tap do
|
20
20
|
@context.enter do
|
@@ -22,13 +22,13 @@ module V8
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def to_s
|
27
27
|
@context.enter do
|
28
28
|
To.rb(@native.ToString())
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def each
|
33
33
|
@context.enter do
|
34
34
|
for prop in To.rb(@native.GetPropertyNames())
|
@@ -36,6 +36,22 @@ module V8
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
39
|
+
|
40
|
+
def respond_to?(method)
|
41
|
+
self[method] != nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(name, *args, &block)
|
45
|
+
return super(name, *args, &block) unless self.respond_to?(name)
|
46
|
+
property = self[name]
|
47
|
+
if property.kind_of?(V8::Function)
|
48
|
+
property.methodcall(self, *args)
|
49
|
+
elsif args.empty?
|
50
|
+
property
|
51
|
+
else
|
52
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
|
53
|
+
end
|
54
|
+
end
|
39
55
|
end
|
40
56
|
end
|
41
57
|
|
data/lib/v8/to.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
require 'v8/jasmine'
|
4
|
+
|
5
|
+
describe V8::Jasmine do
|
6
|
+
|
7
|
+
it "cannot be included" do
|
8
|
+
lambda {
|
9
|
+
Class.new.send(:include, V8::Jasmine)
|
10
|
+
}.should raise_error(ScriptError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can only be used to extend V8::Context objecs" do
|
14
|
+
lambda {
|
15
|
+
V8::Context.new.extend(V8::Jasmine)
|
16
|
+
}.should_not raise_error
|
17
|
+
|
18
|
+
lambda {
|
19
|
+
Object.new.extend(V8::Jasmine)
|
20
|
+
}.should raise_error(ScriptError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "extends a bare context with the jasmine runtime" do
|
24
|
+
V8::Context.new do |cxt|
|
25
|
+
cxt.extend V8::Jasmine
|
26
|
+
cxt['jasmine'].getEnv().should_not be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe V8::Jasmine::Context do
|
32
|
+
|
33
|
+
it "comes pre-bundled with jasmine" do
|
34
|
+
V8::Jasmine::Context.new do |cxt|
|
35
|
+
cxt['jasmine'].should_not be_nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/ext/func_spec.rb
CHANGED
@@ -6,21 +6,21 @@ describe C::Function do
|
|
6
6
|
it "is callable" do
|
7
7
|
Context.new do |cxt|
|
8
8
|
f = cxt.eval('(function() {return "Hello World"})', '<eval>');
|
9
|
-
f.call(
|
9
|
+
f.call().should == "Hello World"
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
it "receives proper argument length from ruby" do
|
14
14
|
Context.new do |cxt|
|
15
15
|
f = cxt.eval('(function() {return arguments.length})', 'eval')
|
16
|
-
f.call(
|
16
|
+
f.call(1, 2, 3).should == 3
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
it "maps all arguments from ruby" do
|
21
21
|
Context.new do |cxt|
|
22
22
|
f = cxt.eval('(function(one, two, three) {return one + two + three})', 'eval')
|
23
|
-
f.call(
|
23
|
+
f.call(1,2,3).should == 6
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -28,7 +28,7 @@ describe C::Function do
|
|
28
28
|
Context.new do |cxt|
|
29
29
|
Object.new.tap do |this|
|
30
30
|
f = cxt.eval('(function() {return this})', 'eval')
|
31
|
-
f.
|
31
|
+
f.methodcall(this).should be(this)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -37,7 +37,7 @@ describe C::Function do
|
|
37
37
|
Context.new do |cxt|
|
38
38
|
@f = cxt.eval('(function() {return "Call Me"})', 'eval')
|
39
39
|
end
|
40
|
-
@f.call(
|
40
|
+
@f.call().should == "Call Me"
|
41
41
|
end
|
42
42
|
|
43
43
|
it "is reflected properly" do
|
data/spec/redjs/jsapi_spec.rb
CHANGED
@@ -81,6 +81,15 @@ describe "Ruby Javascript API" do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
it "can iterate over arrays" do
|
85
|
+
@cxt['a'] = @cxt.eval('[{num: 1},{num:2},{num:3},{num: 4}]')
|
86
|
+
a = @cxt['a']
|
87
|
+
a.inject(0) do |sum, item|
|
88
|
+
sum + item['num']
|
89
|
+
end.should == 10
|
90
|
+
|
91
|
+
end
|
92
|
+
|
84
93
|
it "converts ruby hashes to javascript objects" do
|
85
94
|
@cxt['h'] = {:foo => 'bar', :baz => 'bang', :bar => {'hello' => 'world'}}
|
86
95
|
@cxt['h']['foo'].should == 'bar'
|
@@ -278,16 +287,46 @@ describe "Ruby Javascript API" do
|
|
278
287
|
before(:each) do
|
279
288
|
@cxt = Context.new
|
280
289
|
end
|
281
|
-
|
290
|
+
|
282
291
|
it "allows you to capture a reference to a javascript function and call it" do
|
283
292
|
f = @cxt.eval('(function add(lhs, rhs) {return lhs + rhs})')
|
284
|
-
f.call(
|
293
|
+
f.call(1,2).should == 3
|
294
|
+
end
|
295
|
+
|
296
|
+
it "can path the 'this' object into a function as context with methodcall()" do
|
297
|
+
obj = @cxt.eval('({num: 5})')
|
298
|
+
times = @cxt.eval('(function times(num) {return this.num * num})')
|
299
|
+
times.methodcall(obj, 5).should == 25
|
285
300
|
end
|
286
301
|
|
287
302
|
it "unwraps objects that are backed by javascript objects to pass their native equivalents" do |cxt|
|
288
303
|
@cxt.eval('obj = {foo: "bar"}')
|
289
304
|
f = @cxt.eval('(function() {return this == obj})')
|
290
|
-
f.
|
305
|
+
f.methodcall(@cxt['obj']).should be(true)
|
306
|
+
end
|
307
|
+
|
308
|
+
it "can invoke a javacript constructor and return the new object reflected into ruby" do
|
309
|
+
wrapper = @cxt.eval('(function Wrapper(value) {this.value = value})')
|
310
|
+
wrapper.new(5)['value'].should == 5
|
311
|
+
end
|
312
|
+
|
313
|
+
it "can call a javascript method directly from a ruby object" do
|
314
|
+
obj = @cxt.eval('Object').new
|
315
|
+
obj.should respond_to(:toString)
|
316
|
+
obj.toString().should == '[object Object]'
|
317
|
+
end
|
318
|
+
|
319
|
+
it "can access properties defined on a javascript object through ruby" do
|
320
|
+
obj = @cxt.eval('({str: "bar", num: 5})')
|
321
|
+
obj.str.should == "bar"
|
322
|
+
obj.num.should == 5
|
323
|
+
end
|
324
|
+
|
325
|
+
it "is an error to try and pass parameters to a property" do
|
326
|
+
obj = @cxt.eval('({num: 1})')
|
327
|
+
lambda {
|
328
|
+
obj.num(5)
|
329
|
+
}.should raise_error(ArgumentError)
|
291
330
|
end
|
292
331
|
end
|
293
332
|
|
@@ -492,13 +531,8 @@ end
|
|
492
531
|
cxt.eval('throw "BOOM!"', "three.js")
|
493
532
|
end
|
494
533
|
lambda {
|
495
|
-
|
496
|
-
|
497
|
-
rescue JavascriptError => e
|
498
|
-
rputs e.backtrace
|
499
|
-
raise e
|
500
|
-
end
|
501
|
-
}.should raise_error(JavascriptError) {|e|
|
534
|
+
cxt['one'].call(cxt.scope)
|
535
|
+
}.should raise_error {|e|
|
502
536
|
#TODO: assert something about the contents of the stack?
|
503
537
|
#--cowboyd 05/25/2010
|
504
538
|
}
|