src/melon/core/vm.c
TRet melPerformCustomOpVM(VM* vm, MelonObjectSymbols op, TUint8 args)
{
	assert(args > 0);

    Value* a = melPeekStackOfType(vm, MELON_TYPE_OBJECT, args - 1);

    GCItem* operator = melResolveSymbolObject(vm, a->pack.obj, op);

    if (operator == NULL)
    {
        return 1;
    }

	Closure* operatorCl = melM_closureFromObj(operator);
	assert(operatorCl != NULL);
    Function* operatorFn = melM_functionFromObj(operatorCl->fn);
	
	TSize oldTop = vm->stack.top;
	
	melM_vstackPushGCItem(&vm->stack, operator);

	for (TUint8 i = (operatorFn->method ? args : args - 1); i > 0; --i)
	{
		melM_stackPush(&vm->stack, melM_stackGet(&vm->stack, oldTop - (i - 1) - 1));
	}

    melCallClosureSyncVM(vm, args - (operatorFn->method ? 0 : 1), 0, 1);

    TType resType = melPeekStackType(vm, 0);

	if (resType == MELON_TYPE_BOOL)
	{
		Value* res = melM_stackPop(&vm->stack);

		if (!res->pack.value.boolean)
		{
			return 1;
		}

		melM_stackPopCount(&vm->stack, args);
	}
    else if (resType == MELON_TYPE_NULL)
    {
        // If the function returned nothing or null
        // we simply discard it.

        melM_stackPop(&vm->stack);
    }
	else
	{
		TSize resultsCount = vm->stack.top - oldTop;
		assert(resultsCount <= 1);

		if (resultsCount > 0)
		{
			melSaveAndPauseGC(vm, &vm->gc);	
			Value result = *melM_stackPop(&vm->stack);
			melM_stackPopCount(&vm->stack, args);
			melM_stackPushRaw(&vm->stack, &result);
			melRestorePauseGC(vm, &vm->gc);
		}
	}

    return 0;
}
/**
 * This function invokes a given custom operator on the object
 * with some arguments.
 * 
 * A the moment invoking a custom operator incurs in a performance
 * penalty that is a consequence of how things are laid out on 
 * the stack when this function is called.
 * We usually have the arguments before this function but if
 * a custom operator has been found we have to put the operator
 * Closure *before* the arguments. There's also another issue:
 * If the custom operator returns false it means that code wants
 * the VM to fall back to the standard operator. In this case
 * we have to restore things on the stack as they were before
 * this function was called.
 * 
 * To work around both problems we:
 * - Push the closure on the stack
 * - Copy the arguments after the closure
 * - If the closure returned true it means we can leave things as they are
 * - If the closure returned false it means we have to restore things as
 *   they were. We simply pop the 'false' value and pop the extra arguments.
 * - If the closure returned a non boolean value we have to:
 *   = Pop and save the result (disabling GC)
 *   = Remove the extra arguments
 *   = Push again the result so that it's on the top of the stack as expected
 * 
 * As you see there's quite some extra stuff going on.
 **/
This function invokes a given custom operator on the object
with some arguments.

A the moment invoking a custom operator incurs in a performance
penalty that is a consequence of how things are laid out on
the stack when this function is called.
We usually have the arguments before this function but if
a custom operator has been found we have to put the operator
Closure *before* the arguments. There's also another issue:
If the custom operator returns false it means that code wants
the VM to fall back to the standard operator. In this case
we have to restore things on the stack as they were before
this function was called.

To work around both problems we:
- Push the closure on the stack
- Copy the arguments after the closure
- If the closure returned true it means we can leave things as they are
- If the closure returned false it means we have to restore things as
they were. We simply pop the 'false' value and pop the extra arguments.
- If the closure returned a non boolean value we have to:
= Pop and save the result (disabling GC)
= Remove the extra arguments
= Push again the result so that it's on the top of the stack as expected

