in modules/asc/src/java/macromedia/asc/semantics/FlowAnalyzer.java [1115:1394]
public Value evaluate(Context cx, StatementListNode node)
{
if (debug)
{
System.out.print("\n// +StatementListNode");
}
NodeFactory nodeFactory = cx.getNodeFactory();
if (node.has_pragma) {
NumberUsage blockUsage = new NumberUsage(number_usage_stack.back());
number_usage_stack.push_back(blockUsage);
}
ObjectValue obj = cx.scope();
boolean inside_class = false;
if (obj.builder instanceof InstanceBuilder ||
obj.builder instanceof ClassBuilder)
{
inside_class = true;
}
boolean inside_cinit = ( this_contexts.last() == cinit_this );
ObjectList<DefinitionNode> hoisted_defs = new ObjectList<DefinitionNode>();
ObjectList<Node> instance_inits = instanceinits_sets.back();
{
// iterate through the statements backwards to find the end point dominator
for (int i = node.items.size() - 1; i >= 0; i--)
{
Node n = node.items.get(i);
if (n != null)
{
// While we are iterating backwards set aside any definitions we find,
// to be processed before the other statements. Handle the initializers
// according to their type (e.g. functions get moved to the start of
// statements, var initializers stay put
// cn: a definition might live in a labelled node
LabeledStatementNode label = null;
if (n instanceof LabeledStatementNode)
{
label = (LabeledStatementNode)n;
n = label.statement;
}
if (n.isDefinition())
{
DefinitionNode def = ((n instanceof DefinitionNode) ? (DefinitionNode)n : null);
// Eval the definition. At this point we are iterating backward
// (to find the end point dominator) so definitions are ordered
// last to first. This is weird, but should not be a problem
// since their initializers are evaluated in the right order.
if(def instanceof IncludeDirectiveNode || def instanceof ImportDirectiveNode)
{
// TODO: Remove eventually
/* SPECIAL CASE (for bug 124494)
* So all statements in an included file have IncludeDirectives
* wrapped around them (e.g. evaluated with the correct [sub-]context):
*
* Hoist a copy of the ID node, leave the original ID node where it is.
* Both hoisted definitions and left behind (non-def) statements
* are correctly bracketed.
*
* This is assuming that, semantically, we want non-defs in an
* included file (the include statement being within a classdef)
* to work, and not throw an error. */
hoisted_defs.push_back(def);
// node.items.set(i, nodeFactory.emptyStatement(node.pos())); // don't do this
}
else if( n.isConst() || inside_class )
{
// If definition is const, then put it and its initializer
// at the beginning of the block and replace current item
// with an empty statement.
hoisted_defs.push_back(def); // C B A
// hoisted_defs.insert(hoisted_defs.begin(),def);
if (label == null)
node.items.set(i, nodeFactory.emptyStatement());
else
label.statement = nodeFactory.emptyStatement();
}
else
{
// Otherwise, leave the initializer at the location of the
// original definition and initalize the variable to its default
// value at the beginning of the block.
hoisted_defs.push_back(def);
if( def.attrs != null && def.attrs.hasAttribute(INTRINSIC) )
{
if (label == null)
node.items.set(i, nodeFactory.emptyStatement());
else
label.statement = nodeFactory.emptyStatement();
}
else
{
Node init = n.initializerStatement(cx);
if (init != null)
{
// Put initializer(if there is one) at position
// of original definition.
if (label == null)
node.items.set(i, init);
else
label.statement = init;
}
else
{
if (label == null)
node.items.set(i, nodeFactory.emptyStatement());
else
label.statement = nodeFactory.emptyStatement();
}
}
}
}
else if (!endpoint_dominator_is_set &&
loop_index == 0 && n.isExpressionStatement() &&
cx.getScopes().size() == 1 )
{
// The end point dominator is the statement that leaves
// the continuation value on the stack. All other results,
// don't need to be pushed onto the stack.
endpoint_dominator = n;
endpoint_dominator_is_set = true;
}
}
}
}
ObjectList<Node> inits = new ObjectList<Node>();
{
// Insert initializers for constants and functions at the
// beginning of the block.
for (int i = hoisted_defs.size() - 1; i >=0; i--) // A B C
{
DefinitionNode def = hoisted_defs.get(i);
{
// pre-process namespace definitions and attributes
boolean is_static = false;
boolean is_intrinsic = false;
boolean is_include = def instanceof IncludeDirectiveNode;
boolean is_namespace = def instanceof NamespaceDefinitionNode;
boolean is_use = def instanceof UseDirectiveNode || def instanceof ImportDirectiveNode;
boolean is_const = false; // tbd
if ( is_namespace || is_const || is_use || is_include )
{
if (def.attrs != null)
{
def.attrs.evaluate(cx, this);
is_static = def.attrs.hasStatic;
is_intrinsic = def.attrs.hasIntrinsic;
}
def.evaluate(cx,this);
}
else
if( def.attrs != null)
{
def.attrs.evaluate(cx,this);
is_static = def.attrs.hasStatic;
is_intrinsic = def.attrs.hasIntrinsic;
}
boolean needs_init = (!is_use && !is_namespace && !is_intrinsic && def.isConst()) || inside_class;
if( inside_cinit && !is_static )
{
instance_inits.push_back(def);
if( needs_init )
{
instance_inits.push_back(def.initializerStatement(cx));
}
}
else
{
if( !is_use && !(def instanceof ClassDefinitionNode && needs_init) ) // ISSUE: remove this special case check
{
inits.push_back(def);
}
if( needs_init )
{
inits.push_back(def.initializerStatement(cx)); // A B C
} // otherwise, there is already a initializer at the orginal point of definition
}
}
}
}
{ // add the non-static inits to the beginning of the statements list
for (int i = inits.size() - 1; i>= 0;i--) // C B A
{
node.items.add(0, inits.get(i)); // A B C
}
}
{
// Now rip through the statements
for (Node n : node.items)
{
if (n == endpoint_dominator)
{
// This statement is the end point dominator, therefore
// set the define_cv flag so the evaluator will know that
// this is an implicit assignment to _cv and will create
// a new definition for it.
define_cv = true;
}
else if (!endpoint_dominator_is_set)
{
// This happens when there is no statement that leaves a
// continuation value on the stack. In this case, an
// empty value is pushed onto the stack at start of the
// program.
define_cv = true;
endpoint_dominator_is_set = true;
}
else
{
// This statement is not the end point dominator, and
// The end point dominator has been set, then do nothing
}
if (n != null)
{
if (!doingMethod() && !n.isDefinition())
{
// We are done with definitions, which means we are doing
// program statements.
StartMethod(fun_name_stack.last(), max_params_stack.last(),
max_locals_stack.last(), max_temps_stack.last(),
false, 0);
}
n.evaluate(cx, this);
}
}
if (!doingMethod() ) // If still not doing method, then start doing it now.
// This happens when you have a block with only definitions
{
StartMethod(fun_name_stack.last(), max_params_stack.last(),
max_locals_stack.last(), max_temps_stack.last(), false, 0);
// Now we are doing a method
}
}
/*
if( node.is_block )
{
usednamespaces_sets.back().pop_back();
default_namespaces.pop_back();
}
*/
if (node.has_pragma) {
node.numberUsage = number_usage_stack.back();
number_usage_stack.pop_back();
}
if (debug)
{
System.out.print("\n// -StatementListNode");
}
return null;
}