Named parameter work.

This commit is contained in:
rogerl%netscape.com 2000-10-27 23:55:31 +00:00
parent f0d64921df
commit ddf915e607
8 changed files with 452 additions and 360 deletions

View File

@ -75,16 +75,14 @@ Formatter& operator<<(Formatter &f, ICodeModule &i)
ICodeGenerator::ICodeGenerator(Context *cx, JSClass *aClass, ICodeGeneratorFlags flags)
: mTopRegister(0),
mParameterCount(0),
mExceptionRegister(TypedRegister(NotARegister, &None_Type)),
variableList(new VariableList()),
parameterList(new ParameterList()),
mContext(cx),
mInstructionMap(new InstructionMap()),
mClass(aClass),
mFlags(flags),
pLabels(NULL),
mHasRestParameter(false),
mHasNamedRestParameter(false),
mInitName(cx->getWorld().identifiers["__init__"])
{
iCode = new InstructionStream();
@ -106,7 +104,7 @@ JSType *ICodeGenerator::findType(const StringAtom& typeName)
-Theoretically, mPermanentRegister[n] can be become false when a scope ends and
the registers allocated to contained variables are then available for re-use.
-Mostly the need is to handle overlapping allocation of temps & permanents as the
variables declarations are encountered. This wouldn't be necessary if a function
variables' declarations are encountered. This wouldn't be necessary if a function
presented a list of all variables, or a pre-pass executed to discover same.
*/
TypedRegister ICodeGenerator::allocateRegister(const StringAtom& name, JSType *type)
@ -122,27 +120,11 @@ TypedRegister ICodeGenerator::allocateRegister(const StringAtom& name, JSType *t
mPermanentRegister[r] = true;
TypedRegister result(r, type);
variableList->add(name, result);
mTopRegister = ++r;
return result;
}
TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name, const StringAtom& typeName)
{
return allocateRegister(name, findType(typeName));
}
TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name)
{
return allocateRegister(name, &Any_Type);
}
TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name, JSType *type)
{
return allocateRegister(name, type);
}
ICodeModule *ICodeGenerator::complete(JSType *resultType)
{
#ifdef DEBUG
@ -188,16 +170,13 @@ ICodeModule *ICodeGenerator::complete(JSType *resultType)
*/
ICodeModule* module = new ICodeModule(iCode,
variableList,
parameterList,
mPermanentRegister.size(),
mParameterCount,
mInstructionMap,
mHasRestParameter,
mHasNamedRestParameter,
resultType);
if (pLabels) {
uint32 i;
uint32 parameterInits = pLabels->size() - 1; // there's an extra label at the end for the actual entryPoint
module->mNonOptionalParameterCount -= parameterInits;
module->mParameterInit = new uint32[parameterInits];
for (i = 0; i < parameterInits; i++) {
module->mParameterInit[i] = (*pLabels)[i]->mOffset;
@ -742,6 +721,8 @@ ICodeGenerator::LValueKind ICodeGenerator::resolveIdentifier(const StringAtom &n
{
if (!isWithinWith()) {
v = variableList->findVariable(name);
if (v.first == NotARegister)
v = parameterList->findVariable(name);
if (v.first != NotARegister)
return Var;
else {
@ -1218,12 +1199,11 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
if (p->field && (p->field->getKind() == ExprNode::string))
args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast<StringExprNode *>(p->field))->str]));
else {
/* do this for new argument style to provide default 'positional' name
s << count;
args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s] ));
s.clear();
*/
args->push_back(Argument(genExpr(p->value), NULL ));
// args->push_back(Argument(genExpr(p->value), NULL ));
}
count++;
p = p->next;
@ -1561,15 +1541,16 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
}
break;
case ExprNode::functionLiteral:
case ExprNode::functionLiteral: // XXX FIXME!! This needs to handled by calling genFunction !!!
// - the parameter handling is all wrong
{
FunctionExprNode *f = static_cast<FunctionExprNode *>(p);
ICodeGenerator icg(mContext);
icg.allocateParameter(mContext->getWorld().identifiers["this"]); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false); // always parameter #0
VariableBinding *v = f->function.parameters;
while (v) {
if (v->name && (v->name->getKind() == ExprNode::identifier))
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name);
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, false, &Any_Type);
v = v->next;
}
icg.genStmt(f->function.body);
@ -1649,21 +1630,54 @@ ICodeModule *ICodeGenerator::genFunction(FunctionStmtNode *f, bool isConstructor
ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags;
ICodeGenerator icg(mContext, mClass, flags);
icg.allocateParameter(mContext->getWorld().identifiers["this"], (mClass) ? mClass : &Any_Type); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false, (mClass) ? mClass : &Any_Type); // always parameter #0
VariableBinding *v = f->function.parameters;
bool unnamed = true;
uint32 positionalCount = 0;
StringFormatter s;
while (v) {
if (v->name && (v->name->getKind() == ExprNode::identifier)) {
JSType *pType;
if ((v == f->function.restParameter) && (v->type == NULL))
pType = &Array_Type;
else
pType = extractType(v->type);
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, pType);
if (unnamed && (v == f->function.namedParameters)) { // Track when we hit the first named parameter.
icg.parameterList->setPositionalCount(positionalCount);
unnamed = false;
}
positionalCount++;
// The rest parameter is ignored in this processing - we push it to the end of the list.
// But we need to track whether it comes before or after the |
if (v == f->function.restParameter) {
icg.parameterList->setRestParameter( (unnamed) ? ParameterList::HasRestParameterBeforeBar : ParameterList::HasRestParameterAfterBar );
}
else {
if (v->name && (v->name->getKind() == ExprNode::identifier)) {
JSType *pType = extractType(v->type);
TypedRegister r = icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, (v->initializer != NULL), pType);
IdentifierList *a = v->aliases;
while (a) {
icg.parameterList->add(a->name, r, (v->initializer != NULL));
a = a->next;
}
// every unnamed parameter is also named with it's positional name
if (unnamed) {
s << r.first - 1; // the first positional parameter is '0'
icg.parameterList->add(mContext->getWorld().identifiers[s], r, (v->initializer != NULL));
s.clear();
}
}
}
else
NOT_REACHED("qualified or un-named parameters not handled; bugger off.");
v = v->next;
}
// now allocate the rest parameter
if (f->function.restParameter) {
v = f->function.restParameter;
JSType *pType = (v->type == NULL) ? &Array_Type : extractType(v->type);
if (v->name && (v->name->getKind() == ExprNode::identifier))
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, (v->initializer != NULL), pType);
else
icg.parameterList->setRestParameter(ParameterList::HasUnnamedRestParameter);
}
// generate the code for optional initializers
v = f->function.optParameters;
if (v) {
while (v) { // include the rest parameter, as it may have an initializer
@ -1689,13 +1703,7 @@ ICodeModule *ICodeGenerator::genFunction(FunctionStmtNode *f, bool isConstructor
}
icg.addParameterLabel(icg.setLabel(icg.getLabel())); // to provide the entry-point for the default case
}
v = f->function.restParameter;
if (v) {
icg.mHasRestParameter = true;
if (v->name && (v->name->getKind() == ExprNode::identifier)) {
icg.mHasNamedRestParameter = true;
}
}
if (isConstructor) {
/*
See if the first statement is an expression statement consisting
@ -1851,7 +1859,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
// constructor code generator. Slot variable
// initializers get added to this function.
ccg = new ICodeGenerator(classContext, thisClass, kNoFlags);
ccg->allocateParameter(mContext->getWorld().identifiers["this"], thisClass); // always parameter #0
ccg->allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0
}
ICodeGenerator scg(classContext, thisClass, kIsStaticMethod); // static initializer code generator.
@ -1939,7 +1947,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
TypedRegister thisValue = TypedRegister(0, thisClass);
ArgumentList *args = new ArgumentList();
ICodeGenerator icg(classContext, thisClass, kIsStaticMethod);
icg.allocateParameter(mContext->getWorld().identifiers["this"], thisClass); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0
if (superclass)
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, args);
if (thisClass->hasStatic(mInitName))
@ -2325,7 +2333,6 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
// Bind the incoming exception ...
if (mExceptionRegister.first == NotABanana)
mExceptionRegister = allocateRegister(mContext->getWorld().identifiers["__exceptionObject__"], &Any_Type);
variableList->setRegisterForVariable(c->name, mExceptionRegister);
genStmt(c->stmt);
if (finallyLabel)
@ -2419,7 +2426,7 @@ void ICodeGenerator::readICode(const char *fileName)
Context *classContext = new Context(mContext->getWorld(), thisScope);
ICodeGenerator scg(classContext, thisClass, kIsStaticMethod);
ICodeGenerator ccg(classContext, thisClass, kNoFlags);
ccg.allocateParameter(mContext->getWorld().identifiers["this"], thisClass);
ccg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass);
thisClass->defineStatic(mInitName, &Function_Type);
mContext->getGlobalObject()->defineVariable(className, &Type_Type, JSValue(thisClass));
@ -2433,8 +2440,8 @@ void ICodeGenerator::readICode(const char *fileName)
String methodName, resultTypeName;
element->getValue(widenCString("name"), methodName);
element->getValue(widenCString("type"), resultTypeName);
VariableList *theVariableList = new VariableList();
theVariableList->add(mContext->getWorld().identifiers["this"], TypedRegister(0, thisClass));
ParameterList *theParameterList = new ParameterList();
theParameterList->add(mContext->getWorld().identifiers["this"], TypedRegister(0, thisClass), false);
uint32 pCount = 1;
XMLNodeList &parameters = element->children();
for (XMLNodeList::const_iterator k = parameters.begin(); k != parameters.end(); k++) {
@ -2445,7 +2452,7 @@ void ICodeGenerator::readICode(const char *fileName)
element->getValue(widenCString("name"), parameterName);
element->getValue(widenCString("type"), parameterTypeName);
JSType *parameterType = findType(mContext->getWorld().identifiers[parameterTypeName]);
theVariableList->add(mContext->getWorld().identifiers[parameterName], TypedRegister(pCount++, parameterType));
theParameterList->add(mContext->getWorld().identifiers[parameterName], TypedRegister(pCount++, parameterType), false);
}
}
@ -2461,12 +2468,10 @@ void ICodeGenerator::readICode(const char *fileName)
icp.ParseSourceFromString(str);
ICodeModule *icm = new ICodeModule(icp.mInstructions,
theVariableList, /* VariableList *variables */
NULL, /* VariableList *variables */
theParameterList, /* ParameterList *parameters */
icp.mMaxRegister,
pCount, /* uint32 maxParameter, */
NULL, /* InstructionMap *instructionMap */
false, /* bool hasRestParameter, */
false, /* bool hasNamedRestParameter */
resultType);
thisClass->defineMethod(methodName, new JSFunction(icm));
}
@ -2493,7 +2498,7 @@ void ICodeGenerator::readICode(const char *fileName)
TypedRegister thisValue = TypedRegister(0, thisClass);
ArgumentList *args = new ArgumentList(0);
ICodeGenerator icg(mContext, thisClass, kIsStaticMethod);
icg.allocateParameter(mContext->getWorld().identifiers["this"], thisClass); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0
if (superclass)
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, args);
if (thisClass->hasStatic(mInitName))

