Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > Avisynth Development

Reply
 
Thread Tools Search this Thread Display Modes
Old 26th January 2012, 09:40   #1  |  Link
maxxon
Registered User
 
maxxon's Avatar
 
Join Date: Jan 2012
Location: On the net
Posts: 76
AviSynth scripting language bug?

Has anyone noticed that if you call a function that throws an exception within a try block, all variables are wiped from the stack frame? I mean the entire stack frame is just gone, even in previous function scopes all the way to the top level. But if you throw an exception within the try block without creating another function scope, everything works fine.

Globals don't seem to be affected though.

Here is an example:
Code:
global output__junk = BlankClip(height=0, width=0, length=0)
global output__filename = "output.log"
function output(string s)
{
    output__junk.WriteFileStart(output__filename, "s", append=true)
    return 1
}

global g = "GLOBAL"
l = "LOCAL"

function fail
{
    assert(false)
}

try { assert(false) } catch(e) {}
try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
try { output("l="+l) } catch(e) { output("l doesn't exist.  "+e) }

try { fail } catch(e) {}
try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
try { output("l="+l) } catch(e) { output("l doesn't exist.  "+e) }

l = "LOCAL"
try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
try { output("l="+l) } catch(e) { output("l doesn't exist.  "+e) }

output("fn1")
function fn1
{
    l1 = "LOCAL1"

    try { assert(false) } catch(e) {}
    try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
    try { output("l1="+l1) } catch(e) { output("l1 doesn't exist.  "+e) }
}
fn1
try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
try { output("l="+l) } catch(e) { output("l doesn't exist.  "+e) }

output("fn2")
function fn2
{
    l1 = "LOCAL1"

    try { fail } catch(e) {}
    try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
    try { output("l1="+l1) } catch(e) { output("l1 doesn't exist.  "+e) }
}
fn2
try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
try { output("l="+l) } catch(e) { output("l doesn't exist.  "+e) }
The output.log file will contain this:
Code:
g=GLOBAL
l=LOCAL
g=GLOBAL
l doesn't exist.  I don't know what "l" means
(test.avs, line 23)
g=GLOBAL
l=LOCAL
fn1
g=GLOBAL
l1=LOCAL1
g=GLOBAL
l=LOCAL
fn2
g=GLOBAL
l1 doesn't exist.  I don't know what "l1" means
(test.avs, line 49)
g=GLOBAL
l doesn't exist.  I don't know what "l" means
(test.avs, line 53)
Has anyone experienced this before? I'm using AviSynth 2.60 May 25 2011.

Strange bug,


Mx

Last edited by maxxon; 26th January 2012 at 09:52. Reason: Forgot to add another test case.
maxxon is offline   Reply With Quote
Old 26th January 2012, 11:50   #2  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Interesting...
What is happening is that when an exception causes a jump out of the failing function, its local scope is not being popped off the stack, so the local variables of the caller are still hidden.

With this additional test case:
Code:
function fail2
{  
    l = "FAIL2"
    assert(false)
}

l = "LOCAL"
try { fail2 } catch(e) {}
try { output("g="+g) } catch(e) { output("g doesn't exist.  "+e) }
try { output("l="+l) } catch(e) { output("l doesn't exist.  "+e) }
the log produces:
Code:
g=GLOBAL
l=FAIL2
Here is the code of function ScriptFunction::Execute (script.cpp):
Code:
AVSValue ScriptFunction::Execute(AVSValue args, void* user_data, IScriptEnvironment* env) 
{
  ScriptFunction* self = (ScriptFunction*)user_data;
  env->PushContext();
  for (int i=0; i<args.ArraySize(); ++i)
    env->SetVar(self->param_names[i], args[i]);
  AVSValue result = self->body->Evaluate(env);
  env->PopContext();
  return result;
}
The fix will involve adding code to catch exceptions in the evaluation of the function body and pop the context before passing the exception on to the caller.
__________________
GScript and GRunT - complex Avisynth scripting made easier
Gavino is offline   Reply With Quote
Old 26th January 2012, 17:13   #3  |  Link
maxxon
Registered User
 
maxxon's Avatar
 
Join Date: Jan 2012
Location: On the net
Posts: 76
Quote:
Originally Posted by Gavino View Post
Interesting...
What is happening is that when an exception causes a jump out of the failing function, its local scope is not being popped off the stack, so the local variables of the caller are still hidden.
Wow! Funny.

Quote:
Originally Posted by Gavino View Post
Here is the code of function ScriptFunction::Execute (script.cpp):
Code:
AVSValue ScriptFunction::Execute(AVSValue args, void* user_data, IScriptEnvironment* env) 
{
  ScriptFunction* self = (ScriptFunction*)user_data;
  env->PushContext();
  for (int i=0; i<args.ArraySize(); ++i)
    env->SetVar(self->param_names[i], args[i]);
  AVSValue result = self->body->Evaluate(env);
  env->PopContext();
  return result;
}
The fix will involve adding code to catch exceptions in the evaluation of the function body and pop the context before passing the exception on to the caller.
Sorry, not familiar with the code. Is this where the function call is taking place, between the push and pop contexts? Is an exception in the script actually throwing an exception in the code? That would seem to indicate that there is a try...catch somewhere before this call. So a potential fix would be (if I remember my C++ correctly):
Code:
AVSValue ScriptFunction::Execute(AVSValue args, void* user_data, IScriptEnvironment* env) 
{
  ScriptFunction* self = (ScriptFunction*)user_data;
  env->PushContext();
  try {
    for (int i=0; i<args.ArraySize(); ++i)
      env->SetVar(self->param_names[i], args[i]);
    AVSValue result = self->body->Evaluate(env);
  } catch (std::exception& e) { // reference, pointer, whatever is being used or whatever object base type is being used.
    env->PopContext();
    throw e;
  }
  env->PopContext();
  return result;
}
Which would result in a minimal touching of the code. Mind you, I'm pulling this out of my ass with pretty much no context of the rest of the code.


