What are _loc_ variables when swfs are decompiled into AS3? - actionscript-3

When running swfs through decompilers (my own swfs, not somebody else's), I've noticed a lot of mention of certain variables:
_loc_1
_loc_2
_loc_3
.
.
.
_loc_n
As in the following example:
private function templateFilterFunction(param1) : Boolean
{
var _loc_2:* = false;
if (filterFunction != null)
{
_loc_2 = filterFunction(param1, typedText);
}
return _loc_2;
}
Alright, so these are apparently just normal variables then, right? And they may have had more descriptive names in the original AS3 code, but that's been lost in the bytecode, and now we have the same variables as before, just with non-descript names, right?
Not exactly. For instance:
package
{
public class SomeClass extends Object
{
public var var1:Number;
public var var2:Number;
public var var3:Number;
public function SomeClass(param1:Number, param2:Number, param3:Number)
{
if (!_loc_5)
{
if (!_loc_4)
{
var3 = param1;
if (!_loc_4)
{
var1 = param2;
}
}
}
var2 = param3;
return;
}// end function
}
}
These aren't declared. But they're not exactly members of Object either, and I've never seen them outside of a swf decompilation. What are they then? Thanks.

Not sure about that particular piece of code, but the decompilers I've used (as far as I remember) all call the local variables loc_n, local_n or something like that.
I think you already know why. Local variables are created and pushed onto the execution stack; they are not referenced from outside the local scope and since they are not callable by name, their names are just whipped off the bytecode. (The object pointed by the variable could be allocated on the heap and live outside the scope of the function, however, but that's not the point here).
Now, another thing you might be aware of is that some bytecode generated by the compiler just doesn't traslate to actionscript code. There are things that can be done in bytecode that are not really possible in AS code; an example, off the top of my head: the "dup" opcode (duplicates a value and pushes it onto the stack). There are others (jumps, noops, etc). Reversing this to the original source code is sometimes not possible.
There are other interesting cases such as loops. You may notice that a particular decompiler tends to generate "for loops" (or "while loops") regardless of whether the source code had a for or a while. That's because loops are a higher level construct that are usually implemented in bytcode as conditional jumps. If you want to reverse the bytecode to AS code, you just have to pick a flavor because the loop (as an AS construct) is just not there.
That said, I've seen some decompilers (can't remember which one now) generating invalid or non-sensical source code. To me, that's the case in the example you post. I may be wrong but it seems like the _loc_5 and _loc_4 vars are just gibberish and the original code must be something like:
public function SomeClass(param1:Number, param2:Number, param3:Number):void
{
var var3:Number = param1;
var var1:Number = param2;
var var2:Number = param3;
}

Related

Can I still create Global variables in AS3

Following the answer here, I have created a file called MyGlobals.as and placed some global variables and functions so that I can access it from anywhere within my project just like AS3 buil-in functions such as trace() method.
This is MyGlobals.as which is located in the src folder (top level folder)
package {
public var MessageQueue:Array = new Array();
public var main:Main;
public var BOOKING_STATUS_DATA:Object;
public function postMessage(msg:Object):void {
MessageQueue.push(msg);
}
public function processMessage():void {
var msg:Object = MessageQueue.pop();
if (msg) {
switch (msg.type) {
}
}
}
Looks like my IDE (FD4) is also recognizing all these functions and variables and also highlighting the varibles and functions just like any other built-in global functions. However, I am getting compilation errors "Accessing possibly undefined variable xxx". The code is as simple as trace(MessageQueue) inside my Main (or another classe).
I am wondering if there was any change Adboe has done recently that it can't be done now or am I missing something? I am not sure if I need to give any special instructions to FD to include this MyGlobals.as?
I am using FD4, Flex SKD 3.1, FP12.0
I am aware of the best practices which suggests to avoid using this type of method for creating global variables but I really need it for my project for my comfort which I feel best way (right now) when compared to take any other path which involves daunting task of code refactoring. I just want do something which can be done in AS3 which I guess is not a hack.
I've done some playing around; it looks like you can only define one (1) property or method at package level per .as file. It must be the same name (case-sensitive) as the .as file it is contained in.
So no, nothing has changed since the older Flash Versions.
In your case that would mean you need five separate ActionScript files along the lines of:
MessageQueue.as:
package
{
public var MessageQueue:Array;
}
main.as:
package
{
public var main:Main;
}
...etc. As you can see this is very cumbersome, another downside to the many others when using this approach. I suggest using the singleton pattern in this scenario instead.
package{
public class Singleton{
private static var _instance:Singleton=null;
private var _score:Number=0;
public function Singleton(e:SingletonEnforcer){
trace(‘new instance of singleton created’);
}
public static function getInstance():Singleton{
if(_instance==null){
_instance=new Singleton(new SingletonEnforcer());
}
return _instance;
}
public function get score():Number{
return _score;
}
public function set score(newScore:Number):void{
_score=newScore;
}
}
}
then iin your any as3 class if you import the singleton class
import Singleton
thn where u need to update the global var_score
use for example
var s:Singleton=Singleton.getInstance();
s.score=50;
trace(s.score);
same thing to display the 50 from another class
var wawa:Singleton=Singleton.getInstance();
trace(wawa.score)

How to choose which child class to instantiate dynamically

My current project is in as3, but this is something I am curious about for other languages as well.
I'm attempting to use a factory object to create the appropriate object dynamically. My LevelFactory has a static method that returns a new instance of the level number provided to the method. In the code calling that method, I am able to dynamically create the buttons to call the levels like so:
for (var i:int = 1; i < 4; i++) {
var tempbutton:Sprite = createButton("Level " + i, 25, 25 +(60 * i), start(i));
_buttons.push(button);
}
This code just creates a simple button with the given arguments (ButtonText, x, y, function). It's working fine. The buttons are created, and clicking on one of them calls this method with the appropriate argument
private function start(level:int):Function {
return function(e:MouseEvent):void {
disableButtons();
newLevel = LevelFactory.createLevel(level);
addChild(newLevel);
}
}
This is all working fine; I'm just providing it for background context. The question I have is this: Is it possible to dynamically choose the type of object that my static function returns? Currently, I have am doing it as follows
public static function createLevel(level:int):Level {
var result:Level;
switch(level) {
case 1: result = new Level1(); break;
case 2: result = new Level2(); break;
//etc
}
return result;
}
I should note that all of these Level1, Level2, etc. classes extend my base level class. (Yay polymorphism!) What I would like to do is be able to do something along the lines of
public static function createLevel(level:int):Level {
var result:Level;
var levelType:String = "Level" + level;
return new levelType();
}
Obviously it's not going to work with a string like that, but is there any way to accomplish this in as3? What about other languages, such as Java or Python? Can you dynamically choose what type of child class to instantiate?
Update:
import Levels.*;
import flash.events.*;
import flash.utils.*;
public class LevelFactory
{
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.' + 'Level' + level) as Class;
var result:Level = new ref();
return result;
}
}
Update/Edit: getDefinitionByName seems to be what I'm looking for, but it has a problem. It seems that the compiler will strip unused imports, which means that unless I declare each subclass in the code ahead of time, this method will get a reference error. How can I get around the need to declare each class separately (which defeats the purpose of dynamic instantiation)?
Yes, you sure can, and it's very similar to the string thing that you've provided. The only thing that you are missing is the getDefinitionByName method: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/package.html#getDefinitionByName()
You can generate whatever class name you want, and what this method does is that it searches for that class in it's namespace, and if it finds it - it returns it as a class:
var ClassReference:Class = getDefinitionByName("flash.display.Sprite") as Class;
var instance:Object = new ClassReference();
This piece of code will instantiate a Sprite. This way you can instantiate your classes without all those switches and cases, especially when you have to make a hundred levels :)
Hope that helps! Cheers!
Edit:
In your case, the code should be:
var ref:Class = getDefinitionByName('com.path.Level' + index) as Class;
var level:Level = new ref(); // it will actually be Level1 Class
Since Andrey didn't quite finish helping me out, I am writing up a more complete answer to the question after much research.
getDefinitionByName definitely has the use I am looking for. However, unlike its use in Java, you HAVE to have a hard reference to the class you want instantiated somewhere in your code. Merely imported the class is not enough; the reason for this is that the compiler will strip the reference from any unused import to save space. So if you import the package of classes you want to choose dynamically but don't have a hard reference to them, the compiler will de-reference them. This will lead to a run-time error when the program cannot find the appropriate reference to your class.
Note that you don't actually have to do anything with the reference. You just have to declare a reference so that it can be found at run-time. So the following code will work to eliminate the switch-case statement and allow me to dynamically declare which class I am using at run-time.
{
import Levels.*;
import flash.events.*;
import flash.utils.*;
/**
*
* Returns the requested level using the createLevel class
* ...
* #author Joshua Zollinger
*/
public class LevelFactory
{
Level1, Level2, Level3, Level4, Level5, Level6, Level7;
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.Level' + level) as Class;
var result:Level = new ref(); // it will actually be the correct class
return result;
}}}
The obvious downside to this is that you still have to have a hard-coded reference to every class that can be instantiated like this. In this case, if I try to create a Level8 instance, it will through a run-time error because Level8 is not referenced. So every time I create a new level, I still have to go add a reference to it; I can't just use the reference dynamically.
There are supposedly ways around this that I have not tested yet, such as putting the code for the classes in a separate SWF and importing the SWF at run-time or using outside libraries that will have different functionality. If anyone has a solid way to get a truly dynamic reference that doesn't require a hard coded reference anywhere, I would love to hear about it.
Of course, it's still a lot cleaner this way; I don't have a extensive switch case statement to pack all the levels. And it's easier and faster to add a reference to the list than creating a new case in a switch. Plus it is closer to dynamic programming, which is usually a good thing.

