in modules/asc/src/java/macromedia/asc/semantics/FlowAnalyzer.java [4261:5006]
public Value evaluate(Context unused_cx, ClassDefinitionNode node)
{
// If we are doing a class, then defer this class definition until we
// are done. Put it in the current set of the clsdefs_sets for now.
Context cx = node.cx; // switch contexts so that the original one is used
/* #if 0 // debugging
switch( node.state )
{
case node.INIT:
printf("\ndoing init");
break;
case node.INHERIT:
printf("\ndoing inheritance");
break;
case node.MAIN:
printf("\ndoing body");
break;
default:
cx.internalError("invalid CDN state");
break;
}
#endif */
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
usednamespaces_sets.push_back(node.pkgdef.used_namespaces);
used_def_namespaces_sets.push_back(node.pkgdef.used_def_namespaces);
importednames_sets.push_back(node.pkgdef.imported_names);
}
// First, initialize the node
if( node.cframe == null /*doingClass()*/ )
{
// If this is a toplevel definition (pkgdef!=null), then set up access namespaces
node.used_namespaces.addAll(usednamespaces_sets.back()); // makes a copy
node.imported_names.putAll(importednames_sets.back()); // makes a copy
node.public_namespace = cx.publicNamespace(); // public_namespaces.back();
node.default_namespace = default_namespaces.back();
// clsdefs_sets is a stack of sets of class definitions.
// Each set contains the classes at a particular scope level.
if( clsdefs_sets.size() == 1 ) // otherwise, we've already captured the clsdefs for nested classes
{
int size = clsdefs_sets.last().size();
int i;
// Look for the current node in the set for the current scope.
for (i = 0; i < size && clsdefs_sets.last().get(i) != node; ++i);
// If it is not in the set, then add it.
if (i >= size)
{
if (package_name.length() != 0)
{
node.package_name = package_name;
}
clsdefs_sets.last().add(node);
}
else
{
//cx.internalError("Internal error: the same class definition should never get processed twice.");
// This actually does happen, but the code from this point on is only executed once.
}
}
// boolean is_static = false;
boolean is_intrinsic = false;
if (node.attrs != null)
{
// is_static = node.attrs.hasStatic;
is_intrinsic = node.attrs.hasIntrinsic;
if( node.attrs.hasNative )
{
cx.error(node.pos(), kError_InvalidNative);
}
// Note: node.attrs.hasOverride will have already been checked
// by the hoisted_defs test in StatementListNode
/*if( node.attrs.hasOverride )
{
cx.error(node.pos(), kError_InvalidOverride);
}*/
}
if( cx.getScopes().size() > 1 )
{
if (node.isInterface())
cx.error(node.pos(), kError_InvalidInterfaceNesting);
else
cx.error(node.pos(), kError_InvalidClassNesting);
}
// Only do the following once
if (node.ref == null)
{
ObjectValue ownerobj = cx.scope();
Builder ownerbui = ownerobj.builder;
String region_name = region_name_stack.back();
region_name += region_name.length() > 0 ? "/" : "";
ObjectList<String> namespace_ids = new ObjectList<String>();
computeNamespaces(cx,node.attrs,node.namespaces,namespace_ids);
if (node.pkgdef == null && cx.getScopes().size() == 1 && node.attrs != null)
{
if( node.attrs.hasAttribute(PUBLIC) )
cx.error(node.attrs.pos(), kError_InvalidPublic);
}
Value val = node.name.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
//String fullname = cx.debugName(region_name,node.ref.name,namespace_ids,EMPTY_TOKEN);
QName fullname = cx.computeQualifiedName(region_name, node.ref.name, node.ref.getImmutableNamespaces().back(), EMPTY_TOKEN);
node.private_namespace = cx.getNamespace(fullname.toString(), Context.NS_PRIVATE);
node.protected_namespace = cx.getNamespace(fullname.toString(), Context.NS_PROTECTED);
node.static_protected_namespace = cx.getNamespace(fullname.toString(), Context.NS_STATIC_PROTECTED);
if( cx.isBuiltin(fullname.toString()) )
{
node.cframe = cx.builtin(fullname.toString());
node.iframe = node.cframe.prototype;
}
else
{
node.cframe = TypeValue.defineTypeValue(cx,new ClassBuilder(fullname,node.protected_namespace,node.static_protected_namespace),fullname,TYPE_object); // ISSUE: what should the type tag be?
if( !node.is_default_nullable )
{
node.cframe.is_nullable = false;
}
node.cframe.type = cx.typeType().getDefaultTypeInfo();
if( node.cframe.prototype != null )
{
node.cframe.prototype.clearInstance(cx,new InstanceBuilder(fullname),node.cframe, ObjectValue.EMPTY_STRING, true);
node.iframe = node.cframe.prototype;
}
else
{
node.iframe = new ObjectValue(cx,new InstanceBuilder(fullname),node.cframe);
node.cframe.prototype = node.iframe; // ISSUE: this is not really the prototype, but works for now
node.owns_cframe = true; // so it deletes it
}
}
if (node instanceof InterfaceDefinitionNode)
{
node.default_namespace = node.cframe; // class object and namespace all in one
}
if( node.attrs != null)
{
node.cframe.builder.is_dynamic = node.iframe.builder.is_dynamic = node.attrs.hasDynamic;
node.cframe.builder.is_final = node.iframe.builder.is_final = node.attrs.hasFinal;
node.cframe.builder.is_intrinsic = node.iframe.builder.is_intrinsic = is_intrinsic;
}
Namespaces open_definition_namespaces ;
if( node.attrs != null && node.attrs.hasUserNamespace() )
{
open_definition_namespaces = node.namespaces;
}
else
{
open_definition_namespaces = used_def_namespaces_sets.back();
}
Namespaces hasNamespaces = ownerobj.hasNames(cx,GET_TOKEN,node.ref.name,open_definition_namespaces);
if( hasNamespaces == null )
{
// If this class is intrinsic, then don't implement it
int var_id = -1;
if( node.attrs==null || !node.attrs.hasIntrinsic )
{
var_id = ownerbui.Variable(cx,ownerobj);
}
int slot_id = ownerbui.ExplicitVar(cx,ownerobj,node.ref.name,node.namespaces,cx.typeType(),-1,-1,var_id);
ownerobj.getSlot(cx,slot_id).setValue(node.cframe);
ownerobj.getSlot(cx,slot_id).setConst(true); // class defs are const.
// Implicit method to represent call & construct
ownerbui.ImplicitCall(cx,ownerobj,slot_id,node.cframe,CALL_Method,-1,-1);
ownerbui.ImplicitConstruct(cx,ownerobj,slot_id,node.cframe,CALL_Method,-1,-1);
}
else
{
if( node.isInterface() )
{
cx.error( node.name.pos(), kError_DuplicateInterfaceDefinition, node.ref.name);
}
else
{
cx.error( node.name.pos(), kError_DuplicateClassDefinition, node.ref.name);
found_circular_or_duplicate_class_definition = true;
}
}
// delete hasNamespaces;
}
node.used_namespaces.push_back(node.private_namespace);
node.used_namespaces.push_back(node.protected_namespace);
node.used_namespaces.push_back(node.static_protected_namespace);
node.used_def_namespaces.push_back(node.private_namespace);
node.used_def_namespaces.push_back(node.public_namespace);
node.used_def_namespaces.push_back(node.default_namespace);
node.used_def_namespaces.push_back(node.protected_namespace);
node.used_def_namespaces.push_back(node.static_protected_namespace);
node.state = ClassDefinitionNode.INHERIT;
NodeFactory nodeFactory = cx.getNodeFactory();
QualifiedIdentifierNode qualifiedIdentifier = nodeFactory.qualifiedIdentifier(node.attrs, node.name.name, node.name.pos());
node.init = nodeFactory.expressionStatement(nodeFactory.assignmentExpression(qualifiedIdentifier, CONST_TOKEN, node));
node.init.isVarStatement(true);
clsdefs_sets.push_back(new ObjectList<ClassDefinitionNode>()); // make dummy
cx.pushStaticClassScopes(node);
ObjectList<String> namespace_ids = new ObjectList<String>();
if( node.namespaces.size() != 0 )
{
namespace_ids.push_back(node.namespaces.back().name);
}
else
{
namespace_ids.push_back("error");
}
region_name_stack.push_back(cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,EMPTY_TOKEN));
usednamespaces_sets.push_back(node.used_namespaces);
used_def_namespaces_sets.push_back(node.used_def_namespaces);
for (ClassDefinitionNode n : node.clsdefs)
{
// Haven't done the outer class' statement list yet, so the attrs
// haven't been done yet
if( n.attrs != null )
{
n.attrs.evaluate(cx,this);
}
n.evaluate(cx,this);
}
cx.popStaticClassScopes(node);
region_name_stack.removeLast();
usednamespaces_sets.removeLast();
used_def_namespaces_sets.removeLast();
clsdefs_sets.removeLast();
}
else if( doingClass() || doingMethod() )
{
// Wait
}
else if (resolveInheritance)
{
if (node.baseclass == null && node.cframe != cx.objectType())
{
NodeFactory nf = cx.getNodeFactory();
node.baseclass = nf.memberExpression(null, nf.getExpression(nf.identifier("Object")));
}
if (node.baseref == null)
{
if (node.baseclass != null)
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
Value val2 = node.baseclass.evaluate(cx,this);
fa_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
node.baseref = ((val2 instanceof ReferenceValue) ? (ReferenceValue)val2 : null);
if( node.baseref == null )
{
// uh oh, didn't resolve to anything, but we have a baseclass expression
cx.error(node.baseclass.pos(), kError_InvalidBaseTypeExpression);
}
}
}
if (node.baseref != null)
{
Value val = node.baseref.getValue(cx);
TypeValue type = ((val instanceof TypeValue) ? (TypeValue)val : null);
if (type == null)
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_UnknownBaseClass);
}
else
if( type.builder.is_final )
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_BaseClassIsFinal);
}
else
if ( type.builder instanceof ClassBuilder && ((ClassBuilder)type.builder).is_interface )
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), node.isInterface() ? kError_CannotExtendClass : kError_CannotExtendInterface);
}
else
{
inheritClassSlots(node.cframe, node.iframe, type, cx);
}
}
if( node.interfaces != null )
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
node.interfaces.evaluate(cx,this);
fa_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
}
/*
if (node.interfaces != null && node.interfaces.values != null)
{
for (Value v : node.interfaces.values)
{
if (v instanceof ReferenceValue)
{
ReferenceValue ref = (ReferenceValue) v;
Value v2 = v.getValue(cx);
TypeValue t = dynamic_cast(TypeValue.class, v2);
if (t == null)
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_UnknownBaseClass);
}
else
{
//inheritClassSlots(node.cframe, node.iframe, t, cx);
}
}
}
}
*/
node.state = ClassDefinitionNode.MAIN;
for (ClassDefinitionNode n : node.clsdefs)
{
n.evaluate(cx,this);
}
}
else
if( node.needs_init )
{
node.needs_init = false;
node.init.evaluate(cx,this);
node.needs_init = true;
}
else
{
// Start compiling the class. Statics get put in cframe, everything else
// gets put in iframe.
this_contexts.add(error_this);
strict_context.push_back(true);
usednamespaces_sets.push_back(node.used_namespaces);
used_def_namespaces_sets.push_back(node.used_def_namespaces);
importednames_sets.push_back(node.imported_names);
// Put super instance properties in the instance prototype before compiling
// the current class body.
if (node.baseref != null)
{
Value val = node.baseref.getValue(cx);
TypeValue type = ((val instanceof TypeValue) ? (TypeValue)val : null);
if (type == null)
{
cx.error(node.baseclass.pos(), kError_UnknownBaseClass, node.baseref.name);
}
else
if( type.builder.is_final )
{
cx.error(node.baseclass.pos(), kError_BaseClassIsFinal);
}
else
if ( type.builder instanceof ClassBuilder && ((ClassBuilder)type.builder).is_interface )
{
cx.error(node.baseclass.pos(), kError_CannotExtendInterface);
}
else
{
inheritClassSlots(node.cframe, node.iframe, type, cx);
// No matter what, if the base slot was from an import, we can't early bind.
Slot base_slot = node.baseref.getSlot(node.cx, node.baseref.getKind());
if( base_slot.isImported() && type != cx.noType() ) //Ok if it's object, doesn't have any methods...
{
((InstanceBuilder)node.iframe.builder).canEarlyBind = false;
}
// inherit protected namespaces
ClassBuilder classBuilder;
while( type != null && type != node.cframe && type.resolved)
{
classBuilder = (ClassBuilder) type.builder;
if( classBuilder.static_protected_namespace != null )
{
node.used_namespaces.push_back(classBuilder.static_protected_namespace);
node.used_def_namespaces.push_back(classBuilder.static_protected_namespace);
}
type = type.baseclass;
}
}
}
if (node.interfaces != null && node.interfaces.values != null)
{
ObjectList<ReferenceValue> interface_refs = ((InstanceBuilder)node.iframe.builder).interface_refs;
HashSet<TypeValue> seen_interfs = new HashSet<TypeValue>();
for (int i = 0; i < node.interfaces.values.size(); ++i )
{
Value v = node.interfaces.values.get(i);
if (v instanceof ReferenceValue)
{
ReferenceValue ref = (ReferenceValue) v;
Value v2 = v.getValue(cx);
TypeValue t = ((v2 instanceof TypeValue) ? (TypeValue)v2 : null);
if (t == null )
{
cx.error(node.interfaces.items.get(i).pos(), kError_UnknownInterface, ref.name);
}
else
{
if (t.builder instanceof ClassBuilder)
{
if (!(((ClassBuilder)t.builder).is_interface))
{
cx.error(node.interfaces.items.get(i).pos(), kError_CannotExtendClass, ref.name);
}
else
{
if( seen_interfs.contains(t) )
{
cx.error(node.interfaces.items.get(i).pos(), kError_DuplicateImplements, node.ref.name, ref.name);
}
else
{
seen_interfs.add(t);
}
interface_refs.push_back(ref);
if (node instanceof InterfaceDefinitionNode)
{
// If this is an interface, inherit the super-interface slots.
inheritClassSlots(node.cframe, node.iframe, t, cx);
}
}
}
else
{
cx.error(node.interfaces.items.get(i).pos(), kError_UnknownInterface, ref.name);
}
}
}
else
{
// uh oh, didn't resolve to anything, but we have a baseclass expression
cx.error(node.interfaces.items.get(i).pos(), kError_InvalidInterfaceTypeExpression);
}
}
}
Names lastInterfaceMethods = interfaceMethods;
interfaceMethods = null;
scanInterfaceMethods(cx, node);
processInterfacePublicMethods(cx, node.iframe);
StartClass(node.ref.name);
ObjectList<String> namespace_ids = new ObjectList<String>();
if( node.namespaces.size() != 0 )
{
namespace_ids.push_back(node.namespaces.back().name);
}
else
{
namespace_ids.push_back("error");
}
region_name_stack.push_back(cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,EMPTY_TOKEN));
/*
node->used_namespaces = *used_namespaces_sets.back() // save alias of outer namespaces
...
node->used_namespaces.push_back(node->private_namespace); // add implicitly used namespaces
...
usednamespaces_sets.back(&node->used_namespaces) // add current namespaces to nss sets
...
used_namespaces get deleted
*/
private_namespaces.push_back(node.private_namespace);
default_namespaces.push_back(node.default_namespace);
public_namespaces.push_back(node.public_namespace);
protected_namespaces.push_back(node.protected_namespace);
static_protected_namespaces.push_back(node.static_protected_namespace);
cx.pushStaticClassScopes(node);
this_contexts.removeLast();
this_contexts.add(cinit_this);
// Function expressions that occur in the current block will be
// compiled as though they had occured at the end of the block.
// The variable that references them is initialized at the beginning
// of the block.
fexprs_sets.add(new ObjectList<FunctionCommonNode>());
staticfexprs_sets.add(new ObjectList<FunctionCommonNode>());
instanceinits_sets.add(new ObjectList<Node>());
// Copy the set of nested functions into the node for use
// by later phases.
node.fexprs = fexprs_sets.last();
node.instanceinits = instanceinits_sets.last(); // Holds the static initializers for this class
node.staticfexprs = staticfexprs_sets.last(); // Holds the static initializers for this class
fun_name_stack.add(node.ref.name); // During flow analysis we use the class name
max_params_stack.add(0);
max_locals_stack.add(node.var_count);
max_temps_stack.add(node.temp_count);
StartMethod(fun_name_stack.last(), max_params_stack.last(), max_locals_stack.last());
if (node.statements != null)
{
// Evaluate the statements. When we are done, the static names
// are in the class object builder. The static initializers are
// in the inner staticdefs_sets sets. The instance names are in
// the instance object builder, and the instance initializers
// are in the
node.statements.evaluate(cx, this);
node.temp_count = getTempCount();
node.var_count = node.cframe.var_count;
}
else
{
StartMethod(fun_name_stack.last(), max_params_stack.last(), max_locals_stack.last());
}
node.temp_count = getTempCount(); // Remember the temp count
// Return(TYPE_none);
Return(TYPE_void);
FinishMethod(cx, fun_name_stack.back(), null,null,null,0, cx.getScopes().size(), "",false,false, null);
cx.pushScope(node.iframe);
this_contexts.removeLast();
this_contexts.add(instance_this);
// Evaluate the instance initializers
// (This must be done before we add the default
// constructor if needed, because this is where
// has_ctor gets set)
{
for (Node n : node.instanceinits)
{
if( cx.statics.es4_nullability && !n.isDefinition())
node.iframe.setInitOnly(true);
n.evaluate(cx, this);
if( cx.statics.es4_nullability && !n.isDefinition())
node.iframe.setInitOnly(false);
}
}
ObjectValue obj = node.iframe;
InstanceBuilder bui = ((obj.builder instanceof InstanceBuilder) ? (InstanceBuilder)obj.builder : null);
if( !bui.is_intrinsic && !bui.has_ctor )
{
NodeFactory nf = cx.getNodeFactory();
FunctionNameNode fname = nf.functionName(EMPTY_TOKEN, nf.identifier(node.ref.name,0));
nf.has_rest = false;
nf.has_arguments = false;
FunctionCommonNode fexpr = nf.functionCommon(cx, fname.identifier, nf.functionSignature(null, null, 0), null, 0);
AttributeListNode attrs = nf.attributeList(nf.identifier(PUBLIC,false,0),null);
attrs.evaluate(cx,this);
FunctionDefinitionNode fdef = nf.functionDefinition(cx, attrs, fname, fexpr);
fdef.pkgdef = node.pkgdef;
fdef.evaluate(cx,this);
Node init = fdef.initializerStatement(cx);
init.evaluate(cx,this);
if( null == node.statements )
{
node.statements = nf.statementList(null,init);
}
else
{
node.statements.items.add(0,init);
}
}
// Now turn the static names into definitions
// Generate code for the static property definitions
{
this_contexts.add(error_this);
cx.popScope(); // temporarily
for (Node n : node.staticfexprs)
{
n.evaluate(cx, this);
}
cx.pushScope(node.iframe);
this_contexts.removeLast();
}
fun_name_stack.removeLast();
max_params_stack.removeLast();
max_locals_stack.removeLast();
max_temps_stack.removeLast();
// Now evaluate each function expression
{
for (FunctionCommonNode n : node.fexprs)
{
n.evaluate(cx, this);
}
}
// Remove the top set of nested functions from the stack of sets
fexprs_sets.removeLast();
//ASSERT(fexprs_sets.size() == 0);
private_namespaces.pop_back();
default_namespaces.pop_back();
public_namespaces.pop_back();
protected_namespaces.pop_back();
static_protected_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
FinishClass(cx,node.cframe.builder.classname,null,false, false, false, node.cframe.is_nullable);
this_contexts.removeLast();
// pop the iframe now so we process class defs in static scope
cx.popScope(); // iframe
// Now evaluate each class definition
{
// node.clsdefs have the baseclass.cframe resolved, i.e. we've got fully-qualified class names.
// sort the class names based on "extends" and "implements"...
node.clsdefs = sortClassDefinitions(node.cx, node.clsdefs);
if (found_circular_or_duplicate_class_definition == false)
{
for (ClassDefinitionNode clsdef : node.clsdefs)
{
clsdef.evaluate(cx,this);
}
}
}
// Remove the top set of nested classes from the stack of sets
instanceinits_sets.removeLast();
staticfexprs_sets.removeLast();
cx.popStaticClassScopes(node);
node.debug_name = region_name_stack.back();
// store debug name on the slot as well. asDoc needs fully qualified debug_names for all
// type references.
Slot s = node.ref.getSlot(cx,GET_TOKEN);
if (s != null)
{
s.setDebugName(node.debug_name);
s.setConst(true); // class slots are const
}
region_name_stack.removeLast();
strict_context.pop_back();
//ASSERT(fexprs_sets.size() == 0);
interfaceMethods = lastInterfaceMethods;
}
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
default_namespaces.pop_back();
public_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
}
node.needs_init = true;
if (debug)
{
System.out.print("\n// -ClassDefinitionNode");
}
return node.ref;
}