diff --git a/js/js2/icodegenerator.cpp b/js/js2/icodegenerator.cpp index 28a04cfa2285..dc9352645380 100644 --- a/js/js2/icodegenerator.cpp +++ b/js/js2/icodegenerator.cpp @@ -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(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(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(v->name))->name); + icg.allocateParameter((static_cast(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(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(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(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 ¶meters = 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)) diff --git a/js/js2/icodegenerator.h b/js/js2/icodegenerator.h index 800161760c35..69abe8984e36 100644 --- a/js/js2/icodegenerator.h +++ b/js/js2/icodegenerator.h @@ -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 > VariableMap; + typedef VariableMap::value_type MapValue; + VariableMap variableMap; - - std::vector registerMap; + std::vector 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 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 > 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); diff --git a/js/js2/interpreter.cpp b/js/js2/interpreter.cpp index a42f484feb6e..20e10310fcb6 100644 --- a/js/js2/interpreter.cpp +++ b/js/js2/interpreter.cpp @@ -160,11 +160,11 @@ ICodeModule* Context::compileFunction(const String &source) ICodeGenerator icg(this); ASSERT(e->getKind() == ExprNode::functionLiteral); FunctionExprNode* f = static_cast(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(v->name))->name); + icg.allocateParameter((static_cast(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; diff --git a/js/js2/xmlparser.h b/js/js2/xmlparser.h index 6232a19d0236..9aafebfdd2ef 100644 --- a/js/js2/xmlparser.h +++ b/js/js2/xmlparser.h @@ -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; } diff --git a/js2/src/icodegenerator.cpp b/js2/src/icodegenerator.cpp index 28a04cfa2285..dc9352645380 100644 --- a/js2/src/icodegenerator.cpp +++ b/js2/src/icodegenerator.cpp @@ -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(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(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(v->name))->name); + icg.allocateParameter((static_cast(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(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(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(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 ¶meters = 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)) diff --git a/js2/src/icodegenerator.h b/js2/src/icodegenerator.h index 800161760c35..69abe8984e36 100644 --- a/js2/src/icodegenerator.h +++ b/js2/src/icodegenerator.h @@ -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 > VariableMap; + typedef VariableMap::value_type MapValue; + VariableMap variableMap; - - std::vector registerMap; + std::vector 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 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 > 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); diff --git a/js2/src/interpreter.cpp b/js2/src/interpreter.cpp index a42f484feb6e..20e10310fcb6 100644 --- a/js2/src/interpreter.cpp +++ b/js2/src/interpreter.cpp @@ -160,11 +160,11 @@ ICodeModule* Context::compileFunction(const String &source) ICodeGenerator icg(this); ASSERT(e->getKind() == ExprNode::functionLiteral); FunctionExprNode* f = static_cast(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(v->name))->name); + icg.allocateParameter((static_cast(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; diff --git a/js2/src/xmlparser.h b/js2/src/xmlparser.h index 6232a19d0236..9aafebfdd2ef 100644 --- a/js2/src/xmlparser.h +++ b/js2/src/xmlparser.h @@ -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; }