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