jbarnette-johnson 1.0.0.200806240111

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +5 -0
  2. data/MANIFEST +385 -0
  3. data/MINGW32.mk +124 -0
  4. data/README.rdoc +51 -0
  5. data/Rakefile +166 -0
  6. data/bin/johnson +107 -0
  7. data/cross-compile.txt +38 -0
  8. data/ext/spidermonkey/context.c +122 -0
  9. data/ext/spidermonkey/context.h +19 -0
  10. data/ext/spidermonkey/conversions.c +286 -0
  11. data/ext/spidermonkey/conversions.h +18 -0
  12. data/ext/spidermonkey/debugger.c +208 -0
  13. data/ext/spidermonkey/debugger.h +9 -0
  14. data/ext/spidermonkey/extconf.rb +25 -0
  15. data/ext/spidermonkey/extensions.c +37 -0
  16. data/ext/spidermonkey/extensions.h +12 -0
  17. data/ext/spidermonkey/global.c +40 -0
  18. data/ext/spidermonkey/global.h +11 -0
  19. data/ext/spidermonkey/idhash.c +16 -0
  20. data/ext/spidermonkey/idhash.h +8 -0
  21. data/ext/spidermonkey/immutable_node.c.erb +522 -0
  22. data/ext/spidermonkey/immutable_node.h +22 -0
  23. data/ext/spidermonkey/jroot.h +187 -0
  24. data/ext/spidermonkey/js_land_proxy.c +609 -0
  25. data/ext/spidermonkey/js_land_proxy.h +20 -0
  26. data/ext/spidermonkey/ruby_land_proxy.c +537 -0
  27. data/ext/spidermonkey/ruby_land_proxy.h +17 -0
  28. data/ext/spidermonkey/runtime.c +304 -0
  29. data/ext/spidermonkey/runtime.h +25 -0
  30. data/ext/spidermonkey/spidermonkey.c +20 -0
  31. data/ext/spidermonkey/spidermonkey.h +29 -0
  32. data/js/johnson/browser.js +9 -0
  33. data/js/johnson/browser/env.js +687 -0
  34. data/js/johnson/browser/jquery.js +3444 -0
  35. data/js/johnson/browser/xmlsax.js +1564 -0
  36. data/js/johnson/browser/xmlw3cdom.js +4189 -0
  37. data/js/johnson/cli.js +30 -0
  38. data/js/johnson/prelude.js +80 -0
  39. data/js/johnson/template.js +29 -0
  40. data/lib/hoe.rb +748 -0
  41. data/lib/johnson.rb +46 -0
  42. data/lib/johnson/cli.rb +7 -0
  43. data/lib/johnson/cli/options.rb +56 -0
  44. data/lib/johnson/error.rb +4 -0
  45. data/lib/johnson/nodes.rb +7 -0
  46. data/lib/johnson/nodes/binary_node.rb +64 -0
  47. data/lib/johnson/nodes/for.rb +14 -0
  48. data/lib/johnson/nodes/for_in.rb +12 -0
  49. data/lib/johnson/nodes/function.rb +13 -0
  50. data/lib/johnson/nodes/list.rb +27 -0
  51. data/lib/johnson/nodes/node.rb +68 -0
  52. data/lib/johnson/nodes/ternary_node.rb +20 -0
  53. data/lib/johnson/parser.rb +21 -0
  54. data/lib/johnson/parser/syntax_error.rb +13 -0
  55. data/lib/johnson/runtime.rb +55 -0
  56. data/lib/johnson/spidermonkey/context.rb +10 -0
  57. data/lib/johnson/spidermonkey/debugger.rb +67 -0
  58. data/lib/johnson/spidermonkey/immutable_node.rb +280 -0
  59. data/lib/johnson/spidermonkey/js_land_proxy.rb +62 -0
  60. data/lib/johnson/spidermonkey/mutable_tree_visitor.rb +233 -0
  61. data/lib/johnson/spidermonkey/ruby_land_proxy.rb +52 -0
  62. data/lib/johnson/spidermonkey/runtime.rb +94 -0
  63. data/lib/johnson/version.rb +4 -0
  64. data/lib/johnson/visitable.rb +16 -0
  65. data/lib/johnson/visitors.rb +4 -0
  66. data/lib/johnson/visitors/dot_visitor.rb +167 -0
  67. data/lib/johnson/visitors/ecma_visitor.rb +315 -0
  68. data/lib/johnson/visitors/enumerating_visitor.rb +115 -0
  69. data/lib/johnson/visitors/sexp_visitor.rb +172 -0
  70. data/lib/rails/init.rb +37 -0
  71. data/test/assets/index.html +38 -0
  72. data/test/assets/jquery_test.html +186 -0
  73. data/test/helper.rb +58 -0
  74. data/test/johnson/browser_test.rb +38 -0
  75. data/test/johnson/conversions/array_test.rb +32 -0
  76. data/test/johnson/conversions/boolean_test.rb +17 -0
  77. data/test/johnson/conversions/callable_test.rb +34 -0
  78. data/test/johnson/conversions/file_test.rb +15 -0
  79. data/test/johnson/conversions/nil_test.rb +20 -0
  80. data/test/johnson/conversions/number_test.rb +34 -0
  81. data/test/johnson/conversions/regexp_test.rb +24 -0
  82. data/test/johnson/conversions/string_test.rb +26 -0
  83. data/test/johnson/conversions/struct_test.rb +15 -0
  84. data/test/johnson/conversions/symbol_test.rb +19 -0
  85. data/test/johnson/conversions/thread_test.rb +24 -0
  86. data/test/johnson/error_test.rb +9 -0
  87. data/test/johnson/extensions_test.rb +56 -0
  88. data/test/johnson/nodes/array_literal_test.rb +57 -0
  89. data/test/johnson/nodes/array_node_test.rb +26 -0
  90. data/test/johnson/nodes/binary_node_test.rb +61 -0
  91. data/test/johnson/nodes/bracket_access_test.rb +16 -0
  92. data/test/johnson/nodes/delete_test.rb +11 -0
  93. data/test/johnson/nodes/do_while_test.rb +12 -0
  94. data/test/johnson/nodes/dot_accessor_test.rb +15 -0
  95. data/test/johnson/nodes/export_test.rb +9 -0
  96. data/test/johnson/nodes/for_test.rb +54 -0
  97. data/test/johnson/nodes/function_test.rb +71 -0
  98. data/test/johnson/nodes/if_test.rb +41 -0
  99. data/test/johnson/nodes/import_test.rb +13 -0
  100. data/test/johnson/nodes/label_test.rb +19 -0
  101. data/test/johnson/nodes/object_literal_test.rb +110 -0
  102. data/test/johnson/nodes/return_test.rb +16 -0
  103. data/test/johnson/nodes/semi_test.rb +8 -0
  104. data/test/johnson/nodes/switch_test.rb +55 -0
  105. data/test/johnson/nodes/ternary_test.rb +25 -0
  106. data/test/johnson/nodes/throw_test.rb +9 -0
  107. data/test/johnson/nodes/try_node_test.rb +59 -0
  108. data/test/johnson/nodes/typeof_test.rb +11 -0
  109. data/test/johnson/nodes/unary_node_test.rb +23 -0
  110. data/test/johnson/nodes/void_test.rb +11 -0
  111. data/test/johnson/nodes/while_test.rb +26 -0
  112. data/test/johnson/nodes/with_test.rb +10 -0
  113. data/test/johnson/prelude_test.rb +56 -0
  114. data/test/johnson/runtime_test.rb +46 -0
  115. data/test/johnson/spidermonkey/context_test.rb +21 -0
  116. data/test/johnson/spidermonkey/immutable_node_test.rb +34 -0
  117. data/test/johnson/spidermonkey/js_land_proxy_test.rb +236 -0
  118. data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +225 -0
  119. data/test/johnson/spidermonkey/runtime_test.rb +17 -0
  120. data/test/johnson/version_test.rb +13 -0
  121. data/test/johnson/visitors/dot_visitor_test.rb +39 -0
  122. data/test/johnson/visitors/enumerating_visitor_test.rb +12 -0
  123. data/test/johnson_test.rb +16 -0
  124. data/test/jquery_units/test.js +27 -0
  125. data/test/jquery_units/test_helper.js +197 -0
  126. data/test/jquery_units/units/ajax.js +795 -0
  127. data/test/jquery_units/units/core.js +1563 -0
  128. data/test/jquery_units/units/event.js +299 -0
  129. data/test/jquery_units/units/fx.js +427 -0
  130. data/test/jquery_units/units/offset.js +112 -0
  131. data/test/jquery_units/units/selector.js +224 -0
  132. data/test/jspec/helper.js +7 -0
  133. data/test/jspec/jspec.js +192 -0
  134. data/test/jspec/simple_spec.js +68 -0
  135. data/test/parser_test.rb +276 -0
  136. data/todo/.keep +0 -0
  137. metadata +501 -0