Mx

Hmmm, this forum doesn't have the plugins for code highlighting enabled.
maxxon is offline   Reply With Quote
Old 26th January 2012, 17:34   #4  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Quote:
Originally Posted by maxxon View Post
Sorry, not familiar with the code.
It's part of the Avisynth parser/interpreter.

Quote:
Is this where the function call is taking place, between the push and pop contexts? Is an exception in the script actually throwing an exception in the code? That would seem to indicate that there is a try...catch somewhere before this call. So a potential fix would be (if I remember my C++ correctly):
Code:
AVSValue ScriptFunction::Execute(AVSValue args, void* user_data, IScriptEnvironment* env) 
{
  ScriptFunction* self = (ScriptFunction*)user_data;
  env->PushContext();
  try {
    for (int i=0; i<args.ArraySize(); ++i)
      env->SetVar(self->param_names[i], args[i]);
    AVSValue result = self->body->Evaluate(env);
  } catch (std::exception& e) { // reference, pointer, whatever is being used or whatever object base type is being used.
    env->PopContext();
    throw e;
  }
  env->PopContext();
  return result;
}
That's basically what I had in mind, though I would probably use
Code:
catch (...) {
    env->PopContext();
    throw;
  }
The declaration of result (now without initialisation) would also have to be moved outside the try block.
__________________
GScript and GRunT - complex Avisynth scripting made easier
Gavino is offline   Reply With Quote
Old 26th January 2012, 20:27   #5  |  Link
maxxon
Registered User
 
maxxon's Avatar
 
Join Date: Jan 2012
Location: On the net
Posts: 76
Quote:
Originally Posted by Gavino View Post
It's part of the Avisynth parser/interpreter.
Yeah, I figured that much.

Quote:
Originally Posted by Gavino View Post
That's basically what I had in mind, though I would probably use
Code:
catch (...) {
    env->PopContext();
    throw;
  }
Ah yeah, forgot about how to rethrow.
Quote:
Originally Posted by Gavino View Post
The declaration of result (now without initialisation) would also have to be moved outside the try block.
I take it that AVSValue has an 'undefined' value when not initialised? Doesn't look too hard. Who is in charge of builds?


Mx
maxxon is offline   Reply With Quote
Old 26th January 2012, 20:43   #6  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Quote:
Originally Posted by maxxon View Post
I take it that AVSValue has an 'undefined' value when not initialised?
Yes, it's defined to be 'undefined', if you see what I mean.

Quote:
Who is in charge of builds?
IanB - I guess he'll be along soon to comment.
__________________
GScript and GRunT - complex Avisynth scripting made easier
Gavino is offline   Reply With Quote
Old 26th January 2012, 21:17   #7  |  Link
maxxon
Registered User
 
maxxon's Avatar
 
Join Date: Jan 2012
Location: On the net
Posts: 76
Cool. Am I to assume that some of the builtin functions have magic? Such as WriteFile? Seems to be able to evaluate the strings as if it were in the same scope as it was called from.


|\/|x
maxxon is offline   Reply With Quote
Old 26th January 2012, 21:55   #8  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Quote:
Originally Posted by maxxon View Post
Am I to assume that some of the builtin functions have magic? Such as WriteFile? Seems to be able to evaluate the strings as if it were in the same scope as it was called from.
Not magic, simply that it's only script language functions that start a new scope. The built-in functions (and external plugins) are written using the API, and can optionally use the PushContext and PopContext functions (the same ones used in the ScriptFunction code above) if they really need a new scope (which is rarely).

That's why writing control functions in the script language, using Eval() on a user-supplied code fragment, is not very useful, as any variables used have to be global. I was going to post something about that on your other thread once I had time to look properly at your library package.
__________________
GScript and GRunT - complex Avisynth scripting made easier
Gavino is offline   Reply With Quote
Old 26th January 2012, 23:31   #9  |  Link
IanB
Avisynth Developer
 
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
Yep, as a general rule I guess all env->PushContext()'s should be try/catch protected. I'll do a pass through the source over the weekend.
IanB is offline   Reply With Quote
Old 27th January 2012, 00:55   #10  |  Link
maxxon
Registered User
 
maxxon's Avatar
 
Join Date: Jan 2012
Location: On the net
Posts: 76
Quote:
Originally Posted by Gavino View Post
Not magic, simply that it's only script language functions that start a new scope. The built-in functions (and external plugins) are written using the API, and can optionally use the PushContext and PopContext functions (the same ones used in the ScriptFunction code above) if they really need a new scope (which is rarely).
Ahhh, ic.

Quote:
Originally Posted by Gavino View Post
That's why writing control functions in the script language, using Eval() on a user-supplied code fragment, is not very useful, as any variables used have to be global. I was going to post something about that on your other thread once I had time to look properly at your library package.
Hmmm, I'm not sure that I understand your meaning. Is that how it was or how it is? Perhaps I'll have better insight as to your meaning when I see your comments.


|\/|x
maxxon is offline   Reply With Quote
Old 27th January 2012, 01:16   #11  |  Link
maxxon
Registered User
 
maxxon's Avatar
 
Join Date: Jan 2012
Location: On the net
Posts: 76
Quote:
Originally Posted by IanB View Post
Yep, as a general rule I guess all env->PushContext()'s should be try/catch protected. I'll do a pass through the source over the weekend.
Cool. I wonder why it was never caught before? Not many people pushing the language that hard?


|\/|x
maxxon is offline   Reply With Quote
Reply

Tags
avisynth script, bug, try catch

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 02:52.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.