Main

October 05, 2007

Debugging Tricks

FlexBuilder 3 Beta 2 no longer makes both release and debug versions on every build, in order to speed up build times. This means that the debug version is no longer suffixed with -debug and it appears more than one of you used that to turn on debugging functionality w/o changing your code.

Also, a few folks have asked for a single line of code that could be pasted verbatim into function bodies that would report the name of the function, and a few others have wondered if there is a way to get the name of the calling function.

Assuming you are using a debugger player, the source code at this link demonstrates how to do each of these things. See setDebugFlag(), getFunctionName() and getCallingFunctionName().

Download file

private function setDebugFlag():void
{
	var e:Error = new Error();
	var s:String = e.getStackTrace();
	// trace(s);
	var i:int = s.indexOf("setDebugFlag");
	if (s.charAt(i + 14) == '[')
		debugMode = true;
}

[Bindable]
public var debugMode:Boolean = false;

private function getFunctionName(e:Error):String
{
	var s:String = e.getStackTrace();
	var i:int = s.indexOf("at ");
	var j:int = s.indexOf("()");
	return s.substring(i + 3, j);
}

private function getCallingFunctionName(e:Error):String
{
	var s:String = e.getStackTrace();
	// trace(s);
	var i:int = s.indexOf("at ");
	i = s.indexOf("at ", i + 3);
	if (i == -1)
		return "caller unknown";
	var j:int = s.indexOf("()", i + 3);
	return s.substring(i + 3, j);
}

where the latter two functions are can be used like this:

private function doSomething():void
{
	trace(getFunctionName(new Error()));
	doit();
}

private function doSomethingElse():void
{
	trace(getFunctionName(new Error()));
	doit();
}

private function doit():void
{
	trace(getFunctionName(new Error()));
	trace("   called by", getCallingFunctionName(new Error()));
}

Usual caveats apply, and the numbers and strings might have to be adjusted for different languages.

March 23, 2007

SWF is not a loadable Module

Did you get this error using modules? This error means that you tried to load the module from a different server than the one the loader is running on, and/or you don't have crossdomain.xml permission to load the module from that server.

Flash/Flex has a lot of security built in so that mean people can't use Flash/Flex to do mean things to other servers. All SWFs therefore belong to the domain of the server where that SWF lives.

For example, if we're running a SWF that lives at http://a.b.com/main.SWF, that SWF belongs to the domain "a.b.com". If you try to load a SWF from somewhere else, such as http://c.b.com/module.SWF or http://e.f.org/module.SWF, then you are loading across domains.

The Flash Player will then look in the root directory of c.b.com or e.f.org for a file called crossdomain.xml, and see if a.b.com is listed in that file (or the file lists '*" which means "everybody"). If not, you cannot load the SWF as a module because, due to other security restrictions in the Flash Player, SWF loaded from another domain without crossdomain.xml permission go in a separate SecurityDomain, which means that it will have its own ApplicationDomain which means it cannot share classes with the main SWF and thus the ModuleManager can't see the objects in the SWF as Modules.

Because this is a security-related issue, there are no real workarounds. You either have to make sure that your modules are in the same domain as the SWFs that load them, or you have to set up crossdomain.xml files so they can be used from SWFs in other domains. If you are trying to use a third-party module, you probably don't have access to the third-party's server and thus can't add yourself as an accepted domain in their crossdomain.xml file. That's annoying, but those are the rules. Server owners must either give explicit permission or decide to openly share SWFs resources on a server by listing your domain in crossdomain.xml or using '*".

ArrayCollection, Arrays and Server Data

I call this the "array of one" problem. I make a request to the server for some data, usually a list of things, and it comes back and I stuff it in a dataprovider and it displays just fine....sometimes. Sometimes I get an error about converting/coercing ObjectProxy into an Array or ArrayCollection. After further investigation it turns out that it is only if the list of things has zero or one items that I get the error.

This is an unfortunate fact-of-life when serializing objects as xml. XML is really just formatted text, so all type information is essentially lost. We don't know if the data is a String, Number, Array or Object and end up making a good guess.

For example, if I have the following data to send:

class Employee
{
var Name:String = "Alex";
var EmployeeID:Number = 123;
}

It gets serialized as a one string

"Alex123"

On the receiving side, the code sees that there isn't anything numeric about "Alex" so it leaves it as a string, and that "123" is all digits so it parses it as a Number.

Now if the data is a list of values such as

class Manager
{
var Manager:String = "Steve";
var DirectReportEmployeeIDs:Array = [123, 456, 789];
}

That might get serialized as:

Steve123456789

On the receiving side, the code sees that there are multiple so it decides to make an Array of those values.

Now if there is only one direct report, the data is going to come over as:

Steve123

and sure enough, it no longer looks like an array, does it? That's why this is the "array of one" problem.

So, how to deal with it? There is no single right answer. There are type-preserving serialization technologies but they aren't as popular. You can just check to see if the thing you think is an Array or ArrayCollection is, and if not, convert it to an array by pushing onto a new array, or you can work with the data as XML and use XMLListCollection which will see the "array of one" as an XMLList.

To check, you would add code like:

if (event.result is ObjectProxy)
list.dataProvider = [ event.result ]; // short cut for new array()
else
list.dataProvider = event.result.

To use XML and XMLListCollection, set the resultFormat="e4x".