Added spec:commit task to commit changes to spec/ruby sources.
[rbx.git] / shotgun / lib / cpu.c
blob99c39b18c5e54ae309e1262aafb1b45e2ad90161
1 #include <stdlib.h>
3 #include "shotgun/lib/shotgun.h"
4 #include "shotgun/lib/cpu.h"
5 #include "shotgun/lib/machine.h"
6 #include "shotgun/lib/tuple.h"
7 #include "shotgun/lib/methctx.h"
8 #include "shotgun/lib/object.h"
9 #include "shotgun/lib/bytearray.h"
10 #include "shotgun/lib/string.h"
11 #include "shotgun/lib/module.h"
12 #include "shotgun/lib/class.h"
13 #include "shotgun/lib/hash.h"
14 #include "shotgun/lib/lookuptable.h"
15 #include "shotgun/lib/symbol.h"
17 cpu cpu_new(STATE) {
18 cpu c = (cpu)calloc(1, sizeof(struct rubinius_cpu));
19 c->paths = ptr_array_new(8);
20 return c;
23 void cpu_destroy(cpu c) {
24 /* BUG. Doesn't free the operand stacks. */
25 ptr_array_free(c->paths);
26 free(c);
29 void cpu_initialize(STATE, cpu c) {
30 state->global->tuple = Qnil;
31 state->global->hash = Qnil;
32 state->global->methtbl = Qnil;
33 c->stack_top = (OBJECT*)calloc(InitialStackSize, SIZE_OF_OBJECT);
34 c->sp_ptr = c->stack_top;
35 c->stack_size = InitialStackSize;
36 c->sp = 0;
37 c->ip = 0;
38 c->self = Qnil;
39 c->exception = Qnil;
40 c->enclosing_class = Qnil;
41 c->args = 0;
42 c->depth = 0;
43 c->call_flags = 0;
44 c->current_task = Qnil;
45 c->debug_channel = Qnil;
46 c->control_channel = Qnil;
48 cpu_sampler_init(state, c);
51 void cpu_setup_top_scope(STATE, cpu c) {
52 c->enclosing_class = state->global->object;
55 OBJECT cpu_scope_push(STATE, cpu c, OBJECT mod) {
56 OBJECT scope = staticscope_allocate(state);
57 staticscope_set_module(scope, mod);
58 staticscope_set_parent(scope, c->current_scope);
60 c->current_scope = scope;
61 return scope;
64 OBJECT cpu_scope_pop(STATE, cpu c) {
65 c->current_scope = staticscope_get_parent(c->current_scope);
66 return c->current_scope;
69 /* initializes VM core: from global methods, main routine to current scope, thread and so forth */
70 void cpu_initialize_context(STATE, cpu c) {
71 c->active_context = Qnil;
72 c->depth = 0;
73 c->home_context = c->active_context;
74 c->enclosing_class = state->global->object;
75 c->exception = Qnil;
76 c->main = object_new(state);
77 rbs_const_set(state, state->global->object, "MAIN", c->main);
79 state->global->method_missing = string_to_sym(state,
80 string_new(state, "method_missing"));
82 state->global->sym_inherited = string_to_sym(state,
83 string_new(state, "inherited"));
85 state->global->sym_method_added = string_to_sym(state,
86 string_new(state, "__method_added__"));
88 state->global->sym_s_method_added = string_to_sym(state,
89 string_new(state, "singleton_method_added"));
91 state->global->sym_plus = symbol_from_cstr(state, "+");
92 state->global->sym_minus = symbol_from_cstr(state, "-");
93 state->global->sym_equal = symbol_from_cstr(state, "==");
94 state->global->sym_nequal = symbol_from_cstr(state, "!=");
95 state->global->sym_tequal = symbol_from_cstr(state, "===");
96 state->global->sym_lt = symbol_from_cstr(state, "<");
97 state->global->sym_gt = symbol_from_cstr(state, ">");
98 state->global->sym_send = symbol_from_cstr(state, "__send__");
99 state->global->sym_public = symbol_from_cstr(state, "public");
100 state->global->sym_private = symbol_from_cstr(state, "private");
101 state->global->sym_protected = symbol_from_cstr(state, "protected");
102 state->global->sym_const_missing = SYM("const_missing");
103 state->global->sym_object_id = SYM("object_id");
104 state->global->sym_from_literal = SYM("from_literal");
105 state->global->sym_opened_class = SYM("opened_class");
106 state->global->sym_initialize = SYM("initialize");
107 state->global->sym_init_copy = SYM("initialize_copy");
108 state->global->sym_call = SYM("call");
110 c->current_thread = Qnil;
111 c->current_scope = staticscope_allocate(state);
112 staticscope_set_module(c->current_scope, BASIC_CLASS(object));
114 c->current_thread = cpu_thread_new(state, c);
115 c->main_thread = c->current_thread;
116 c->current_task = cpu_thread_get_task(state, c->current_thread);
117 c->main_task = c->current_task;
119 cpu_scope_push(state, c, BASIC_CLASS(object));
120 state->global->top_scope = c->current_scope;
123 void cpu_add_roots(STATE, cpu c, ptr_array roots) {
124 int i, len;
125 xpointer t;
126 #define ar(obj) if(REFERENCE_P(obj)) { \
127 ptr_array_append(roots, (xpointer)obj); \
130 ar(c->active_context);
131 ar(c->home_context);
132 ar(c->sender);
134 ar(c->self);
135 ar(c->exception);
136 ar(c->enclosing_class);
137 ar(c->main);
138 ar(c->current_thread);
139 ar(c->main_thread);
140 ar(c->current_task);
141 ar(c->main_task);
142 ar(c->debug_channel);
143 ar(c->control_channel);
144 ar(c->current_scope);
145 ar(c->locals);
146 len = ptr_array_length(c->paths);
147 ptr_array_append(roots, (xpointer)I2N(len));
148 // printf("Paths: %d\n", len);
149 for(i = 0; i < len; i++) {
150 t = ptr_array_remove_index_ordered(c->paths, 0);
151 //printf("Pulled %s out of paths.\n", _inspect(t));
152 ar(t);
154 //printf("Paths should be empty: %d\n", c->paths->len);
156 #undef ar
159 int cpu_ip2line(STATE, OBJECT meth, int ip) {
160 OBJECT lines, tup;
161 int l, total, start, nd, op;
163 if(meth->obj_type != CMethodType) return 0;
165 lines = cmethod_get_lines(meth);
166 total = NUM_FIELDS(lines);
167 for(l = 0; l < total; l++) {
168 tup = tuple_at(state, lines, l);
169 start = N2I(tuple_at(state, tup, 0));
170 nd = N2I(tuple_at(state, tup, 1));
171 op = N2I(tuple_at(state, tup, 2));
173 if(ip >= start && ip <= nd) {
174 return op;
178 return 0;
181 void cpu_update_roots(STATE, cpu c, ptr_array roots, int start) {
182 xpointer tmp;
183 int i, len;
184 #define ar(obj) if(REFERENCE_P(obj)) { \
185 tmp = ptr_array_get_index(roots, start++); \
186 obj = (OBJECT)tmp; \
189 ar(c->active_context);
190 ar(c->home_context);
191 ar(c->sender);
193 ar(c->self);
194 ar(c->exception);
195 ar(c->enclosing_class);
196 ar(c->main);
197 ar(c->current_thread);
198 ar(c->main_thread);
199 ar(c->current_task);
200 ar(c->main_task);
201 ar(c->debug_channel);
202 ar(c->control_channel);
203 ar(c->current_scope);
204 ar(c->locals);
205 tmp = ptr_array_get_index(roots, start++);
206 len = N2I((OBJECT)tmp);
207 for(i = 0; i < len; start++, i++) {
208 tmp = ptr_array_get_index(roots, start);
209 //printf("Adding path %s back in...\n", _inspect(tmp));
210 ptr_array_append(c->paths, tmp);
212 //printf("Paths is %d\n", c->paths->len);
213 #undef ar
215 cpu_flush_ip(c);
218 OBJECT cpu_new_exception(STATE, cpu c, OBJECT klass, const char *msg) {
219 OBJECT obj, str;
221 obj = class_new_instance(state, klass);
222 str = string_new(state, msg);
223 exception_set_message(obj, str);
224 methctx_reference(state, c->active_context);
225 exception_set_context(obj, c->active_context);
226 return obj;
229 OBJECT cpu_new_exception2(STATE, cpu c, OBJECT klass, const char *msg, ...) {
230 OBJECT obj, str;
231 static char buffer[1024];
232 int count;
233 va_list ap;
235 va_start(ap, msg);
236 count = vsnprintf(buffer, 1024, msg, ap);
237 va_end(ap);
239 obj = class_new_instance(state, klass);
240 str = string_new2(state, buffer, count);
241 exception_set_message(obj, str);
242 methctx_reference(state, c->active_context);
243 exception_set_context(obj, c->active_context);
244 return obj;
248 OBJECT cpu_const_get_in_context(STATE, cpu c, OBJECT sym) {
249 OBJECT cur, klass, start, tbl, val;
250 OBJECT cref, cbase;
252 /* Look up the lexical scope first */
254 cref = cpu_current_scope(state, c);
255 if(NIL_P(cref)) {
256 start = state->global->object;
257 } else {
258 cbase = cref;
260 while(!NIL_P(cbase)) {
261 klass = staticscope_get_module(cbase);
263 /* If we hit Object in the chain, we stop there. */
264 if(klass == state->global->object) break;
266 tbl = module_get_constants(klass);
267 val = lookuptable_find(state, tbl, sym);
268 if(val != Qundef) return val;
270 cbase = staticscope_get_parent(cbase);
273 start = cur = staticscope_get_module(cref);
275 while(!NIL_P(cur)) {
277 tbl = module_get_constants(cur);
278 val = lookuptable_find(state, tbl, sym);
279 if(val != Qundef) return val;
280 cur = module_get_superclass(cur);
284 // As a last rescue, we search in Object's constants
285 tbl = module_get_constants(state->global->object);
286 val = lookuptable_find(state, tbl, sym);
287 if(val != Qundef) return val;
289 stack_push(sym);
290 cpu_send(state, c, start, state->global->sym_const_missing, 1, Qnil);
291 return Qundef;
294 OBJECT cpu_const_get_from(STATE, cpu c, OBJECT sym, OBJECT under) {
295 OBJECT cur, tbl, val;
296 char *str;
298 // printf("Looking for %s under %s.\n", rbs_symbol_to_cstring(state, sym), rbs_symbol_to_cstring(state, module_get_name(under)));
300 if(!(RISA(under, class) || RISA(under, module))) {
301 str = malloc(256);
302 snprintf(str, 256, "%s is not a class/module", _inspect(under));
303 cpu_raise_exception(state, c,
304 cpu_new_exception(state, c, state->global->exc_type, str));
305 free(str);
306 return Qundef;
309 cur = under;
311 while(!NIL_P(cur)) {
312 // printf(" looking in %s\n", rbs_symbol_to_cstring(state, module_get_name(cur)));
314 tbl = module_get_constants(cur);
315 val = lookuptable_find(state, tbl, sym);
316 if(val != Qundef) {
317 return val;
319 /* Object's superclass MUST be nil, but we check directly just
320 to be safe. */
321 if(cur == state->global->object) break;
322 cur = class_get_superclass(cur);
325 // Didn't find it, so fire const_missing
326 stack_push(sym);
327 cpu_send(state, c, under, state->global->sym_const_missing, 1, Qnil);
328 return Qundef;
331 OBJECT cpu_const_get(STATE, cpu c, OBJECT sym, OBJECT under) {
332 return cpu_const_get_from(state, c, sym, under);
335 OBJECT cpu_const_set(STATE, cpu c, OBJECT sym, OBJECT val, OBJECT under) {
336 OBJECT tbl;
338 tbl = module_get_constants(under);
339 lookuptable_store(state, tbl, sym, val);
340 return val;
343 void cpu_set_encloser_path(STATE, cpu c, OBJECT cls) {
344 int len;
345 OBJECT method;
346 len = ptr_array_length(c->paths);
347 ptr_array_append(c->paths, (xpointer)c->enclosing_class);
348 method = cpu_current_method(state, c);
350 cmethod_set_staticscope(method, cpu_scope_push(state, c, cls));
351 c->enclosing_class = cls;
354 void cpu_push_encloser(STATE, cpu c) {
355 int len;
356 len = ptr_array_length(c->paths);
357 if(len > 0) {
358 c->enclosing_class = (OBJECT)ptr_array_remove_index_ordered(c->paths, len - 1);
359 cpu_scope_pop(state, c);
363 /* Increments serial numbers up the superclass chain. */
364 static void cpu_increment_serials(STATE, OBJECT module, OBJECT sym) {
365 OBJECT tbl, meth;
367 while(!NIL_P(module)) {
368 tbl = module_get_method_table(module);
369 meth = lookuptable_fetch(state, tbl, sym);
371 if(REFERENCE_P(meth)) {
372 if(CLASS_OBJECT(meth) == BASIC_CLASS(tuple)) {
373 meth = tuple_at(state, meth, 1);
375 fast_inc(meth, CMETHOD_f_SERIAL);
378 module = class_get_superclass(module);
382 void cpu_add_method(STATE, cpu c, OBJECT target, OBJECT sym, OBJECT method) {
383 OBJECT meths, vis, cref;
385 if(!ISA(target, BASIC_CLASS(module))) {
386 cref = cmethod_get_staticscope(cpu_current_method(state, c));
387 if(NIL_P(cref)) {
388 target = state->global->object;
389 } else {
390 target = staticscope_get_module(cref);
394 cpu_clear_cache_for_method(state, c, sym, FALSE);
396 cpu_increment_serials(state, target, sym);
397 meths = module_get_method_table(target);
399 switch(c->call_flags) {
400 default:
401 case 0:
402 vis = state->global->sym_public;
403 break;
404 case 1:
405 vis = state->global->sym_private;
406 break;
407 case 2:
408 vis = state->global->sym_protected;
409 break;
412 /* force initialize to be private. */
413 if(sym == state->global->sym_initialize) {
414 vis = state->global->sym_private;
417 if(EXCESSIVE_TRACING) {
418 printf("=> Adding method %s to %s.\n", rbs_symbol_to_cstring(state, sym), _inspect(target));
421 // HACK. the 10 sucks, it protects things that go in a method table, but
422 // aren't exactly CompiledMethods.
423 // A method inherits the static scope of the method that that add/attaches it.
424 if(NUM_FIELDS(method) > 10 && NIL_P(cmethod_get_staticscope(method))) {
425 cmethod_set_staticscope(method, cpu_current_scope(state, c));
428 lookuptable_store(state, meths, sym, tuple_new2(state, 2, vis, method));
429 c->call_flags = 0;
432 void cpu_attach_method(STATE, cpu c, OBJECT target, OBJECT sym, OBJECT method) {
433 OBJECT meta;
434 meta = object_metaclass(state, target);
435 /* static visibility scope doesn't impact singleton classes.
436 we force it to public everytime it's used. */
437 c->call_flags = 0;
438 cpu_add_method(state, c, meta, sym, method);
441 /* Updates the cpu registers by reading out of the active context.
442 These get out of sync when the GC runs. */
443 void cpu_hard_cache(STATE, cpu c) {
444 struct fast_context *fc;
446 cpu_flush_ip(c);
448 fc = (struct fast_context*)BYTES_OF(c->active_context);
449 c->data = fc->data;
451 cpu_cache_ip(c);