tomato 0.0.1.prealpha1 → 0.0.1.prealpha2
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/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);
|