public Value evaluate()

in modules/asc/src/java/macromedia/asc/semantics/ConstantEvaluator.java [508:669]


    public Value evaluate(Context cx, CallExpressionNode node)
    {

        Slot slot = null;
        TypeInfo type = cx.noType().getDefaultTypeInfo();
        int kind = node.is_new ? NEW_TOKEN : EMPTY_TOKEN;
        
        if (node.ref != null)
        {
            /*if (node.ref.getBase() == null)
                            System.out.println("call "+node.ident.name);
                        else
                            System.out.println("call "+node.ref.getBase().type.name+"."+node.ident.name);*/
            node.ref.calcUseDefinitions(cx, rch_bits);

            slot = node.ref.getSlot(cx, kind);
            type = node.ref.getType(cx, kind);
            if( cx.useStaticSemantics() )
            {
                if( slot == null )
                {
                    slot = node.ref.getSlot(cx, GET_TOKEN);
                    // check for function/Class valued properties and getter functions which return functions/Classes
                    if (slot != null)
                    {
                        TypeValue t = slot.getType() != null ? slot.getType().getTypeValue() : null;
                        // note: you can call a Class valued var, it acts like an explicit cast
                        if ( t != cx.typeType() && t != cx.functionType() && t != cx.objectType() && t != cx.noType())
                        {
                            slot = null;
                            // don't permanently bind to the get slot.
                            node.ref.slot = null;
                        }
                    }

                    if (slot == null)
                    {
                        ObjectValue base = node.ref.getBase();
                        if( base == null )
                        {
                            if( cx.statics.withDepth == -1 ) cx.error(node.pos(), kError_Strict_PlainUndefinedMethod,node.ref.name);
                        }
                        //  Note: Function is dynamic, but methods of a class are MethodClosures, a non-dynamic subclass of Function.
                        //  The compiler doesn't have an internal representation of MethodClosure, though the only reason it would
                        //  need one is for this check.  Rather than modify a lot of existing compiler code to check type.memberOf(cx.functionType())
                        //  instead of type == cx.functionType() (and likely some other less obviuos dependances), we're just going to check
                        //  the builder here to distinguish between a global function and a non-global function.
                        else if (!base.isDynamic() || (base.getType(cx).getTypeValue() == cx.functionType() && !(base.builder instanceof GlobalBuilder)) )
                        {
							if ( (base instanceof TypeValue) || (base.type != null && base.type.getTypeValue() != cx.noType()) )
							{
								String className = (base instanceof TypeValue) ? "Class" : base.getType(cx).getName(cx).toString();

								if (base.hasNameUnqualified(cx, node.ref.name, GET_TOKEN))
									cx.error(node.pos(), kError_InaccessibleMethodReference, node.ref.name, className);
								else
									cx.error(node.pos(), kError_Strict_UndefinedMethod, node.ref.name, className);
							}
                        }
                    }
                    if (slot != null)
                    {
                        // don't permanently bind to the get slot.
                        node.ref.slot = null;
                        slot = null;
                    }
                }
            }
            if( slot == null && node.is_new)
            {
                Slot callSlot = node.ref.getSlot(cx,EMPTY_TOKEN);
                Slot getSlot  = node.ref.getSlot(cx,GET_TOKEN);
                if ( callSlot != null && getSlot != null && getSlot.declaredBy != null &&
                     ((getSlot.declaredBy.builder instanceof ClassBuilder) || (getSlot.declaredBy.builder instanceof InstanceBuilder)) )
                {
                    cx.error(node.pos(), kError_MethodIsNotAConstructor);
                }
            }
            type = type != null ? type : cx.noType().getDefaultTypeInfo();
            if (node.is_new && slot != null && slot.getType() != null)
            {
                    Builder bui = slot.getType().getBuilder();
                    if (bui instanceof ClassBuilder && ((ClassBuilder)bui).is_interface)
                    {
                        cx.error(node.pos(), kError_CannotInstantiateInterface);
                    }
            }
        }
        else
        {
            node.expr.evaluate(cx, this);
        }

        boolean callOfClass = false;
        if ( !node.is_new && node.ref != null )
        {
            Slot newSlot = node.ref.getSlot(cx, NEW_TOKEN);
            if (newSlot != null && newSlot.getType() != null && newSlot.getType().getTypeValue() != cx.noType()) // cn: in !, global Functions have new slots.  Only classes have non-object types for their new slot
                callOfClass = true;
        }

        if (callOfClass)
        {
            // Special treatment of calling a class closure as a function
            if (node.args != null)
            {
                if (cx.useStaticSemantics() && node.args.expected_types == null)
                {
                    node.args.addType(cx.noType().getDefaultTypeInfo());
                    node.args.addDeclStyle(PARAM_Required);
                }
                node.args.evaluate(cx, this);
            }
            else if (cx.useStaticSemantics())
            {
                // explicit cast without an argument, log error if this isn't that ES3 legacy problem "Date()"
                // Watch out for Date() which returns (new Date().toString().  It shouldn't be an error to call Date()
                //  with no arguments.
                if ( !("Date".equals(node.ref.name)))
                    cx.error(node.pos(), kError_WrongNumberOfArguments, "1");
            }
        }
        else
        {
            if (node.args != null)
            {
                if (slot != null)
                {
                    node.args.expected_types = slot.getTypes();
                    node.args.decl_styles = slot.getDeclStyles();
                }
                if (cx.useStaticSemantics() && node.is_new && 
					node.ref != null && node.ref.getSlot(cx,NEW_TOKEN) != null && 
					node.args.decl_styles == null) // calling default constructor of class with no constructor declared, no arguments should be supplied.
                {
                    cx.error(node.pos(), kError_WrongNumberOfArguments, "0");
                }
                node.args.evaluate(cx, this);
            }
             // check if function expects arguments
              else if (slot != null && size(slot.getDeclStyles()) != 0 && slot.getDeclStyles().get(0) == PARAM_Required &&
                       cx.useStaticSemantics())
              {
                 int expected_num_args = slot.getDeclStyles().size();
                 for(; expected_num_args > 0; expected_num_args--)
                 {
                     if (slot.getDeclStyles().at(expected_num_args-1) == PARAM_Required)
                         break;
                 }
                StringBuilder err_arg_buf = new StringBuilder();
                err_arg_buf.append(expected_num_args);
                cx.error(node.pos(), kError_WrongNumberOfArguments, err_arg_buf.toString());
            }

        }

        // a new expression will never result in null, so it's "non-nullable"
        if( node.is_new )
            type = type.getTypeValue().getTypeInfo(false);

        return type.getPrototype();
    }