in modules/asc/src/java/macromedia/asc/semantics/FlowAnalyzer.java [3497:4055]
public Value evaluate(Context unused_cx, FunctionDefinitionNode node)
{
Context cx = node.cx; // switch context to the one used to parse this node, for error reporting
// If this is a toplevel definition (pkgdef!=null), then set up access namespaces
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);
Builder temp_bui = cx.scope().builder;
GlobalBuilder bui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( bui != null )
{
bui.is_in_package = true;
} // otherwise, internal error
}
// Attributes
boolean is_static = false;
boolean is_intrinsic = false;
boolean is_native = false;
boolean is_ctor = false;
boolean is_final = false;
boolean is_override = false;
boolean is_prototype = node.is_prototype;
boolean is_dynamic = false;
Namespaces namespaces = new Namespaces();
ObjectList<String> namespace_ids = new ObjectList<String>(1);
if( node.attrs != null)
{
if( node.attrs.hasVirtual && node.attrs.hasFinal )
{
cx.error(node.attrs.pos(), kError_FuncIsVirtualAndFinal);
}
if( node.attrs.hasStatic && node.attrs.hasVirtual )
{
cx.error(node.attrs.pos(), kError_FuncIsStaticAndVirtual);
}
if( node.attrs.hasStatic && node.attrs.hasOverride )
{
cx.error(node.attrs.pos(), kError_FuncIsStaticAndOverride);
}
if( node.attrs.hasStatic && node.attrs.hasDynamic )
{
cx.error(node.attrs.pos(), kError_InvalidDynamic);
}
is_static = node.attrs.hasStatic;
is_intrinsic = node.attrs.hasIntrinsic;
is_native = node.attrs.hasNative;
is_dynamic = node.attrs.hasDynamic;
if( is_static )
{
is_final = true; // statics are always final
}
else
{
is_final = node.attrs.hasFinal;
}
is_override = node.attrs.hasOverride;
}
computeNamespaces(cx,node.attrs,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);
}
NodeFactory nodeFactory = cx.getNodeFactory();
QualifiedIdentifierNode qualifiedIdentifier = nodeFactory.qualifiedIdentifier(node.attrs, node.name.identifier.name, node.name.identifier.pos());
node.init = nodeFactory.expressionStatement(nodeFactory.assignmentExpression(qualifiedIdentifier, CONST_TOKEN, node.fexpr));
node.init.isVarStatement(true); // var statements always have a empty result
// Compute reference
boolean is_first_time = node.ref == null;
Value val = node.name.identifier.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
node.fexpr.namespace_ids = namespace_ids;
// Get the current object and its builder
ObjectValue obj = getVariableDefinitionScope(cx);
Builder bui = obj.builder;
region_name_stack.push_back(cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,node.name.kind));
// Constructor? Tweak the name
String cname = fun_name_stack.back();
if( cname.equals(node.ref.name))
{
if( bui instanceof InstanceBuilder)
{
is_ctor = true;
node.ref.name = "$construct";
is_final = true; // not strictly speaking, but can't be hidden, can't be overriden
namespaces.push_back(cx.publicNamespace());
}
else
{
cx.error(node.pos(), kError_ConstructorsMustBeInstanceMethods);
}
if( node.name.kind == SET_TOKEN || node.name.kind == GET_TOKEN)
{
cx.error(node.pos(), kError_ConstructorCannnotBeGetterSetter);
}
if( node.attrs != null )
{
for ( ObjectValue ns : node.attrs.namespaces )
{
if( ns != cx.publicNamespace() )
{
cx.error(node.pos(), kError_ConstructorMustBePublic);
break;
}
}
}
}
if (bui instanceof ActivationBuilder)
{
if (node.name.kind == SET_TOKEN || node.name.kind == GET_TOKEN)
{
cx.error(node.pos(), kError_InvalidNestedAccessor);
}
}
boolean is_interface_method = false;
boolean is_instance_method = false;
if( bui instanceof InstanceBuilder )
{
is_instance_method = true;
is_interface_method = (obj.type != null && obj.type.isInterface());
}
else if( bui instanceof ClassBuilder)
{
if( ((ClassBuilder)bui).is_interface )
{
is_interface_method = true;
}
}
if( is_interface_method || is_native )
{
if (is_interface_method && is_native)
{
cx.error(node.pos(), kError_InvalidInterfaceNative);
}
if( node.fexpr.isUserDefinedBody() )
{
cx.error(node.pos(), is_interface_method ? kError_InterfaceMethodWithBody : kError_NativeMethodWithBody);
}
}
else
{
if( !node.fexpr.isUserDefinedBody() && !is_ctor && !is_native && !is_dynamic ) //ctors and native and dynamic methods don't need bodies
{
cx.error(node.pos(), kError_FunctionWithoutBody);
}
}
if( node.attrs != null )
{
if( node.attrs.hasFinal && (!is_instance_method || is_interface_method) )
{
cx.error(node.pos(), kError_InvalidFinalUsage);
}
if( is_interface_method && (node.attrs.hasPrivate || node.attrs.hasProtected || node.attrs.hasInternal || node.attrs.hasPublic) && (!node.ref.name.equals("$construct") ))
{
// todo fix error msg
cx.error(node.pos(), kError_BadAccessInterfaceMember);
}
}
if( is_static )
{
if (is_interface_method)
{
cx.error(node.pos(), kError_InvalidStatic);
}
}
if (is_interface_method)
{
// Namespace attributes are not allowed on interface methods
if (node.attrs != null && node.attrs.getUserNamespace() != null)
{
cx.error(node.pos(), kError_InterfaceNamespaceAttribute);
}
}
// If the method is public and matches an interface method name, add
// the interface namespaces.
else if (interfaceMethods != null &&
interfaceMethods.size() > 0 &&
namespaces.contains(cx.publicNamespace()))
{
Qualifiers q;
if(Builder.removeBuilderNames) // TODO: {pmd} both ways on this if look very similar, review this
{
q = interfaceMethods.get(node.ref.name, node.name.kind == SET_TOKEN ? Names.SET_NAMES : Names.GET_NAMES );
}
else
{
q = interfaceMethods.get(node.ref.name, Names.getTypeFromKind(node.name.kind));
}
if(q != null)
{
for (ObjectValue ns : q.keySet())
{
namespaces.push_back(ns);
namespace_ids.push_back(ns.name);
}
}
}
/*
Define a Call slot on the current frame. There are three possible
interpretations for this node:
1/ it overrides an inherited method - reuse the inherited explicit binding
give it a new implicit binding
2/ it overrides an non-inherited method - if it is in a strict context, then
report an error, otherwise give it a new implicit binding
3/ it introduces a new method - give it a new explicit binding
*/
int slot_id = -1;
int kind = node.name.kind == SET_TOKEN?SET_TOKEN:GET_TOKEN;
Namespaces open_definition_namespaces ;
if( node.attrs != null && node.attrs.hasUserNamespace())
{
open_definition_namespaces = namespaces;
}
else
{
open_definition_namespaces = used_def_namespaces_sets.back();
}
Namespaces hasNamespaces = obj.hasNames(cx,kind ,node.ref.name,open_definition_namespaces);
if( hasNamespaces != null )
{
// Can only override instance methods
if( bui instanceof InstanceBuilder )
{
slot_id = obj.getSlotIndex(cx,kind,node.ref.name,hasNamespaces.back());
// ISSUE: need to check that all names are to the same slot
// Get the implicit method slot to get the default method_id
int implied_id = obj.getImplicitIndex(cx,slot_id,EMPTY_TOKEN);
// If slot id is less than zero, then this is a getter or setter. Getter
// and setter method ids are encoded in their explicit slot, so nothing
// do do here
if( implied_id >= 0 ) // else, do nothing
{
// check that the override is at least as accessible as the overriden
if (!is_ctor && !namespacesContains(cx, namespaces, hasNamespaces))
{
if( !( namespaces.size() == 1 && hasNamespaces.size() == 1 && namespaces.at(0).isProtected() && hasNamespaces.at(0).isProtected() ) )
{
cx.error(node.pos(), kError_IncompatibleOverride);
}
}
int overridden_kind = (slot_id == implied_id) ? kind : EMPTY_TOKEN;
if (overridden_kind != node.name.kind && !is_ctor)
{
cx.error(node.pos(), kError_IncompatibleOverride);
}
if( true /* check signature and final */ )
{
Slot slot = obj.getSlot(cx,implied_id);
is_dynamic = slot.isIntrinsic();
int method_id = slot.getMethodID();
if( slot.isFinal() && !is_ctor )
{
if( node.name.kind == SET_TOKEN || node.name.kind == GET_TOKEN)
cx.error(node.pos(), kError_OverrideFinalAccessor);
else
cx.error(node.pos(), kError_FinalMethodRedefinition);
}
if( slot.declaredBy == obj )
{
// This was already defined at this level, it was not inherited from a base class
cx.error(node.pos(), kError_DuplicateFunction);
}
else if( is_prototype || is_dynamic )
{
is_override = true;
}
else if( !is_override && !is_ctor )
{
cx.error(node.pos(), kError_OverrideOfFuncNotMarkedForOverride);
}
if( node.name.kind == GET_TOKEN )
{
slot_id = bui.ExplicitGet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
if( node.name.kind == SET_TOKEN )
{
slot_id = bui.ExplicitSet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
{
slot_id = bui.ExplicitCall(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
if( !is_ctor )
{
// Constructors don't actually override the base class constructor
// Can have different signatures, so don't mark it as having an overriden slot
// so we won't do signature matching later.
Slot overriddenSlot = slot;
Slot overrideSlot = obj.getSlot(cx,obj.getImplicitIndex(cx,slot_id,EMPTY_TOKEN));
overrideSlot.setOverriddenSlot(overriddenSlot);
}
}
else
{
cx.error(node.pos(), kError_IncompatibleOverride);
}
}
else // cn: I think accessors now always end up in the block above, making this else block obsolete.
{
Slot slot = obj.getSlot(cx,slot_id);
if( slot.getMethodID() <= 0 )
{
cx.error(node.pos(), kError_OverrideFinalAccessor);
}
// ISSUE: implement accessor overriding
}
}
else
if( bui instanceof ClassBuilder )
{
cx.error(node.pos(), kError_DuplicateFunction);
}
else
if (bui instanceof GlobalBuilder && (node.pkgdef != null || namespaces.at(0) != hasNamespaces.at(0)) && is_first_time )
{
cx.error(node.pos(), kError_DuplicateFunction);
}
else
if( cx.useStaticSemantics() && is_first_time ) // ISSUE: remove use of this flag by not evaluating this code twice
{
cx.error(node.pos(), kError_DuplicateFunction);
}
else
{
slot_id = obj.getSlotIndex(cx,GET_TOKEN,node.ref.name,hasNamespaces.at(0));
}
}
else
{
if( is_override && !is_ctor)
{
if (is_interface_method)
{
cx.error(node.pos(), kError_InvalidOverrideUsage);
}
else
{
ObjectValue n = namespaces.at(0);
UnresolvedNamespace un = n instanceof UnresolvedNamespace ? (UnresolvedNamespace) n : null;
if( un == null || un.resolved )
{
cx.error(node.pos(), kError_OverrideNotFound);
}
else
{
cx.error(un.node.pos(), kError_Unknown_Namespace);
}
}
}
if( node.name.kind == GET_TOKEN )
{
int method_id = bui.Method(cx,obj,(node.ref.name+"$get").intern(),namespaces,is_intrinsic); // Add getter to local dispatch table
slot_id = bui.ExplicitGet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
if( node.name.kind == SET_TOKEN )
{
int method_id = bui.Method(cx,obj,(node.ref.name+"$set").intern(),namespaces,is_intrinsic);
slot_id = bui.ExplicitSet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
{
int method_id = bui.Method(cx,obj,node.ref.name,namespaces,is_intrinsic);
slot_id = bui.ExplicitCall(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
}
/*
At this point we have either reported a redefinition error, or have
the explicit slot for the function being defined, and an implicit
slot for the implementation (i.e. method name, call seq)
Now we specify the implementation
*/
if( is_intrinsic )
{
}
else
if( node.fexpr != null && slot_id >= 0 )
{
node.fexpr.setNative(is_native);
node.fexpr.kind = node.name.kind; // inherited attribute
Slot slot = obj.getSlot(cx,slot_id);
// FunctionCommonNode gets evaluated twice. The first time is for initializing
// the function object and adding it to the list of nodes to be evaluated later.
// The second time (this time) is for evaluating the function body.
val = node.fexpr.evaluate(cx,this);
val = val != null ? val.getValue(cx) : null;
slot.setObjectValue((val instanceof ObjectValue ? (ObjectValue)val : null));
// slot.objValue = dynamic_cast<ObjectValue>(val!=null?val.getValue(cx) : null);
if( slot.getObjectValue() != null)
{
// Resolve this function to its local dispatch id, if it is virtual, or a global
// method id (method info) if it is not. Local method ids share the same local
// name (the name of the original method). If B overrides m in A, then the local
// method id for B.m will be something like A$m. If B.n is new, then its local
// name will be something like B$n. A name consists of the classname,simplename,and
// namespace.
// B inherits the names of A. B declares an override of A.m. The new method slot for
// m in B has the internal name that is the same as m in A, and therefore the same
// dispatch id ( = local method name id).
// The key is comparing names so that the rules for overriding are correctly implemented.
if( is_ctor )
{
InstanceBuilder ib = ((bui instanceof InstanceBuilder) ? (InstanceBuilder)bui : null);
if( ib != null)
{
ib.has_ctor = true;
ib.ctor_name = node.fexpr.internal_name;
}
else
{
cx.error(node.pos(), kError_ConstructorsMustBeInstanceMethods);
}
}
else
if( bui instanceof ClassBuilder || bui instanceof InstanceBuilder || bui instanceof PackageBuilder )
{
}
else
{
}
/*
Copy the implementation details into the implicit call slot
*/
slot_id = obj.getImplicitIndex(cx,slot_id,EMPTY_TOKEN);
obj.getSlot(cx,slot_id).setMethodName(node.fexpr.internal_name);
obj.getSlot(cx,slot_id).setIntrinsic(is_dynamic);
slot.getObjectValue().name = node.fexpr.internal_name;
}
node.fexpr.debug_name = region_name_stack.back();
}
region_name_stack.pop_back();
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
public_namespaces.pop_back();
default_namespaces.pop_back();
Builder temp_bui = cx.scope().builder;
GlobalBuilder gbui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( gbui != null )
{
gbui.is_in_package = false;
} // otherwise, internal error
}
if( node.needs_init )
{
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
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);
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
Builder temp_bui = cx.scope().builder;
GlobalBuilder gbui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( gbui != null )
{
gbui.is_in_package = true;
} // otherwise, internal error
}
node.init.evaluate(cx,this);
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.pop_back();
default_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
Builder temp_bui = cx.scope().builder;
GlobalBuilder gbui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( gbui != null )
{
gbui.is_in_package = false;
} // otherwise, internal error
}
return null;
}
return null;
}