public Value evaluate()

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;
    }