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.
@@ -0,0 +1,8 @@
1
+ #ifndef BINDING_METHODS_H
2
+ #define BINDING_METHODS_H
3
+
4
+ extern Handle<Value> bound_method(const Arguments& args);
5
+ extern int store_rb_message(const Arguments &args, V8Tomato **out_tomato, VALUE *out_receiver, ID *out_method_id);
6
+ extern void store_args(V8Tomato *tomato, VALUE rbargs, const Arguments &args);
7
+
8
+ #endif//BINDING_METHODS_H
Binary file
@@ -4,6 +4,7 @@ static Handle<Value> js_array_from(V8Tomato *tomato, VALUE value);
4
4
  static Handle<Value> js_hash_from(V8Tomato *tomato, VALUE value);
5
5
  static Handle<Value> js_symbol_to_string(const Arguments& args);
6
6
  static Handle<Value> js_symbol_from(VALUE value);
7
+ static Handle<Value> js_date_from(VALUE value);
7
8
 
8
9
  Handle<Value> js_value_of(V8Tomato *tomato, VALUE value)
9
10
  {
@@ -32,6 +33,16 @@ Handle<Value> js_value_of(V8Tomato *tomato, VALUE value)
32
33
  else return js_symbol_from(value);
33
34
  //return String::New(rb_id2name(SYM2ID(value)));
34
35
  };
36
+
37
+ /* take care of some JS types that aren't reflected above */
38
+ if (rb_funcall(value, rb_intern("kind_of?"), 1, rb_const_get(rb_cObject, rb_intern("Date"))) == Qtrue ||
39
+ rb_funcall(value, rb_intern("kind_of?"), 1, rb_const_get(rb_cObject, rb_intern("Time"))) == Qtrue ||
40
+ rb_funcall(value, rb_intern("kind_of?"), 1, rb_const_get(rb_cObject, rb_intern("DateTime"))) == Qtrue ||
41
+ rb_funcall(value, rb_intern("kind_of?"), 1, rb_const_get(rb_const_get(rb_cObject, rb_intern("ActiveSupport")), rb_intern("TimeWithZone"))) == Qtrue)
42
+ {
43
+ return js_date_from(value);
44
+ }
45
+
35
46
  return inspect_rb(value);
36
47
  }
37
48
 
@@ -112,66 +123,9 @@ Handle<Value> inspect_rb(VALUE value)
112
123
  return String::New(StringValuePtr(string));
113
124
  }
114
125
 
115
- /*
116
- Handle<Value> v8_value_of(V8Tomato *tomato, VALUE object)
126
+ Handle<Value> js_date_from(VALUE value)
117
127
  {
118
-
119
- switch(TYPE(object))
120
- {
121
- case T_NIL :
122
- case T_OBJECT :
123
- //case T_CLASS :
124
- //case T_MODULE :
125
- case T_FLOAT :
126
- case T_STRING :
127
- case T_REGEXP :
128
- case T_ARRAY :
129
- case T_HASH :
130
- //case T_STRUCT :
131
- case T_BIGNUM :
132
- case T_FIXNUM :
133
- //case T_COMPLEX :
134
- //case T_RATIONAL:
135
- //case T_FILE :
136
- case T_TRUE :
137
- case T_FALSE :
138
- //case T_DATA :
139
- case T_SYMBOL :
140
- symbol_from(object)
141
- break;
142
- default: string_from(object);
143
- };
144
- }
145
- */
146
- /*
147
- if (result->IsUndefined()) { return ID2SYM(rb_intern("undefined")); }
148
- if (result->IsNull()) { return Qnil; }
149
- if (result->IsTrue()) { return Qtrue; }
150
- if (result->IsFalse()) { return Qfalse; }
151
- if (result->IsString()) { return ruby_string_from(result); }
152
- if (result->IsFunction()) { return ruby_string_from(result); }
153
- if (result->IsArray()) { Handle<Array> array = Handle<Array>::Cast(result); return ruby_array_from(tomato, array); }
154
- if (result->IsNumber()) { return ruby_numeric_from(result); }
155
- if (result->IsDate()) { return ruby_date_from(result); }
156
-
157
-
158
- if (result->IsObject())
159
- {
160
- Handle<Value> json = tomato->context->Global()->Get(String::New("JSON"));
161
- if (json->IsObject())
162
- {
163
- Handle<Value> stringify = Handle<Object>::Cast(json)->Get(String::New("stringify"));
164
- if (stringify->IsFunction())
165
- {
166
- String::Utf8Value str(Handle<Function>::Cast(stringify)->Call(
167
- Handle<Object>::Cast(json),
168
- 1,
169
- &result
170
- ));
171
- return rb_str_new2(ToCString(str));
172
- }
173
- }
174
- }
175
- return Qnil;
128
+ VALUE ival = rb_funcall(value, rb_intern("to_f"), 0);
129
+ double time = NUM2DBL(ival) * 1000.0;
130
+ return Date::New(time);
176
131
  }
177
- */
@@ -5,6 +5,7 @@ static VALUE ruby_numeric_from(const Handle<Value> &number);
5
5
  static VALUE ruby_date_from(const Handle<Value> &date);