View File

@ -51,59 +51,92 @@ namespace ICG {
using namespace JSClasses;
struct VariableList {
struct VariableList { // Maps from variable (parameter) name to a TypedRegister.
// But because we also want to map from a register number to
// it's type, we keep the TypedRegsters in a separate array and
// just store the index in the name map.
typedef std::map<String, uint32, std::less<String> > VariableMap;
typedef VariableMap::value_type MapValue;
VariableMap variableMap;
std::vector<TypedRegister> registerMap;
std::vector<TypedRegister> registerList;
void setRegisterForVariable(const StringAtom& name, TypedRegister r)
{
VariableMap::iterator i = variableMap.find(name);
ASSERT(i != variableMap.end());
registerMap[(*i).second] = r;
}
TypedRegister findVariable(const StringAtom& name)
{
VariableMap::iterator i = variableMap.find(name);
return (i == variableMap.end()) ? TypedRegister(NotARegister, &None_Type) : registerMap[(*i).second];
return (i == variableMap.end())
? TypedRegister(NotARegister, &None_Type)
: registerList[i->second];
}
void add(const StringAtom& name, TypedRegister r)
{
variableMap[name] = r.first;
registerMap.resize(r.first + 1);
registerMap[r.first] = r;
variableMap.insert(MapValue(name, r.first));
registerList.resize(r.first + 1);
registerList[r.first] = r;
}
TypedRegister getRegister(uint32 i)
{
ASSERT(i < registerMap.size());
return registerMap[i];
ASSERT(i < registerList.size());
return registerList[i];
}
};
struct ParameterList : public VariableList {
typedef enum {
NoRestParameter,
HasRestParameterBeforeBar,
HasRestParameterAfterBar,
HasUnnamedRestParameter
} RestParameterStatus;
std::vector<bool> mOptionalParameters; // whether or not a parameter has an optional value
// ordered by lexical ordering === register number.
RestParameterStatus mRestParameter;
uint32 mPositionalCount; // number of positional parameters
ParameterList() : mRestParameter(NoRestParameter), mPositionalCount(0) { }
void add(const StringAtom& name, TypedRegister r, bool isOptional)
{
VariableList::add(name, r);
mOptionalParameters.resize(r.first + 1);
mOptionalParameters[r.first] = isOptional;
}
uint32 size() { return registerList.size(); } // the variableMap may be larger since it contains aliases
bool isOptional(uint32 i) { ASSERT(i < mOptionalParameters.size()); return mOptionalParameters[i]; }
void setRestParameter(RestParameterStatus rs) { mRestParameter = rs; }
void setPositionalCount(uint32 x) { mPositionalCount = x; }
};
typedef std::map<uint32, uint32, std::less<uint32> > InstructionMap;
class ICodeModule {
public:
ICodeModule(InstructionStream *iCode, VariableList *variables,
uint32 maxRegister, uint32 maxParameter,
ParameterList *parameters,
uint32 maxRegister,
InstructionMap *instructionMap,
bool hasRestParameter, bool hasNamedRestParameter,
JSType *resultType) :
its_iCode(iCode), itsVariables(variables),
mParameterCount(maxParameter), itsMaxRegister(maxRegister),
its_iCode(iCode), itsVariables(variables), itsParameters(parameters),
itsMaxRegister(maxRegister),
mID(++sMaxID), mInstructionMap(instructionMap),
mParameterInit(NULL),
mNonOptionalParameterCount(maxParameter),
mEntryPoint(0),
mHasRestParameter(hasRestParameter),
mHasNamedRestParameter(hasNamedRestParameter),
mResultType(resultType)
{
}
@ -122,16 +155,13 @@ namespace ICG {
InstructionStream *its_iCode;
VariableList *itsVariables;
uint32 mParameterCount;
ParameterList *itsParameters;
uint32 itsMaxRegister;
uint32 mID;
InstructionMap *mInstructionMap;
String mFileName;
uint32 *mParameterInit;
uint32 mNonOptionalParameterCount;
uint32 mEntryPoint;
bool mHasRestParameter;
bool mHasNamedRestParameter;
JSType *mResultType;
static uint32 sMaxID;
@ -172,10 +202,10 @@ namespace ICG {
LabelList labels;
Register mTopRegister; // highest (currently) allocated register
uint32 mParameterCount; // number of parameters declared for the function
// these must come before any variables declared.
TypedRegister mExceptionRegister; // reserved to carry the exception object.
VariableList *variableList; // name|register pair for each variable
ParameterList *parameterList; // name|register pair for each parameter
// (with #0 reserved for 'this' regardless of scope)
Context *mContext; // the world and global object
LabelStack mLabelStack; // stack of LabelEntry objects, one per nested looping construct
@ -185,8 +215,6 @@ namespace ICG {
JSClass *mClass; // enclosing class when generating code for methods
ICodeGeneratorFlags mFlags; // assorted flags
LabelList *pLabels; // label for each parameter initialization entry point
bool mHasRestParameter; // true if this function has a ... parameter
bool mHasNamedRestParameter; // true if this function has a named ... parameter
const StringAtom &mInitName;
@ -204,7 +232,7 @@ namespace ICG {
return mTopRegister++;
}
void resetTopRegister() { mTopRegister = mParameterCount; }
void resetTopRegister() { mTopRegister = 0; }
void resetStatement() { resetTopRegister(); }
TypedRegister allocateRegister(const StringAtom& name, JSType *type);
@ -280,15 +308,40 @@ namespace ICG {
void throwStmt(TypedRegister r) { iCode->push_back(new Throw(r)); }
void debuggerStmt() { iCode->push_back(new Debugger()); }
TypedRegister allocateVariable(const StringAtom& name);
TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName);
TypedRegister allocateVariable(const StringAtom& name, JSType *type) ;
TypedRegister allocateVariable(const StringAtom& name)
{
return allocateVariable(name, &Any_Type);
}
TypedRegister allocateParameter(const StringAtom& name) { mParameterCount++; return allocateRegister(name, &Any_Type); }
TypedRegister allocateParameter(const StringAtom& name, const StringAtom& typeName)
{ mParameterCount++; return allocateRegister(name, findType(typeName)); }
TypedRegister allocateParameter(const StringAtom& name, JSType *type)
{ mParameterCount++; return allocateRegister(name, type); }
TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName)
{
return allocateVariable(name, findType(typeName));
}
TypedRegister allocateVariable(const StringAtom& name, JSType *type)
{
TypedRegister r = allocateRegister(name, type);
variableList->add(name, r);
return r;
}
TypedRegister allocateParameter(const StringAtom& name, bool isOptional)
{
return allocateParameter(name, isOptional, &Any_Type);
}
TypedRegister allocateParameter(const StringAtom& name, bool isOptional, const StringAtom& typeName)
{
return allocateParameter(name, isOptional, findType(typeName));
}
TypedRegister allocateParameter(const StringAtom& name, bool isOptional, JSType *type)
{
TypedRegister r = allocateRegister(name, type);
parameterList->add(name, r, isOptional);
return r;
}
Formatter& print(Formatter& f);

View File

@ -160,11 +160,11 @@ ICodeModule* Context::compileFunction(const String &source)
ICodeGenerator icg(this);
ASSERT(e->getKind() == ExprNode::functionLiteral);
FunctionExprNode* f = static_cast<FunctionExprNode*>(e);
icg.allocateParameter(getWorld().identifiers["this"]); // always parameter #0
icg.allocateParameter(getWorld().identifiers["this"], false); // always parameter #0
VariableBinding* v = f->function.parameters;
while (v) {
if (v->name && (v->name->getKind() == ExprNode::identifier))
icg.allocateParameter((static_cast<IdentifierExprNode*>(v->name))->name);
icg.allocateParameter((static_cast<IdentifierExprNode*>(v->name))->name, false);
v = v->next;
}
icg.genStmt(f->function.body);
@ -719,97 +719,85 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
ICodeModule *icm = target->getICode();
ArgumentList *args = op4(call);
// mParameterCount includes 'this' and also 1 for a named rest parameter
// the parameter count includes 'this' and any named rest parameter
//
uint32 pCount = icm->mParameterCount - 1;
uint32 pCount = icm->itsParameters->size() - 1; // we won't be passing 'this' via the arg list array
// callArgs will be the actual args passed to the target, put into correct register order.
// It has room for the rest parameter.
ArgumentList *callArgs = new ArgumentList(pCount, Argument(TypedRegister(NotARegister, &Null_Type), NULL));
if (icm->mHasNamedRestParameter) pCount--;
/*
For new style, there must be enough un-named parameters to satisfy #unnamed-positional, but no
more than #total positional (unless there's a rest parameter). Extras go to the optional parameters first, then to rest.
Named parameters
*/
// walk along the given arguments, handling each.
// might be good to optimize the case of calls without named arguments and/or rest parameters
JSArray *restArg = NULL;
uint32 restIndex = 0;
// don't want to count the rest parameter while processing the others
if (icm->itsParameters->mRestParameter != ParameterList::NoRestParameter) pCount--;
uint32 i;
JSArray *restArg = NULL;
// first match all named arguments with their intended target locations
for (i = 0; i < args->size(); i++) {
if ((*args)[i].second) { // a named argument
TypedRegister r = icm->itsVariables->findVariable(*((*args)[i].second));
bool isParameter = false;
if (r.first != NotABanana) { // we found the name in the target's list of variables
if (r.first < icm->mParameterCount) { // make sure we didn't match a local var
ASSERT(r.first <= callArgs->size());
// the named argument is arriving in slot i, but needs to be r instead
// r.first is the intended target register, we subtract 1 since the callArgs array doesn't include 'this'
// here's where we could detect over-writing a positional arg with a named one if that is illegal
// if (callArgs[r.first - 1].first.first != NotARegister)...
(*registers)[(*args)[i].first.first] = (*registers)[(*args)[i].first.first].convert(r.second);
(*callArgs)[r.first - 1] = Argument((*args)[i].first, NULL); // no need to copy the name through?
isParameter = true;
}
}
if (!isParameter) { // wasn't a parameter, make it a property of the rest parameter (if there is one)
if (icm->mHasRestParameter) {
if (icm->mHasNamedRestParameter) {
if (restArg == NULL) {
restArg = new JSArray();
restArg->setProperty(*(*args)[i].second, (*registers)[(*args)[i].first.first]);
(*registers)[(*args)[i].first.first] = restArg;
(*callArgs)[pCount] = Argument(TypedRegister((*args)[i].first.first, &Array_Type), NULL);
}
else
restArg->setProperty(*(*args)[i].second, (*registers)[(*args)[i].first.first]);
const StringAtom *argName = (*args)[i].second;
TypedRegister parameter = icm->itsParameters->findVariable(*argName);
if (parameter.first == NotARegister) {
// arg name doesn't match any parameter name, it's a candidate
// for the rest parameter (if there is one)
if (icm->itsParameters->mRestParameter == ParameterList::NoRestParameter)
throw new JSException("Named argument doesn't match parameter name in call with no rest parameter");
else {
if (icm->itsParameters->mRestParameter != ParameterList::HasUnnamedRestParameter) {
// if the name is a numeric literal >= 0, use it as an array index
// otherwise just set the named property.
const char16 *c = argName->c_str();
const char16 *end;
double d = stringToDouble(c, c + argName->length(), end);
if ((d != d) || (d < 0) || (d != (int)d)) { // a non-numeric or negative value
if (icm->itsParameters->mRestParameter == ParameterList::HasRestParameterBeforeBar)
throw new JSException("Non-numeric or negative argument name for positional rest parameter");
}
// else just throw it away
else { // shift the index value down by the number of positional parameters
StringFormatter s;
s << ((int)d - icm->itsParameters->mPositionalCount);
argName = &mWorld.identifiers[s];
}
TypedRegister argument = (*args)[i].first; // this is the argument whose name didn't match
if (restArg == NULL) {
// allocate the rest argument and then subvert the register being used for the
// argument under consideration to hold the newly created rest argument.
restArg = new JSArray();
restArg->setProperty(*argName, (*registers)[argument.first]); // put it into the rest argument
(*registers)[argument.first] = restArg;
// The callArgs for the rest parameter position gets loaded from that slot
(*callArgs)[pCount] = Argument(TypedRegister(argument.first, &Array_Type), NULL);
}
else
restArg->setProperty(*argName, (*registers)[argument.first]);
}
else
throw new JSException("Named argument doesn't match parameter name in call with no rest parameter");
// what about matching the named rest parameter ? aaarrggh!
// else just throw it away
}
}
else {
if (i >= pCount) { // more args than expected
if (icm->mHasRestParameter) {
if (icm->mHasNamedRestParameter) {
if (restArg == NULL) {
restArg = new JSArray();
(*restArg)[restIndex++] = (*registers)[(*args)[i].first.first];
(*registers)[(*args)[i].first.first] = restArg;
(*callArgs)[pCount] = Argument(TypedRegister((*args)[i].first.first, &Array_Type), NULL);
}
else
(*restArg)[restIndex++] = (*registers)[(*args)[i].first.first];
}
// else just throw it away
}
else
throw new JSException("Too many arguments in call");
}
else {
TypedRegister r = icm->itsVariables->getRegister(i + 1); // the variable list includes 'this'
(*registers)[(*args)[i].first.first] = (*registers)[(*args)[i].first.first].convert(r.second);
(*callArgs)[i] = (*args)[i]; // it's a positional, just slap it in place
}
uint32 targetIndex = parameter.first - 1; // this is the register number we're targetting
TypedRegister targetParameter = (*callArgs)[targetIndex].first;
if (targetParameter.first != NotARegister) // uh oh, some other argument wants this parameter
throw new JSException("Two (or more) arguments have the same name");
(*callArgs)[targetIndex] = (*args)[i];
}
}
uint32 contiguousArgs = 0;
for (i = 0; i < args->size(); i++) {
Argument &arg = (*args)[i];
if (arg.first.first == NotARegister) break;
contiguousArgs++;
// make sure that all non-optional parameters have values
for (i = 0; i < pCount; i++) {
TypedRegister parameter = (*callArgs)[i].first;
if (parameter.first == NotARegister) { // doesn't have an assigned argument
if (!icm->itsParameters->isOptional(i + 1)) // and parameter (allowing for 'this') doesn't have an optional value
throw new JSException("No argument supplied for non-optional parameter");
}
}
if ((contiguousArgs + 1) < icm->mNonOptionalParameterCount) // there's always a 'this' in R0 (even though it might be null)
throw new JSException("Too few arguments in call");
mLinkage = new Linkage(mLinkage, ++mPC, mActivation, mGlobal, op1(call));
mActivation = new Activation(icm, mActivation, (*registers)[op3(call).first], callArgs);
registers = &mActivation->mRegisters;

View File

@ -63,7 +63,7 @@ public:
XMLTag(String name) : mName(name), mFlag(Tag) { }
void addAttribute(const String &name, const String &value) { mAttributeList.insert(AttributeValue(name, value) ); }
bool getValue(const String &name, String &value);
bool getValue(const String &name, String &value); // returns the value of the most newly inserted attribute 'name'.
bool hasAttribute(const String &name) { return (mAttributeList.find(name) != mAttributeList.end()); }
String &name() { return mName; }

View File

@ -75,16 +75,14 @@ Formatter& operator<<(Formatter &f, ICodeModule &i)
ICodeGenerator::ICodeGenerator(Context *cx, JSClass *aClass, ICodeGeneratorFlags flags)
: mTopRegister(0),
mParameterCount(0),
mExceptionRegister(TypedRegister(NotARegister, &None_Type)),
variableList(new VariableList()),
parameterList(new ParameterList()),
mContext(cx),
mInstructionMap(new InstructionMap()),
mClass(aClass),
mFlags(flags),
pLabels(NULL),
mHasRestParameter(false),
mHasNamedRestParameter(false),
mInitName(cx->getWorld().identifiers["__init__"])
{
iCode = new InstructionStream();
@ -106,7 +104,7 @@ JSType *ICodeGenerator::findType(const StringAtom& typeName)
-Theoretically, mPermanentRegister[n] can be become false when a scope ends and
the registers allocated to contained variables are then available for re-use.
-Mostly the need is to handle overlapping allocation of temps & permanents as the
variables declarations are encountered. This wouldn't be necessary if a function
variables' declarations are encountered. This wouldn't be necessary if a function
presented a list of all variables, or a pre-pass executed to discover same.
*/
TypedRegister ICodeGenerator::allocateRegister(const StringAtom& name, JSType *type)
@ -122,27 +120,11 @@ TypedRegister ICodeGenerator::allocateRegister(const StringAtom& name, JSType *t
mPermanentRegister[r] = true;
TypedRegister result(r, type);
variableList->add(name, result);
mTopRegister = ++r;
return result;
}
TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name, const StringAtom& typeName)
{
return allocateRegister(name, findType(typeName));
}
TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name)
{
return allocateRegister(name, &Any_Type);
}
TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name, JSType *type)
{
return allocateRegister(name, type);
}
ICodeModule *ICodeGenerator::complete(JSType *resultType)
{
#ifdef DEBUG
@ -188,16 +170,13 @@ ICodeModule *ICodeGenerator::complete(JSType *resultType)
*/
ICodeModule* module = new ICodeModule(iCode,
variableList,
parameterList,
mPermanentRegister.size(),
mParameterCount,
mInstructionMap,
mHasRestParameter,
mHasNamedRestParameter,
resultType);
if (pLabels) {
uint32 i;
uint32 parameterInits = pLabels->size() - 1; // there's an extra label at the end for the actual entryPoint
module->mNonOptionalParameterCount -= parameterInits;
module->mParameterInit = new uint32[parameterInits];
for (i = 0; i < parameterInits; i++) {
module->mParameterInit[i] = (*pLabels)[i]->mOffset;
@ -742,6 +721,8 @@ ICodeGenerator::LValueKind ICodeGenerator::resolveIdentifier(const StringAtom &n
{
if (!isWithinWith()) {
v = variableList->findVariable(name);
if (v.first == NotARegister)
v = parameterList->findVariable(name);
if (v.first != NotARegister)
return Var;
else {
@ -1218,12 +1199,11 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
if (p->field && (p->field->getKind() == ExprNode::string))
args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast<StringExprNode *>(p->field))->str]));
else {
/* do this for new argument style to provide default 'positional' name
s << count;
args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s] ));
s.clear();
*/
args->push_back(Argument(genExpr(p->value), NULL ));
// args->push_back(Argument(genExpr(p->value), NULL ));
}
count++;
p = p->next;
@ -1561,15 +1541,16 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
}
break;
case ExprNode::functionLiteral:
case ExprNode::functionLiteral: // XXX FIXME!! This needs to handled by calling genFunction !!!
// - the parameter handling is all wrong
{
FunctionExprNode *f = static_cast<FunctionExprNode *>(p);
ICodeGenerator icg(mContext);
icg.allocateParameter(mContext->getWorld().identifiers["this"]); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false); // always parameter #0
VariableBinding *v = f->function.parameters;
while (v) {
if (v->name && (v->name->getKind() == ExprNode::identifier))
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name);
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, false, &Any_Type);
v = v->next;
}
icg.genStmt(f->function.body);
@ -1649,21 +1630,54 @@ ICodeModule *ICodeGenerator::genFunction(FunctionStmtNode *f, bool isConstructor
ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags;
ICodeGenerator icg(mContext, mClass, flags);
icg.allocateParameter(mContext->getWorld().identifiers["this"], (mClass) ? mClass : &Any_Type); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false, (mClass) ? mClass : &Any_Type); // always parameter #0
VariableBinding *v = f->function.parameters;
bool unnamed = true;
uint32 positionalCount = 0;
StringFormatter s;
while (v) {
if (v->name && (v->name->getKind() == ExprNode::identifier)) {
JSType *pType;
if ((v == f->function.restParameter) && (v->type == NULL))
pType = &Array_Type;
else
pType = extractType(v->type);
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, pType);
if (unnamed && (v == f->function.namedParameters)) { // Track when we hit the first named parameter.
icg.parameterList->setPositionalCount(positionalCount);
unnamed = false;
}
positionalCount++;
// The rest parameter is ignored in this processing - we push it to the end of the list.
// But we need to track whether it comes before or after the |
if (v == f->function.restParameter) {
icg.parameterList->setRestParameter( (unnamed) ? ParameterList::HasRestParameterBeforeBar : ParameterList::HasRestParameterAfterBar );
}
else {
if (v->name && (v->name->getKind() == ExprNode::identifier)) {
JSType *pType = extractType(v->type);
TypedRegister r = icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, (v->initializer != NULL), pType);
IdentifierList *a = v->aliases;
while (a) {
icg.parameterList->add(a->name, r, (v->initializer != NULL));
a = a->next;
}
// every unnamed parameter is also named with it's positional name
if (unnamed) {
s << r.first - 1; // the first positional parameter is '0'
icg.parameterList->add(mContext->getWorld().identifiers[s], r, (v->initializer != NULL));
s.clear();
}
}
}
else
NOT_REACHED("qualified or un-named parameters not handled; bugger off.");
v = v->next;
}
// now allocate the rest parameter
if (f->function.restParameter) {
v = f->function.restParameter;
JSType *pType = (v->type == NULL) ? &Array_Type : extractType(v->type);
if (v->name && (v->name->getKind() == ExprNode::identifier))
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name, (v->initializer != NULL), pType);
else
icg.parameterList->setRestParameter(ParameterList::HasUnnamedRestParameter);
}
// generate the code for optional initializers
v = f->function.optParameters;
if (v) {
while (v) { // include the rest parameter, as it may have an initializer
@ -1689,13 +1703,7 @@ ICodeModule *ICodeGenerator::genFunction(FunctionStmtNode *f, bool isConstructor
}
icg.addParameterLabel(icg.setLabel(icg.getLabel())); // to provide the entry-point for the default case
}
v = f->function.restParameter;
if (v) {
icg.mHasRestParameter = true;
if (v->name && (v->name->getKind() == ExprNode::identifier)) {
icg.mHasNamedRestParameter = true;
}
}
if (isConstructor) {
/*
See if the first statement is an expression statement consisting
@ -1851,7 +1859,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
// constructor code generator. Slot variable
// initializers get added to this function.
ccg = new ICodeGenerator(classContext, thisClass, kNoFlags);
ccg->allocateParameter(mContext->getWorld().identifiers["this"], thisClass); // always parameter #0
ccg->allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0
}
ICodeGenerator scg(classContext, thisClass, kIsStaticMethod); // static initializer code generator.
@ -1939,7 +1947,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
TypedRegister thisValue = TypedRegister(0, thisClass);
ArgumentList *args = new ArgumentList();
ICodeGenerator icg(classContext, thisClass, kIsStaticMethod);
icg.allocateParameter(mContext->getWorld().identifiers["this"], thisClass); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0
if (superclass)
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, args);
if (thisClass->hasStatic(mInitName))
@ -2325,7 +2333,6 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
// Bind the incoming exception ...
if (mExceptionRegister.first == NotABanana)
mExceptionRegister = allocateRegister(mContext->getWorld().identifiers["__exceptionObject__"], &Any_Type);
variableList->setRegisterForVariable(c->name, mExceptionRegister);
genStmt(c->stmt);
if (finallyLabel)
@ -2419,7 +2426,7 @@ void ICodeGenerator::readICode(const char *fileName)
Context *classContext = new Context(mContext->getWorld(), thisScope);
ICodeGenerator scg(classContext, thisClass, kIsStaticMethod);
ICodeGenerator ccg(classContext, thisClass, kNoFlags);
ccg.allocateParameter(mContext->getWorld().identifiers["this"], thisClass);
ccg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass);
thisClass->defineStatic(mInitName, &Function_Type);
mContext->getGlobalObject()->defineVariable(className, &Type_Type, JSValue(thisClass));
@ -2433,8 +2440,8 @@ void ICodeGenerator::readICode(const char *fileName)
String methodName, resultTypeName;
element->getValue(widenCString("name"), methodName);
element->getValue(widenCString("type"), resultTypeName);
VariableList *theVariableList = new VariableList();
theVariableList->add(mContext->getWorld().identifiers["this"], TypedRegister(0, thisClass));
ParameterList *theParameterList = new ParameterList();
theParameterList->add(mContext->getWorld().identifiers["this"], TypedRegister(0, thisClass), false);
uint32 pCount = 1;
XMLNodeList &parameters = element->children();
for (XMLNodeList::const_iterator k = parameters.begin(); k != parameters.end(); k++) {
@ -2445,7 +2452,7 @@ void ICodeGenerator::readICode(const char *fileName)
element->getValue(widenCString("name"), parameterName);
element->getValue(widenCString("type"), parameterTypeName);
JSType *parameterType = findType(mContext->getWorld().identifiers[parameterTypeName]);
theVariableList->add(mContext->getWorld().identifiers[parameterName], TypedRegister(pCount++, parameterType));
theParameterList->add(mContext->getWorld().identifiers[parameterName], TypedRegister(pCount++, parameterType), false);
}
}
@ -2461,12 +2468,10 @@ void ICodeGenerator::readICode(const char *fileName)
icp.ParseSourceFromString(str);
ICodeModule *icm = new ICodeModule(icp.mInstructions,
theVariableList, /* VariableList *variables */
NULL, /* VariableList *variables */
theParameterList, /* ParameterList *parameters */
icp.mMaxRegister,
pCount, /* uint32 maxParameter, */
NULL, /* InstructionMap *instructionMap */
false, /* bool hasRestParameter, */
false, /* bool hasNamedRestParameter */
resultType);
thisClass->defineMethod(methodName, new JSFunction(icm));
}
@ -2493,7 +2498,7 @@ void ICodeGenerator::readICode(const char *fileName)
TypedRegister thisValue = TypedRegister(0, thisClass);
ArgumentList *args = new ArgumentList(0);
ICodeGenerator icg(mContext, thisClass, kIsStaticMethod);
icg.allocateParameter(mContext->getWorld().identifiers["this"], thisClass); // always parameter #0
icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0
if (superclass)
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, args);
if (thisClass->hasStatic(mInitName))

View File

@ -51,59 +51,92 @@ namespace ICG {
using namespace JSClasses;
struct VariableList {
struct VariableList { // Maps from variable (parameter) name to a TypedRegister.
// But because we also want to map from a register number to
// it's type, we keep the TypedRegsters in a separate array and
// just store the index in the name map.
typedef std::map<String, uint32, std::less<String> > VariableMap;
typedef VariableMap::value_type MapValue;
VariableMap variableMap;
std::vector<TypedRegister> registerMap;
std::vector<TypedRegister> registerList;
void setRegisterForVariable(const StringAtom& name, TypedRegister r)
{
VariableMap::iterator i = variableMap.find(name);
ASSERT(i != variableMap.end());
registerMap[(*i).second] = r;
}
TypedRegister findVariable(const StringAtom& name)
{
VariableMap::iterator i = variableMap.find(name);
return (i == variableMap.end()) ? TypedRegister(NotARegister, &None_Type) : registerMap[(*i).second];
return (i == variableMap.end())
? TypedRegister(NotARegister, &None_Type)
: registerList[i->second];
}
void add(const StringAtom& name, TypedRegister r)
{
variableMap[name] = r.first;
registerMap.resize(r.first + 1);
registerMap[r.first] = r;
variableMap.insert(MapValue(name, r.first));
registerList.resize(r.first + 1);
registerList[r.first] = r;
}
TypedRegister getRegister(uint32 i)
{
ASSERT(i < registerMap.size());
return registerMap[i];
ASSERT(i < registerList.size());
return registerList[i];
}
};
struct ParameterList : public VariableList {
typedef enum {
NoRestParameter,
HasRestParameterBeforeBar,
HasRestParameterAfterBar,
HasUnnamedRestParameter
} RestParameterStatus;
std::vector<bool> mOptionalParameters; // whether or not a parameter has an optional value
// ordered by lexical ordering === register number.
RestParameterStatus mRestParameter;
uint32 mPositionalCount; // number of positional parameters
ParameterList() : mRestParameter(NoRestParameter), mPositionalCount(0) { }
void add(const StringAtom& name, TypedRegister r, bool isOptional)
{
VariableList::add(name, r);
mOptionalParameters.resize(r.first + 1);
mOptionalParameters[r.first] = isOptional;
}
uint32 size() { return registerList.size(); } // the variableMap may be larger since it contains aliases
bool isOptional(uint32 i) { ASSERT(i < mOptionalParameters.size()); return mOptionalParameters[i]; }
void setRestParameter(RestParameterStatus rs) { mRestParameter = rs; }
void setPositionalCount(uint32 x) { mPositionalCount = x; }
};
typedef std::map<uint32, uint32, std::less<uint32> > InstructionMap;
class ICodeModule {
public:
ICodeModule(InstructionStream *iCode, VariableList *variables,
uint32 maxRegister, uint32 maxParameter,
ParameterList *parameters,
uint32 maxRegister,
InstructionMap *instructionMap,
bool hasRestParameter, bool hasNamedRestParameter,
JSType *resultType) :
its_iCode(iCode), itsVariables(variables),
mParameterCount(maxParameter), itsMaxRegister(maxRegister),
its_iCode(iCode), itsVariables(variables), itsParameters(parameters),
itsMaxRegister(maxRegister),
mID(++sMaxID), mInstructionMap(instructionMap),
mParameterInit(NULL),
mNonOptionalParameterCount(maxParameter),
mEntryPoint(0),
mHasRestParameter(hasRestParameter),
mHasNamedRestParameter(hasNamedRestParameter),
mResultType(resultType)
{
}
@ -122,16 +155,13 @@ namespace ICG {
InstructionStream *its_iCode;
VariableList *itsVariables;
uint32 mParameterCount;
ParameterList *itsParameters;
uint32 itsMaxRegister;
uint32 mID;
InstructionMap *mInstructionMap;
String mFileName;
uint32 *mParameterInit;
uint32 mNonOptionalParameterCount;
uint32 mEntryPoint;
bool mHasRestParameter;
bool mHasNamedRestParameter;
JSType *mResultType;
static uint32 sMaxID;
@ -172,10 +202,10 @@ namespace ICG {
LabelList labels;
Register mTopRegister; // highest (currently) allocated register
uint32 mParameterCount; // number of parameters declared for the function
// these must come before any variables declared.
TypedRegister mExceptionRegister; // reserved to carry the exception object.
VariableList *variableList; // name|register pair for each variable
ParameterList *parameterList; // name|register pair for each parameter
// (with #0 reserved for 'this' regardless of scope)
Context *mContext; // the world and global object
LabelStack mLabelStack; // stack of LabelEntry objects, one per nested looping construct
@ -185,8 +215,6 @@ namespace ICG {
JSClass *mClass; // enclosing class when generating code for methods
ICodeGeneratorFlags mFlags; // assorted flags
LabelList *pLabels; // label for each parameter initialization entry point
bool mHasRestParameter; // true if this function has a ... parameter
bool mHasNamedRestParameter; // true if this function has a named ... parameter
const StringAtom &mInitName;
@ -204,7 +232,7 @@ namespace ICG {
return mTopRegister++;
}
void resetTopRegister() { mTopRegister = mParameterCount; }
void resetTopRegister() { mTopRegister = 0; }
void resetStatement() { resetTopRegister(); }
TypedRegister allocateRegister(const StringAtom& name, JSType *type);
@ -280,15 +308,40 @@ namespace ICG {
void throwStmt(TypedRegister r) { iCode->push_back(new Throw(r)); }
void debuggerStmt() { iCode->push_back(new Debugger()); }
TypedRegister allocateVariable(const StringAtom& name);
TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName);
TypedRegister allocateVariable(const StringAtom& name, JSType *type) ;
TypedRegister allocateVariable(const StringAtom& name)
{
return allocateVariable(name, &Any_Type);
}
TypedRegister allocateParameter(const StringAtom& name) { mParameterCount++; return allocateRegister(name, &Any_Type); }
TypedRegister allocateParameter(const StringAtom& name, const StringAtom& typeName)
{ mParameterCount++; return allocateRegister(name, findType(typeName)); }
TypedRegister allocateParameter(const StringAtom& name, JSType *type)
{ mParameterCount++; return allocateRegister(name, type); }
TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName)
{
return allocateVariable(name, findType(typeName));
}
TypedRegister allocateVariable(const StringAtom& name, JSType *type)
{
TypedRegister r = allocateRegister(name, type);
variableList->add(name, r);
return r;
}
TypedRegister allocateParameter(const StringAtom& name, bool isOptional)
{
return allocateParameter(name, isOptional, &Any_Type);
}
TypedRegister allocateParameter(const StringAtom& name, bool isOptional, const StringAtom& typeName)
{
return allocateParameter(name, isOptional, findType(typeName));
}
TypedRegister allocateParameter(const StringAtom& name, bool isOptional, JSType *type)
{
TypedRegister r = allocateRegister(name, type);
parameterList->add(name, r, isOptional);
return r;
}
Formatter& print(Formatter& f);

View File

@ -160,11 +160,11 @@ ICodeModule* Context::compileFunction(const String &source)
ICodeGenerator icg(this);
ASSERT(e->getKind() == ExprNode::functionLiteral);
FunctionExprNode* f = static_cast<FunctionExprNode*>(e);
icg.allocateParameter(getWorld().identifiers["this"]); // always parameter #0
icg.allocateParameter(getWorld().identifiers["this"], false); // always parameter #0
VariableBinding* v = f->function.parameters;
while (v) {
if (v->name && (v->name->getKind() == ExprNode::identifier))
icg.allocateParameter((static_cast<IdentifierExprNode*>(v->name))->name);
icg.allocateParameter((static_cast<IdentifierExprNode*>(v->name))->name, false);
v = v->next;
}
icg.genStmt(f->function.body);
@ -719,97 +719,85 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
ICodeModule *icm = target->getICode();
ArgumentList *args = op4(call);
// mParameterCount includes 'this' and also 1 for a named rest parameter
// the parameter count includes 'this' and any named rest parameter
//
uint32 pCount = icm->mParameterCount - 1;
uint32 pCount = icm->itsParameters->size() - 1; // we won't be passing 'this' via the arg list array
// callArgs will be the actual args passed to the target, put into correct register order.
// It has room for the rest parameter.
ArgumentList *callArgs = new ArgumentList(pCount, Argument(TypedRegister(NotARegister, &Null_Type), NULL));
if (icm->mHasNamedRestParameter) pCount--;
/*
For new style, there must be enough un-named parameters to satisfy #unnamed-positional, but no
more than #total positional (unless there's a rest parameter). Extras go to the optional parameters first, then to rest.
Named parameters
*/
// walk along the given arguments, handling each.
// might be good to optimize the case of calls without named arguments and/or rest parameters
JSArray *restArg = NULL;
uint32 restIndex = 0;
// don't want to count the rest parameter while processing the others
if (icm->itsParameters->mRestParameter != ParameterList::NoRestParameter) pCount--;
uint32 i;
JSArray *restArg = NULL;
// first match all named arguments with their intended target locations
for (i = 0; i < args->size(); i++) {
if ((*args)[i].second) { // a named argument
TypedRegister r = icm->itsVariables->findVariable(*((*args)[i].second));
bool isParameter = false;
if (r.first != NotABanana) { // we found the name in the target's list of variables
if (r.first < icm->mParameterCount) { // make sure we didn't match a local var
ASSERT(r.first <= callArgs->size());
// the named argument is arriving in slot i, but needs to be r instead
// r.first is the intended target register, we subtract 1 since the callArgs array doesn't include 'this'
// here's where we could detect over-writing a positional arg with a named one if that is illegal
// if (callArgs[r.first - 1].first.first != NotARegister)...
(*registers)[(*args)[i].first.first] = (*registers)[(*args)[i].first.first].convert(r.second);
(*callArgs)[r.first - 1] = Argument((*args)[i].first, NULL); // no need to copy the name through?
isParameter = true;
}
}
if (!isParameter) { // wasn't a parameter, make it a property of the rest parameter (if there is one)
if (icm->mHasRestParameter) {
if (icm->mHasNamedRestParameter) {
if (restArg == NULL) {
restArg = new JSArray();
restArg->setProperty(*(*args)[i].second, (*registers)[(*args)[i].first.first]);
(*registers)[(*args)[i].first.first] = restArg;
(*callArgs)[pCount] = Argument(TypedRegister((*args)[i].first.first, &Array_Type), NULL);
}
else
restArg->setProperty(*(*args)[i].second, (*registers)[(*args)[i].first.first]);
const StringAtom *argName = (*args)[i].second;
TypedRegister parameter = icm->itsParameters->findVariable(*argName);
if (parameter.first == NotARegister) {
// arg name doesn't match any parameter name, it's a candidate
// for the rest parameter (if there is one)
if (icm->itsParameters->mRestParameter == ParameterList::NoRestParameter)
throw new JSException("Named argument doesn't match parameter name in call with no rest parameter");
else {
if (icm->itsParameters->mRestParameter != ParameterList::HasUnnamedRestParameter) {
// if the name is a numeric literal >= 0, use it as an array index
// otherwise just set the named property.
const char16 *c = argName->c_str();
const char16 *end;
double d = stringToDouble(c, c + argName->length(), end);
if ((d != d) || (d < 0) || (d != (int)d)) { // a non-numeric or negative value
if (icm->itsParameters->mRestParameter == ParameterList::HasRestParameterBeforeBar)
throw new JSException("Non-numeric or negative argument name for positional rest parameter");
}
// else just throw it away
else { // shift the index value down by the number of positional parameters
StringFormatter s;
s << ((int)d - icm->itsParameters->mPositionalCount);
argName = &mWorld.identifiers[s];
}
TypedRegister argument = (*args)[i].first; // this is the argument whose name didn't match
if (restArg == NULL) {
// allocate the rest argument and then subvert the register being used for the
// argument under consideration to hold the newly created rest argument.
restArg = new JSArray();
restArg->setProperty(*argName, (*registers)[argument.first]); // put it into the rest argument
(*registers)[argument.first] = restArg;
// The callArgs for the rest parameter position gets loaded from that slot
(*callArgs)[pCount] = Argument(TypedRegister(argument.first, &Array_Type), NULL);
}
else
restArg->setProperty(*argName, (*registers)[argument.first]);
}
else
throw new JSException("Named argument doesn't match parameter name in call with no rest parameter");
// what about matching the named rest parameter ? aaarrggh!
// else just throw it away
}
}
else {
if (i >= pCount) { // more args than expected
if (icm->mHasRestParameter) {
if (icm->mHasNamedRestParameter) {
if (restArg == NULL) {
restArg = new JSArray();
(*restArg)[restIndex++] = (*registers)[(*args)[i].first.first];
(*registers)[(*args)[i].first.first] = restArg;
(*callArgs)[pCount] = Argument(TypedRegister((*args)[i].first.first, &Array_Type), NULL);
}
else
(*restArg)[restIndex++] = (*registers)[(*args)[i].first.first];
}
// else just throw it away
}
else
throw new JSException("Too many arguments in call");
}
else {
TypedRegister r = icm->itsVariables->getRegister(i + 1); // the variable list includes 'this'
(*registers)[(*args)[i].first.first] = (*registers)[(*args)[i].first.first].convert(r.second);
(*callArgs)[i] = (*args)[i]; // it's a positional, just slap it in place
}
uint32 targetIndex = parameter.first - 1; // this is the register number we're targetting
TypedRegister targetParameter = (*callArgs)[targetIndex].first;
if (targetParameter.first != NotARegister) // uh oh, some other argument wants this parameter
throw new JSException("Two (or more) arguments have the same name");
(*callArgs)[targetIndex] = (*args)[i];
}
}
uint32 contiguousArgs = 0;
for (i = 0; i < args->size(); i++) {
Argument &arg = (*args)[i];
if (arg.first.first == NotARegister) break;
contiguousArgs++;
// make sure that all non-optional parameters have values
for (i = 0; i < pCount; i++) {
TypedRegister parameter = (*callArgs)[i].first;
if (parameter.first == NotARegister) { // doesn't have an assigned argument
if (!icm->itsParameters->isOptional(i + 1)) // and parameter (allowing for 'this') doesn't have an optional value
throw new JSException("No argument supplied for non-optional parameter");
}
}
if ((contiguousArgs + 1) < icm->mNonOptionalParameterCount) // there's always a 'this' in R0 (even though it might be null)
throw new JSException("Too few arguments in call");
mLinkage = new Linkage(mLinkage, ++mPC, mActivation, mGlobal, op1(call));
mActivation = new Activation(icm, mActivation, (*registers)[op3(call).first], callArgs);
registers = &mActivation->mRegisters;

View File

@ -63,7 +63,7 @@ public:
XMLTag(String name) : mName(name), mFlag(Tag) { }
void addAttribute(const String &name, const String &value) { mAttributeList.insert(AttributeValue(name, value) ); }
bool getValue(const String &name, String &value);
bool getValue(const String &name, String &value); // returns the value of the most newly inserted attribute 'name'.
bool hasAttribute(const String &name) { return (mAttributeList.find(name) != mAttributeList.end()); }
String &name() { return mName; }