tomato 0.0.1.prealpha1 → 0.0.1.prealpha2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +115 -31
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/ext/tomato/Makefile +32 -37
- data/ext/tomato/binding_methods.cpp +47 -20
- data/ext/tomato/binding_methods.h +8 -0
- data/ext/tomato/binding_methods.o +0 -0
- data/ext/tomato/conversions_to_js.cpp +15 -61
- data/ext/tomato/conversions_to_js.o +0 -0
- data/ext/tomato/conversions_to_rb.cpp +31 -8
- data/ext/tomato/conversions_to_rb.o +0 -0
- data/ext/tomato/depend +2 -1
- data/ext/tomato/errors.o +0 -0
- data/ext/tomato/extconf.rb +1 -1
- data/ext/tomato/mkmf.log +5 -5
- data/ext/tomato/object_chain.cpp +241 -0
- data/ext/tomato/object_chain.o +0 -0
- data/ext/tomato/tomato.bundle +0 -0
- data/ext/tomato/tomato.cpp +22 -2
- data/ext/tomato/tomato.h +5 -1
- data/ext/tomato/tomato.o +0 -0
- data/ext/tomato/v8.o +0 -0
- data/lib/tomato.rb +104 -2
- data/pkg/tomato-0.0.1.prealpha1.gem +0 -0
- data/spec/lib/bound_class_spec.rb +48 -0
- data/spec/lib/bound_methods_spec.rb +20 -0
- data/spec/lib/bound_object_spec.rb +33 -0
- data/spec/lib/conversions_spec.rb +16 -37
- data/t.rb +6 -0
- data/tomato.gemspec +14 -3
- metadata +30 -6
data/README.rdoc
CHANGED
@@ -20,44 +20,105 @@ hope to release it as a real gem after it's reached a sufficient level of functi
|
|
20
20
|
When JS code is executed, it'll do its thing internally and then return the result:
|
21
21
|
|
22
22
|
tomato.run("(1+1);") # => 2
|
23
|
+
|
24
|
+
==== Bindings
|
25
|
+
|
26
|
+
You can bind Ruby methods to JavaScript, as well. You can bind an instance method of Tomato like so:
|
27
|
+
|
28
|
+
tomato.bind_method(:inspect)
|
29
|
+
tomato.run("inspect();") #=> "#<Tomato>"
|
23
30
|
|
24
|
-
|
31
|
+
Or, perhaps more usefully, you can bind an instance method of some other object:
|
25
32
|
|
26
|
-
tomato.
|
33
|
+
tomato.bind_method(:inspect, 5)
|
34
|
+
tomato.run("inspect();") #=> "5"
|
27
35
|
|
36
|
+
It's also easy to bind methods to arbitrary JavaScript objects. If a JS object in the chain doesn't exist, Tomato will
|
37
|
+
silently generate it for you on-the-fly:
|
38
|
+
|
39
|
+
|
40
|
+
def say(something)
|
41
|
+
puts something
|
42
|
+
end
|
43
|
+
|
44
|
+
tomato.bind_method(:say, self, :to => "person.mouth")
|
45
|
+
|
46
|
+
# Tomato generated both the "person" and "mouth" objects for us:
|
47
|
+
tomato.run "person.mouth.say('Hello there');"
|
48
|
+
|
28
49
|
# Produces:
|
29
|
-
#
|
30
|
-
# Tomato::Error: (dynamic):error
|
31
|
-
# throw 'error';
|
32
|
-
# ^
|
33
50
|
#
|
34
|
-
#
|
35
|
-
#
|
51
|
+
# Hello there
|
52
|
+
# => nil
|
53
|
+
|
54
|
+
puts tomato.run("this")
|
36
55
|
|
37
|
-
|
38
|
-
|
39
|
-
|
56
|
+
# Produces:
|
57
|
+
#
|
58
|
+
# {"person":{"mouth":{}}}
|
59
|
+
# => nil
|
40
60
|
|
41
|
-
|
42
|
-
|
61
|
+
In the spirit of true Ruby dynamicism, (is that a word?), you can feel free to re-bind new methods over old ones:
|
62
|
+
|
63
|
+
tomato.bind_method(:say)
|
64
|
+
tomato.run("say('something');")
|
65
|
+
#=> error! say is not an instance method of Tomato
|
43
66
|
|
44
|
-
|
67
|
+
tomato.bind_method(:say, self)
|
68
|
+
def say(something); puts something; end
|
69
|
+
tomato.run("say('something');")
|
70
|
+
#=> something
|
71
|
+
|
72
|
+
You can also easily bind an entire object to JavaScript:
|
45
73
|
|
46
|
-
|
47
|
-
|
48
|
-
|
74
|
+
# By default objects are mapped to 'ruby.[object_class_name]'
|
75
|
+
tomato.bind_object(Time.now)
|
76
|
+
tomato.run("ruby.time.to_s();")
|
77
|
+
#=> "2010-06-25 18:12:23 -0400"
|
78
|
+
|
79
|
+
# Or give it an object name or chain of names:
|
80
|
+
tomato.bind_object(Time.now, "time.current")
|
81
|
+
tomato.run("time.current.to_s();")
|
82
|
+
#=> "2010-06-25 18:12:23 -0400"
|
83
|
+
|
84
|
+
Even better, you can also bind a whole class and instantiate it from within JavaScript. If you return the object to
|
85
|
+
Ruby, it'll be seamlessly converted into the corresponding Ruby object.
|
86
|
+
|
87
|
+
class Person
|
88
|
+
def initialize(name)
|
89
|
+
@name = name
|
90
|
+
end
|
91
|
+
attr_accessor :name, :age
|
49
92
|
end
|
50
|
-
|
93
|
+
|
94
|
+
tomato.bind_object(Person, "world.Person")
|
95
|
+
tomato.run <<-end_js
|
96
|
+
var colin = new world.Person("Colin");
|
97
|
+
colin.age = 25;
|
98
|
+
colin;
|
99
|
+
end_js
|
100
|
+
|
101
|
+
#=> #<Person:0x00000100dbbc50 @name="Colin", @age=25>
|
102
|
+
|
103
|
+
==== Error Handling
|
104
|
+
|
105
|
+
When JS code encounters an error of some kind, it'll get raised in Ruby:
|
106
|
+
|
107
|
+
tomato.run("throw 'error';")
|
51
108
|
|
52
109
|
# Produces:
|
110
|
+
#
|
111
|
+
# Tomato::Error: (dynamic):error
|
112
|
+
# throw 'error';
|
113
|
+
# ^
|
53
114
|
#
|
54
|
-
#
|
55
|
-
#
|
115
|
+
# from (irb):3:in `run'
|
116
|
+
# from (irb):3
|
56
117
|
|
57
118
|
You can also catch Ruby errors in JavaScript:
|
58
119
|
|
59
|
-
tomato.bind_method(:raise_err)
|
60
|
-
def
|
120
|
+
tomato.bind_method(:raise_err, self)
|
121
|
+
def raise_err
|
61
122
|
raise ArgumentError, "Not what I meant!"
|
62
123
|
end
|
63
124
|
tomato.run("try { raise_err(); } catch(e) {}");
|
@@ -68,8 +129,8 @@ You can also catch Ruby errors in JavaScript:
|
|
68
129
|
|
69
130
|
Or let them bubble up to Ruby:
|
70
131
|
|
71
|
-
tomato.bind_method(:raise_err)
|
72
|
-
def
|
132
|
+
tomato.bind_method(:raise_err, self)
|
133
|
+
def raise_err
|
73
134
|
raise ArgumentError, "Not what I meant!"
|
74
135
|
end
|
75
136
|
begin
|
@@ -83,8 +144,6 @@ Or let them bubble up to Ruby:
|
|
83
144
|
# Error: Not what I meant!
|
84
145
|
# => nil
|
85
146
|
|
86
|
-
That's all I've implemented so far, but I think it looks pretty cool.
|
87
|
-
|
88
147
|
=== Object Types
|
89
148
|
|
90
149
|
Some objects in Ruby don't exist in JS. So far the ones I've implemented are Hashes and Symbols.
|
@@ -92,7 +151,17 @@ Some objects in Ruby don't exist in JS. So far the ones I've implemented are Has
|
|
92
151
|
==== Symbols
|
93
152
|
|
94
153
|
A Symbol is converted to an object in JS with a "symbol" attribute and a "toString()" method. That same object, if
|
95
|
-
returned to Ruby, is converted back to a Symbol.
|
154
|
+
returned to Ruby, is converted back to a Symbol. Examples:
|
155
|
+
|
156
|
+
def fetch_a_symbol
|
157
|
+
:a_symbol
|
158
|
+
end
|
159
|
+
|
160
|
+
t = Tomato.new
|
161
|
+
t.bind_method(:fetch_a_symbol, self)
|
162
|
+
t.run("fetch_a_symbol()") #=> :a_symbol
|
163
|
+
t.run("fetch_a_symbol().symbol") #=> "a_symbol"
|
164
|
+
t.run("fetch_a_symbol().toString()") #=> "a_symbol"
|
96
165
|
|
97
166
|
|
98
167
|
==== Hashes
|
@@ -127,11 +196,26 @@ That should be all there is to it.
|
|
127
196
|
|
128
197
|
== Performance
|
129
198
|
|
130
|
-
|
199
|
+
ruby-1.9.1-p378 > javascript = <<-end_js
|
200
|
+
ruby-1.9.1-p378"> var str = "";
|
201
|
+
ruby-1.9.1-p378"> for (var i = 0; i < 1000000; i++)
|
202
|
+
ruby-1.9.1-p378"> str = 'a';
|
203
|
+
ruby-1.9.1-p378"> end_js
|
204
|
+
ruby-1.9.1-p378 > require 'benchmark'
|
205
|
+
ruby-1.9.1-p378 > require 'tomato'
|
206
|
+
ruby-1.9.1-p378 > t = Tomato.new
|
207
|
+
ruby-1.9.1-p378 >
|
208
|
+
ruby-1.9.1-p378 > # Setup complete, let's start the test
|
209
|
+
ruby-1.9.1-p378 > puts Benchmark.measure { for i in 1..1000000; str = 'a'; end }
|
210
|
+
ruby-1.9.1-p378 > 0.350000 0.000000 0.350000 ( 0.358106)
|
211
|
+
ruby-1.9.1-p378 >
|
212
|
+
ruby-1.9.1-p378 > puts Benchmark.measure { t.run(javascript) }
|
213
|
+
ruby-1.9.1-p378 > 0.010000 0.000000 0.010000 ( 0.012603)
|
214
|
+
|
215
|
+
'Nuff said.
|
131
216
|
|
132
|
-
Since V8's JavaScript code is compiled into byte code, this
|
133
|
-
in some applications. Just translate your code to JavaScript and let
|
134
|
-
cool results.
|
217
|
+
Since V8's JavaScript code is compiled into byte code, this can have a positive impact on performance
|
218
|
+
in some applications. Just translate your code to JavaScript and let Tomato compile it into byte code.
|
135
219
|
|
136
220
|
== Note on Patches/Pull Requests
|
137
221
|
|
data/Rakefile
CHANGED
@@ -11,6 +11,7 @@ begin
|
|
11
11
|
gem.email = "sinisterchipmunk@gmail.com"
|
12
12
|
gem.homepage = "http://www.thoughtsincomputation.com"
|
13
13
|
gem.authors = ["Colin MacKenzie IV"]
|
14
|
+
gem.add_dependency "sc-core-ext", ">= 1.2.0"
|
14
15
|
gem.add_development_dependency "rspec", ">= 1.3.0"
|
15
16
|
gem.files = FileList['**/*']
|
16
17
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.1.
|
1
|
+
0.0.1.prealpha2
|
data/ext/tomato/Makefile
CHANGED
@@ -4,19 +4,17 @@ SHELL = /bin/sh
|
|
4
4
|
#### Start of system configuration section. ####
|
5
5
|
|
6
6
|
srcdir = .
|
7
|
-
topdir = /Users/colin/.rvm/rubies/ruby-1.9.
|
8
|
-
hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.
|
9
|
-
arch_hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.
|
7
|
+
topdir = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1
|
8
|
+
hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1
|
9
|
+
arch_hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/$(arch)
|
10
10
|
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
11
|
-
prefix = $(DESTDIR)/Users/colin/.rvm/rubies/ruby-1.9.
|
12
|
-
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
11
|
+
prefix = $(DESTDIR)/Users/colin/.rvm/rubies/ruby-1.9.1-p378
|
13
12
|
exec_prefix = $(prefix)
|
14
13
|
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
15
14
|
sitehdrdir = $(rubyhdrdir)/site_ruby
|
16
|
-
rubyhdrdir = $(includedir)/$(
|
17
|
-
vendordir = $(
|
18
|
-
sitedir = $(
|
19
|
-
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
15
|
+
rubyhdrdir = $(includedir)/$(RUBY_INSTALL_NAME)-$(ruby_version)
|
16
|
+
vendordir = $(libdir)/$(RUBY_INSTALL_NAME)/vendor_ruby
|
17
|
+
sitedir = $(libdir)/$(RUBY_INSTALL_NAME)/site_ruby
|
20
18
|
mandir = $(datarootdir)/man
|
21
19
|
localedir = $(datarootdir)/locale
|
22
20
|
libdir = $(exec_prefix)/lib
|
@@ -36,7 +34,7 @@ datarootdir = $(prefix)/share
|
|
36
34
|
libexecdir = $(exec_prefix)/libexec
|
37
35
|
sbindir = $(exec_prefix)/sbin
|
38
36
|
bindir = $(exec_prefix)/bin
|
39
|
-
rubylibdir = $(
|
37
|
+
rubylibdir = $(libdir)/$(ruby_install_name)/$(ruby_version)
|
40
38
|
archdir = $(rubylibdir)/$(arch)
|
41
39
|
sitelibdir = $(sitedir)/$(ruby_version)
|
42
40
|
sitearchdir = $(sitelibdir)/$(sitearch)
|
@@ -53,35 +51,34 @@ OUTFLAG = -o
|
|
53
51
|
COUTFLAG = -o
|
54
52
|
|
55
53
|
RUBY_EXTCONF_H =
|
56
|
-
cflags =
|
57
|
-
optflags = -
|
58
|
-
debugflags = -
|
59
|
-
warnflags = -
|
60
|
-
CFLAGS =
|
54
|
+
cflags = $(optflags) $(debugflags) $(warnflags)
|
55
|
+
optflags = -O2
|
56
|
+
debugflags = -g
|
57
|
+
warnflags = -Wall -Wno-parentheses
|
58
|
+
CFLAGS = -fno-common $(cflags) -fno-common -pipe -fno-common
|
61
59
|
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include
|
62
60
|
DEFS =
|
63
|
-
CPPFLAGS =
|
64
|
-
CXXFLAGS = $(CFLAGS)
|
61
|
+
CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags) -Wall -g -rdynamic
|
62
|
+
CXXFLAGS = $(CFLAGS) $(cxxflags)
|
65
63
|
ldflags = -L. -L/usr/local/lib
|
66
|
-
dldflags =
|
67
|
-
|
68
|
-
DLDFLAGS = $(ldflags) $(dldflags)
|
69
|
-
LDSHARED =
|
70
|
-
LDSHAREDXX = $(
|
64
|
+
dldflags =
|
65
|
+
archflag =
|
66
|
+
DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
|
67
|
+
LDSHARED = cc -dynamic -bundle -undefined suppress -flat_namespace
|
68
|
+
LDSHAREDXX = $(LDSHARED)
|
71
69
|
AR = ar
|
72
70
|
EXEEXT =
|
73
71
|
|
74
|
-
RUBY_BASE_NAME = ruby
|
75
72
|
RUBY_INSTALL_NAME = ruby
|
76
|
-
RUBY_SO_NAME = ruby
|
77
|
-
arch =
|
78
|
-
sitearch =
|
73
|
+
RUBY_SO_NAME = ruby
|
74
|
+
arch = i386-darwin10.4.0
|
75
|
+
sitearch = i386-darwin10.4.0
|
79
76
|
ruby_version = 1.9.1
|
80
|
-
ruby = /Users/colin/.rvm/rubies/ruby-1.9.
|
77
|
+
ruby = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/bin/ruby
|
81
78
|
RUBY = $(ruby)
|
82
79
|
RM = rm -f
|
83
80
|
RM_RF = $(RUBY) -run -e rm -- -rf
|
84
|
-
RMDIRS = rmdir -p
|
81
|
+
RMDIRS = $(RUBY) -run -e rmdir -- -p
|
85
82
|
MAKEDIRS = mkdir -p
|
86
83
|
INSTALL = /usr/bin/install -c
|
87
84
|
INSTALL_PROG = $(INSTALL) -m 0755
|
@@ -104,9 +101,9 @@ extout =
|
|
104
101
|
extout_prefix =
|
105
102
|
target_prefix =
|
106
103
|
LOCAL_LIBS =
|
107
|
-
LIBS = $(LIBRUBYARG_SHARED) -lobjc -lpthread -lpthread -ldl -lobjc
|
108
|
-
SRCS = binding_methods.cpp conversions_to_js.cpp conversions_to_rb.cpp errors.cpp tomato.cpp v8.cpp
|
109
|
-
OBJS = binding_methods.o conversions_to_js.o conversions_to_rb.o errors.o tomato.o v8.o
|
104
|
+
LIBS = $(LIBRUBYARG_SHARED) -lobjc -lpthread -lpthread -ldl -lobjc -lv8
|
105
|
+
SRCS = binding_methods.cpp conversions_to_js.cpp conversions_to_rb.cpp errors.cpp object_chain.cpp tomato.cpp v8.cpp
|
106
|
+
OBJS = binding_methods.o conversions_to_js.o conversions_to_rb.o errors.o object_chain.o tomato.o v8.o
|
110
107
|
TARGET = tomato
|
111
108
|
DLLIB = $(TARGET).bundle
|
112
109
|
EXTSTATIC =
|
@@ -125,8 +122,6 @@ CLEANOBJS = *.o *.bak
|
|
125
122
|
|
126
123
|
all: $(DLLIB)
|
127
124
|
static: $(STATIC_LIB)
|
128
|
-
.PHONY: all install static install-so install-rb
|
129
|
-
.PHONY: clean clean-so clean-rb
|
130
125
|
|
131
126
|
clean-rb-default::
|
132
127
|
clean-rb::
|
@@ -148,8 +143,7 @@ install: install-so install-rb
|
|
148
143
|
install-so: $(RUBYARCHDIR)
|
149
144
|
install-so: $(RUBYARCHDIR)/$(DLLIB)
|
150
145
|
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
|
151
|
-
|
152
|
-
$(INSTALL_PROG) $(DLLIB) $(@D)
|
146
|
+
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
153
147
|
install-rb: pre-install-rb install-rb-default
|
154
148
|
install-rb-default: pre-install-rb-default
|
155
149
|
pre-install-rb: Makefile
|
@@ -185,14 +179,15 @@ $(DLLIB): $(OBJS) Makefile
|
|
185
179
|
|
186
180
|
|
187
181
|
###
|
188
|
-
binding_methods.o: binding_methods.cpp tomato.h
|
182
|
+
binding_methods.o: binding_methods.cpp tomato.h binding_methods.h
|
189
183
|
conversions_to_js.o: conversions_to_js.cpp tomato.h
|
190
184
|
conversions_to_rb.o: conversions_to_rb.cpp tomato.h
|
191
185
|
errors.o: errors.cpp tomato.h
|
186
|
+
object_chain.o: object_chain.cpp tomato.h binding_methods.h
|
192
187
|
tomato.o: tomato.cpp tomato.h
|
193
188
|
v8.o: v8.cpp tomato.h
|
194
189
|
|
195
190
|
test: all
|
196
191
|
@echo Running specs...
|
197
|
-
spec
|
192
|
+
spec spec -c
|
198
193
|
|
@@ -1,9 +1,7 @@
|
|
1
1
|
#include "tomato.h"
|
2
|
+
#include "binding_methods.h"
|
2
3
|
|
3
4
|
static VALUE bound_method_call(VALUE args);
|
4
|
-
static Handle<Value> bound_method(const Arguments& args);
|
5
|
-
static int store_rb_message(const Arguments &args, V8Tomato **out_tomato, VALUE *out_receiver, ID *out_method_id);
|
6
|
-
|
7
5
|
|
8
6
|
Handle<Value> bound_method(const Arguments& args)
|
9
7
|
{
|
@@ -13,12 +11,17 @@ Handle<Value> bound_method(const Arguments& args)
|
|
13
11
|
ID rb_method_id;
|
14
12
|
V8Tomato *tomato;
|
15
13
|
int code = store_rb_message(args, &tomato, &receiver, &rb_method_id);
|
16
|
-
|
17
|
-
|
14
|
+
switch(code)
|
15
|
+
{
|
16
|
+
case -1: return ThrowException(String::New("Error: _tomato is not an object (BUG: please report)"));
|
17
|
+
case -2: return ThrowException(String::New("Error: _tomato_receiver_index is not an Int32 (BUG: please report)"));
|
18
|
+
case -3: return ThrowException(String::New("Error: _tomato_receiver_index is greater than @receivers.length (BUG: please report)"));
|
19
|
+
};
|
18
20
|
|
19
|
-
VALUE rbargs = rb_ary_new2(2);
|
21
|
+
VALUE rbargs = rb_ary_new2(2+args.Length());
|
20
22
|
rb_ary_store(rbargs, 0, receiver);
|
21
23
|
rb_ary_store(rbargs, 1, ID2SYM(rb_method_id));
|
24
|
+
store_args(tomato, rbargs, args);
|
22
25
|
|
23
26
|
int error;
|
24
27
|
VALUE result = rb_protect(bound_method_call, rbargs, &error);
|
@@ -30,27 +33,50 @@ Handle<Value> bound_method(const Arguments& args)
|
|
30
33
|
return js_value_of(tomato, result);
|
31
34
|
}
|
32
35
|
|
36
|
+
void store_args(V8Tomato *tomato, VALUE rbargs, const Arguments &args)
|
37
|
+
{
|
38
|
+
int length = args.Length();
|
39
|
+
int offset = RARRAY_LEN(rbargs);
|
40
|
+
for (int i = 0; i < length; i++)
|
41
|
+
rb_ary_store(rbargs, offset+i, ruby_value_of(tomato, args[i]));
|
42
|
+
}
|
43
|
+
|
33
44
|
static VALUE bound_method_call(VALUE args)
|
34
45
|
{
|
35
|
-
VALUE receiver =
|
36
|
-
VALUE rb_method_id = SYM2ID(
|
37
|
-
|
38
|
-
VALUE result = rb_funcall(receiver, rb_method_id, 0);
|
39
|
-
|
46
|
+
VALUE receiver = rb_ary_shift(args);
|
47
|
+
VALUE rb_method_id = SYM2ID(rb_ary_shift(args));
|
48
|
+
VALUE result = rb_funcall2(receiver, rb_method_id, RARRAY_LEN(args), RARRAY_PTR(args));
|
40
49
|
return result;
|
41
50
|
}
|
42
51
|
|
43
52
|
int store_rb_message(const Arguments &args, V8Tomato **out_tomato, VALUE *out_receiver, ID *out_method_id)
|
44
53
|
{
|
54
|
+
// get the function
|
45
55
|
Handle<Function> function = args.Callee();
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
|
57
|
+
// pull the binding data from the function (stored there by fTomato_bind_method)
|
58
|
+
Local<Value> v8_tomato = function->Get(String::New("_tomato"));
|
59
|
+
Local<Value> v8_receiver_index = function->Get(String::New("_tomato_receiver_index"));
|
60
|
+
|
61
|
+
// make sure the data is what we expect it to be
|
62
|
+
if (!v8_tomato->IsExternal()) return -1;
|
63
|
+
if (!v8_receiver_index->IsInt32()) return -2;
|
49
64
|
|
50
|
-
|
51
|
-
|
65
|
+
// find the tomato
|
66
|
+
V8Tomato *tomato = (V8Tomato *)Local<External>::Cast(v8_tomato)->Value();
|
67
|
+
|
68
|
+
// find the receiver index, and make sure it's a valid index
|
69
|
+
int receiver_index = v8_receiver_index->Int32Value();
|
70
|
+
VALUE receivers = rb_iv_get(tomato->rb_instance, "@receivers"); //rb_funcall(tomato->rb_instance, rb_intern("receivers"));
|
71
|
+
if (RARRAY_LEN(receivers) < receiver_index) return -3;
|
72
|
+
|
73
|
+
// get the receiver
|
74
|
+
VALUE receiver = (RARRAY_PTR(receivers)[receiver_index]);
|
75
|
+
|
76
|
+
// get the method name from the function name
|
52
77
|
String::Utf8Value method_name(function->GetName());
|
53
78
|
|
79
|
+
// store the output and return success.
|
54
80
|
*out_tomato = tomato;
|
55
81
|
*out_method_id = rb_intern(ToCString(method_name));
|
56
82
|
*out_receiver = receiver;
|
@@ -59,22 +85,23 @@ int store_rb_message(const Arguments &args, V8Tomato **out_tomato, VALUE *out_re
|
|
59
85
|
|
60
86
|
VALUE fTomato_bind_method(int argc, VALUE *argv, VALUE self)
|
61
87
|
{
|
62
|
-
if (argc !=
|
88
|
+
if (argc != 3) rb_raise(rb_eArgError,
|
89
|
+
"Expected: _bind_method(method_name<str>, receiver_index<int>, object_chain<array[string] or nil>)");
|
63
90
|
const char *method_name = rb_id2name(rb_to_id(argv[0]));
|
64
91
|
|
65
92
|
V8Tomato *tomato;
|
66
93
|
Data_Get_Struct(self, V8Tomato, tomato);
|
67
94
|
|
68
95
|
HandleScope handle_scope;
|
69
|
-
Context::Scope context_scope(tomato->context);
|
70
|
-
Handle<Value> value = tomato
|
96
|
+
Context::Scope context_scope(tomato->context);
|
97
|
+
Handle<Value> value = find_or_create_object_chain(tomato, argv[2]);
|
71
98
|
if (value->IsObject())
|
72
99
|
{
|
73
100
|
Handle<Object> object = Handle<Object>::Cast(value);
|
74
|
-
Handle<Value> proto_value = object->GetPrototype();
|
75
101
|
Handle<Function> function = FunctionTemplate::New(bound_method)->GetFunction();
|
76
102
|
|
77
103
|
function->Set(String::New("_tomato"), External::New(tomato));
|
104
|
+
function->Set(String::New("_tomato_receiver_index"), Int32::New(FIX2INT(argv[1])));
|
78
105
|
function->SetName(String::New(method_name));
|
79
106
|
|
80
107
|
object->Set(String::New(method_name), function);
|