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
@@ -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
|
-
|
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
|
-
*/
|
Binary file
|
@@ -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
|
-
|
42
|
-
if (object->Get(String::New("
|
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
|
-
|
118
|
+
static VALUE ruby_unwrapped_object_from(V8Tomato *tomato, const Handle<Object> &value)
|
119
119
|
{
|
120
|
-
VALUE
|
121
|
-
|
122
|
-
|
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
|
|
Binary file
|
data/ext/tomato/depend
CHANGED
@@ -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
|
data/ext/tomato/errors.o
CHANGED
Binary file
|
data/ext/tomato/extconf.rb
CHANGED
data/ext/tomato/mkmf.log
CHANGED
@@ -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.
|
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.
|
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.
|
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.
|
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.
|
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
|
+
}
|