Value Will Set properly, but Get receives Null

So, I have successfully grabbed a value out of an XML document and set it into a separate class called "AddCommas." The trace functions have shown me that it sets properly.
For more details, my objective is to take the language indicator ("fr" for french or "en" for english), set it inside the appropriate class and into a variable I will use. Now, I am using this variable to be used in an if statement; which will help me format a number properly (commas, decimals, spaces) per the clients request.
However, my problem is when I try to get the value to use it. It always comes back as Null. I have placed traces all over my program trying to pinpoint when this happens, but I cannot find it. Here's the code...
The pull from the XML file and into the set (this works fine, but I am adding it for your benefit in case I missed something)
public var commaHold = new AddCommas();
localLanguage = xmlObj.localLanguage;
trace("localLanguage + " + localLanguage);
commaHold.setLanguage(localLanguage); // Set Language
//More code follows...
This is the set function istelf...
public function setLanguage(localLanguage:String){
langHold = localLanguage;
trace("Set Language = " + langHold); //This always shows a successful set
}
Now am I wrong in thinking that in AS3, once langHold in my AddCommas class has been set I should be able to use it without calling a get within the function I am using the If Statement in, right? Such as this?
var language = langHold;
if (language == "en"){
trace("Language is = " + language); // More code follows afterwards and as of now, this shows NULL
Now, I have attempted plenty of Get functions to add the language variable in the call itself to this function and it's always the same. Am I missing some fundamentals here?
Thank you very much for your time.
If you expect a string comparison you need to use quotes, unless en is a String variable since langHold is a String, like:
if (language == "en"){
Consider modifying the set function to use the as3 keyword like:
private var _language:String;
public function set language(value:String):void {
_language = value;
//do other stuff here if necessary, put a breakpoint on the line above
}
public function get language():String{
return _language;
//put a breakpoint on the line above
}
You should be able to see when any instance of your class has the property changed. The only other issue I can think of is it is not the same instance of the class and therefore doesn't share the property value you set earlier. In the debugger you can check the "hashCode" or "address" it shows for this to see if it changes when it hits the breakpoints.
Here's a sample Singleton structure in AS3 (this all goes in one file):
package com.shaunhusain.singletonExample
{
public class SingletonExample
{
private static var instance:SingletonExample;
public static function getIntance():SingletonExample
{
if( instance == null ) instance = new SingletonExample( new SingletonEnforcer() );
return instance;
}
/**
*
* #param se Blocks creation of new managers instead use static method getInstance
*/
public function SingletonExample(se:SingletonEnforcer)
{
}
}
}
internal class SingletonEnforcer {public function SingletonEnforcer(){}}
using this single shared instance from any other class would look something like this:
private var singletonInstance:SingletonExample = SingletonExample.getInstance();
ShaunHusain's theory of using a Singleton was the perfect solution I needed. However, his code gave me a bizarre 1061 error and my format and code appeared to be error free. Regardless, I looked up another way to use a Singleton as follows that worked perfectly for me. Honestly, Shaun's code should work for anyone and I have no idea why it wasn't. I am perfectly willing to admit that it was probably a typo on my end that I just did not see.
I ended up embedding the Set and Get within the Singletons class and used it as an intermediary to hold the information I needed. It worked perfectly.
package chart {
import chart.*;
//
public class StaticInstance {
private static var instance:StaticInstance;
private static var allowInstantiation:Boolean;
private var language:String;
public static function getInstance():StaticInstance {
if (instance == null) {
allowInstantiation = true;
instance = new StaticInstance();
allowInstantiation = false;
}
return instance;
}
public function StaticInstance():void {
if (!allowInstantiation) {
throw new Error("Error: Instantiation failed: Use StaticInsance.getInstance() instead of new.");
}
}
public function setLanguage(_language:String):void{
language = _language;
trace("language set = " + language);
}
public function getLanguage():String{
return language;
}
}
}
This code allowed me to hold the data and call upon it again from two different classes. It's a very hack job instead of just being able to pass on the variable from function to function, but in my case we didn't create this file, we are modifying it and attempting to do things beyond the original scope of the project.
Thanks again for your help Shaun! I hope this helps other people!

ActionScript Basic Question

i've only ever created external .as files that extended a class such as sprite. now i just want to create one that doesn't extend anything and call it from a frame script.
package
{
public class Test
{
public function Test(val:Number, max:Number)
{
trace(val, max);
}
}
}
from my frame script of an .fla that is in the same folder as Test.as, i'll write this:
Test(50, 100);
this produces the following error:
1137: Incorrect number of arguments. Expected no more than 1.
Your code will be interpreted as cast to Test. It makes no sense to cast 2 numbers as a Test object.
What you want is an instance (an object) of the class Test.
For this, you need the new operator.
var testInstance:Test = new Test(50,100);
Then, you can use your object as needed, for example, calling methods, setting or getting values, etc.
testInstance.someMethod("hello");
testInstance.someNumber = 10;
var n:Number = testInstance.someNumber;
// etc...

How do I reference an object, add a tween to it and put this in an Eventlistener in AS3?

I'm having quite some trouble to try and get an app I wrote in AS2 to AS3. The reason I need to go to AS3 is something icky, so I won't go into detail about it.
I've got 90% of the application running with the new code.
Now I've come to the point where I have to convert this code from AS2,
function setAnimation(theObject,id)
{
theObject.vensterid=id;
theObject.onEnterFrame = function()
{
var myHoriTween:Tween = new Tween (this,"_x",Strong.easeOut,this._x,(130+((theObject.vensterid-frameno)*260)),1,true);
}
}
setAnimation(venster0,0);
, to AS3. My attempt of doing this ended up like
function setAnimation(anObject,id) {
var theObject = this[anObject];
theObject.vensterid=id;
function slideHorizontal(event:Event)
{
var myTween:Tween = new Tween (theObject,"x",Strong.easeOut,this.x,(130+((theObject.vensterid-frameno)*260)),1,true);
}
theObject.addEventListener(Event.ENTER_FRAME,slideHorizontal);
}
setAnimation(venster0,0);
and gives me the following non-error (it doesn't show as a compiler error, but as output):
TypeError: Error #1010: A term is undefined and has no properties.
at sliding_windows_as3_fla::SlideMenu_1/setAnimation()
at sliding_windows_as3_fla::SlideMenu_1/frame1()
I think this is very strange since it doesn't say anything about which term (and there are quite a lot) and googling didn't find me an explanation either.
I didn't get the chance to test your code, because it's difficult to set up a context for it, but my thoughts would be:
You should declare the parameter types: function setAnimation(anObject:Object,id:uint):void. It's at least good practice.
var theObject = this[anObject]; is completely unnecessary if your variable anObject is an object. I think var theObject = this[anObject]; doesn't work, theObject ends up being null and that's why you get your error. If you have declared a variable called venster0, that is the instance of a class that extends Object, then you can pass the reference to it without any other trouble.
Depending on the object you work with, theObject.vensterid=id; might not work. The class that theObject instances must have the 'vensterid' property, or you will get `1119: Access of possibly undefined property vensterid through a reference with static type ...
I think your problem here is following string:
var theObject = this[anObject];
Just replace it with
var theObject = anObject;
I hope that's what you need.
Alternatively instead of
setAnimation(venster0,0);
you could pass an instance name (i.e. String):
setAnimation("venster0",0);
That will work because by this['propertyname'] you are actually accessing Object's property by name.
Just going to throw out that using the built-in Tween classes in Flash/Flex is a pain. Look into using Tweening libraries instead: Tweener, TweenLite, etc. They are much easier to work with, and you don't have to worry about maintaining references until the Tween completes.