if (debug)
{
System.out.print("\n// +FunctionCommonNode");
}
Context cx = node.cx; // switch to original context
// getQualifiedErrorOrigin defaults to getErrorOrigin if
// the qualified_origin isn't set - this is used to prevent
// naming conflicts in the debug info for authoring (i.e.,
// multiple scripts with the name "frame1"
setOrigin(cx.getQualifiedErrorOrigin());
if (cx.input != null)
{
setPosition(cx.input.getLnNum(node.pos()),cx.input.getColPos(node.pos()),node.pos());
}
if (doingMethod())
{
boolean anon_with_identifier = node.isNamedInnerFunc();
if( anon_with_identifier )
{
NewObject(0);
PushWith();
}
NewFunctionObject(node.internal_name);
if( anon_with_identifier )
{
if ( !node.isVoidResult() ) {
Dup();
}
GetBaseObject(cx.getScopeDepth()-frame.firstInnerScope);
Swap();
SetProperty(node.identifier.name, new Namespaces(cx.publicNamespace()), true, false, false, false);
PopScope();
}
else
if ( node.isVoidResult() ) {
Pop();
}
return null; // defer until the current method is done.
}
int savedWithDepth = cx.statics.withDepth;
if( node.with_depth != -1)
{
// FCN was hoisted by an earlier pass
cx.statics.withDepth = node.with_depth;
}
ObjectList<ObjectValue>saved_scopes = null;
if( node.scope_chain != null )
{
saved_scopes = cx.swapScopeChain(node.scope_chain);
}
Slot getSlot = node.ref.getSlot(cx, GET_TOKEN);
Slot setSlot = node.ref.getSlot(cx, SET_TOKEN);
boolean with_this = getSlot instanceof MethodSlot || setSlot instanceof MethodSlot;
ObjectValue fun = node.fun;
cx.pushScope(fun.activation);
// Do nested functions
boolean needs_activation = false;
if( node.fexprs.size() > 0 )
{
needs_activation = true;
}
if (node.isWithUsed())
{
needs_activation = true;
}
if (node.isExceptionsUsed())
{
needs_activation = true;
}
/*
for (int i = (node.fexprs == null) ? -1 : node.fexprs.size() - 1; i >= 0; i--)
{
Node fexpr = node.fexprs.get(i);
fexpr.evaluate(cx, this);
}
*/
used_namespaces_sets.push_back(node.used_namespaces);
for (FunctionCommonNode def : node.fexprs)
{
def.evaluate(cx, this);
}
// reset debug position. nested Function evaulation above will have updated it, we need to reset to top of this function.
if (cx.input != null)
{
setPosition(cx.input.getLnNum(node.pos()),cx.input.getColPos(node.pos()),node.pos());
}
pushStackFrame();
frame.functionName = node.internal_name;
frame.maxParams = node.signature.size();
frame.maxLocals = node.body != null ? node.var_count : 0;
frame.maxTemps = node.body != null ? node.temp_count : 0;
frame.needsArguments = node.needsArguments;
frame.withThis = with_this;
frame.firstInnerScope = cx.getScopes().size()-1;
if (with_this)
{
frame.firstInnerScope--;
}
// If there are nested functions, this will be true
frame.activationIsExposed = needs_activation;
frame.registerScopeIndex = needs_activation ? -1 : (cx.getScopes().size()-1);
StartMethod(frame.functionName,frame.maxParams,frame.maxLocals,0,needs_activation,node.needsArguments);
// If this is a constructor, then insert a call to the base constructor,
// and the instance initializer
if( "$construct".equals(node.ref.name) && cx.statics.es4_nullability )
{
// Must run property initializers before this, or activation scope is pushed. Setting properties
// will be handled with getlocal0, setproperty and arguments will use getlocal since there can be no intervening
// scopes at this point (even if the method later needs an activation object)
doCtorSetup(node, cx, needs_activation);
}
if (with_this)
{
LoadThis();
PushScope();
}
// initialize local variables that are in registers.
ObjectValue activation = node.fun.activation;
int firstlocal = node.signature.size();
if (node.needsArguments != 0)
{
firstlocal++;
}
int reg_offset = activation.builder.reg_offset;
int var_offset = activation.builder.var_offset;
if (needs_activation)
{
NewActivation();
int temp_activation_reg = allocateTemp();
activation.builder.temp_reg = temp_activation_reg;
Dup();
StoreRegister(reg_offset+temp_activation_reg,TYPE_none);
PushScope();
// create a 'local' name for the activation object for the debugger to use
DefineSlotVariable(cx, node.internal_name, node.debug_name, node.pos(), ObjectValue.objectPrototype.type, temp_activation_reg);
}
if (activation.slots != null)
{
int base_offset = needs_activation ? var_offset : reg_offset;
for (Slot s: activation.slots)
{
int index = base_offset + s.getVarIndex();
if ( s.needsInit() && s.getVarIndex() >= firstlocal)
{
StoreDefaultValue(cx, index, s, needs_activation);
}
}
}
if (needs_activation)
{
// Copy the arguments into the activation object
int n=frame.maxParams;
if (node.needsArguments != 0) n++;
for (int i=0; i<n; i++)
{
GetActivationObject(cx.getScopes().size()-1);
LoadRegister(i+1,TYPE_object);
StoreVar(i);
}
}
// for debug purposes dump out the list of args
ParameterListNode parms = node.signature.parameter;
String[] arg_names = null;
if (parms != null)
{
arg_names = new String[parms.items.size()];
for(int i = 0; i < parms.items.size(); ++i )
{
ParameterNode parm = parms.items.at(i);
ReferenceValue ref;
Slot slot;
if (parm == null)
; // no good
else if ((ref = parm.ref) == null)
; // no good
else if ((slot = ref.getSlot(cx)) == null)
; // no good
else
{
TypeInfo expr_type = ref.getType(cx);
DefineSlotVariable(cx, ref.name, ref.name, pos, expr_type, slot.getVarIndex());
arg_names[i] = ref.name;
}
}
}
if( "$construct".equals(node.ref.name) && !cx.statics.es4_nullability )
{
doCtorSetup(node, cx, needs_activation);
}
if( node.body != null)
{
if( node.default_dxns != null )
{
node.default_dxns.evaluate(cx,this);
}
boolean old_in_anonymous_function = this.in_anonymous_function;
this.in_anonymous_function = (node.isFunctionDefinition() == false); // set flag if we are processing an anonymous function
node.body.evaluate(cx,this);
this.in_anonymous_function = old_in_anonymous_function;
}
if (cx.input != null)
{
setPosition(cx.input.getLnNum(node.signature.pos()),cx.input.getColPos(node.signature.pos()),node.signature.pos());
}
// If there is a nested function, then pass the activation object so traits can be emitted
if (!needs_activation)
activation = null;
// ISSUE: this logic can be more subtle. We also might need the activation
// if there is an embedded eval or with. And we might not need the activation
// object for all functions with nested functions
int scope_depth = activation != null ? cx.getScopes().size() : cx.getScopes().size()-1;
TypeInfo type = node.signature.type;
ObjectList<TypeInfo> types = node.signature.parameter!=null?node.signature.parameter.types:null;
node.fun.method_info = FinishMethod(cx,
frame.functionName,
type,
types,
activation,
node.needsArguments,
scope_depth,
node.debug_name,
node.isNative(),
(currentClass instanceof InterfaceDefinitionNode),
arg_names);
cx.popScope();
this.is_ctor = false;
// We don't need this or the activation builder anymore
node.fun.activation = null;
// don't need the FunctionBuilder anymore either
node.fun.builder = null;
popStackFrame();
if( saved_scopes != null )
{
cx.swapScopeChain(saved_scopes);
}
cx.statics.withDepth = savedWithDepth;
// Call code is compiled as though register 0 is the scope
// stack, register 1 is the current object, register 2 is the