How to use ASSetPropFlags in ActionScript 2.0

There are multiple undocumented functions in ActionScript 2.0 of which one is ASSetPropFlags. I needed to use this function on a project recently and quickly found out that most of the unofficial documentation is either incorrect or overly brief. What I'm going to do here is describe how the function works and then show a program that will test and confirm some of the more complex functionality. 

ASSetPropFlags lets you modify the following three flags of an object's child properties.

  1. Write Protection: When a child property is write protected, its value can no longer be modified.
  2. Delete Protection: When a child property is delete protected, it can no longer be deleted from its parent.
  3. Hidden: When a child property is hidden it is no longer processed in a for..in loop enumerating over its parent's children.

These flags are usually modified for one of two reasons.

  1. You can protect properties of an object you made to prevent user's of your code from modifying variables that should remain constant.
  2. You can remove restrictions on properties of libraries you are using. For example, you could disable write protection of a function such as Mouse.show() and then set it to execute a new function of your own.

Now that we've covered the purpose of ASSetPropFlags, let's take a look at the function prototype and discuss each parameter.

Prototype

function ASSetPropFlags( object, properties, setFlags, clearFlags )

Parameters

objectThe parent object of the properties to change.
propertiesThe desired properties to change. Property names can be listed as an array of strings or as a comma delimited string. For example, the array ["myChild1", "myChild2", "myChild3"] and the string "myChild1,myChild2,myChild3" would produce the same result. If you want to modify all of an object's properties, a value of null should be supplied here.
setFlags
This parameter specifies which flags should be enabled. Note that if a flag is specified to be enabled by the setFlags parameter and disabled by the clearFlags parameter, the flag will be enabled (i.e. enabling has priority over disabling). This parameter takes an integer value in the inclusive range from 0 to 7 where each decimal value represents the appropriate set of three bit flags in binary.
 

The following table shows the decimal value you pass to ASSetPropFlags, what its binary representation is, and how it changes each flag.

 
 
decimal (value passed to function)binarywrite protecteddelete protectedhidden
0000do not changedo not changedo not change
1001do not changedo not changeenable
2010do not changeenabledo not change
3011do not changeenableenable
4100enabledo not changedo not change
5101enabledo not changeenable
6110enableenabledo not change
7111enableenableenable
 
For example, passing a value of 5 will enable the write protected and hidden flags of a property while leaving the delete protected flag unchanged.
clearFlags
This parameter specifies which flags should be disabled. Note that if a flag is specified to be enabled by the setFlags parameter and disabled by the clearFlags parameter, the flag will be enabled (i.e. enabling has priority over disabling). This parameter takes an integer value in the inclusive range from 0 to 7 where each decimal value represents the appropriate set of three bit flags in binary.
 

The following table shows the decimal value you pass to ASSetPropFlags, what its binary representation is, and how it changes each flag.

 
 
decimal (value passed to function)binarywrite protecteddelete protectedhidden
0000do not changedo not changedo not change
1001do not changedo not changedisable
2010do not changedisabledo not change
3011do not changedisabledisable
4100disabledo not changedo not change
5101disabledo not changedisable
6110disabledisabledo not change
7111disabledisabledisable
 
For example, passing a value of 5 will disable the write protected and hidden flags of a property while leaving the delete protected flag unchanged.

Proof

The largest confusion in the online documentation of this function surrounds the functionality of the third and fourth parameters. Given my description above, flags of a property are set (enabled) and cleared (disabled) with the third and fourth parameters. If a flag is set and cleared at the same time, the setting takes priority. We can interpret this as a two step operation: first clear the flags specified by the fourth parameter (clearFlags) and then set the flags specified by the third parameter (setFlags). We can perform these steps with the following bitwise operation to update a given property's flags.

flags = (flags & ~clearFlags) | setFlags;

