Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
Locked
0

Is it possible to prevent an AIR application from halting execution on minor errors?

Engaged ,
Oct 03, 2013 Oct 03, 2013

AIR's error messages on runtime are very useful when developing and debugging an application, but the problem is that even on final production releases, errors halt the application as well.

Once an application has been compiled as final release (IPA or APK), errors should not halt an application. At least not simple errors such as a null variable.

For example, take this code:

import flash.text.TextField;

var tfTest:TextField = new TextField();

var appendedText:String = ' World';

tfTest.text = 'Hello';

tfTest.appendText(appendedText);

addChild(tfTest);

This just makes the sentence "Hello World" appear in the screen. But imagine if the contents of appendedString came from a server, and for some reason the server outputted "null" at some time. That would cause the application to halt. Why? Just the lack of a string shouldn't halt the application!

Try running this code:

import flash.text.TextField;

var tfTest:TextField = new TextField();

var appendedText:String = null;

tfTest.text = 'Hello';

tfTest.appendText(appendedText);

addChild(tfTest);

If appendedText is null, then AS3 execution will halt at the tfTest.appendText() call, so the text field won't be even added to stage.

Ideally, I think we should at least get the word "Hello", even if the other word is null. That's better than halting the app so we don't get ANYTHING AT ALL.

This is just a very simple example, but I hope you get the point. Why should the app halt for such a unimportant reason? It's just a text string! If it's null, then don't append anything and carry on with the app execution! Don't halt the execution of the entire app (which makes the app unusable) just for a null variable!

I know maybe someone will say that I should check all variables for null before using them, and I usually do that, but there's some instances where I maybe never thought about the possibility of a null value, or I simply forgot to check one of the many variables in my app. And it's very annoying to get that behaviour in an app which is in the App Store, because it can make an app look "crashed". And everything could have just been avoided if the app just kept executing despite that missing string.

If execution didn't halt, that missing string would have been a very minor bug (just some missing text) instead of a CRITICAL BUG WHICH MAKES THE APP NOT WORK AT ALL!

So as you can see, it makes a BIG difference.

Halting the app with an error message during development is useful, but it doesn't make sense to halt execution in a production app, where a halted app just annoys the user, who can't do anything about it and can't see the error message anyway.

I can understand halting on "fatal errors", but just appending a null string is hardly a fatal error!! That shouldn't stop executing the app.

So is it possible to have a less picky error system when an app is compiled as release so execution is not halted in unimportant errors?

TOPICS
Development
2.9K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Oct 04, 2013 Oct 04, 2013

Hi,

I don't think it is halting the application just discontinuing the function call in which the error occurs.

This, for example, would display your field even if there was a null value:

import flash.text.TextField;

var tfTest:TextField = new TextField();

// Move addChild up to before the problem null pointer

addChild(tfTest);

var appendedText:String = null;

tfTest.text = 'Hello';

tfTest.appendText(appendedText);

//addChild(tfTest);

G

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Oct 04, 2013 Oct 04, 2013

It shouldn't halt application execution, only that specific series of executions. The next interaction from the user, assuming the halting of that series of executions does not prevent further interaction, should still happen correctly. The halting of all app execution is restricted to debug mode, not release mode. And even in debug mode, you can push through it manually by hitting the "resume execution" button in the IDE

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 04, 2013 Oct 04, 2013

Thanks for your answers.

@Gaius Coffey: I know that putting addChild before the problematic line would "solve" the problem in the code above. But that was just a tiny example to get my point cross, not a real problem. The problem lies with longer and more complex codes.

@ApocalypticOn3: Well, yes, maybe I didn't use the right words. I know that doesn't really halt an application completely, but just halting execution in a block of code is bad enough. Why? Imagine that for some reason block code execution is halted early in a function because a non-important problem, but that causes that a lot of addEventListeners, addChilds and other stuff is NOT executed, so the user gets a non-responsive UI. That's a BIG problem, which could have been a really tiny problem instead if execution carried on despite of a null variable being called.

I've also had apps appear to have crashed (even if they really didn't) because I was showing a "Loading" overlay which prevented app use during load, and since some block code execution halted, the overlay never disappeared (since the removing of the overlay is made at the very end of the block, after everything else is done), so it appeared as if the app had crashed, even if internally the AIR app was still running fine.

