LoginLogin

Magical arguments

Root / Programming Questions / [.]

computableeCreated:
So I'm making Nossrec and what have you, and I come across this weird glitch. SETUP: Use PRGINS to write code similar to the following:
COMMON DEF FUNC
FUNC2 'make sure this one requires parameters
END

'in a different slot
FUNC 'pass FUNC2's required arguments
Somehow, the arguments are forced into FUNC and are passed to FUNC2, yielding the desired result! I am currently exploiting this glitch, so I hope SmileBoom doesn't patch it XD. This glitch also works if FUNC2 has a return value. The setup is no different, but somehow, if FUNC2 has a return, FUNC will automatically return that value. Here is an example. First, download Nossrec because I'm not 100% sure on the setup. Read how to write programs in Nossrec. Second, load PRG1:TEST. That program is as follows:
class AddOne
  def public int addit(int a)
    return a+1
  end
end class

static class entry
  def public void main
    object t <- AddOne
    t=t.addit(val(readline()))
    writeline str(t)
  end
end static
Third, look at how t_addit is called in Slot 2, and then look at how it's defined in Slot 3. This is what you see.
[Slot 2]
t_addit(VAL_(READLINE$()))

[Slot 3]
COMMON DEF t_addit
AddOne_addit
END
Note that AddOne requires one parameter and has a return value.

If I understand correctly, what you're saying is that this code:
COMMON DEF NEWFUNC
OLDFUNC
END
Essentially makes NEWFUNC identical to OLDFUNC, including argument and return values, even though they're not explicitly defined for NEWFUNC. Interesting! Is it useful though? Does it still work if you add more code? e.g.:
COMMON DEF NEWFUNC
OLDFUNC
PRINT "BAR"
END

DEF OLDFUNC T$
PRINT T$
END

[other slot]
NEWFUNC "FOO" 
What's the result of this code? Also, how in the world did you come across this?! :P

This actually is an indication that SmileBasic may have done something really rather clever: treat functions as values. I'm not sure I trust SmileBoom to do this properly - I am investigating.

Even more interestingly, this does not work if you put the code directly into the editor and run it; you will get an invalid function call or something (don't have my 3DS right now).

I think I know how this happens when Nossrec does it. 1) Nossrec writes the code into Slot 2. The SB interpreter does not take t_addit as a syntax error because it might be defined later. 2) Nossrec then writes the t_addit function, but with the wrong amount of arguments. Since this is done JIT, the code in slot 2 is already running. Thus, no errors can be caught. 3) Since invalid function calls are caught with the precompiler, the arguments passed to t_addit must go somewhere as no errors can be thrown. Since AddOne_addit needs arguments, they are passed to that function.

I think I know how this happens when Nossrec does it. 1) Nossrec writes the code into Slot 2. The SB interpreter does not take t_addit as a syntax error because it might be defined later. 2) Nossrec then writes the t_addit function, but with the wrong amount of arguments. Since this is done JIT, the code in slot 2 is already running. Thus, no errors can be caught. 3) Since invalid function calls are caught with the precompiler, the arguments passed to t_addit must go somewhere as no errors can be thrown. Since AddOne_addit needs arguments, they are passed to that function.
I think what is happening in 3 is stack stuff. The parameters have been put on the stack since the number accepted can't be checked. t_addit pops none of the args off the stack, and then AddOne_addit pops the args it needs. At this point though, wouldn't AddOne_addit be defined enough to complain you aren't passing any? EDIT: and shouldn't there be a return address on the stack? AdcOne_addit might get the right args because of offsets instead of popping. And I guess the return addresses get popped as you return EDIT2: This is magical

It's possible the 'return' stack is distinct from the 'parameter' stack. It would be a highly unconventional design, but that hasn't stopped SmileBoom before. Actually, now that I think about it that way, that seems like a more probable explanation than mine. I'm having a tough time replicating the behaviour in a more controlled way to study it, though.

I'm pretty sure function/parameter checking is done at runtime. Persson, are you definitely running USE 2 after every time you modify slot 2? It's possible that it's running an old version of the slot which is making things not match up with the plain-text program.