You can run the below ActionScript to verify the functionality. It first tests that every desired set of flags can be applied to a newly constructed object. After verifying that this functionality works, it tests that every possible modification to every possible set of initial flags produces the expected result. If any tests do not produce the expected values, information about that permutation is traced to the output. By placing this function in the first frame of a movie and running it, you will see the following output printed to the console showing that all expected results were achieved.

START: Test setting of flags
FINISHED: Test setting of flags
START: Test modification of flags
FINISHED: Test modification of flags

//******************************************************************************
// Test functionality of setting individual flags with the third parameter of
// of ASSetPropFlags.
//******************************************************************************
function TestSetPropFlags(setFlags)
{
    var foo = new Object;
    foo.bar="initialValue";
 
    // set the initial flags
    ASSetPropFlags(foo, null, setFlags, 0);
 
    // test hidden
    var hidden = 1;
    for( var i in foo )
    {
        if( i == "bar" )
            hidden = 0;
    }
 
    // test write protection
    foo.bar="newValue";
    var writeProtected = (foo.bar == "initialValue") ? 1 : 0;
 
    // test delete protection
    delete foo.bar;
    var deleteProtected = (foo.bar != null) ? 1 : 0;
 
    // check that the resulting flags match teh expected value
    var resultFlags = (writeProtected << 2) | (deleteProtected << 1) | (hidden << 0);
    if( resultFlags != setFlags )
    {
        trace(  " set:" + setFlags +
                " resultFlags:" + resultFlags +
                " writeProtected:" + writeProtected +
                " deleteProtected:" + deleteProtected +
                " hidden:" + hidden );
    }
}
 
//******************************************************************************
// Test functionality of modifying flags using the third and forth parameters
// of ASSetPropFlags.
//******************************************************************************
function TestModifyPropFlags(initFlags, setFlags, clearFlags)
{
    var foo = new Object;
    foo.bar="initialValue";
 
    // set the initial flags
    ASSetPropFlags(foo, null, initFlags, 0);
 
    // apply the change
    ASSetPropFlags(foo, null, setFlags, clearFlags);
 
    // test hidden
    var hidden = 1;
    for( var i in foo )
    {
        if( i == "bar" )
            hidden = 0;
    }
 
    // test write protection
    foo.bar="newValue";
    var writeProtected = (foo.bar == "initialValue") ? 1 : 0;
 
    // test delete protection
    delete foo.bar;
    var deleteProtected = (foo.bar != null) ? 1 : 0;
 
    // apply the expected formula to the input values for comparison
    var expectedFlags = (initFlags & ~clearFlags) | setFlags;
 
    // check that the resulting flags match the expected value
    var resultFlags = (writeProtected << 2) | (deleteProtected << 1) | (hidden << 0);
    if( resultFlags != expectedFlags )
    {
        trace(  " init:" + initFlags +
                " set:" + setFlags +
                " clear:" + clearFlags +
                " expected:" + expectedFlags +
                " resultFlags:" + resultFlags +
                " writeProtected:" + writeProtected +
                " deleteProtected:" + deleteProtected +
                " hidden:" + hidden );
    }
}
 
//******************************************************************************
// Declare variables.
//******************************************************************************
var initFlags;
var setFlags;
var clearFlags;
 
//******************************************************************************
// Run flag setting tests on every permutation
//******************************************************************************
trace("START: Test setting of flags");
for( setFlags = 0; setFlags < 8; ++setFlags )
{
    TestSetPropFlags(setFlags);
}
trace("FINISHED: Test setting of flags");
 
//******************************************************************************
// Run flag modification tests on every permutation.
//******************************************************************************
trace("START: Test modification of flags");
for( initFlags = 0; initFlags < 8; ++initFlags )
{
    for( setFlags = 0; setFlags < 8; ++setFlags )
    {
        for( clearFlags = 0; clearFlags < 8; ++clearFlags )
        {
            TestModifyPropFlags(initFlags,setFlags,clearFlags);
        }
    }
}
trace("FINISHED: Test modification of flags");
 
//******************************************************************************
// Stop the movie from looping.
//******************************************************************************
stop();

Comments