6
6
  static VALUE ruby_string_from(const Handle<Value> &value);
7
7
  static VALUE ruby_symbol_from(const Handle<Object> &value);
8
+ static VALUE ruby_unwrapped_object_from(V8Tomato *tomato, const Handle<Object> &value);
8
9
  static VALUE ruby_object_from(V8Tomato *tomato, Handle<Value> result);
9
10
  static VALUE ruby_hash_from(V8Tomato *tomato, const Handle<Object> &object);
10
11
 
@@ -37,10 +38,9 @@ static VALUE ruby_object_from(V8Tomato *tomato, Handle<Value> result)
37
38
  {
38
39
  Handle<Object> object = Handle<Object>::Cast(result);
39
40
 
40
- if (object->Get(String::New("_tomato_hash"))->IsTrue())
41
- return ruby_hash_from(tomato, object);
42
- if (object->Get(String::New("_tomato_symbol"))->IsTrue())
43
- return ruby_symbol_from(object);
41
+ if (object->Get(String::New("_tomato_hash"))->IsTrue()) return ruby_hash_from(tomato, object);
42
+ if (object->Get(String::New("_tomato_symbol"))->IsTrue()) return ruby_symbol_from(object);
43
+ if (object->Get(String::New("_tomato_ruby_wrapper"))->IsTrue()) return ruby_unwrapped_object_from(tomato, object);
44
44
  }
45
45
 
46
46
  /* Call Javascript's JSON.stringify(object) method. If that can't be done for any reason, return nil. */
@@ -115,10 +115,33 @@ static VALUE ruby_numeric_from(const Handle<Value> &number)
115
115
  return DBL2NUM(number->NumberValue());
116
116
  }
117
117
 
118
- void inspect_js(V8Tomato *tomato, Handle<Value> obj)
118
+ static VALUE ruby_unwrapped_object_from(V8Tomato *tomato, const Handle<Object> &value)
119
119
  {
120
- VALUE rbObj = ruby_value_of(tomato, obj);
121
- VALUE rbStr = rb_funcall(rbObj, rb_intern("inspect"), 0);
122
- printf("%s\n", StringValuePtr(rbStr));
120
+ VALUE receivers = rb_funcall2(tomato->rb_instance, rb_intern("receivers"), 0, 0);
121
+ int len = RARRAY_LEN(receivers);
122
+ int index = value->Get(String::New("_tomato_receiver_index"))->Int32Value();
123
+ if (len <= index)
124
+ return Qnil;
125
+ return *(RARRAY_PTR(receivers)+index);
126
+ }
127
+
128
+ Handle<Value> inspect_js(V8Tomato *tomato, Handle<Value> obj)
129
+ {
130
+ /* Call Javascript's JSON.stringify(object) method. If that can't be done for any reason, return an error. */
131
+ Handle<Value> json = tomato->context->Global()->Get(String::New("JSON"));
132
+ if (json->IsObject())
133
+ {
134
+ Handle<Value> stringify = Handle<Object>::Cast(json)->Get(String::New("stringify"));
135
+ if (stringify->IsFunction())
136
+ {
137
+ String::Utf8Value str(Handle<Function>::Cast(stringify)->Call(
138
+ Handle<Object>::Cast(json),
139
+ 1,
140
+ &obj
141
+ ));
142
+ return String::New(ToCString(str));
143
+ }
144
+ }
145
+ return ThrowException(String::New("Could not JSONify the object"));
123
146
  }