As I have said, I haven't been able to recreate the circumstances in a more controlled environment to do good experiments, but I'm quite sure that at no time does t_addit refer to a function which takes one paramater, and at no time does either AddOne_addit or AddOne_addit% refer to a function with no parameters.

I'm pretty sure function/parameter checking is done at runtime. Persson, are you definitely running USE 2 after every time you modify slot 2? It's possible that it's running an old version of the slot which is making things not match up with the plain-text program.
Slot 2 is the main program, which is only edited during main compilation. Slot 3 is constantly being changed, and every time I change it, I'm using EXEC 3 to update all of the functions.

I believe I've found another bug similar to this one. Code:
PRINT str(t_AddOne%(VAL_(READLINE$()))) 'Invalid Function Call to t_AddOne%()

[in another slot]
DEF Add_Add%(a%,b%)
 RETURN a%+b%
END
COMMON DEF t_AddOne%(a%)
 RETURN Add_Add%(a%,1)
END

[in yet another slot]
COMMON DEF str(X)
 'code here
END
COMMON DEF VAL_(X)
 'code here
END
This is very frustrating. My code would work wonderfully...IF THE SB INTERPRETER WOULD WORK LIKE IT'S SUPPOSED TO!!! What's even more taunting is that the line will run in DIRECT mode, but not in the actual program.

I'm pretty sure function/parameter checking is done at runtime. Persson, are you definitely running USE 2 after every time you modify slot 2? It's possible that it's running an old version of the slot which is making things not match up with the plain-text program.
Slot 2 is the main program, which is only edited during main compilation. Slot 3 is constantly being changed, and every time I change it, I'm using EXEC 3 to update all of the functions.
You're supposed to USE to update, not EXEC. EXEC runs the last state USE'd for the slot. USE updates the slot state to reflect changes in the source code.

I'm pretty sure function/parameter checking is done at runtime. Persson, are you definitely running USE 2 after every time you modify slot 2? It's possible that it's running an old version of the slot which is making things not match up with the plain-text program.
Slot 2 is the main program, which is only edited during main compilation. Slot 3 is constantly being changed, and every time I change it, I'm using EXEC 3 to update all of the functions.
You're supposed to USE to update, not EXEC. EXEC runs the last state USE'd for the slot. USE updates the slot state to reflect changes in the source code.
I fixed this, but the glitch still occurred. I fixed the glitch by placing a few functions near the end of the source code instead of at the beginning (how the f--- this this fix it), but now changing the value of objects is painfully slow because PRGGET$() is terrible laggy.

You're supposed to USE to update, not EXEC. EXEC runs the last state USE'd for the slot. USE updates the slot state to reflect changes in the source code.
I'm not entirely sure that's correct, I'm pretty sure EXEC does a USE before it executes (though I haven't tested whether it skips the USE if no program editing had been performed since the last time). I do know that it updates the program state though based on recent tests.
I fixed this, but the glitch still occurred. I fixed the glitch by placing a few functions near the end of the source code instead of at the beginning (how the f--- this this fix it), but now changing the value of objects is painfully slow because PRGGET$() is terrible laggy.
Actually, I recently found out that PRGGET$() is quite fast, but PRGEDIT's speed is directly related to the line number (or really the amount of data up to that line number). PRGGET$() does advance to the next line each time it is called, so it could be very useful for streaming lines from a program, but that probably doesn't apply to your situation.

So why is PRGEDIT slower as the line number increases? Another stupid implementation design?

So why is PRGEDIT slower as the line number increases? Another stupid implementation design?
Because it doesn't keep track of where the start of every single line in the file is (which can change while you're editing, especially if you insert a string containing newlines), so it has to search from the beginning.

You're supposed to USE to update, not EXEC. EXEC runs the last state USE'd for the slot. USE updates the slot state to reflect changes in the source code.
I'm not entirely sure that's correct, I'm pretty sure EXEC does a USE before it executes (though I haven't tested whether it skips the USE if no program editing had been performed since the last time). I do know that it updates the program state though based on recent tests.
Huh, I guess it does. Weird.

Could not reproduce.