@@ -0,0 +1,22 @@
1
+ #ifndef JOHNSON_SPIDERMONKEY_IMMUTABLE_NODE_H
2
+ #define JOHNSON_SPIDERMONKEY_IMMUTABLE_NODE_H
3
+
4
+ #include "spidermonkey.h"
5
+ #include "jsparse.h"
6
+ #include "jsatom.h"
7
+ #include "jsscan.h"
8
+ #include "jsarena.h"
9
+ #include "jsfun.h"
10
+ #include "jscntxt.h"
11
+
12
+ typedef struct {
13
+ JSParseContext * pc;
14
+ JSParseNode * node;
15
+ JSContext * js;
16
+ JSRuntime * runtime;
17
+ } ImmutableNodeContext;
18
+
19
+ VALUE jsop_to_symbol(JSUint32 jsop);
20
+ void init_Johnson_SpiderMonkey_Immutable_Node(VALUE spidermonkey);
21
+
22
+ #endif
@@ -0,0 +1,187 @@
1
+ #ifndef JOHNSON_JROOT_H
2
+ #define JOHNSON_JROOT_H
3
+
4
+ #define _JROOT_NAMESIZE 200
5
+ #define _JROOT_ERRSIZE 500
6
+
7
+ #define _JROOT_ROOT (void*)(1)
8
+
9
+ #define OUR_CONTEXT(js_context) \
10
+ ({ \
11
+ JohnsonContext* _context; \
12
+ const VALUE _ruby_context = (VALUE)JS_GetContextPrivate(js_context); \
13
+ Data_Get_Struct(_ruby_context, JohnsonContext, _context); \
14
+ _context; \
15
+ })
16
+
17
+ #define OUR_RUNTIME(js_context) \
18
+ ({ \
19
+ JohnsonRuntime* _johnson_runtime; \
20
+ JSRuntime * _js_runtime = JS_GetRuntime(js_context);\
21
+ const VALUE _ruby_runtime = (VALUE)JS_GetRuntimePrivate(_js_runtime); \
22
+ Data_Get_Struct(_ruby_runtime, JohnsonRuntime, _johnson_runtime); \
23
+ _johnson_runtime; \
24
+ })
25
+
26
+
27
+ #define _PREPARE_JROOTS(rb, context, cleancount) \
28
+ const bool _jroot_ruby = (rb); \
29
+ const int _jroot_cleans = (cleancount); \
30
+ void (*_jroot_cleanup[_jroot_cleans])(JSContext*, void*); \
31
+ void* _jroot_cleanup_data[_jroot_cleans]; \
32
+ JSContext* const _jroot_context = (context); \
33
+ int _jroot_cleanidx = 0;
34
+
35
+ #define PREPARE_JROOTS(context, cleancount) \
36
+ _PREPARE_JROOTS(false, context, cleancount)
37
+
38
+ #define PREPARE_RUBY_JROOTS(context, cleancount) \
39
+ _PREPARE_JROOTS(true, context, cleancount)
40
+
41
+ #define JCLEANUP(func, data) \
42
+ do \
43
+ { \
44
+ assert(_jroot_cleanidx < _jroot_cleans); \
45
+ _jroot_cleanup[_jroot_cleanidx] = (func); \
46
+ _jroot_cleanup_data[_jroot_cleanidx] = (data); \
47
+ _jroot_cleanidx++; \
48
+ } while(0)
49
+
50
+ #define _JROOT(ptr, name) \
51
+ do \
52
+ { \
53
+ static char _name[_JROOT_NAMESIZE] = ""; \
54
+ void* const _root = (ptr); \
55
+ if (*_name == '\0') \
56
+ snprintf(_name, _JROOT_NAMESIZE, "%s[%d]:%s: %s", __FILE__, __LINE__, __func__, (name)); \
57
+ JCHECK(JS_AddNamedRoot(_jroot_context, _root, _name)); \
58
+ JCLEANUP(_JROOT_ROOT, _root); \
59
+ } while(0)
60
+
61
+ #define JROOT(var) _JROOT(&(var), #var)
62
+ #define JROOT_PTR(ptr) _JROOT(ptr, #ptr)
63
+
64
+ #define JUNROOT(var) \
65
+ do \
66
+ { \
67
+ void* const _jroot_match = &(var); \
68
+ int _jroot_i; \
69
+ for (_jroot_i = _jroot_cleanidx - 1; _jroot_i >= 0; _jroot_i--) \
70
+ if (_jroot_cleanup[_jroot_i] == _JROOT_ROOT && _jroot_cleanup_data[_jroot_i] == _jroot_match) \
71
+ { \
72
+ JS_RemoveRoot(_jroot_context, _jroot_cleanup_data[_jroot_i]); \
73
+ if (_jroot_i == _jroot_cleanidx - 1) _jroot_cleanidx--; \
74
+ _jroot_cleanup[_jroot_i] = NULL; \
75
+ } \
76
+ } while (0)
77
+
78
+ #define REMOVE_JROOTS \
79
+ do \
80
+ { \
81
+ int _jroot_i; \
82
+ for (_jroot_i = _jroot_cleanidx - 1; _jroot_i >= 0; _jroot_i--) \
83
+ { \
84
+ if (_jroot_cleanup[_jroot_i] == _JROOT_ROOT) \
85
+ JS_RemoveRoot(_jroot_context, _jroot_cleanup_data[_jroot_i]); \
86
+ else if (_jroot_cleanup[_jroot_i]) \
87
+ (_jroot_cleanup[_jroot_i])(_jroot_context, _jroot_cleanup_data[_jroot_i]); \
88
+ } \
89
+ } while (0)
90
+
91
+ #define JCHECK_RUBY(cond) \
92
+ do \
93
+ { \
94
+ assert(_jroot_ruby); \
95
+ if (!(cond)) \
96
+ { \
97
+ REMOVE_JROOTS; \
98
+ raise_js_error_in_ruby(OUR_RUNTIME(_jroot_context)); \
99
+ } \
100
+ } while (0)
101
+
102
+ #define JCHECK(cond) \
103
+ do \
104
+ { \
105
+ if (!(cond)) \
106
+ { \
107
+ REMOVE_JROOTS; \
108
+ if (_jroot_ruby) \
109
+ raise_js_error_in_ruby(OUR_RUNTIME(_jroot_context)); \
110
+ else \
111
+ return JS_FALSE; \
112
+ } \
113
+ } while (0)
114
+
115
+ #define JPROTECT(func, data) \
116
+ ({ \
117
+ int _state; \
118
+ const VALUE _old_errinfo = ruby_errinfo; \
119
+ const VALUE _result = rb_protect((func), (data), &_state); \
120
+ if (_state) \
121
+ { \
122
+ REMOVE_JROOTS; \
123
+ if (_jroot_ruby) \
124
+ rb_jump_tag(_state); \
125
+ else \
126
+ return report_ruby_error_in_js(OUR_RUNTIME(_jroot_context), _state, _old_errinfo); \
127
+ } \
128
+ _result; \
129
+ })
130
+
131
+ #define JRETURN \
132
+ do \
133
+ { \
134
+ assert(!_jroot_ruby); \
135
+ REMOVE_JROOTS; \
136
+ return JS_TRUE; \
137
+ } while(0)
138
+
139
+ #define JRETURN_RUBY(value) \
140
+ do \
141
+ { \
142
+ assert(_jroot_ruby); \
143
+ typeof(value) _jroot_result = (value); \
144
+ REMOVE_JROOTS; \
145
+ return _jroot_result; \
146
+ } while(0)
147
+
148
+ #define JERROR(format, args...) \
149
+ do \
150
+ { \
151
+ REMOVE_JROOTS; \
152
+ char _jroot_msg[_JROOT_ERRSIZE]; \
153
+ snprintf(_jroot_msg, _JROOT_ERRSIZE, (format) , ## args); \
154
+ if (_jroot_ruby) \
155
+ rb_raise(rb_eRuntimeError, _jroot_msg); \
156
+ else \
157
+ { \
158
+ JSString* _jroot_err_str = JS_NewStringCopyZ(_jroot_context, _jroot_msg); \
159
+ if (_jroot_err_str) JS_SetPendingException(_jroot_context, STRING_TO_JSVAL(_jroot_err_str)); \
160
+ return JS_FALSE; \
161
+ } \
162
+ } while(0)
163
+
164
+
165
+
166
+
167
+ #define ARGLIST1(a) _data->a
168
+ #define ARGLIST2(a, b) _data->a, _data->b
169
+ #define ARGLIST3(a, b, c) _data->a, _data->b, _data->c
170
+ #define ARGLIST4(a, b, c, d) _data->a, _data->b, _data->c, _data->d
171
+ #define ARGLIST5(a, b, c, d, e) _data->a, _data->b, _data->c, _data->d, _data->e
172
+
173
+ #define DECLARE_RUBY_WRAPPER(name, args) \
174
+ typedef struct { args; } name ## _args; \
175
+ VALUE name ## _invoke(VALUE magic);
176
+ #define DEFINE_RUBY_WRAPPER(name, func, arglist) \
177
+ VALUE name ## _invoke(VALUE magic) \
178
+ { \
179
+ name ## _args * _data = (name ## _args *)(FIX2INT(magic) << 2); \
180
+ return func(arglist); \
181
+ }
182
+ #define RUBY_WRAPPER_ARG(name, args...) ({ name ## _args _x = { args }; INT2FIX((int)(&_x) >> 2); })
183
+ #define RUBY_WRAPPER(name) name ## _invoke
184
+ #define CALL_RUBY_WRAPPER(name, args...) JPROTECT(RUBY_WRAPPER(name), RUBY_WRAPPER_ARG(name, args))
185
+
186
+
187
+ #endif
@@ -0,0 +1,609 @@
1
+ #include "js_land_proxy.h"
2
+ #include "conversions.h"
3
+
4
+ static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval);
5
+ static JSBool set(JSContext* context, JSObject* obj, jsval id, jsval* retval);
6
+ static JSBool construct(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval);
7
+ static JSBool resolve(JSContext *js_context, JSObject *obj, jsval id, uintN flags, JSObject **objp);
8
+ static JSBool call(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval);
9
+ static void finalize(JSContext* context, JSObject* obj);
10
+
11
+ static JSClass JSLandProxyClass = {
12
+ "JSLandProxy", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
13
+ JS_PropertyStub,
14
+ JS_PropertyStub,
15
+ get,
16
+ set,
17
+ JS_EnumerateStub,
18
+ (JSResolveOp) resolve,
19
+ JS_ConvertStub,
20
+ finalize
21
+ };
22
+
23
+ static JSClass JSLandClassProxyClass = {
24
+ "JSLandClassProxy", JSCLASS_HAS_PRIVATE,
25
+ JS_PropertyStub,
26
+ JS_PropertyStub,
27
+ get,
28
+ set,
29
+ JS_EnumerateStub,
30
+ JS_ResolveStub,
31
+ JS_ConvertStub,
32
+ finalize,
33
+ NULL,
34
+ NULL,
35
+ NULL,
36
+ construct
37
+ };
38
+
39
+ static JSClass JSLandCallableProxyClass = {
40
+ "JSLandCallableProxy", JSCLASS_HAS_PRIVATE,
41
+ JS_PropertyStub,
42
+ JS_PropertyStub,
43
+ get,
44
+ set,
45
+ JS_EnumerateStub,
46
+ JS_ResolveStub,
47
+ JS_ConvertStub,
48
+ finalize,
49
+ NULL,
50
+ NULL,
51
+ call
52
+ };
53
+
54
+ static VALUE call_ruby_from_js_invoke(VALUE args)
55
+ {
56
+ VALUE self = rb_ary_pop(args);
57
+ VALUE id = rb_ary_pop(args);
58
+ return rb_apply(self, SYM2ID(id), args);
59
+ }
60
+
61
+ JSBool call_ruby_from_js_va(JohnsonRuntime* runtime, VALUE* result, VALUE self, ID id, int argc, va_list va)
62
+ {
63
+ VALUE old_errinfo = ruby_errinfo;
64
+ VALUE args = rb_ary_new2(argc + 2);
65
+
66
+ int i;
67
+ for(i = 0; i < argc; i++)
68
+ rb_ary_store(args, i, va_arg(va, VALUE));
69
+
70
+ rb_ary_store(args, argc, ID2SYM(id));
71
+ rb_ary_store(args, argc + 1, self);
72
+
73
+ int state;
74
+ *result = rb_protect(call_ruby_from_js_invoke, args, &state);
75
+
76
+ if (state)
77
+ return report_ruby_error_in_js(runtime, state, old_errinfo);
78
+
79
+ return JS_TRUE;
80
+ }
81
+
82
+ JSBool call_ruby_from_js(JohnsonRuntime* runtime, jsval* retval, VALUE self, ID id, int argc, ...)
83
+ {
84
+ VALUE result;
85
+ va_list va;
86
+ va_start(va, argc);
87
+ JSBool okay = call_ruby_from_js_va(runtime, &result, self, id, argc, va);
88
+ va_end(va);
89
+ if (!okay) return JS_FALSE;
90
+ return retval ? convert_to_js(runtime, result, retval) : JS_TRUE;
91
+ }
92
+
93
+ JSBool call_ruby_from_js2(JohnsonRuntime* runtime, VALUE* retval, VALUE self, ID id, int argc, ...)
94
+ {
95
+ va_list va;
96
+ va_start(va, argc);
97
+ JSBool okay = call_ruby_from_js_va(runtime, retval, self, id, argc, va);
98
+ va_end(va);
99
+ return okay;
100
+ }
101
+
102
+ static bool autovivified_p(VALUE UNUSED(ruby_context), VALUE self, char* name)
103
+ {
104
+ return RTEST(rb_funcall(Johnson_SpiderMonkey_JSLandProxy(), rb_intern("autovivified?"), 2,
105
+ self, rb_str_new2(name)));
106
+ }
107
+
108
+ static bool const_p(VALUE self, char* name)
109
+ {
110
+ return rb_obj_is_kind_of(self, rb_cModule)
111
+ && rb_is_const_id(rb_intern(name))
112
+ && RTEST( rb_funcall(self, rb_intern("const_defined?"), 1, ID2SYM(rb_intern(name))) );
113
+ }
114
+
115
+ static bool global_p(char* name)
116
+ {
117
+ return *name == '$' && rb_ary_includes(rb_f_global_variables(), rb_str_new2(name));
118
+ }
119
+
120
+ static bool method_p(VALUE self, char* name)
121
+ {
122
+ return RTEST( rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern(name))) );
123
+ }
124
+
125
+ static bool attribute_p(VALUE self, char* name)
126
+ {
127
+ if (!method_p(self, name))
128
+ return false;
129
+
130
+ VALUE rb_id = rb_intern(name);
131
+ VALUE rb_method = rb_funcall(self, rb_intern("method"), 1, ID2SYM(rb_id));
132
+
133
+ if (TYPE(rb_method) == T_DATA)
134
+ {
135
+ VALUE klass = CLASS_OF(rb_method);
136
+ if (klass == rb_cMethod)
137
+ {
138
+ METHOD* method;
139
+ Data_Get_Struct(rb_method, METHOD, method);
140
+
141
+ if (method && nd_type(method->body) == NODE_IVAR)
142
+ return true;
143
+ }
144
+ }
145
+
146
+ return RTEST(rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
147
+ rb_intern("js_property?"), 2, self, ID2SYM(rb_id)));
148
+ }
149
+
150
+ static bool indexable_p(VALUE self)
151
+ {
152
+ return RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]"))));
153
+ }
154
+
155
+ static bool has_key_p(VALUE self, char* name)
156
+ {
157
+ return RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]"))))
158
+ && RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("key?"))))
159
+ && RTEST(rb_funcall(self, rb_intern("key?"), 1, rb_str_new2(name)));
160
+ }
161
+
162
+ static bool respond_to_p(JSContext* js_context, JSObject* obj, char* name)
163
+ {
164
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
165
+
166
+ JohnsonContext* context;
167
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
168
+
169
+ VALUE self = (VALUE)JS_GetInstancePrivate(
170
+ context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
171
+
172
+ if (!self) return false;
173
+
174
+ return autovivified_p(ruby_context, self, name)
175
+ || const_p(self, name)
176
+ || global_p(name)
177
+ || attribute_p(self, name)
178
+ || method_p(self, name)
179
+ || has_key_p(self, name);
180
+ }
181
+
182
+ static jsval evaluate_js_property_expression(JohnsonRuntime * runtime, const char * property, jsval* retval) {
183
+ JSContext * context = johnson_get_current_context(runtime);
184
+ return JS_EvaluateScript(context, runtime->global,
185
+ property, strlen(property), "johnson:evaluate_js_property_expression", 1,
186
+ retval);
187
+ }
188
+
189
+ static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
190
+ {
191
+ // pull out our Ruby context, which is embedded in js_context
192
+
193
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
194
+
195
+ // get our struct, which is embedded in ruby_context
196
+
197
+ JohnsonContext* context;
198
+ JohnsonRuntime* runtime;
199
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
200
+
201
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
202
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
203
+
204
+ PREPARE_JROOTS(js_context, 1);
205
+ JROOT(id);
206
+
207
+ // get the Ruby object that backs this proxy
208
+
209
+ VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
210
+
211
+ // Short-circuit for numeric indexes
212
+
213
+ if (JSVAL_IS_INT(id))
214
+ {
215
+ if (indexable_p(self)) {
216
+ VALUE idx = INT2FIX(JSVAL_TO_INT(id));
217
+ JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("[]"), 1, idx));
218
+ }
219
+
220
+ JRETURN;
221
+ }
222
+
223
+ char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
224
+ VALUE ruby_id = rb_intern(name);
225
+
226
+ // FIXME: we should probably just JS_DefineProperty this, and it shouldn't be enumerable
227
+
228
+ if (!strcasecmp("__iterator__", name)) {
229
+ JCHECK(evaluate_js_property_expression(runtime, "Johnson.Generator.create", retval));
230
+ }
231
+
232
+ // if the Ruby object has a dynamic js property with a key
233
+ // matching the property we're looking for, pull the value out of
234
+ // that map.
235
+
236
+ else if (autovivified_p(ruby_context, self, name))
237
+ {
238
+ JCHECK(call_ruby_from_js(runtime, retval, Johnson_SpiderMonkey_JSLandProxy(),
239
+ rb_intern("autovivified"), 2, self, rb_str_new2(name)));
240
+ }
241
+
242
+ // if the Ruby object is a Module or Class and has a matching
243
+ // const defined, return the converted result of const_get
244
+
245
+ else if (const_p(self, name))
246
+ {
247
+ JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("const_get"),
248
+ 1, ID2SYM(ruby_id)));
249
+ }
250
+
251
+ // otherwise, if it's a global, return the global
252
+ else if (global_p(name))
253
+ {
254
+ JCHECK(convert_to_js(runtime, rb_gv_get(name), retval));
255
+ }
256
+
257
+ // otherwise, if the Ruby object has a an attribute method matching
258
+ // the property we're trying to get, call it and return the converted result
259
+
260
+ else if (attribute_p(self, name))
261
+ {
262
+ JCHECK(call_ruby_from_js(runtime, retval, self, ruby_id, 0));
263
+ }
264
+
265
+ // otherwise, if the Ruby object quacks sorta like a hash (it responds to
266
+ // "[]" and "key?"), index it by key and return the converted result
267
+
268
+ else if (has_key_p(self, name))
269
+ {
270
+ JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("[]"), 1, rb_str_new2(name)));
271
+ }
272
+
273
+ // otherwise, it's a method being accessed as a property, which means
274
+ // we need to return a lambda
275
+
276
+ // FIXME: this should really wrap the Method for 'name' in a JS class
277
+ // rather than generating a wrapper Proc
278
+
279
+ else if (method_p(self, name))
280
+ {
281
+ JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("method"), 1, rb_str_new2(name)));
282
+ }
283
+
284
+ // else it's undefined (JS_VOID) by default
285
+ JRETURN;
286
+ }
287
+
288
+ // called for lazily resolved properties, which should go away
289
+ static JSBool get_and_destroy_resolved_property(
290
+ JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
291
+ {
292
+ PREPARE_JROOTS(js_context, 1);
293
+ JROOT(id);
294
+ char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
295
+ JCHECK(JS_DeleteProperty(js_context, obj, name));
296
+ JCHECK(get(js_context, obj, id, retval));
297
+ JRETURN;
298
+ }
299
+
300
+ static JSBool set(JSContext* js_context, JSObject* obj, jsval id, jsval* value)
301
+ {
302
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
303
+
304
+ JohnsonContext* context;
305
+ JohnsonRuntime* runtime;
306
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
307
+
308
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
309
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
310
+
311
+ PREPARE_JROOTS(js_context, 2);
312
+ JROOT(id);
313
+ JROOT_PTR(value);
314
+
315
+ VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
316
+
317
+ // Short-circuit for numeric indexes
318
+
319
+ if (JSVAL_IS_INT(id))
320
+ {
321
+ if (indexable_p(self))
322
+ {
323
+ VALUE idx = INT2FIX(JSVAL_TO_INT(id));
324
+ VALUE val = CONVERT_TO_RUBY(runtime, *value);
325
+
326
+ JCHECK(call_ruby_from_js(runtime, NULL, self, rb_intern("[]="), 2, idx, val));
327
+ }
328
+
329
+ JRETURN;
330
+ }
331
+
332
+ VALUE ruby_key = CONVERT_TO_RUBY(runtime, id);
333
+ VALUE ruby_value = CONVERT_TO_RUBY(runtime, *value);
334
+
335
+ VALUE setter = rb_str_append(rb_str_new3(ruby_key), rb_str_new2("="));
336
+ VALUE setter_id = rb_intern(StringValueCStr(setter));
337
+
338
+ VALUE settable_p, indexable_p;
339
+ JCHECK(call_ruby_from_js2(runtime, &settable_p, self, rb_intern("respond_to?"), 1, ID2SYM(setter_id)));
340
+ JCHECK(call_ruby_from_js2(runtime, &indexable_p, self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]="))));
341
+
342
+ if (settable_p)
343
+ {
344
+ VALUE method, arity;
345
+ JCHECK(call_ruby_from_js2(runtime, &method, self, rb_intern("method"), 1, ID2SYM(setter_id)));
346
+ JCHECK(call_ruby_from_js2(runtime, &arity, method, rb_intern("arity"), 0));
347
+
348
+ // if the Ruby object has a 1-arity method named "property=",
349
+ // call it with the converted value
350
+
351
+ if (NUM2INT(arity) == 1)
352
+ JCHECK(call_ruby_from_js(runtime, NULL, self, setter_id, 1, ruby_value));
353
+ }
354
+ else if(indexable_p)
355
+ {
356
+ // otherwise, if the Ruby object quacks sorta like a hash for assignment
357
+ // (it responds to "[]="), assign it by key
358
+
359
+ JCHECK(call_ruby_from_js(runtime, NULL, self, rb_intern("[]="), 2, ruby_key, ruby_value));
360
+ }
361
+ else
362
+ {
363
+ JCHECK(call_ruby_from_js(runtime, NULL, Johnson_SpiderMonkey_JSLandProxy(), rb_intern("autovivify"),
364
+ 3, self, ruby_key, ruby_value));
365
+ }
366
+
367
+ JRETURN;
368
+ }
369
+
370
+ static JSBool construct(JSContext* js_context, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* retval)
371
+ {
372
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
373
+
374
+ JohnsonContext* context;
375
+ JohnsonRuntime* runtime;
376
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
377
+
378
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
379
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
380
+
381
+ PREPARE_JROOTS(js_context, 0);
382
+
383
+ VALUE klass = CONVERT_TO_RUBY(runtime, JS_ARGV_CALLEE(argv));
384
+ VALUE args = rb_ary_new();
385
+
386
+ uintN i;
387
+ for (i = 0; i < argc; ++i)
388
+ rb_ary_push(args, CONVERT_TO_RUBY(runtime, argv[i]));
389
+
390
+ JCHECK(call_ruby_from_js(runtime, retval, Johnson_SpiderMonkey_JSLandProxy(),
391
+ rb_intern("send_with_possible_block"), 3, klass, ID2SYM(rb_intern("new")), args));
392
+ JRETURN;
393
+ }
394
+
395
+ static JSBool resolve(JSContext *js_context, JSObject *obj, jsval id, uintN UNUSED(flags), JSObject **objp)
396
+ {
397
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
398
+
399
+ JohnsonContext* context;
400
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
401
+
402
+ PREPARE_JROOTS(js_context, 1);
403
+ JROOT(id);
404
+
405
+ char* name = JS_GetStringBytes(JS_ValueToString(js_context, id));
406
+
407
+ if (respond_to_p(js_context, obj, name))
408
+ {
409
+ JCHECK(JS_DefineProperty(js_context, obj, name, JSVAL_VOID,
410
+ get_and_destroy_resolved_property, set, JSPROP_ENUMERATE));
411
+
412
+ *objp = obj;
413
+ }
414
+
415
+ JRETURN;
416
+ }
417
+
418
+ static JSBool to_string(JSContext* js_context, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* retval)
419
+ {
420
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
421
+
422
+ JohnsonContext* context;
423
+ JohnsonRuntime* runtime;
424
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
425
+
426
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
427
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
428
+
429
+ PREPARE_JROOTS(js_context, 0);
430
+
431
+ VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
432
+
433
+ JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("to_s"), 0));
434
+ JRETURN;
435
+ }
436
+
437
+ static JSBool to_array(JSContext* js_context, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* retval)
438
+ {
439
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
440
+
441
+ JohnsonContext* context;
442
+ JohnsonRuntime* runtime;
443
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
444
+
445
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
446
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
447
+
448
+ PREPARE_JROOTS(js_context, 0);
449
+
450
+ VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
451
+
452
+ JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("to_a"), 0));
453
+ JRETURN;
454
+ }
455
+
456
+ static JSBool method_missing(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
457
+ {
458
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
459
+
460
+ JohnsonContext* context;
461
+ JohnsonRuntime* runtime;
462
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
463
+
464
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
465
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
466
+
467
+ PREPARE_JROOTS(js_context, 0);
468
+
469
+ VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
470
+
471
+ assert(argc >= 2);
472
+
473
+ char* key = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
474
+ VALUE ruby_id = rb_intern(key);
475
+
476
+ // FIXME: this is horrible and lazy, to_a comes from enumerable on proxy (argv[1] is a JSArray)
477
+ VALUE args;
478
+ JCHECK(call_ruby_from_js2(runtime, &args, CONVERT_TO_RUBY(runtime, argv[1]), rb_intern("to_a"), 0));
479
+
480
+ JCHECK(call_ruby_from_js(runtime, retval, Johnson_SpiderMonkey_JSLandProxy(),
481
+ rb_intern("send_with_possible_block"), 3, self, ID2SYM(ruby_id), args));
482
+
483
+ JRETURN;
484
+ }
485
+
486
+ static JSBool call(JSContext* js_context, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* retval)
487
+ {
488
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
489
+
490
+ JohnsonContext* context;
491
+ JohnsonRuntime* runtime;
492
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
493
+
494
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
495
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
496
+
497
+ PREPARE_JROOTS(js_context, 0);
498
+
499
+ VALUE self = (VALUE)JS_GetInstancePrivate(context->js, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), &JSLandCallableProxyClass, NULL);
500
+
501
+ VALUE args = rb_ary_new();
502
+
503
+ uintN i;
504
+ for (i = 0; i < argc; ++i)
505
+ rb_ary_push(args, CONVERT_TO_RUBY(runtime, argv[i]));
506
+
507
+ JCHECK(call_ruby_from_js(runtime, retval, Johnson_SpiderMonkey_JSLandProxy(),
508
+ rb_intern("send_with_possible_block"), 3, self, ID2SYM(rb_intern("call")), args));
509
+ JRETURN;
510
+ }
511
+
512
+ bool js_value_is_proxy(JohnsonRuntime* MAYBE_UNUSED(runtime), jsval maybe_proxy)
513
+ {
514
+ JSClass* klass = JS_GET_CLASS(
515
+ johnson_get_current_context(runtime),
516
+ JSVAL_TO_OBJECT(maybe_proxy));
517
+
518
+ return &JSLandProxyClass == klass
519
+ || &JSLandClassProxyClass == klass
520
+ || &JSLandCallableProxyClass == klass;
521
+ }
522
+
523
+ VALUE unwrap_js_land_proxy(JohnsonRuntime* runtime, jsval proxy)
524
+ {
525
+ VALUE value;
526
+ JSObject *proxy_object = JSVAL_TO_OBJECT(proxy);
527
+ JSContext * context = johnson_get_current_context(runtime);
528
+
529
+ value = (VALUE)JS_GetInstancePrivate(context, proxy_object,
530
+ JS_GET_CLASS(context, proxy_object), NULL);
531
+
532
+ return value;
533
+ }
534
+
535
+ static void finalize(JSContext* js_context, JSObject* obj)
536
+ {
537
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
538
+
539
+ if (ruby_context)
540
+ {
541
+ JohnsonContext* context;
542
+ JohnsonRuntime* runtime;
543
+ Data_Get_Struct(ruby_context, JohnsonContext, context);
544
+
545
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
546
+ Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);
547
+
548
+ VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj,
549
+ JS_GET_CLASS(context->js, obj), NULL);
550
+
551
+ // remove the proxy OID from the id map
552
+ JS_HashTableRemove(runtime->rbids, (void *)self);
553
+
554
+ // free up the ruby value for GC
555
+ call_ruby_from_js(runtime, NULL, ruby_runtime, rb_intern("remove_gcthing"), 1, self);
556
+ }
557
+ }
558
+
559
+ JSBool make_js_land_proxy(JohnsonRuntime* runtime, VALUE value, jsval* retval)
560
+ {
561
+ *retval = (jsval)JS_HashTableLookup(runtime->rbids, (void *)value);
562
+
563
+ if (*retval)
564
+ {
565
+ return JS_TRUE;
566
+ }
567
+ else
568
+ {
569
+ JSContext * context = johnson_get_current_context(runtime);
570
+ PREPARE_JROOTS(context, 1);
571
+
572
+ JSObject *jsobj;
573
+
574
+ JSClass *klass = &JSLandProxyClass;
575
+ if (T_CLASS == TYPE(value)) klass = &JSLandClassProxyClass;
576
+
577
+ // FIXME: hack; should happen in Rubyland
578
+ if (T_STRUCT == TYPE(value))
579
+ rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
580
+ rb_intern("treat_all_properties_as_methods"), 1, value);
581
+
582
+ bool callable_p = Qtrue == rb_funcall(value,
583
+ rb_intern("respond_to?"), 1, rb_str_new2("call"));
584
+
585
+ if (callable_p)
586
+ klass = &JSLandCallableProxyClass;
587
+
588
+ JCHECK((jsobj = JS_NewObject(context, klass, NULL, NULL)));
589
+ JROOT(jsobj);
590
+
591
+ JCHECK(JS_SetPrivate(context, jsobj, (void*)value));
592
+
593
+ JCHECK(JS_DefineFunction(context, jsobj, "__noSuchMethod__", method_missing, 2, 0));
594
+
595
+ JCHECK(JS_DefineFunction(context, jsobj, "toArray", to_array, 0, 0));
596
+ JCHECK(JS_DefineFunction(context, jsobj, "toString", to_string, 0, 0));
597
+
598
+ *retval = OBJECT_TO_JSVAL(jsobj);
599
+
600
+ // put the proxy OID in the id map
601
+ JCHECK(JS_HashTableAdd(runtime->rbids, (void *)value, (void *)(*retval)));
602
+
603
+ // root the ruby value for GC
604
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(runtime->js);
605
+ rb_funcall(ruby_runtime, rb_intern("add_gcthing"), 1, value);
606
+
607
+ JRETURN;
608
+ }
609
+ }