124
147
 
@@ -1,6 +1,7 @@
1
- binding_methods.o: binding_methods.cpp tomato.h
1
+ binding_methods.o: binding_methods.cpp tomato.h binding_methods.h
2
2
  conversions_to_js.o: conversions_to_js.cpp tomato.h
3
3
  conversions_to_rb.o: conversions_to_rb.cpp tomato.h
4
4
  errors.o: errors.cpp tomato.h
5
+ object_chain.o: object_chain.cpp tomato.h binding_methods.h
5
6
  tomato.o: tomato.cpp tomato.h
6
7
  v8.o: v8.cpp tomato.h
Binary file
@@ -38,7 +38,7 @@ makefile.print <<EOF
38
38
 
39
39
  test: all
40
40
  @echo Running specs...
41
- spec -O spec/spec.opts spec
41
+ spec spec -c
42
42
 
43
43
  EOF
44
44
  end
@@ -1,6 +1,6 @@
1
1
  find_header: checking for v8.h in /Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include... -------------------- yes
2
2
 
3
- "gcc -o conftest -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe conftest.c -L. -L/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/lib -L. -L/usr/local/lib -lruby.1.9.1-static -lpthread -ldl -lobjc "
3
+ "gcc -o conftest -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/i386-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O2 -g -Wall -Wno-parentheses -fno-common -pipe -fno-common conftest.c -L. -L/Users/colin/.rvm/rubies/ruby-1.9.1-p378/lib -L. -L/usr/local/lib -lruby-static -lpthread -ldl -lobjc "
4
4
  checked program was:
5
5
  /* begin */
6
6
  1: #include "ruby.h"
@@ -8,7 +8,7 @@ checked program was:
8
8
  3: int main() {return 0;}
9
9
  /* end */
10
10
 
11
- "gcc -E -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe conftest.c -o conftest.i"
11
+ "gcc -E -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/i386-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O2 -g -Wall -Wno-parentheses -fno-common -pipe -fno-common conftest.c -o conftest.i"
12
12
  conftest.c:3:16: error: v8.h: No such file or directory
13
13
  checked program was:
14
14
  /* begin */
@@ -17,7 +17,7 @@ checked program was:
17
17
  3: #include <v8.h>
18
18
  /* end */
19
19
 
20
- "gcc -E -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include conftest.c -o conftest.i"
20
+ "gcc -E -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/i386-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O2 -g -Wall -Wno-parentheses -fno-common -pipe -fno-common -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include conftest.c -o conftest.i"
21
21
  checked program was:
22
22
  /* begin */
23
23
  1: #include "ruby.h"
@@ -29,7 +29,7 @@ checked program was:
29
29
 
30
30
  have_library: checking for main() in -lpthread... -------------------- yes
31
31
 
32
- "gcc -o conftest -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1 -I. -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe conftest.c -L. -L/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/lib -L. -L/usr/local/lib -lruby.1.9.1-static -lpthread -lpthread -ldl -lobjc "
32
+ "gcc -o conftest -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/i386-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1 -I. -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O2 -g -Wall -Wno-parentheses -fno-common -pipe -fno-common conftest.c -L. -L/Users/colin/.rvm/rubies/ruby-1.9.1-p378/lib -L. -L/usr/local/lib -lruby-static -lpthread -lpthread -ldl -lobjc "
33
33
  checked program was:
34
34
  /* begin */
35
35
  1: #include "ruby.h"
@@ -43,7 +43,7 @@ checked program was:
43
43
 
44
44
  have_library: checking for main() in -lobjc... -------------------- yes
45
45
 
46
- "gcc -o conftest -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1 -I. -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe conftest.c -L. -L/Users/colin/.rvm/rubies/ruby-1.9.2-preview3/lib -L. -L/usr/local/lib -lpthread -lruby.1.9.1-static -lobjc -lpthread -lpthread -ldl -lobjc "
46
+ "gcc -o conftest -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/i386-darwin10.4.0 -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/ruby/backward -I/Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1 -I. -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O2 -g -Wall -Wno-parentheses -fno-common -pipe -fno-common conftest.c -L. -L/Users/colin/.rvm/rubies/ruby-1.9.1-p378/lib -L. -L/usr/local/lib -lpthread -lruby-static -lobjc -lpthread -lpthread -ldl -lobjc "
47
47
  checked program was:
