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