That's my point. The only solution is checking aaaall variables every time (which I try to do, but sometimes I miss something out), or maybe making sure that "important" stuff is executed first? (such as the solution @Gaius Coffey suggested). That's really annoying. Why should I have to care about the order of execution to do stuff such as adding Event Listeners, adding elements to stage, etc.? Why do I have to check for null EVERY time?!

Error checking when executing a final production release version should be a lot less strict. Strict error checking is useful only for developers during development and debugging. But end-users don't benefit from such a strict error checking at all! On the contrary, they just get a non-working app for stupid reasons (a missing string).

So isn't there any way to turn that block code halting off when the app is compiled as final release?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Oct 04, 2013 Oct 04, 2013

What you suggest really isn't possible. AS3 uses a fairly strict linear execution model. What happens on one line affects what happens on the next line and so on and so forth. For that reason, the runtime assumes that what happens on one line is needed for what happens on the next line (generally a good assumption to make). By that logic, if there is an error on line 4 and line 5 needs what happened on line 4, an error/unexpected result is likely to happen on line 5. The Flash runtime halts execution purposefully to prevent further errors. By halting execution, it keeps the error with just the single object. If it kept going, you could have an infinite number of objects with unexpected/improper values.

And a null object reference is not a minor error. Sure, it is minor when you're trying to set text and the value is null. But at other times, a null reference can screw up the internal workings of an entire object. Think about the addChild()/addElement() methods. Every DisplayObject you pass to those affects every object passed to them in the past and in the future, as well as the entire DisplayList chain up to the main application. Passing a null reference to one of those could have negative effects on every single object on the stage.

Yes, that is an extreme case. But the entire basis of good development is to hope for the best, plan for the worst.The runtime can't assume a null reference isn't a big issue. It can't assume that you built your code to handle untold errors. It has to assume that an error is going to have dire consequences for the rest of the application. I know you'll suggest they could ignore null references on standard datatypes (like String and Number), but those datatypes are used for much more than just setting elements on the stage. Most internal settings, settings that could affect not only your app but the rest of the system if Adobe did not sandbox correctly, are set using numbers and strings. If Adobe allowed invalid values to be set, it could be dangerous for the device.

Really, this is not on Adobe or the runtime, it is on you. It is on us. We, the developers, need to plan for such possibilities. It is no different than forgetting to validate user-provided data or not planning for all the possible situations an event handler might be called. This is part of your job in creating a quality application. Adobe's responsiblity is to create a secure and usable SDK/Runtime, not to make your job easier.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 04, 2013 Oct 04, 2013

Thanks again for your answer.

What you suggest really isn't possible. AS3 uses a fairly strict linear execution model. What happens on one line affects what happens on the next line and so on and so forth. For that reason, the runtime assumes that what happens on one line is needed for what happens on the next line (generally a good assumption to make). By that logic, if there is an error on line 4 and line 5 needs what happened on line 4, an error/unexpected result is likely to happen on line 5. The Flash runtime halts execution purposefully to prevent further errors. By halting execution, it keeps the error with just the single object. If it kept going, you could have an infinite number of objects with unexpected/improper values.

Well, maybe prevent halting in all instances is bad, but there are a lot of instances where it wouldn't do any harm, and halting is precisely doing a lot of harm (preventing an app from working properly), where it could have just run fine.

And a null object reference is not a minor error. Sure, it is minor when you're trying to set text and the value is null. But at other times, a null reference can screw up the internal workings of an entire object. Think about the addChild()/addElement() methods.

I don't think I would ever had that problem of doing an addChild(null) because I control what I add and what I don't, but still, what would be so wrong about passing null to addChild? If the argument is null, then don't add anything and just issue a warning through traces (that's what other languages such as PHP do), then continue execution.

Why would it be so terrible to try to add a null child, failing silently and carrying on with the execution? (only in release execution; it should halt during testing/debugging to bring this problem to the developer's attention).

Also if the display object you want to add elements to is null a similar thing could happen:

var spr:Sprite = null;

spr.addChild(element);

If spr is null, then the addChild method doesn't exist in that instance (it's a null object).

So just don't execute the non-existent method, issue a warning and that's it!

The runtime can't assume a null reference isn't a big issue.

Well, AS2 just carries on executing in that instance. JavaScript, which is a popular language nowadays, does too. It doesn't halt because of such a tiny problem (null string).

Try this code in both AS2 and AS3 (add tfTest textfield to the stage manually):

           var appendedText:String = ' World';

           tfTest.text = null;

           tfTest.text += appendedText;

           trace('OK');