48
48
  /* begin */
49
49
  1: #include "ruby.h"
@@ -0,0 +1,241 @@
1
+ #include "tomato.h"
2
+ #include "binding_methods.h"
3
+
4
+ static VALUE create_new(VALUE args);
5
+ static v8::Handle<v8::Value> ruby_class_constructor(const Arguments &args);
6
+ static Handle<Value> bind_methods(Local<Object> js, VALUE rb, V8Tomato *tomato);
7
+ static Handle<Value> bound_getter(Local<String> property, const AccessorInfo &info);
8
+ static void bound_setter(Local<String> property, Local<Value> value, const AccessorInfo &info);
9
+ static VALUE protected_get(VALUE args);
10
+ static VALUE protected_set(VALUE args);
11
+
12
+ VALUE fTomato_bind_class(VALUE self, VALUE receiver_index, VALUE chain)
13
+ {
14
+ V8Tomato *tomato;
15
+ Data_Get_Struct(self, V8Tomato, tomato);
16
+
17
+ HandleScope handle_scope;
18
+ Context::Scope context_scope(tomato->context);
19
+ VALUE js_class_name = rb_ary_pop(chain);
20
+ // This is kind of a misnomer. We're creating a JavaScript function ("method") to stand in for
21
+ // the Ruby class. So the method_name has to be the Ruby class name. Consider: "new" is not a
22
+ // method in JS -- it's a keyword.
23
+ Handle<String> method_name = String::New(StringValuePtr(js_class_name));
24
+ Handle<Value> parent = find_or_create_object_chain(tomato, chain);
25
+
26
+ if (parent->IsObject())
27
+ {
28
+ Handle<Object> object = Handle<Object>::Cast(parent);
29
+ Handle<Function> function = FunctionTemplate::New(ruby_class_constructor)->GetFunction();
30
+
31
+ function->Set(String::New("_tomato"), External::New(tomato));
32
+ function->Set(String::New("_tomato_receiver_index"), Int32::New(FIX2INT(receiver_index)));
33
+ function->SetName(method_name);
34
+
35
+ Handle<Value> current_value = object->Get(method_name);
36
+ if (current_value->IsObject())
37
+ {
38
+ // we're about to overwrite an object. First clone any of its registered functions.
39
+ Handle<Object> current = Handle<Object>::Cast(current_value);
40
+ Local<Array> properties = current->GetPropertyNames();
41
+ int length = properties->Length();
42
+ for (int i = 0; i < length; i++)
43
+ {
44
+ Local<Value> property = properties->Get(i);
45
+ String::Utf8Value stra(inspect_js(tomato, property));
46
+ String::Utf8Value str(inspect_js(tomato, current->Get(property)));
47
+ function->Set(property, current->Get(property));
48
+ }
49
+ }
50
+ object->Set(method_name, function);
51
+ return Qtrue;
52
+ }
53
+ return Qfalse;
54
+ }
55
+
56
+ v8::Handle<v8::Value> ruby_class_constructor(const Arguments &args)
57
+ {
58
+ // throw if called without `new'
59
+ if (!args.IsConstructCall())
60
+ return ThrowException(String::New("Cannot call constructor as function"));
61
+
62
+ // we get the method ID and then call it, so that any C++ destructors that need to fire before
63
+ // we do so, can.
64
+ VALUE receiver;
65
+ ID rb_method_id;
66
+ V8Tomato *tomato;
67
+ int code = store_rb_message(args, &tomato, &receiver, &rb_method_id);
68
+ switch(code)
69
+ {
70
+ case -1: return ThrowException(String::New("Error: _tomato is not an object (BUG: please report)"));
71
+ case -2: return ThrowException(String::New("Error: _tomato_receiver_index is not an Int32 (BUG: please report)"));
72
+ case -3: return ThrowException(String::New("Error: _tomato_receiver_index is greater than @receivers.length (BUG: please report)"));
73
+ };
74
+
75
+ VALUE rbargs = rb_ary_new2(1+args.Length());
76
+ rb_ary_store(rbargs, 0, receiver);
77
+ store_args(tomato, rbargs, args);
78
+
79
+ int error;
80
+ VALUE result = rb_protect(create_new, rbargs, &error);
81
+ if(error)
82
+ {
83
+ return ThrowException(js_error_from(rb_gv_get("$!")));
84
+ }
85
+
86
+ Local<Object> holder = args.Holder();
87
+ holder->Set(String::New("_tomato_ruby_wrapper"), Boolean::New(true), DontEnum);
88
+ holder->Set(String::New("_tomato_receiver_index"),
89
+ Int32::New(FIX2INT(rb_funcall(tomato->rb_instance, rb_intern("receiver_index"), 1, result))), DontEnum);
90
+ return bind_methods(holder, result, tomato);
91
+ }
92
+
93
+ Handle<Value> bind_methods(Local<Object> js, VALUE rb, V8Tomato *tomato)
94
+ {
95
+ VALUE methods = rb_funcall(rb, rb_intern("public_methods"), 0);
96
+ int receiver_index = FIX2INT(rb_funcall(tomato->rb_instance, rb_intern("receiver_index"), 1, rb));
97
+
98
+ HandleScope handle_scope;
99
+ Context::Scope context_scope(tomato->context);
100
+ Handle<String> method_name;
101
+ js->Set(String::New("_tomato"), External::New(tomato));
102
+ for (int i = 0; i < RARRAY_LEN(methods); i++)
103
+ {
104
+ method_name = String::New(StringValuePtr(*(RARRAY_PTR(methods)+i)));
105
+ Handle<Function> function = FunctionTemplate::New(bound_method)->GetFunction();
106
+
107
+ function->Set(String::New("_tomato"), External::New(tomato));
108
+ function->Set(String::New("_tomato_receiver_index"), Int32::New(receiver_index));
109
+ function->SetName(method_name);
110
+
111
+ js->Set(method_name, function);
112
+ js->SetAccessor(method_name, bound_getter, bound_setter);
113
+ }
114
+ return js;
115
+ }
116
+
117
+ static VALUE protected_get(VALUE args)
118
+ {
119
+ VALUE receiver = *(RARRAY_PTR(args));
120
+ VALUE method = *(RARRAY_PTR(args)+1);
121
+ return rb_funcall2(receiver, SYM2ID(method), 0, 0);
122
+ }
123
+
124
+ static VALUE protected_set(VALUE args)
125
+ {
126
+ VALUE receiver = *(RARRAY_PTR(args));
127
+ VALUE method = *(RARRAY_PTR(args)+1);
128
+ VALUE value = *(RARRAY_PTR(args)+2);
129
+
130
+ method = rb_funcall(method, rb_intern("to_s"), 0);
131
+ method = rb_funcall(method, rb_intern("+"), 1, rb_str_new2("="));
132
+ return rb_funcall2(receiver, rb_intern(StringValuePtr(method)), 1, &value);
133
+ return Qnil;
134
+ }
135
+
136
+ static Handle<Value> bound_getter(Local<String> property, const AccessorInfo &info)
137
+ {
138
+ int error;
139
+ Local<Object> self = info.Holder();
140
+
141
+ // pull the binding data from the function (stored there by fTomato_bind_method)
142
+ Local<Value> v8_tomato = self->Get(String::New("_tomato"));
143
+ Local<Value> v8_receiver_index = self->Get(String::New("_tomato_receiver_index"));
144
+
145
+ // make sure the data is what we expect it to be
146
+ if (!v8_tomato->IsExternal()) return ThrowException(String::New("_tomato is not an external! (bug: please report)"));
147
+ if (!v8_receiver_index->IsInt32()) return ThrowException(String::New("_tomato_receiver_index is not an Int32! (bug: please report)"));
148
+
149
+ // find the tomato
150
+ V8Tomato *tomato = (V8Tomato *)Local<External>::Cast(v8_tomato)->Value();
151
+
152
+ // find the receiver index, and make sure it's a valid index
153
+ int receiver_index = v8_receiver_index->Int32Value();
154
+ VALUE receivers = rb_iv_get(tomato->rb_instance, "@receivers"); //rb_funcall(tomato->rb_instance, rb_intern("receivers"));
155
+ if (RARRAY_LEN(receivers) < receiver_index) return ThrowException(String::New("_tomato_receiver_index is too small! (bug: please report)"));
156
+
157
+ // get the receiver
158
+ VALUE receiver = (RARRAY_PTR(receivers)[receiver_index]);
159
+
160
+ VALUE args = rb_ary_new();
161
+ String::Utf8Value property_name(property);
162
+ rb_ary_push(args, receiver);
163
+ rb_ary_push(args, ID2SYM(rb_intern(ToCString(property_name))));
164
+ VALUE result = rb_protect(protected_get, args, &error);
165
+ if (error)
166
+ return ThrowException(js_error_from(rb_gv_get("$!")));
167
+ return js_value_of(tomato, result);
168
+ }
169
+
170
+ static void bound_setter(Local<String> property, Local<Value> value, const AccessorInfo &info)
171
+ {
172
+ int error;
173
+ Local<Object> self = info.Holder();
174
+
175
+ // pull the binding data from the function (stored there by fTomato_bind_method)
176
+ Local<Value> v8_tomato = self->Get(String::New("_tomato"));
177
+ Local<Value> v8_receiver_index = self->Get(String::New("_tomato_receiver_index"));
178
+
179
+ // make sure the data is what we expect it to be
180
+ if (!v8_tomato->IsExternal()) { ThrowException(String::New("_tomato is not an external! (bug: please report)")); return; }
181
+ if (!v8_receiver_index->IsInt32()) { ThrowException(String::New("_tomato_receiver_index is not an Int32! (bug: please report)")); return; }
182
+
183
+ // find the tomato
184
+ V8Tomato *tomato = (V8Tomato *)Local<External>::Cast(v8_tomato)->Value();
185
+
186
+ // find the receiver index, and make sure it's a valid index
187
+ int receiver_index = v8_receiver_index->Int32Value();
188
+ VALUE receivers = rb_iv_get(tomato->rb_instance, "@receivers"); //rb_funcall(tomato->rb_instance, rb_intern("receivers"));
189
+ if (RARRAY_LEN(receivers) < receiver_index) { ThrowException(String::New("_tomato_receiver_index is too small! (bug: please report)")); return; }
190
+
191
+ // get the receiver
192
+ VALUE receiver = (RARRAY_PTR(receivers)[receiver_index]);
193
+
194
+ VALUE args = rb_ary_new();
195
+ String::Utf8Value property_name(property);
196
+ rb_ary_push(args, receiver);
197
+ rb_ary_push(args, ID2SYM(rb_intern(ToCString(property_name))));
198
+ rb_ary_push(args, ruby_value_of(tomato, value));
199
+ rb_protect(protected_set, args, &error);
200
+ if (error)
201
+ ThrowException(js_error_from(rb_gv_get("$!")));
202
+ }
203
+
204
+ static VALUE create_new(VALUE args)
205
+ {
206
+ VALUE receiver = rb_ary_shift(args);
207
+ VALUE result = rb_funcall2(receiver, rb_intern("new"), RARRAY_LEN(args), RARRAY_PTR(args));
208
+ return result;
209
+ }
210
+
211
+ Handle<Value> find_or_create_object_chain(V8Tomato *tomato, VALUE chain)
212
+ {
213
+ Handle<Value> value = tomato->context->Global();
214
+ if (value->IsObject())
215
+ {
216
+ Handle<Object> object = Handle<Object>::Cast(value);
217
+ if (chain != Qnil)
218
+ {
219
+ int len = RARRAY_LEN(chain);
220
+ VALUE *items = RARRAY_PTR(chain);
221
+
222
+ for (int i = 0; i < len; i++)
223
+ {
224
+ Handle<String> object_name = String::New(StringValuePtr(items[i]));
225
+ value = object->Get(object_name);
226
+ if (value->IsObject())
227
+ {
228
+ object = Handle<Object>::Cast(value);
229
+ }
230
+ else
231
+ {
232
+ Handle<Object> new_object = Object::New();
233
+ object->Set(object_name, new_object);
234
+ object = new_object;
235
+ }
236
+ }
237
+ }
238
+ return object;
239
+ }
240
+ return value;
241
+ }