public Value evaluate()

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