In AS2 it shows "null World" and the trace statement works (shows 'OK' in the Output).

In AS3 it halts in the second line and NOTHING is shown (not even the trace).

Showing "null" is bad and should be corrected, but is still A LOT better than not showing anything AT ALL!

In AS2 you could even call a nonExistentFunction() and code execution didn't halt (maybe that was a bit extreme, but it worked that way).

Now, don't get me wrong. I don't long for the old AS2 days or anything. I've been using AS3 for several years already and I find it's highly superior to AS2, for, among other things, its OOP features and strict error checking, BUT only during development. Its strictness during release execution is more of a problem than anything.

If Adobe allowed invalid values to be set, it could be dangerous for the device.

So you mean AS2 is dangerous for devices?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 04, 2013 Oct 04, 2013

AS3 is simply more fragile than AS2 or JS or HTML. I've never understood the decision to tradeoff the implementation of strictness with dramatic assumptions about errors. For every "but if we don't abort on the first tiny error there might be more errors!" argument there's a "you practically ENSURE there will be MORE dramatic errors by aborting rather than attempting to continue execution" counter-argument. Personally I'd love to see a language handle strictness like AS3, but uncaught error handling like Javascript.

We can all have our opinions, of course, but we don't all get to actually build our own language and runtime embedded in the most installed piece of software of all time.

Cheers.

-Aaron

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 04, 2013 Oct 04, 2013

Aaron Beall escribió:

AS3 is simply more fragile than AS2 or JS or HTML. I've never understood the decision to tradeoff the implementation of strictness with dramatic assumptions about errors.

Yes, I understand what you mean. In fact, I really like AS3's strictness when developing and debugging. It actually makes developers' lives easier, by pointing out errors instead of failing silently (as AS2 did) and having to hunt for bugs.

BUT, I only like that behaviour during coding/debugging, not in front of an end-user!

If for some reason some random string went null unpredictably and I happened not to check for null in that particular instance (it can happen even if I do check usually), it's terrible user experience to stop execution in front of the user, instead of just not showing some random string and working perfectly otherwise.

Would it be impossible to make the language behave both ways? (strict when debugging and slightly more lax when running for the end-user).

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Oct 06, 2013 Oct 06, 2013

>>Would it be impossible to make the language behave both ways?

>>(strict when debugging and slightly more lax when running for the end-user).

What, you mean, run all my testing and development in an environment that doesn't match the release environment in significant dimensions of behaviour?

No thank-you.

Programming has always worked on the principle of garbage-in == garbage-out. I see no reason why AS should be any different.

A null pointer in your data is just as (or more) serious as a null pointer in the display objects. It is your responsibility to ensure those very serious errors are caught and resolved. Throwing errors ensures you can do that. Proper parsing of received data into proper data objects allows you to properly initialise values that you might later come to rely on.

Like all these things, it comes down to design.

G

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Oct 06, 2013 Oct 06, 2013

However...

What I _would_ like to see would be a modification to UncaughtErrorEvent that allows us to obtain a stack trace _even_ when compiled for release so that we can trap and usefully diagnose issues that are encountered post compile for release.

G

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 06, 2013 Oct 06, 2013

What, you mean, run all my testing and development in an environment that doesn't match the release environment in significant dimensions of behaviour?

I think it's not as crazy as it sounds. Ideally, you've already thoroughly debugged your app so most errors are out of the app. But those edge cases you could have missed (and are unexpected anyway) don't affect the app so severely. Nearly always, attempting to run all of the code despite an error will have a better result than just preventing almost a whole block of code from executing.

The rest of code would run as expected, in the same way as when debugging.

A null pointer in your data is just as (or more) serious as a null pointer in the display objects. It is your responsibility to ensure those very serious errors are caught and resolved. Throwing errors ensures you can do that. Proper parsing of received data into proper data objects allows you to properly initialise values that you might later come to rely on.

I don't mean this would be a substitute for good coding, of course. But still, even if trying to check for everything, sometimes there can be something you overlooked, and it'd be a better fallback not to stop everything because of that.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Oct 07, 2013 Oct 07, 2013

OMA2k wrote:

Nearly always, attempting to run all of the code despite an error will have a better result than just preventing almost a whole block of code from executing.

The rest of code would run as expected, in the same way as when debugging.

Ouch.

God no!

NOOOO!

