ID:1958074
 
Resolved
Throwing an exception outside of a try/catch block, when world.Error was there to catch it, did not result in the same exception being sent to world.Error(). Additionally, the actual new exception sent to world.Error() had a memory leak.
BYOND Version:509.1305
Operating System:Windows 7 Home Premium 64-bit
Web Browser:Chrome 47.0.2526.8
Applies to:Dream Daemon
Status: Resolved (510.1337)

This issue has been resolved.
If you throw an exception object without try/catch, a runtime is generated and world/Error() catches it and creates a new exception (copying the original's name), rather than using the one that was thrown.

Here's a quick example:

mob/verb
create_exception()
throw new /exception ("error", osrc = src)

world/Error(exception/E)
world << "___Exception caught: \ref[E]"
for(var/x in E.vars)
world << "[x]: [E.vars[x]]"

//using osrc to show that nothing is copied from the original exception
exception
var
osrc
New(name, file, line, osrc)
. = ..()
src.osrc = osrc
world << "___Exception created: \ref[src]"

for(var/x in vars)
world << "[x]: [vars[x]]"


example output:
___Exception created: [0x21000000]
name: error
desc:
file: Super Saiyan X
line:
osrc:
tag:
type: /exception
parent_type: /datum
vars: /list
___Exception caught: [0x21000001]
name: Exception thrown: error (/exception)
desc: proc name: create exception (/mob/verb/create_exception)
source file: exception_test.dm,5
usr: (src)
src: Super Saiyan X (/mob)
src.loc: null
call stack:
Super Saiyan X (/mob): create exception()

file: exception_test.dm
line: 5
osrc:
tag:
type: /exception
parent_type: /datum
vars: /list


It'd make more sense if the exception world/Error() receives is the same as the one thrown, rather than the runtime generated by an un-caught throw. It'd be cool if it could fill in the same missing info (file, line, desc) if not provided in the exception creation. This lets us provide extra information for logging purposes, which would otherwise be lost in world/Error(). Through, when a non-exception object is thrown, it should still probably keep the current behavior.

Otherwise, the behavior of the throw instruction is exactly the same as that of CRASH(), except for the name of exception object being passed to the new runtime.

I'm really having trouble wrapping my head around this one. Can you break it down a little further?
I throw an /exception object, outside of a try/catch block. I expect this to be the same object that is passed to world/Error(). But, it's not, instead it follows the same behavior as other things that call world/Error().

If I throw a string, null, or a built-in runtime occurs, outside of a try/catch block, this information is used to create an exception object which is then passed world/Error().

So, everything thrown (even an /exception), CRASH'd, or a runtime error creates another /exception object. Throwing an /exception, shouldn't create a second /exception, it should just be updated with e.desc and then passed to world/error().

It's hard to explain this, maybe.



The black lines are what is happening now, but for a thrown /exception, the red line should happen.
Little example to demonstrate what's happening.

/world/New()
var/exception/e = new /exception("foo")
world.log << "post-creation: \ref[e]"
throw(e)

/exception/New()
..()
world.log << "creating: \ref[src]"

/world/Error(exception/e)
world.log << "world.Error: \ref[e]"


Expected output:

creating: [ref]
post-creation: [ref]
world.Error: [ref]

where ref is the same each time.

Actual output:

creating: [0x21000000]
post-creation: [0x21000000]
world.Error: [0x21000001]
Lummox JR resolved issue with message:
Throwing an exception outside of a try/catch block, when world.Error was there to catch it, did not result in the same exception being sent to world.Error(). Additionally, the actual new exception sent to world.Error() had a memory leak.