public Value evaluate()

in modules/asc/src/java/macromedia/asc/semantics/CodeGenerator.java [764:1113]


    public Value evaluate( Context cx, CallExpressionNode node )
    {
        if( debug ) System.out.print("\n// +CallExpression");

        int slot_kind = node.is_new?NEW_TOKEN:EMPTY_TOKEN;
        int disp_kind = DISP_undefined;
        int call_seq  = CALL_Closure; // default
        int this_kind = THIS_undefined;

        String method_name = "";

        boolean is_super = node.isSuper();
        boolean is_qualified = node.isQualified();
        boolean is_attribute = node.isAttributeIdentifier();

        Slot slot;
        TypeInfo expr_type=null;
        int  base_index=-1;
        int  scope_depth = cx.getScopes().size()-1;

        Builder basebui;

        boolean is_localref = false;


        if( node.ref == null )
        {
            if (node.is_new)
            {
                call_seq  = CALL_EnvThisArgs;
                disp_kind = DISP_ConstructClosure;
                this_kind = THIS_None;
            }
            else
            if( node.isRvalue() )
            {
                call_seq  = CALL_EnvThisArgs;
                disp_kind = DISP_CallClosure;
                this_kind = THIS_Global;
            }
            else
            {
                call_seq  = CALL_EnvThisArgs;
                disp_kind = DISP_CallClosure;
                this_kind = THIS_Base;
            }
        }
        else
        {
            slot                 = node.ref.getSlot(cx,GET_TOKEN);
            expr_type            = node.ref.getType(cx);
            base_index           = node.ref.getScopeIndex(GET_TOKEN);

            basebui              = base_index >= 0 ? cx.scope(base_index).builder : null;

            boolean is_outerfunc;

            boolean is_globalref;
            boolean is_dotref;
            boolean is_cinit_ref;

            is_localref          = isLocalScope(cx, base_index);
            is_outerfunc         = base_index != 0 && base_index <  frame.firstInnerScope && basebui instanceof ActivationBuilder;
            is_globalref         = base_index == 0;
            is_dotref            = base_index == -2;
            boolean is_lexref    = !(is_localref || is_dotref);
            is_cinit_ref         = slot != null && isClassInitializerReference(cx, slot.getValue());

            if (is_cinit_ref)
            {
                 // Special case for $cinit functions that reference the class
                // being initialized.  See bug #113887.
                LoadThis();

                call_seq = CALL_Args;
                disp_kind = node.is_new ? DISP_ConstructClosure : DISP_CallClosure;
                this_kind = node.is_new ? THIS_None : THIS_Scope;
            }
            else
            if( is_localref )
            {
                call_seq  = CALL_EnvThisArgs;
                disp_kind = DISP_CallClosure;
                this_kind = THIS_Global;
            }
            else
            if( is_globalref )  // some of these can be turned into CallFinal
            {
            //#if 1 // when OP_constructproperty is supported
                call_seq  = CALL_Args;
                disp_kind = node.is_new?DISP_ConstructProperty:DISP_CallProperty;
                this_kind = node.is_new?THIS_None:THIS_Base;
            //#else
            //                    call_seq  = CALL_EnvThisArgs;
            //                    disp_kind = DISP_CallClosure;
            //                    this_kind = THIS_Base;
            //#endif
            }
            else
            if( is_dotref )
            {
                call_seq  = CALL_Args;
                disp_kind = node.is_new?DISP_ConstructProperty:DISP_CallProperty;
                this_kind = node.is_new?THIS_None:THIS_Base;
            }
            else
            if( is_lexref )  // some of these can be turned into CallFinal
            {
                if( is_outerfunc )
                {
            //#if 1 // when OP_callproplex is supported
                    call_seq  = CALL_Args;
                    disp_kind = node.is_new?DISP_ConstructProperty:DISP_CallPropLex;
                    this_kind = node.is_new?THIS_None:THIS_Base;
            //#else
            //                    call_seq  = CALL_EnvThisArgs;
            //                    disp_kind = DISP_CallClosure;
            //                    this_kind = THIS_Global;
            //#endif
                }
                else
                {
            //#if 1 // when OP_constructproperty is supported
                    call_seq  = CALL_Args;
                    disp_kind = node.is_new?DISP_ConstructProperty:DISP_CallProperty;
                    this_kind = node.is_new?THIS_None:THIS_Base;
            //#else
            //                    call_seq  = CALL_EnvThisArgs;
            //                    disp_kind = DISP_CallClosure;
            //                    this_kind = node.is_new?THIS_None:THIS_Base;
            //#endif
                }
            }
            else
            {
                cx.internalError("missing call case");
            }

            // if it is monomorphic and lexical and in an inner scope, then call final

            Slot method_slot = node.ref.getSlot(cx,slot_kind);
            if( base_index > 0 && method_slot != null )
            {
                // Has an implied method (call or construct), so use it. For now,
                // we just do this for functions, we could do it for inherited
                // methods where the implementing class is defined in the current
                // script.

                if( is_outerfunc && !in_with && method_slot.getMethodID() >= 0 )
                {
                    call_seq  = method_slot.getCallSequence();
                    method_name = method_slot.getMethodName();
                    disp_kind = DISP_CallFinal; //method_slot.dispatch_kind;
                    this_kind = THIS_Global; // is_dotref?THIS_Base:is_outerfunc?THIS_Global:THIS_Base;
                }
            }

            this_kind = node.is_new?THIS_None:this_kind;  // if its a new expression, then clear this_kind
        }

        int temp_this_reg = -1;
        ObjectValue obj = cx.scope(scope_depth);
        Builder bui = obj.builder;
        int reg_offset = getRegisterOffset(cx);
        int var_offset = bui.var_offset;

        // Save this, which was on the stack when we entered this function

        {
            switch( this_kind )
            {
                case THIS_Global:
                // do nothing
                break;
                case THIS_Scope:
                // do nothing
                break;
                case THIS_Base:
                if( (call_seq & PUSH_env) != 0)
                {   // need to save base in temp
                    this_kind = THIS_Temp;
                    temp_this_reg = allocateTemp();
                    Dup();
                    StoreRegister(reg_offset+temp_this_reg,TYPE_none);
                }
                break;
                case THIS_None:
                // do nothing
                break;
                default:
                cx.internalError("invalid this_kind");
                break;
            }
        }

        int size = node.args!=null?node.args.size():0;

        // Get the function object

        if( (call_seq & PUSH_env) != 0 )
        {
            if( node.ref == null )
            {
                if( node.isRvalue() )
                {
                    // Literal function expression
                    // x = function () { return 10 } ()

                    node.expr.evaluate(cx,this);
                }
                else   // runtime qualified identifier  // o.ns::[expr]
                if( node.getIdentifier() != null )
                {
                    node.expr.evaluate(cx,this);  // if it is qualified, then eval the qualifier

                    QualifiedExpressionNode qen = node.getIdentifier() instanceof QualifiedExpressionNode? (QualifiedExpressionNode)node.getIdentifier() : null;
                    if( qen != null )
                    {
                        qen.expr.evaluate(cx,this);

                        if( qen.nss != null )
                        {
                            GetProperty(false/*is_qualified*/, is_super,is_attribute, qen.nss);
                        }
                        else
                        if( qen.qualifier != null )
                        {
                            ToString();
                            GetProperty(true/*is_qualified*/, is_super,is_attribute, used_namespaces_sets.back());
                        }
                        else
                        {
                            GetProperty(false/*is_qualified*/, is_super,is_attribute, used_namespaces_sets.back());
                        }
                    }
                    else
                    {
                        GetProperty(node.getIdentifier().name, is_super, is_attribute);
                    }
                }
                else
                {
                    // Indexed member expression
                    // x = o['foo']()

                    node.expr.evaluate(cx,this);
                    GetProperty(is_qualified,is_super,is_attribute,used_namespaces_sets.back());
                }
            }
            else
            if( is_localref )
            {
                // Fixed local
                if (frame.registerScopeIndex != base_index)
                {
                    GetActivationObject(base_index);
                    LoadVar(var_offset+node.ref.getSlot(cx,GET_TOKEN).getVarIndex());
                }
                else
                {   // cn: don't use expr_type here.  Its the type of what the function returns.  Use the get slot's type instead.
                    LoadRegister(reg_offset+node.ref.getSlot(cx,GET_TOKEN).getVarIndex(),node.ref.getSlot(cx,GET_TOKEN).getType().getTypeId());
                }
            }
            else
            {
                GetProperty(node.ref.name,node.ref.getImmutableNamespaces(),node.ref.isQualified(),is_super,is_attribute);
            }
        }

    // Push the args

    {
        if( (call_seq & PUSH_env)!=0 )
        {
            GetScopeChain();
        }

        switch( this_kind )
        {
            case THIS_Global:
            GetGlobalScope();
            break;
            case THIS_Scope:
            GetBaseObject(base_index); // never used
            break;
            case THIS_Base:
            // do nothing, already on the stack
            break;
            case THIS_Temp:
            LoadRegister(reg_offset+temp_this_reg,TYPE_none);
            break;
            case THIS_None:
            // do nothing
            break;
            default:
            cx.internalError("invalid this_kind");
            break;
        }

        if( (call_seq&PUSH_args)!=0 )
        {
            if( node.args!=null )
            {
                node.args.evaluate(cx,this);
            }
        }
    }

    // Call the function

    switch( disp_kind )
    {
        case DISP_CallProperty:
        CallProperty(node.ref.name,node.ref.getImmutableNamespaces(),size,node.ref.isQualified(),is_super,is_attribute,false);
        break;
        case DISP_CallPropLex:
        CallProperty(node.ref.name,node.ref.getImmutableNamespaces(),size,node.ref.isQualified(),is_super,is_attribute,true);
        break;
        case DISP_ConstructProperty:
        ConstructProperty(node.ref.name,node.ref.getImmutableNamespaces(),size,node.ref.isQualified(),is_super,is_attribute);
        break;
        case DISP_CallClosure:
        case DISP_ConstructClosure:
        InvokeClosure(node.is_new,size);
        break;
        case DISP_CallFinal:
        {
            int method_info = cx.getEmitter().GetMethodInfo(method_name);
            InvokeMethod(false /*DISPATCH_final*/,method_info,size);
            break;
        }
        default:
        cx.internalError("invalid disp_kind");
        break;
    }

        if( node.void_result )
        {
            Pop();
        }

        if (temp_this_reg != -1)
        {
            freeTemp(temp_this_reg); // temp_this_reg
        }

        if( debug ) System.out.print("\n// -CallExpression");

        return null;
    }