If I encounter an unexpected null-pointer, the thought of carrying on without knowing what it is is horrendous - trebly horrendous when you consider that you haven't been able to see what will break as a result because your test environment doesn't behave that way.

Seriously, _try_ it.

Next time you encounter a null-pointer error in some seemingly innoccuous bit of code, instead of fixing the problem, wrap it in a try-catch and then force the value to null... I would bet a sizeable chunk of cash that you would discover something _FAR_ worse than merely a failed text field display...

A case-in-point is the near certainty of _CREATING_ a gazillion opportunities for infinite loops:

EG:
// myData.length resolves to null

for(var i:int=0;i<myData.length;i++) {

     // this loop NEVER COMPLETES

}

Errors are your friend. Seriously.

G

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 07, 2013 Oct 07, 2013

If I encounter an unexpected null-pointer, the thought of carrying on without knowing what it is is horrendous - trebly horrendous when you consider that you haven't been able to see what will break as a result because your test environment doesn't behave that way.

If the same exact case happened during testing, I could have seen what would break in my test environment, because I would have seen the actual code-stopping error message with the stack trace and all, so I would have been able to fix it.

So having different behaviours doesn't prevent me from "seeing what will break", as you say. The testing environment is MORE strict, not less! You sound as if I was meaning the other way around. But no, of course I think that during testing I should be able to see all errors, and stop execution accordingly, as it is now.

The problem is when there is some very particular case that for some reason wasn't seen during testing. Then it doesn't matter that the behaviour changes between testing and final, because it was never seen during testing anyway.

In those unpredictable cases which for some reason weren't seen in testing, then continuing execution can't be worse than stopping the execution of perfectly good code after the error, even if part of the code might depend on that null variable.

Why? Because there's at least a chance of succeeding even if the display is not perfect (some text is missing). Maybe we can get really unlucky and fall in an infinite loop, as you suggest, but it's worth a try, at least. Most of the time it would have worked. If you stop execution of the block code, then (paraphrasing @Aaron) you practically ENSURE there will be problems.

Seriously, _try_ it.

Next time you encounter a null-pointer error in some seemingly innoccuous bit of code, instead of fixing the problem, wrap it in a try-catch and then force the value to null... I would bet a sizeable chunk of cash that you would discover something _FAR_ worse than merely a failed text field display...

Well, at least when I've found those kinds of errors in one of my apps in production, I KNOW for sure that if AS3 just carried on executing, it would have worked, as they were mostly unimportant variables, not used in loops or anything.

A case-in-point is the near certainty of _CREATING_ a gazillion opportunities for infinite loops:

EG:
// myData.length resolves to null

for(var i:int=0;i<myData.length;i++) {

     // this loop NEVER COMPLETES

}

Well, I think I always check those kinds of data objects/arrays before using their properties just in case, so that wouldn't happen normally. But ok, let's pretend I forgot to check for null in that example for whatever reason.

In that case, the user will see practically no difference between getting stuck in a loading screen because an infinite loop has been reached, and getting stuck in a loading screen because block execution has stopped and thus the loading overlay is never removed.

In both cases, the user gets stuck and can't do anything but manually close the application.

To sum it up:

- If the block execution carries on after the error, there's a fair chance there won't be any big problem such as the infinite loop presented here, so the app will work mostly fine except a few unimportant problems.

- On the other hand, if block execution is stopped, there's NO chance at all. The app will get stuck in the loading screen. It's "dying without trying" 😉

Errors are your friend. Seriously.

Again, I agree. It's much better the AS3 way of showing errors than what happened with the old AS2, where you hardly got any error messages at all and you had to figure out what was going on.

BUT, if for some reason there's some unhandled error in production environment (I try not to have them, but it can happen sometimes), I think it would be better to at least try carrying on with the execution like the old AS2 did. Most of the times that makes little errors little, as opposed to stopping, which makes little errors HUGE (non-working app because of a null string).

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Oct 08, 2013 Oct 08, 2013

OMA2k wrote:

A case-in-point is the near certainty of _CREATING_ a gazillion opportunities for infinite loops:

EG:
// myData.length resolves to null

for(var i:int=0;i<myData.length;i++) {

     // this loop NEVER COMPLETES

}

Well, I think I always check those kinds of data objects/arrays before using their properties just in case, so that wouldn't happen normally. But ok, let's pretend I forgot to check for null in that example for whatever reason.