As you see there's quite some extra stuff going on.
This function invokes a given custom operator on the object with some arguments . A the moment invoking a custom operator incurs in a performance penalty that is a consequence of how things are laid out on the stack when this function is called . We usually have the arguments before this function but if a custom operator has been found we have to put the operator Closure * before * the arguments . There ' s also another issue : If the custom operator returns false it means that code wants the VM to fall back to the standard operator . In this case we have to restore things on the stack as they were before this function was called . To work around both problems we : - Push the closure on the stack - Copy the arguments after the closure - If the closure returned true it means we can leave things as they are - If the closure returned false it means we have to restore things as they were . We simply pop the ' false ' value and pop the extra arguments . - If the closure returned a non boolean value we have to : = Pop and save the result ( disabling GC ) = Remove the extra arguments = Push again the result so that it ' s on the top of the stack as expected As you see there ' s quite some extra stuff going on .
TRet melPerformCustomOpVM(VM* vm, MelonObjectSymbols op, TUint8 args)
{
	assert(args > 0);
    Value* a = melPeekStackOfType(vm, MELON_TYPE_OBJECT, args - 1);
    GCItem* operator = melResolveSymbolObject(vm, a->pack.obj, op);
    if (operator == NULL)
    {
        return 1;
    }
	Closure* operatorCl = melM_closureFromObj(operator);
	assert(operatorCl != NULL);
    Function* operatorFn = melM_functionFromObj(operatorCl->fn);
	TSize oldTop = vm->stack.top;
	melM_vstackPushGCItem(&vm->stack, operator);
	for (TUint8 i = (operatorFn->method ? args : args - 1); i > 0; --i)
	{
		melM_stackPush(&vm->stack, melM_stackGet(&vm->stack, oldTop - (i - 1) - 1));
	}
    melCallClosureSyncVM(vm, args - (operatorFn->method ? 0 : 1), 0, 1);
    TType resType = melPeekStackType(vm, 0);
	if (resType == MELON_TYPE_BOOL)
	{
		Value* res = melM_stackPop(&vm->stack);
		if (!res->pack.value.boolean)
		{
			return 1;
		}
		melM_stackPopCount(&vm->stack, args);
	}
    else if (resType == MELON_TYPE_NULL)
    {
        melM_stackPop(&vm->stack);
    }
	else
	{
		TSize resultsCount = vm->stack.top - oldTop;
		assert(resultsCount <= 1);
		if (resultsCount > 0)
		{
			melSaveAndPauseGC(vm, &vm->gc);	
			Value result = *melM_stackPop(&vm->stack);
			melM_stackPopCount(&vm->stack, args);
			melM_stackPushRaw(&vm->stack, &result);
			melRestorePauseGC(vm, &vm->gc);
		}
	}
    return 0;
}
TRet melPerformCustomOpVM ( VM * vm , MelonObjectSymbols op , TUint8 args ) { assert ( args > 0 ) ; Value * a = melPeekStackOfType ( vm , MELON_TYPE_OBJECT , args - 1 ) ; GCItem * operator = melResolveSymbolObject ( vm , a -> pack . obj , op ) ; if ( operator == NULL ) { return 1 ; } Closure * operatorCl = melM_closureFromObj ( operator ) ; assert ( operatorCl != NULL ) ; Function * operatorFn = melM_functionFromObj ( operatorCl -> fn ) ; TSize oldTop = vm -> stack . top ; melM_vstackPushGCItem ( & vm -> stack , operator ) ; for ( TUint8 i = ( operatorFn -> method ? args : args - 1 ) ; i > 0 ; -- i ) { melM_stackPush ( & vm -> stack , melM_stackGet ( & vm -> stack , oldTop - ( i - 1 ) - 1 ) ) ; } melCallClosureSyncVM ( vm , args - ( operatorFn -> method ? 0 : 1 ) , 0 , 1 ) ; TType resType = melPeekStackType ( vm , 0 ) ; if ( resType == MELON_TYPE_BOOL ) { Value * res = melM_stackPop ( & vm -> stack ) ; if ( ! res -> pack . value . boolean ) { return 1 ; } melM_stackPopCount ( & vm -> stack , args ) ; } else if ( resType == MELON_TYPE_NULL ) { melM_stackPop ( & vm -> stack ) ; } else { TSize resultsCount = vm -> stack . top - oldTop ; assert ( resultsCount <= 1 ) ; if ( resultsCount > 0 ) { melSaveAndPauseGC ( vm , & vm -> gc ) ; Value result = * melM_stackPop ( & vm -> stack ) ; melM_stackPopCount ( & vm -> stack , args ) ; melM_stackPushRaw ( & vm -> stack , & result ) ; melRestorePauseGC ( vm , & vm -> gc ) ; } } return 0 ; }
This function invokes a given custom operator on the object
 with some arguments.
This function invokes a given custom operator on the object with some arguments .
// If the function returned nothing or null // we simply discard it.