Fixed remaining usage of MAX_OPTS/MAX_ARGS in the VM.
[cslatevm.git] / src / vm / method.cpp
blob8620f051f12e37502cc8f43f588fabdc09fc0dcf
1 #include "slate.hpp"
4 word_t method_pic_hash(struct object_heap* oh, struct CompiledMethod* callerMethod, word_t arity, struct Object* args[]) {
5 word_t arraySize = array_size(callerMethod->calleeCount);
6 word_t entryStart = (hash_selector(oh, NULL, args, arity) % (arraySize/CALLER_PIC_ENTRY_SIZE)) * CALLER_PIC_ENTRY_SIZE;
7 return entryStart;
12 void method_save_cache(struct object_heap* oh, struct MethodDefinition* md, struct Symbol* name, struct Object* arguments[], word_t n) {
13 struct MethodCacheEntry* cacheEntry;
14 word_t i;
15 word_t hash = hash_selector(oh, name, arguments, n);
16 assert(n <= METHOD_CACHE_ARITY);
17 cacheEntry = &oh->methodCache[hash & (METHOD_CACHE_SIZE-1)];
18 cacheEntry->method = md;
19 cacheEntry->selector = name;
20 for (i = 0; i < n; i++) {
21 cacheEntry->maps[i] = object_get_map(oh, arguments[i]);
26 struct MethodDefinition* method_check_cache(struct object_heap* oh, struct Symbol* selector, struct Object* arguments[], word_t n) {
27 struct MethodCacheEntry* cacheEntry;
28 word_t hash = hash_selector(oh, selector, arguments, n);
29 assert(n <= METHOD_CACHE_ARITY);
30 oh->method_cache_access++;
31 cacheEntry = &oh->methodCache[hash & (METHOD_CACHE_SIZE-1)];
32 if (cacheEntry->selector != selector) return NULL;
34 /*fix findOn: might not call us with enough elements*/
35 if (object_to_smallint(selector->cacheMask) > (1 << n)) return NULL;
37 switch (object_to_smallint(selector->cacheMask)) { /*the found positions for the function*/
38 case 1: if (object_get_map(oh, arguments[0]) != cacheEntry->maps[0]) return NULL; break;
39 case 2: if (object_get_map(oh, arguments[1]) != cacheEntry->maps[1]) return NULL; break;
40 case 4: if (object_get_map(oh, arguments[2]) != cacheEntry->maps[2]) return NULL; break;
42 case 3: if (object_get_map(oh, arguments[0]) != cacheEntry->maps[0] ||
43 object_get_map(oh, arguments[1]) != cacheEntry->maps[1]) return NULL; break;
44 case 5: if (object_get_map(oh, arguments[0]) != cacheEntry->maps[0] ||
45 object_get_map(oh, arguments[2]) != cacheEntry->maps[2]) return NULL; break;
46 case 6: if (object_get_map(oh, arguments[1]) != cacheEntry->maps[1] ||
47 object_get_map(oh, arguments[2]) != cacheEntry->maps[2]) return NULL; break;
49 case 7: if (object_get_map(oh, arguments[0]) != cacheEntry->maps[0] ||
50 object_get_map(oh, arguments[1]) != cacheEntry->maps[1] ||
51 object_get_map(oh, arguments[2]) != cacheEntry->maps[2]) return NULL; break;
53 oh->method_cache_hit++;
54 return cacheEntry->method;
61 * This is the main dispatch function
64 struct MethodDefinition* method_dispatch_on(struct object_heap* oh, struct Symbol* name,
65 struct Object* arguments[], word_t arity, struct Object* resendMethod) {
67 struct MethodDefinition *dispatch, *bestDef;
68 struct Object* slotLocation;
69 word_t bestRank, depth, delegationCount, resendRank, restricted, i;
72 #ifdef PRINT_DEBUG_DISPATCH
73 printf("dispatch to: '");
74 print_symbol(name);
75 printf("' (arity: %" PRIdPTR ")\n", arity);
76 for (i = 0; i < arity; i++) {
77 printf("arguments[%" PRIdPTR "] (%p) = ", i, (void*)arguments[i]); print_type(oh, arguments[i]);
79 /* printf("resend: "); print_object(resendMethod);*/
80 #endif
82 dispatch = NULL;
83 slotLocation = NULL;
85 #ifndef SLATE_DISABLE_METHOD_CACHE
86 if (resendMethod == NULL && arity <= METHOD_CACHE_ARITY) {
87 dispatch = method_check_cache(oh, name, arguments, arity);
88 if (dispatch != NULL) return dispatch;
90 #endif
92 oh->current_dispatch_id++;
93 bestRank = 0;
94 bestDef = NULL;
95 resendRank = ((resendMethod == NULL) ? WORDT_MAX : 0);
97 for (i = 0; i < arity; i++) {
98 struct Object *obj;
99 struct Map* map;
100 struct Object* arg = arguments[i];
101 delegationCount = 0;
102 depth = 0;
103 restricted = WORDT_MAX; /*pointer in delegate_stack (with sp of delegateCount) to where we don't trace further*/
105 do {
106 /* Set up obj to be a pointer to the object, or SmallInteger if it's
107 a direct SmallInt. */
108 if (object_is_smallint(arg)) {
109 obj = get_special(oh, SPECIAL_OOP_SMALL_INT_PROTO);
110 } else {
111 obj = arg;
113 /* Identify the map, and update its dispatchID and reset the visited mask
114 if it hasn't been visited during this call already. */
115 map = obj->map;
117 if (map->dispatchID != oh->current_dispatch_id) {
118 map->dispatchID = oh->current_dispatch_id;
119 map->visitedPositions = 0;
122 /* we haven't been here before */
123 if ((map->visitedPositions & (1 << i)) == 0) {
125 struct RoleEntry* role;
127 /* If the map marks an obj-meta transition and the top of the stack
128 is not the original argument, then mark the restriction point at
129 the top of the delegation stack. */
131 if (((word_t)map->flags & MAP_FLAG_RESTRICT_DELEGATION) && (arg != arguments[i])) {
132 restricted = delegationCount;
134 map->visitedPositions |= (1 << i);
135 role = role_table_entry_for_name(oh, map->roleTable, name);
136 while (role != NULL) {
137 if ((object_to_smallint(role->rolePositions) & (1 << i)) != 0) {
138 struct MethodDefinition* def = role->methodDefinition;
139 /* If the method hasn't been visited this time, mark it
140 so and clear the other dispatch marks.*/
141 if (def->dispatchID != oh->current_dispatch_id) {
142 def->dispatchID = oh->current_dispatch_id;
143 def->foundPositions = 0;
144 def->dispatchRank = 0;
146 /*If the method hasn't been found at this position...*/
147 if ((def->foundPositions & (1 << i)) == 0) {
148 /*fix*/
150 def->dispatchRank |= ((31 - depth) << ((5 - i) * 5));
151 def->foundPositions |= (1 << i);
153 #ifdef PRINT_DEBUG_FOUND_ROLE
154 printf("found role index %" PRIdPTR " <%p> for '%s' foundPos: %" PRIuPTR "x dispatchPos: %" PRIuPTR "x\n",
156 (void*) role,
157 ((struct Symbol*)(role->name))->elements, def->foundPositions, def->dispatchPositions);
158 #endif
160 if (def->method == resendMethod) {
161 struct RoleEntry* rescan = role_table_entry_for_name(oh, map->roleTable, name);
162 resendRank = def->dispatchRank;
163 while (rescan != role) {
164 struct MethodDefinition* redef = rescan->methodDefinition;
165 if (redef->foundPositions == redef->dispatchPositions &&
166 (dispatch == NULL || redef->dispatchRank <= resendRank)) {
167 dispatch = redef;
168 slotLocation = obj;
169 if (redef->dispatchRank > bestRank) {
170 bestRank = redef->dispatchRank;
171 bestDef = redef;
174 if (rescan->nextRole == oh->cached.nil) {
175 rescan = NULL;
176 } else {
177 rescan = &map->roleTable->roles[object_to_smallint(rescan->nextRole)];
182 } else /*not a resend*/ {
183 if (def->foundPositions == def->dispatchPositions &&
184 (dispatch == NULL || def->dispatchRank > dispatch->dispatchRank) &&
185 def->dispatchRank <= resendRank) {
186 dispatch = def;
187 slotLocation = obj;
188 if (def->dispatchRank > bestRank) {
189 bestRank = def->dispatchRank;
190 bestDef = def;
194 if (def->dispatchRank >= bestRank && def != bestDef) {
195 bestRank = def->dispatchRank;
196 bestDef = NULL;
201 role = ((role->nextRole == oh->cached.nil) ? NULL : &map->roleTable->roles[object_to_smallint(role->nextRole)]);
202 } /*while role != NULL*/
204 if (depth > 31) { /*fix wordsize*/
205 assert(0);
207 if (dispatch != NULL && bestDef == dispatch) {
208 if (dispatch->slotAccessor != oh->cached.nil) {
209 arguments[0] = slotLocation;
210 /*do we need to try to do a heap_store_into?*/
211 #ifdef PRINT_DEBUG_DISPATCH_SLOT_CHANGES
212 printf("arguments[0] changed to slot location: \n");
213 print_detail(oh, arguments[0]);
214 #endif
216 if (resendMethod == 0 && arity <= METHOD_CACHE_ARITY) method_save_cache(oh, dispatch, name, arguments, arity);
217 return dispatch;
220 depth++;
223 /* We add the delegates to the list when we didn't just finish checking a restricted object*/
224 if (delegationCount <= restricted && array_size(map->delegates) > 0) {
225 struct OopArray* delegates = map->delegates;
226 word_t offset = object_array_offset((struct Object*)delegates);
227 word_t limit = object_total_size((struct Object*)delegates);
228 for (; offset != limit; offset += sizeof(word_t)) {
229 struct Object* delegate = object_slot_value_at_offset((struct Object*)delegates, offset);
230 if (delegate != oh->cached.nil) {
231 oh->delegation_stack[delegationCount++] = delegate;
236 } /*end haven't been here before*/
240 delegationCount--;
241 if (delegationCount < restricted) restricted = WORDT_MAX; /*everything is unrestricted now*/
243 if (delegationCount < 0 || delegationCount >= DELEGATION_STACK_SIZE) break;
245 arg = oh->delegation_stack[delegationCount];
248 } while (1);
253 if (dispatch != NULL && dispatch->slotAccessor != oh->cached.nil) {
254 /*check heap store into?*/
255 arguments[0] = slotLocation;
256 #ifdef PRINT_DEBUG_DISPATCH_SLOT_CHANGES
257 printf("arguments[0] changed to slot location: \n");
258 print_detail(oh, arguments[0]);
259 #endif
263 #ifndef SLATE_DISABLE_METHOD_CACHE
264 if (dispatch != NULL && resendMethod == 0 && arity < METHOD_CACHE_ARITY) {
265 method_save_cache(oh, dispatch, name, arguments, arity);
267 #endif
269 return dispatch;
273 bool method_on_call_stack(struct object_heap* oh, struct CompiledMethod* method) {
275 struct Interpreter* i = oh->cached.interpreter;
276 word_t fp = i->framePointer;
277 struct Closure* closure = (struct Closure*)i->method;
278 do {
279 if (closure->method == method) return true;
280 fp = object_to_smallint(i->stack->elements[fp - FRAME_OFFSET_PREVIOUS_FRAME_POINTER]);
281 if (fp < FUNCTION_FRAME_SIZE) break;
282 closure = (struct Closure*)i->stack->elements[fp - FRAME_OFFSET_METHOD];
283 } while (fp >= FUNCTION_FRAME_SIZE);
285 if (closure->method == method) return true;
286 return false;
290 void method_unoptimize(struct object_heap* oh, struct CompiledMethod* method) {
291 #ifdef PRINT_DEBUG_UNOPTIMIZER
292 printf("Unoptimizing '"); print_symbol(method->selector); printf("'\n");
293 #endif
294 if (method_on_call_stack(oh, method)) {
295 printf("Fixme cannot unoptimizing because on call stack: '"); print_symbol(method->selector); printf("'\n");
296 return;
299 method->code = method->oldCode;
300 heap_store_into(oh, (struct Object*) method->code, (struct Object*) method->oldCode);
301 method->isInlined = oh->cached.false_object;
302 method->calleeCount = (struct OopArray*)oh->cached.nil;
303 method->callCount = smallint_to_object(0);
304 oh->optimizedMethods.erase(method);
309 void method_remove_optimized_sending(struct object_heap* oh, struct Symbol* symbol) {
310 if (oh->optimizedMethods.empty()) return;
311 for (std::multiset<struct CompiledMethod*>::iterator i = oh->optimizedMethods.begin(); i != oh->optimizedMethods.end(); i++) {
312 struct CompiledMethod* method = *i;
313 /*resend?*/
314 if (method->selector == symbol) {
315 method_unoptimize(oh, method);
316 continue;
318 for (int j = 0; j < array_size(method->selectors); j++) {
319 if (array_elements(method->selectors)[j] == (struct Object*)symbol) {
320 method_unoptimize(oh, method);
321 break;
330 void method_optimize(struct object_heap* oh, struct CompiledMethod* method) {
332 /* only optimize old objects because they don't move in memory and
333 * we don't want to have to update our method cache every gc */
334 if (object_is_young(oh, (struct Object*)method)) return;
337 // make sure we don't optimize something already on the stack
338 // since a return will put us possibly at a different op code
339 if (method_on_call_stack(oh, method)) {
340 return;
343 if (!optimizer_method_can_be_optimized(oh, method)) {
344 //don't bother trying anymore
345 method->isInlined = oh->cached.true_object;
346 method->oldCode = method->code;
347 method->nextInlineAtCallCount = oh->cached.nil;
348 return;
351 #ifdef PRINT_DEBUG_OPTIMIZER
352 printf("Optimizing '"); print_symbol(method->selector); printf("'\n");
353 #endif
356 #ifdef PRINT_DEBUG_OPTIMIZER2
357 method_print_debug_info(oh, method);
358 printf("This method is called by:\n");
359 method_print_debug_info(oh, oh->cached.interpreter->method);
360 #endif
362 if ((struct Object*)method->oldCode == oh->cached.nil) {
363 method->oldCode = method->code;
364 heap_store_into(oh, (struct Object*) method->oldCode, (struct Object*) method->code);
367 //whether to start with a fresh slate
368 //not starting may give us another degree of inlining
369 heap_store_into(oh, (struct Object*) method->oldCode, (struct Object*) method->code);
371 method->isInlined = oh->cached.true_object;
372 oh->optimizedMethods.insert(method);
373 #ifdef SLATE_SHOW_INLINER_CODE
374 printf("before:\n");
375 print_type(oh, (struct Object*)method->selector);
376 print_code_disassembled(oh, method->code);
377 #endif
378 optimizer_inline_callees(oh, method);
379 #ifdef SLATE_SHOW_INLINER_CODE
380 printf("after:\n");
381 print_code_disassembled(oh, method->code);
382 #endif
386 void method_pic_setup(struct object_heap* oh, struct CompiledMethod* caller) {
388 caller->calleeCount = heap_clone_oop_array_sized(oh, get_special(oh, SPECIAL_OOP_ARRAY_PROTO), CALLER_PIC_SIZE*CALLER_PIC_ENTRY_SIZE);
389 heap_store_into(oh, (struct Object*) caller, (struct Object*) caller->calleeCount);
393 struct MethodDefinition* method_pic_match_selector(struct object_heap* oh, struct Object* picEntry[],
394 struct Symbol* selector,
395 word_t arity, struct Object* args[], word_t incrementWhenFound) {
396 struct Closure* closure = (struct Closure*) ((struct MethodDefinition*)picEntry[PIC_CALLEE])->method;
397 struct Symbol* methodSelector;
398 /*primitive methods store their selector in a different place*/
399 if (closure->base.map->delegates->elements[0] == oh->cached.primitive_method_window) {
400 methodSelector = (struct Symbol*)((struct PrimitiveMethod*)closure)->selector;
401 } else {
402 methodSelector = closure->method->selector;
405 if (methodSelector == selector
406 && object_to_smallint(picEntry[PIC_CALLEE_ARITY]) == arity) {
407 /*callee method and arity work out, check the maps*/
408 word_t j, success = 1;
409 for (j = 0; j < arity; j++) {
410 if ((struct Map*)((struct OopArray*)picEntry[PIC_CALLEE_MAPS])->elements[j] != object_get_map(oh, args[j])) {
411 success = 0;
412 break;
415 if (success) {
416 if (incrementWhenFound) {
417 picEntry[PIC_CALLEE_COUNT] = smallint_to_object(object_to_smallint(picEntry[PIC_CALLEE_COUNT]) + 1);
419 return (struct MethodDefinition*)picEntry[PIC_CALLEE];
422 return NULL;
426 void method_pic_insert(struct object_heap* oh, struct OopArray* calleeCount,
427 struct Object* picEntry[], struct MethodDefinition* def,
428 word_t arity, struct Object* args[]) {
430 word_t j;
431 picEntry[PIC_CALLEE] = (struct Object*)def;
432 picEntry[PIC_CALLEE_ARITY] = smallint_to_object(arity);
433 picEntry[PIC_CALLEE_COUNT] = smallint_to_object(1);
434 picEntry[PIC_CALLEE_MAPS] =
435 (struct Object*)heap_clone_oop_array_sized(oh, get_special(oh, SPECIAL_OOP_ARRAY_PROTO), arity);
437 heap_store_into(oh, (struct Object*) calleeCount, picEntry[PIC_CALLEE]);
438 heap_store_into(oh, (struct Object*) calleeCount, picEntry[PIC_CALLEE_MAPS]);
440 for (j = 0; j < arity; j++) {
441 ((struct OopArray*)picEntry[PIC_CALLEE_MAPS])->elements[j] = (struct Object*)object_get_map(oh, args[j]);
442 heap_store_into(oh, picEntry[PIC_CALLEE_MAPS], ((struct OopArray*)picEntry[PIC_CALLEE_MAPS])->elements[j]);
447 void method_pic_flush_caller_pics(struct object_heap* oh, struct CompiledMethod* callee) {
449 int i;
450 if (callee->base.map->delegates->elements[0] == oh->cached.closure_method_window) callee = callee->method;
451 assert (callee->base.map->delegates->elements[0] == oh->cached.compiled_method_window);
452 if (!object_is_smallint(callee->cachedInCallersCount)) return;
453 for (i = 0; i < object_to_smallint(callee->cachedInCallersCount); i++) {
454 /*this should reset the pic*/
455 method_pic_setup(oh, (struct CompiledMethod*)callee->cachedInCallers->elements[i]);
461 /*when a function is redefined, we need to know what PICs to flush. Here each method will
462 keep a list of all the pics that it is in */
463 void method_pic_add_callee_backreference(struct object_heap* oh,
464 struct CompiledMethod* caller, struct CompiledMethod* callee) {
466 if (callee->base.map->delegates->elements[0] == oh->cached.closure_method_window) callee = callee->method;
467 if (callee->base.map->delegates->elements[0] == oh->cached.primitive_method_window) return;
469 assert (callee->base.map->delegates->elements[0] == oh->cached.compiled_method_window);
471 if ((struct Object*)callee->cachedInCallers == oh->cached.nil) {
472 callee->cachedInCallers = heap_clone_oop_array_sized(oh, get_special(oh, SPECIAL_OOP_ARRAY_PROTO), 32);
473 heap_store_into(oh, (struct Object*)callee, (struct Object*)callee->cachedInCallers);
474 callee->cachedInCallersCount = smallint_to_object(0);
477 if (object_to_smallint(callee->cachedInCallersCount) >= array_size(callee->cachedInCallers)) {
478 struct OopArray* newArray = heap_clone_oop_array_sized(oh, get_special(oh, SPECIAL_OOP_ARRAY_PROTO), array_size(callee->cachedInCallers) * 2);
479 copy_words_into(callee->cachedInCallers->elements, array_size(callee->cachedInCallers), newArray->elements);
480 callee->cachedInCallers = newArray;
481 heap_store_into(oh, (struct Object*)callee, (struct Object*)callee->cachedInCallers);
484 callee->cachedInCallers->elements[object_to_smallint(callee->cachedInCallersCount)] = (struct Object*)caller;
485 heap_store_into(oh, (struct Object*)callee->cachedInCallers, (struct Object*)caller);
486 callee->cachedInCallersCount = smallint_to_object(object_to_smallint(callee->cachedInCallersCount) + 1);
490 void method_pic_add_callee(struct object_heap* oh, struct CompiledMethod* callerMethod, struct MethodDefinition* def,
491 word_t arity, struct Object* args[]) {
492 word_t i;
493 word_t arraySize = array_size(callerMethod->calleeCount);
494 word_t entryStart = method_pic_hash(oh, callerMethod, arity, args);
496 #if 0
497 if (callerMethod->calleeCount->elements[i+PIC_CALLEE] == oh->cached.nil) {
498 Pinned<struct OopArray> pinArray(oh);
499 pinArray = callerMethod->calleeCount;
500 method_pic_insert(oh, callerMethod->calleeCount, &callerMethod->calleeCount->elements[i], def, arity, args);
501 Pinned<struct CompiledMethod> defMethod(oh);
502 defMethod = (struct CompiledMethod*) def->method;
503 method_pic_add_callee_backreference(oh, callerMethod, (struct CompiledMethod*) defMethod);
504 return;
507 /*this old slow code will fill up the hash table instead of only looking at the entry where the hash lands us*/
508 #else
509 for (i = entryStart; i < arraySize; i+= CALLER_PIC_ENTRY_SIZE) {
510 /* if it's nil, we need to insert it*/
511 if (callerMethod->calleeCount->elements[i+PIC_CALLEE] == oh->cached.nil) {
512 Pinned<struct OopArray> pinArray(oh);
513 pinArray = callerMethod->calleeCount;
514 method_pic_insert(oh, callerMethod->calleeCount, &callerMethod->calleeCount->elements[i], def, arity, args);
515 Pinned<struct CompiledMethod> defMethod(oh);
516 defMethod = (struct CompiledMethod*) def->method;
517 method_pic_add_callee_backreference(oh, callerMethod, (struct CompiledMethod*) defMethod);
518 return;
521 for (i = 0; i < entryStart; i+= CALLER_PIC_ENTRY_SIZE) {
522 /*MUST be same as first loop*/
523 if (callerMethod->calleeCount->elements[i+PIC_CALLEE] == oh->cached.nil) {
524 method_pic_insert(oh, callerMethod->calleeCount, &callerMethod->calleeCount->elements[i], def, arity, args);
525 Pinned<struct CompiledMethod> defMethod(oh);
526 defMethod = (struct CompiledMethod*) def->method;
527 method_pic_add_callee_backreference(oh, callerMethod, (struct CompiledMethod*)defMethod);
528 return;
531 #endif
536 struct MethodDefinition* method_pic_find_callee(struct object_heap* oh, struct CompiledMethod* callerMethod,
537 struct Symbol* selector, word_t arity, struct Object* args[]) {
539 #ifdef SLATE_DISABLE_PIC_LOOKUP
540 return NULL;
541 #endif
543 Pinned<struct MethodDefinition> retval(oh);
544 word_t i;
545 word_t arraySize = array_size(callerMethod->calleeCount);
546 word_t entryStart = method_pic_hash(oh, callerMethod, arity, args);
549 #if 0
550 if (callerMethod->calleeCount->elements[i+PIC_CALLEE] == oh->cached.nil) return NULL;
551 retval = method_pic_match_selector(oh, &callerMethod->calleeCount->elements[i], selector, arity, args, TRUE);
552 if ((struct MethodDefinition*)retval != NULL) return retval;
553 return NULL; /*only look at first match*/
555 #else /*this old code goes through the whole hash table which will take a while in a bad case*/
556 for (i = entryStart; i < arraySize; i+= CALLER_PIC_ENTRY_SIZE) {
557 if (callerMethod->calleeCount->elements[i+PIC_CALLEE] == oh->cached.nil) return NULL;
558 retval = method_pic_match_selector(oh, &callerMethod->calleeCount->elements[i], selector, arity, args, TRUE);
559 if ((struct MethodDefinition*)retval != NULL) return retval;
561 for (i = 0; i < entryStart; i+= CALLER_PIC_ENTRY_SIZE) {
562 /*MUST be same as first loop*/
563 if (callerMethod->calleeCount->elements[i+PIC_CALLEE] == oh->cached.nil) return NULL;
564 retval = method_pic_match_selector(oh, &callerMethod->calleeCount->elements[i], selector, arity, args, TRUE);
565 if ((struct MethodDefinition*)retval != NULL) return retval;
567 #endif
568 return NULL;
571 struct MethodDefinition* method_is_on_arity(struct object_heap* oh, struct Object* method, struct Symbol* selector, struct Object* args[], word_t n) {
573 word_t positions, i;
574 struct MethodDefinition* def;
576 positions = 0;
577 def = NULL;
579 for (i = 0; i < n; i++) {
580 if (!object_is_smallint(args[i])) {
582 struct MethodDefinition* roleDef = object_has_role_named_at(args[i], selector, 1<<i, method);
583 if (roleDef != NULL) {
584 positions |= 1<<i;
585 def = roleDef;
591 return ((def != NULL && positions == def->dispatchPositions)? def : NULL);
596 struct MethodDefinition* method_define(struct object_heap* oh, struct Object* method, struct Symbol* selector, struct Object* args[], word_t n) {
598 word_t positions, i;
599 struct Object* argBuffer[MAX_ARITY];
600 Pinned<struct MethodDefinition> def(oh);
601 Pinned<struct MethodDefinition> oldDef(oh);
603 def = (struct MethodDefinition*)heap_clone_special(oh, SPECIAL_OOP_METHOD_DEF_PROTO);
604 positions = 0;
605 for (i = 0; i < n; i++) {
606 if (!object_is_smallint(args[i]) && args[i] != get_special(oh, SPECIAL_OOP_NO_ROLE)) {
607 positions |= (1 << i);
611 /* any methods that call the same symbol must be decompiled because they might call an old version */
612 method_remove_optimized_sending(oh, selector);
613 selector->cacheMask = smallint_to_object(object_to_smallint(selector->cacheMask) | positions);
614 assert(n <= MAX_ARITY);
616 copy_words_into(args, n, argBuffer); /* method_dispatch_on modifies its arguments (first argument)*/
617 oldDef = method_dispatch_on(oh, selector, argBuffer, n, NULL);
618 if (oldDef == (struct Object*)NULL || oldDef->dispatchPositions != positions || oldDef != method_is_on_arity(oh, oldDef->method, selector, args, n)) {
619 oldDef = NULL;
621 if (oldDef != (struct Object*)NULL) {
622 Pinned<struct CompiledMethod> oldDefMethod(oh);
623 oldDefMethod = (struct CompiledMethod*)oldDef->method;
624 method_pic_flush_caller_pics(oh, (struct CompiledMethod*)oldDefMethod);
626 def->method = method;
627 heap_store_into(oh, (struct Object*) def, (struct Object*) method);
628 def->dispatchPositions = positions;
631 for (i = 0; i < n; i++) {
632 if (!object_is_smallint(args[i]) && (struct Object*)args[i] != get_special(oh, SPECIAL_OOP_NO_ROLE)) {
633 if (oldDef != (struct Object*)NULL) {
634 object_remove_role(oh, args[i], selector, oldDef);
636 object_add_role_at(oh, args[i], selector, 1<<i, def);
639 return def;