In that case, the user will see practically no difference between getting stuck in a loading screen because an infinite loop has been reached, and getting stuck in a loading screen because block execution has stopped and thus the loading overlay is never removed.

Well... No, actually.

If execution is halted, the plugin will immediately become responsive again, the application can continue with other tasks, buttons can be shown, an UncaughtErrorEvent intercept can be used to notify the user and assist them in fixing whatever problem has been encountered (even if that is as simple as starting again).

Whereas...

If an infinite loop occurs, the plugin will become unresponsive for as long as it takes Flash to decide it has gone on long enough. (Can be thirty seconds or more...) This can be serious enough to make the _device_ unresponsive.

Horses for courses, I guess, and a matter of opinion, but you are not going to sell me on the idea of continuing with an unknown error! I would much prefer to stop there than to risk corrupting many, many other systems by sending them garbage data due to a null pointer in something that might otherwise be considered trivial.

G

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Oct 08, 2013 Oct 08, 2013

Sorry to just jump in out of no where on this, but to me this problem has an extremely easy solution. As Gaius mentioned wrapping a method or calculation in a try-catch will stop the frustration you're talking about, maybe you're thinking too hard about it?

From your examples you suggest that you know where these errors in your code may or may not appear, in which case, if you suspect that there's going to be a problem, wrap it.

try{

     //let's attempt to do this

     var mc:MovieClip = null;

     this.addChild(mc); //will produce error

}

catch(e:Error){

     //if the try above didn't work then do something else

     trace("ERROR: " + e.message);

}

Nothing else is needed, your code continues, and everyone is happy, plus you get to log your error should you wish too.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Oct 08, 2013 Oct 08, 2013

try...catch is a CPU intensive task and as a general rule, should be avoided for that reason. There are times when it is needed, but as a handler for null object reference errors does not seem to be one of those times.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 08, 2013 Oct 08, 2013
LATEST

@Gaius Coffey:

If execution is halted, the plugin will immediately become responsive again, the application can continue with other tasks, buttons can be shown

If execution is halted, buttons will likely NOT be shown, either because it was halted before they could be drawn, or because a "loading" overlay is preventing the user from seeing anything (that's the most likely scenario in my app).

If an infinite loop occurs, the plugin will become unresponsive for as long as it takes Flash to decide it has gone on long enough. (Can be thirty seconds or more...) This can be serious enough to make the _device_ unresponsive.

An infinite loop can certainly rise the CPU load and lead to an unresponsive system (though the OS should be able to protect itself from that), but from the perspective of the user it's not that different from halting execution in a loading screen: The end result is the app doesn't work.

But if execution continued, there's a chance the app could still be presented to the user (in my experience, the infinite loop case is unlikely), whereas halting, it's almost a certainty the UI won't be properly shown to the user, if at all.

@Ollie Edge:

Sorry to just jump in out of no where on this, but to me this problem has an extremely easy solution. As Gaius mentioned wrapping a method or calculation in a try-catch will stop the frustration you're talking about, maybe you're thinking too hard about it?

From your examples you suggest that you know where these errors in your code may or may not appear, in which case, if you suspect that there's going to be a problem, wrap it.

I'm not talking a about cases where I suspect it might break. In those cases, I certainly use a try/catch block, such as when trying to write a file to the system, since there can be an issue when writing (no permission or no free space, for example). I'm talking more about unpredictable cases which were not foreseen and not encountered during testing.

But I can't use a try/catch block for each and every separate line in my code, so it only skips the errored line and continues with the rest. As @ApocalypticOn3 says, using a lot of those blocks is a CPU intensive task, not to mention coding like that would result in ugly and unreadable code!

@ApocalipticOn3:

try...catch is a CPU intensive task and as a general rule, should be avoided for that reason. There are times when it is needed, but as a handler for null object reference errors does not seem to be one of those times.

You're right. For a null object, a try/catch block is overkill. It's a lot better to check for null. And I usually do. But there might be some instances where I might have missed checking for EVERYTHING or it was a variable I wrongly assumed as being always non-null.

Anyway, I think properties such as textField.text or methods such as textField.appendText() should take null strings. Nothing bad can possibly happen for leaving a TextField empty because of a null argument. But in that particular case I guess the solution is easy: Creating wrapper methods which check for null before trying to assign text to a textfield or appending to a textfield, doing nothing if null, and use those alternative methods instead of the original AS3 ones.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines