forked from jbarnette/johnson
/
js_proxy.c
197 lines (153 loc) · 6.12 KB
/
js_proxy.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include "js_proxy.h"
static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval);
static void finalize(JSContext* context, JSObject* obj);
static JSBool set(JSContext* context, JSObject* obj, jsval id, jsval* retval);
static JSClass JSProxyClass = {
"JSProxy", JSCLASS_HAS_PRIVATE,
JS_PropertyStub,
JS_PropertyStub,
get,
set,
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
finalize
};
static void finalize(JSContext* context, JSObject* obj)
{
// FIXME
}
// void Johnson_RubyProxy_finalize(JSContext *js_context, JSObject *obj)
// {
// VALUE ruby;
// VALUE self = (VALUE)JS_GetContextPrivate(js_context);
//
// ruby = (VALUE)JS_GetInstancePrivate(js_context, obj, &gRubyProxyClass, NULL);
// VALUE c_name = rb_funcall(
// rb_funcall(self, rb_intern("class"), 0),
// rb_intern("to_s"), 0);
//
// if(rb_ivar_defined(self, rb_intern("@converted_objects"))) {
// rb_hash_delete( rb_iv_get(self, "@converted_objects"),
// rb_funcall(ruby, rb_intern("object_id"), 0));
// }
// }
//
static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
{
// pull out our Ruby object, which is embedded in js_context
VALUE ruby_context;
assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
// get our struct, which is embedded in ruby_context
OurContext* context;
Data_Get_Struct(ruby_context, OurContext, context);
// get the Ruby object that backs this proxy
VALUE self;
assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj, &JSProxyClass, NULL));
char* key = JS_GetStringBytes(JSVAL_TO_STRING(id));
VALUE ruby_id = rb_intern(key);
VALUE is_method = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(ruby_id));
if (is_method)
{
VALUE method = rb_funcall(self, rb_intern("method"), 1, ID2SYM(ruby_id));
int arity = NUM2INT(rb_funcall(method, rb_intern("arity"), 0));
// if the Ruby object has a 0-arity method named the same as the property
// we're trying to get, call it and return the converted result
if (arity == 0)
*retval = convert_to_js(context, rb_funcall(self, ruby_id, 0));
}
else
{
// otherwise, if the Ruby object quacks sorta like a hash (it responds to
// "[]" and "key?"), index it by key and return the converted result
VALUE is_indexable = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]")));
VALUE has_key_p = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("key?")));
if (is_indexable && has_key_p)
*retval = convert_to_js(context, rb_funcall(self, rb_intern("[]"), 1, rb_str_new2(key)));
}
return JS_TRUE;
}
static JSBool set(JSContext* js_context, JSObject* obj, jsval id, jsval* value)
{
VALUE ruby_context;
assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
OurContext* context;
Data_Get_Struct(ruby_context, OurContext, context);
VALUE self;
assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj, &JSProxyClass, NULL));
char* key = JS_GetStringBytes(JSVAL_TO_STRING(id));
VALUE ruby_key = rb_str_new2(key);
VALUE setter = rb_str_append(rb_str_new3(ruby_key), rb_str_new2("="));
VALUE setter_id = rb_intern(StringValuePtr(setter));
VALUE has_setter = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(setter_id));
if (has_setter)
{
VALUE method = rb_funcall(self, rb_intern("method"), 1, ID2SYM(setter_id));
int arity = NUM2INT(rb_funcall(method, rb_intern("arity"), 0));
// if the Ruby object has a 1-arity method named "property=",
// call it with the converted value
if (arity == 1)
rb_funcall(self, setter_id, 1, convert_to_ruby(context, *value));
}
else
{
// otherwise, if the Ruby object quacks sorta like a hash for assignment
// (it responds to "[]="), assign it by key
VALUE is_index_assignable =
rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]=")));
if (is_index_assignable)
rb_funcall(self, rb_intern("[]="), 2, ruby_key, convert_to_ruby(context, *value));
}
return JS_TRUE;
}
static JSBool method_missing(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
{
VALUE ruby_context;
assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
OurContext* context;
Data_Get_Struct(ruby_context, OurContext, context);
VALUE self;
assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj, &JSProxyClass, NULL));
char* key = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
VALUE ruby_id = rb_intern(key);
// FIXME: this could probably be a lot faster, to_a comes from enumerable on proxy
VALUE args = rb_funcall(convert_to_ruby(context, argv[1]), rb_intern("to_a"), 0);
// Context#jsend: if the last arg is a function, it'll get passed along as a &block
*retval = convert_to_js(context,
rb_funcall(ruby_context, rb_intern("jsend"), 3, self, ID2SYM(ruby_id), args));
return JS_TRUE;
}
JSBool js_value_is_proxy(jsval maybe_proxy)
{
return JS_FALSE;
}
VALUE unwrap_js_proxy(OurContext* context, jsval proxy)
{
return Qnil;
}
// static jsval convert_ruby_object_to_jsval(CombinedContext* context, VALUE ruby)
// {
// VALUE self = (VALUE)JS_GetContextPrivate(context->js);
// JSObject * js = JS_NewObject(context->js, &gRubyProxyClass, NULL, NULL);
// if(!js) Johnson_Error_raise("failed JS_NewObject");
//
// rb_hash_aset( rb_iv_get(self, "@converted_objects"),
// rb_funcall(ruby, rb_intern("object_id"), 0),
// ruby );
//
// if(JS_SetPrivate(context->js, js, (void*)ruby) == JS_FALSE)
// Johnson_Error_raise("failed JS_SetPrivate");
//
// JS_DefineFunction(context->js, js, "__noSuchMethod__",
// Johnson_RubyProxy_method_missing, 2, 0);
//
// return OBJECT_TO_JSVAL(js);
// }
jsval make_js_proxy(OurContext* context, VALUE value)
{
JSObject *js;
assert(js = JS_NewObject(context->js, &JSProxyClass, NULL, NULL));
assert(JS_SetPrivate(context->js, js, (void*)value));
assert(JS_DefineFunction(context->js, js, "__noSuchMethod__", method_missing, 2, 0));
return OBJECT_TO_JSVAL(js);
}