From 5ef03957d6bf284b98f1e2e5551bb5646c5e490d Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Fri, 19 Jan 2001 23:56:37 +0000 Subject: [PATCH] Fixes and enhancements to get class references, constructors and scripts working from .xml input. --- js/js2/exception.cpp | 51 ------------ js/js2/exception.h | 115 --------------------------- js/js2/icodeasm.cpp | 41 ++++++++-- js/js2/icodeasm.h | 2 +- js/js2/icodegenerator.cpp | 41 ++++++++-- js/js2/icodegenerator.h | 2 +- js/js2/interpreter.cpp | 153 ++++++++++++++++++------------------ js/js2/interpreter.h | 20 ++--- js/js2/js2.cpp | 4 +- js2/src/exception.cpp | 4 +- js2/src/exception.h | 4 +- js2/src/icodeasm.cpp | 41 ++++++++-- js2/src/icodeasm.h | 2 +- js2/src/icodegenerator.cpp | 41 ++++++++-- js2/src/icodegenerator.h | 2 +- js2/src/interpreter.cpp | 153 ++++++++++++++++++------------------ js2/src/interpreter.h | 20 ++--- js2/tests/cpp/js2_shell.cpp | 4 +- 18 files changed, 328 insertions(+), 372 deletions(-) diff --git a/js/js2/exception.cpp b/js/js2/exception.cpp index 3a00ec1b554..e69de29bb2d 100644 --- a/js/js2/exception.cpp +++ b/js/js2/exception.cpp @@ -1,51 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * The contents of this file are subject to the Netscape Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the JavaScript 2 Prototype. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU Public License (the "GPL"), in which case the - * provisions of the GPL are applicable instead of those above. - * If you wish to allow use of your version of this file only - * under the terms of the GPL and not to allow others to use your - * version of this file under the NPL, indicate your decision by - * deleting the provisions above and replace them with the notice - * and other provisions required by the GPL. If you do not delete - * the provisions above, a recipient may use your version of this - * file under either the NPL or the GPL. - */ - -#include "exception.h" - -namespace JavaScript { - - extern const char* exception_types[]; - extern const char* exception_msgs[]; - - void - JSException::toString8 (string8 &rval) - { - rval = *exception_types[mType] + " Exception: " + - *exception_msgs[mID]; - if (mSource.size() != 0) - rval += " in " + mSource; - } - -} - diff --git a/js/js2/exception.h b/js/js2/exception.h index 21fd8a989fb..e69de29bb2d 100644 --- a/js/js2/exception.h +++ b/js/js2/exception.h @@ -1,115 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * The contents of this file are subject to the Netscape Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the JavaScript 2 Prototype. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU Public License (the "GPL"), in which case the - * provisions of the GPL are applicable instead of those above. - * If you wish to allow use of your version of this file only - * under the terms of the GPL and not to allow others to use your - * version of this file under the NPL, indicate your decision by - * deleting the provisions above and replace them with the notice - * and other provisions required by the GPL. If you do not delete - * the provisions above, a recipient may use your version of this - * file under either the NPL or the GPL. - */ - -#ifndef exception_h___ -#define exception_h___ -#include "utilities.h" - -namespace JavaScript { - - enum ExceptionType { - etUnknown = 0, - etLexer, - etParser, - etRuntime, - etCount - }; - - enum ExceptionID { - eidExpectBool = 0, - eidExpectDouble, - eidExpectInt32, - eidExpectUInt32, - eidExpectRegister, - eidExpectArgList, - eidExpectColon, - eidExpectCloseParen, - eidExpectBinaryOp, - eidExpectString, - eidExpectLabel, - eidExpectComma, - eidExpectNewline, - eidExpectIdentifier, - eidDuplicateLabel, - eidUnknownICode, - eidUnknownBinaryOp, - eidUnterminatedString, - eidCount - }; - - class JSException { - public: - JSException (ExceptionID ID, string8_citer pos = 0, - string8 source = 0, ExceptionType type = etUnknown) - : mID(ID), mType(type), mPos(pos), mSource(source) {} - ExceptionID mID; - ExceptionType mType; - string8_citer mPos; - string8 mSource; - - public: - void toString8(string8 &rval); - /* - private: - JSException(const JSException&); - */ - - }; - - class JSLexException : public JSException { - public: - JSLexException (ExceptionID ID, string8_citer pos = 0, - string8 source = "") : - JSException(ID, pos, source, etLexer) {} - /* - private: - JSLexException (const JSLexException&); - */ - }; - - class JSParseException : public JSException { - public: - JSParseException (ExceptionID ID, string8_citer pos = 0, - string8 source = 0) : - JSException(ID, pos, source, etParser) {} - /* - private: - JSParseException (const JSParseException&); - */ - }; - -} - -#endif /* exception_h___ */ - - diff --git a/js/js2/icodeasm.cpp b/js/js2/icodeasm.cpp index bc37d00d318..7b16cc0f6ce 100644 --- a/js/js2/icodeasm.cpp +++ b/js/js2/icodeasm.cpp @@ -108,6 +108,7 @@ namespace ICodeASM { throw JSParseException (eidExpectArgList); tl = seekTokenStart (tl.begin + 1, end); + StringFormatter s_fmt; while (tl.estimate == teString || tl.estimate == teAlpha) { string *argName = 0; @@ -143,6 +144,15 @@ namespace ICodeASM { sap = &(mCx->getWorld().identifiers[argName->c_str()]); delete argName; } + else { + /* if an argument name was not specified, use the position + * to build a default name. + */ + s_fmt << (uint32)al->size(); + sap = &(mCx->getWorld().identifiers[s_fmt.getString()]); + s_fmt.clear(); + + } VM::Argument arg = VM::Argument (tr, sap); al->push_back(arg); @@ -176,7 +186,7 @@ namespace ICodeASM { for (int i = 0; keyword_exprNodeKinds[i] != 0; ++i) if (cmp_nocase (*str, keyword_exprNodeKinds[i], keyword_exprNodeKinds[i] + - strlen (keyword_exprNodeKinds[i]) + 1) == 0) { + strlen (keyword_exprNodeKinds[i])) == 0) { *rval = exprNodeOps[i]; delete str; return end; @@ -220,11 +230,28 @@ namespace ICodeASM { } string8_citer - ICodeParser::parseJSClassOperand (string8_citer /*begin*/, - string8_citer end, string ** /*rval*/) + ICodeParser::parseJSClassOperand (string8_citer begin, + string8_citer end, JSTypes::JSType **rval) { - NOT_REACHED ("JSClasses are hard, lets go shopping."); + TokenLocation tl = seekTokenStart (begin, end); + + if (tl.estimate != teString) + throw JSParseException (eidExpectString); + + string8 *str; + end = lexString8 (tl.begin, end, &str); + StringAtom &typename_atom = mCx->getWorld().identifiers[str->c_str()]; + delete str; + JSTypes::JSValue jsv = + mCx->getGlobalObject()->getVariable(typename_atom); + if (jsv.isType()) + *rval = jsv.type; + else + *rval = &(JSTypes::Any_Type); + return end; +// NOT_REACHED ("JSClasses are hard, lets go shopping."); +// return end; } string8_citer @@ -287,7 +314,7 @@ namespace ICodeASM { begin = lexAlpha (tl.begin, end, &str); if (cmp_nocase(*str, keyword_offset, keyword_offset + - strlen(keyword_offset) + 1) == 0) { + strlen(keyword_offset)) == 0) { delete str; /* got the "Offset" keyword, treat next thing as a jump offset * expressed as "Offset +/-N" */ @@ -391,7 +418,7 @@ namespace ICodeASM { CASE_TYPE(Bool, bool, static_cast); CASE_TYPE(Double, double, static_cast); CASE_TYPE(ICodeModule, string *, reinterpret_cast); - CASE_TYPE(JSClass, string *, reinterpret_cast); + CASE_TYPE(JSClass, JSTypes::JSType *, reinterpret_cast); CASE_TYPE(JSString, JSTypes::JSString *, reinterpret_cast); CASE_TYPE(JSFunction, string *, reinterpret_cast); CASE_TYPE(JSType, JSTypes::JSType *, reinterpret_cast); @@ -491,7 +518,7 @@ namespace ICodeASM { for (uint i = 0; i < icodemap_size; ++i) if (cmp_nocase(icode_str, &icodemap[i].name[0], &icodemap[i].name[0] + - strlen(icodemap[i].name) + 1) == 0) + strlen(icodemap[i].name)) == 0) /* if match found, parse it's operands */ return parseInstruction (i, firstTokenEnd, end); /* otherwise, choke on it */ diff --git a/js/js2/icodeasm.h b/js/js2/icodeasm.h index 7dfd0beac49..2c5f06b7d15 100644 --- a/js/js2/icodeasm.h +++ b/js/js2/icodeasm.h @@ -121,7 +121,7 @@ namespace ICodeASM { string8 **rval); string8_citer parseJSClassOperand (string8_citer begin, string8_citer end, - string8 **rval); + JSTypes::JSType **rval); string8_citer parseJSStringOperand (string8_citer begin, string8_citer end, JSTypes::JSString **rval); diff --git a/js/js2/icodegenerator.cpp b/js/js2/icodegenerator.cpp index 846d3aad193..ed93bd247ea 100644 --- a/js/js2/icodegenerator.cpp +++ b/js/js2/icodegenerator.cpp @@ -39,6 +39,7 @@ #include "icodegenerator.h" #include "interpreter.h" #include "xmlparser.h" +#include "exception.h" #include "icodeasm.h" #include @@ -2552,8 +2553,10 @@ Formatter& operator<<(Formatter &f, string &s) } -void ICodeGenerator::readICode(const char *fileName) +ICodeModule *ICodeGenerator::readICode(const char *fileName) { + ICodeModule *result = NULL; + XMLParser xp(fileName); XMLNode *top = xp.parseDocument(); stdOut << *top; @@ -2582,12 +2585,13 @@ void ICodeGenerator::readICode(const char *fileName) mContext->getGlobalObject()->defineVariable(className, &Type_Type, JSValue(thisClass)); - bool hasDefaultConstructor = false; +// bool hasDefaultConstructor = false; XMLNodeList &elements = node->children(); for (XMLNodeList::const_iterator j = elements.begin(); j != elements.end(); j++) { XMLNode *element = *j; + bool isConstructor = (element->name().compare(widenCString("constructor")) == 0); - if (element->name().compare(widenCString("method")) == 0) { + if (isConstructor || (element->name().compare(widenCString("method")) == 0)) { String methodName, resultTypeName; element->getValue(widenCString("name"), methodName); element->getValue(widenCString("type"), resultTypeName); @@ -2631,7 +2635,12 @@ void ICodeGenerator::readICode(const char *fileName) NULL, /* InstructionMap *instructionMap */ resultType, NotABanana); /* exception register */ - thisClass->defineMethod(methodName, new JSFunction(icm)); + if (isConstructor) { + thisClass->defineConstructor(methodName); + scg.setStatic(thisClass, mContext->getWorld().identifiers[methodName], scg.newFunction(icm)); + } + else + thisClass->defineMethod(methodName, new JSFunction(icm)); } } else { @@ -2651,7 +2660,7 @@ void ICodeGenerator::readICode(const char *fileName) } } scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete(&Void_Type))); - +/* if (!hasDefaultConstructor) { TypedRegister thisValue = TypedRegister(0, thisClass); ArgumentList *args = new ArgumentList(0); @@ -2665,6 +2674,7 @@ void ICodeGenerator::readICode(const char *fileName) thisClass->defineConstructor(className); scg.setStatic(thisClass, mContext->getWorld().identifiers[className], scg.newFunction(icg.complete(&Void_Type))); } +*/ thisClass->complete(); if (scg.getICode()->size()) { @@ -2676,7 +2686,24 @@ void ICodeGenerator::readICode(const char *fileName) } else { if (node->name().compare(widenCString("script")) == 0) { - // build an icode module and execute it + String &body = node->body(); + if (body.length()) { + std::string str(body.length(), char()); + std::transform(body.begin(), body.end(), str.begin(), narrow); + ICodeParser icp(mContext); + + stdOut << "(script) Calling ICodeParser with :\n" << str << "\n"; + + icp.parseSourceFromString(str); + + result = new ICodeModule(icp.mInstructions, + NULL, /* VariableList *variables */ + NULL, /* ParameterList *parameters */ + icp.mMaxRegister, + NULL, /* InstructionMap *instructionMap */ + &Void_Type, + NotABanana); /* exception register */ + } } else { if (node->name().compare(widenCString("instance")) == 0) { @@ -2691,7 +2718,7 @@ void ICodeGenerator::readICode(const char *fileName) } } - + return result; } diff --git a/js/js2/icodegenerator.h b/js/js2/icodegenerator.h index 46be19604a1..a9a21cc9b0a 100644 --- a/js/js2/icodegenerator.h +++ b/js/js2/icodegenerator.h @@ -297,7 +297,7 @@ namespace ICG { } ICodeModule *complete(JSType *resultType); - void readICode(const char *fileName); + ICodeModule *readICode(const char *fileName); JSType *extractType(ExprNode *t); JSType *getParameterType(FunctionDefinition &function, int index); diff --git a/js/js2/interpreter.cpp b/js/js2/interpreter.cpp index 886c3780174..43b5b3af05a 100644 --- a/js/js2/interpreter.cpp +++ b/js/js2/interpreter.cpp @@ -162,10 +162,10 @@ ICodeModule* Context::genCode(StmtNode *p, const String &fileName) return icm; } -void Context::loadClass(const char *fileName) +ICodeModule* Context::loadClass(const char *fileName) { ICodeGenerator icg(this); - icg.readICode(fileName); // loads it into the global object + return icg.readICode(fileName); // loads it into the global object } JSValues& Context::getRegisters() { return mActivation->mRegisters; } @@ -604,96 +604,99 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) else { ICodeModule *icm = target->getICode(); ArgumentList *args = op3(call); + ArgumentList *callArgs = NULL; - // if all the parameters are positional -// if (icm->itsParameters->mPositionalCount == icm->itsParameters->size()) + if (icm->itsParameters) { - // the parameter count includes 'this' and any named rest parameter - // - 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 all the parameters are positional + // if (icm->itsParameters->mPositionalCount == icm->itsParameters->size()) - // don't want to count the rest parameter while processing the others - if (icm->itsParameters->mRestParameter != ParameterList::NoRestParameter) pCount--; + // the parameter count includes 'this' and any named rest parameter + // + 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. + callArgs = new ArgumentList(pCount, Argument(TypedRegister(NotARegister, &Null_Type), NULL)); + + // don't want to count the rest parameter while processing the others + if (icm->itsParameters->mRestParameter != ParameterList::NoRestParameter) pCount--; - uint32 i; - JSArray *restArg = NULL; + uint32 i; + JSArray *restArg = NULL; - // first match all named arguments with their intended target locations - for (i = 0; i < args->size(); i++) { + // first match all named arguments with their intended target locations + for (i = 0; i < args->size(); i++) { - const StringAtom *argName = (*args)[i].second; + 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->data(); - const char16 *end; - double d = stringToDouble(c, c + argName->size(), end); - int index = -1; + 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->data(); + const char16 *end; + double d = stringToDouble(c, c + argName->size(), end); + int index = -1; - 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 { // shift the index value down by the number of positional parameters - index = (int)d; - index -= icm->itsParameters->mPositionalCount; - } + 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 { // shift the index value down by the number of positional parameters + index = (int)d; + index -= icm->itsParameters->mPositionalCount; + } - TypedRegister argument = (*args)[i].first; // this is the argument whose name didn't match + 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(); - if (index == -1) - restArg->setProperty(*argName, (*registers)[argument.first]); - else - (*restArg)[uint32(index)] = (*registers)[argument.first]; - (*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 { - if (index == -1) - restArg->setProperty(*argName, (*registers)[argument.first]); - else - (*restArg)[uint32(index)] = (*registers)[argument.first]; + 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(); + if (index == -1) + restArg->setProperty(*argName, (*registers)[argument.first]); + else + (*restArg)[uint32(index)] = (*registers)[argument.first]; + (*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 { + if (index == -1) + restArg->setProperty(*argName, (*registers)[argument.first]); + else + (*restArg)[uint32(index)] = (*registers)[argument.first]; + } } + // else just throw it away } - // else just throw it away + } + else { + 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]; } } - else { - 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]; + + + // 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"); + } } } - - - // 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"); - } - } - mLinkage = new Linkage(mLinkage, ++mPC, mActivation, mGlobal, op1(call), mICode, mCurrentClosure); mICode = icm; mActivation = new Activation(mICode->itsMaxRegister, mActivation, target->getThis(), callArgs); diff --git a/js/js2/interpreter.h b/js/js2/interpreter.h index 7d3cf53dafb..597ac35355e 100644 --- a/js/js2/interpreter.h +++ b/js/js2/interpreter.h @@ -81,7 +81,7 @@ namespace Interpreter { ICodeModule* genCode(StmtNode *p, const String &fileName); JSValue readEvalFile(FILE* in, const String& fileName); - void loadClass(const char *fileName); + ICodeModule* loadClass(const char *fileName); const JSValue findBinaryOverride(JSValue &operand1, JSValue &operand2, ExprNode::Kind op); @@ -142,14 +142,16 @@ namespace Interpreter { // copy caller's parameter list to initial registers. JSValues::iterator dest = mRegisters.begin(); *dest++ = thisArg; - const JSValues& params = caller->mRegisters; - for (ArgumentList::const_iterator src = list->begin(), - end = list->end(); src != end; ++src, ++dest) { - Register r = (*src).first.first; - if (r != NotARegister) - *dest = params[r]; - else - *dest = JSValue(JSValue::uninitialized_tag); + if (list) { + const JSValues& params = caller->mRegisters; + for (ArgumentList::const_iterator src = list->begin(), + end = list->end(); src != end; ++src, ++dest) { + Register r = (*src).first.first; + if (r != NotARegister) + *dest = params[r]; + else + *dest = JSValue(JSValue::uninitialized_tag); + } } } diff --git a/js/js2/js2.cpp b/js/js2/js2.cpp index 59210fa9340..921c43d8fbf 100644 --- a/js/js2/js2.cpp +++ b/js/js2/js2.cpp @@ -182,7 +182,9 @@ static JSValue loadxml(Context *cx, const JSValues &argv) JSString& fileName = *val.string; std::string str(fileName.length(), char()); std::transform(fileName.begin(), fileName.end(), str.begin(), narrow); - cx->loadClass(str.c_str()); + ICodeModule *icm = cx->loadClass(str.c_str()); + if (icm) + result = JSValue(new JSFunction(icm)); } } } diff --git a/js2/src/exception.cpp b/js2/src/exception.cpp index 3a00ec1b554..7c5a7d08c01 100644 --- a/js2/src/exception.cpp +++ b/js2/src/exception.cpp @@ -41,8 +41,8 @@ namespace JavaScript { void JSException::toString8 (string8 &rval) { - rval = *exception_types[mType] + " Exception: " + - *exception_msgs[mID]; + rval = string8(exception_types[mType]) + " Exception: " + + string8(exception_msgs[mID]); if (mSource.size() != 0) rval += " in " + mSource; } diff --git a/js2/src/exception.h b/js2/src/exception.h index 21fd8a989fb..4fed93e905c 100644 --- a/js2/src/exception.h +++ b/js2/src/exception.h @@ -70,7 +70,7 @@ namespace JavaScript { class JSException { public: JSException (ExceptionID ID, string8_citer pos = 0, - string8 source = 0, ExceptionType type = etUnknown) + string8 source = "", ExceptionType type = etUnknown) : mID(ID), mType(type), mPos(pos), mSource(source) {} ExceptionID mID; ExceptionType mType; @@ -100,7 +100,7 @@ namespace JavaScript { class JSParseException : public JSException { public: JSParseException (ExceptionID ID, string8_citer pos = 0, - string8 source = 0) : + string8 source = "") : JSException(ID, pos, source, etParser) {} /* private: diff --git a/js2/src/icodeasm.cpp b/js2/src/icodeasm.cpp index bc37d00d318..7b16cc0f6ce 100644 --- a/js2/src/icodeasm.cpp +++ b/js2/src/icodeasm.cpp @@ -108,6 +108,7 @@ namespace ICodeASM { throw JSParseException (eidExpectArgList); tl = seekTokenStart (tl.begin + 1, end); + StringFormatter s_fmt; while (tl.estimate == teString || tl.estimate == teAlpha) { string *argName = 0; @@ -143,6 +144,15 @@ namespace ICodeASM { sap = &(mCx->getWorld().identifiers[argName->c_str()]); delete argName; } + else { + /* if an argument name was not specified, use the position + * to build a default name. + */ + s_fmt << (uint32)al->size(); + sap = &(mCx->getWorld().identifiers[s_fmt.getString()]); + s_fmt.clear(); + + } VM::Argument arg = VM::Argument (tr, sap); al->push_back(arg); @@ -176,7 +186,7 @@ namespace ICodeASM { for (int i = 0; keyword_exprNodeKinds[i] != 0; ++i) if (cmp_nocase (*str, keyword_exprNodeKinds[i], keyword_exprNodeKinds[i] + - strlen (keyword_exprNodeKinds[i]) + 1) == 0) { + strlen (keyword_exprNodeKinds[i])) == 0) { *rval = exprNodeOps[i]; delete str; return end; @@ -220,11 +230,28 @@ namespace ICodeASM { } string8_citer - ICodeParser::parseJSClassOperand (string8_citer /*begin*/, - string8_citer end, string ** /*rval*/) + ICodeParser::parseJSClassOperand (string8_citer begin, + string8_citer end, JSTypes::JSType **rval) { - NOT_REACHED ("JSClasses are hard, lets go shopping."); + TokenLocation tl = seekTokenStart (begin, end); + + if (tl.estimate != teString) + throw JSParseException (eidExpectString); + + string8 *str; + end = lexString8 (tl.begin, end, &str); + StringAtom &typename_atom = mCx->getWorld().identifiers[str->c_str()]; + delete str; + JSTypes::JSValue jsv = + mCx->getGlobalObject()->getVariable(typename_atom); + if (jsv.isType()) + *rval = jsv.type; + else + *rval = &(JSTypes::Any_Type); + return end; +// NOT_REACHED ("JSClasses are hard, lets go shopping."); +// return end; } string8_citer @@ -287,7 +314,7 @@ namespace ICodeASM { begin = lexAlpha (tl.begin, end, &str); if (cmp_nocase(*str, keyword_offset, keyword_offset + - strlen(keyword_offset) + 1) == 0) { + strlen(keyword_offset)) == 0) { delete str; /* got the "Offset" keyword, treat next thing as a jump offset * expressed as "Offset +/-N" */ @@ -391,7 +418,7 @@ namespace ICodeASM { CASE_TYPE(Bool, bool, static_cast); CASE_TYPE(Double, double, static_cast); CASE_TYPE(ICodeModule, string *, reinterpret_cast); - CASE_TYPE(JSClass, string *, reinterpret_cast); + CASE_TYPE(JSClass, JSTypes::JSType *, reinterpret_cast); CASE_TYPE(JSString, JSTypes::JSString *, reinterpret_cast); CASE_TYPE(JSFunction, string *, reinterpret_cast); CASE_TYPE(JSType, JSTypes::JSType *, reinterpret_cast); @@ -491,7 +518,7 @@ namespace ICodeASM { for (uint i = 0; i < icodemap_size; ++i) if (cmp_nocase(icode_str, &icodemap[i].name[0], &icodemap[i].name[0] + - strlen(icodemap[i].name) + 1) == 0) + strlen(icodemap[i].name)) == 0) /* if match found, parse it's operands */ return parseInstruction (i, firstTokenEnd, end); /* otherwise, choke on it */ diff --git a/js2/src/icodeasm.h b/js2/src/icodeasm.h index 7dfd0beac49..2c5f06b7d15 100644 --- a/js2/src/icodeasm.h +++ b/js2/src/icodeasm.h @@ -121,7 +121,7 @@ namespace ICodeASM { string8 **rval); string8_citer parseJSClassOperand (string8_citer begin, string8_citer end, - string8 **rval); + JSTypes::JSType **rval); string8_citer parseJSStringOperand (string8_citer begin, string8_citer end, JSTypes::JSString **rval); diff --git a/js2/src/icodegenerator.cpp b/js2/src/icodegenerator.cpp index 846d3aad193..ed93bd247ea 100644 --- a/js2/src/icodegenerator.cpp +++ b/js2/src/icodegenerator.cpp @@ -39,6 +39,7 @@ #include "icodegenerator.h" #include "interpreter.h" #include "xmlparser.h" +#include "exception.h" #include "icodeasm.h" #include @@ -2552,8 +2553,10 @@ Formatter& operator<<(Formatter &f, string &s) } -void ICodeGenerator::readICode(const char *fileName) +ICodeModule *ICodeGenerator::readICode(const char *fileName) { + ICodeModule *result = NULL; + XMLParser xp(fileName); XMLNode *top = xp.parseDocument(); stdOut << *top; @@ -2582,12 +2585,13 @@ void ICodeGenerator::readICode(const char *fileName) mContext->getGlobalObject()->defineVariable(className, &Type_Type, JSValue(thisClass)); - bool hasDefaultConstructor = false; +// bool hasDefaultConstructor = false; XMLNodeList &elements = node->children(); for (XMLNodeList::const_iterator j = elements.begin(); j != elements.end(); j++) { XMLNode *element = *j; + bool isConstructor = (element->name().compare(widenCString("constructor")) == 0); - if (element->name().compare(widenCString("method")) == 0) { + if (isConstructor || (element->name().compare(widenCString("method")) == 0)) { String methodName, resultTypeName; element->getValue(widenCString("name"), methodName); element->getValue(widenCString("type"), resultTypeName); @@ -2631,7 +2635,12 @@ void ICodeGenerator::readICode(const char *fileName) NULL, /* InstructionMap *instructionMap */ resultType, NotABanana); /* exception register */ - thisClass->defineMethod(methodName, new JSFunction(icm)); + if (isConstructor) { + thisClass->defineConstructor(methodName); + scg.setStatic(thisClass, mContext->getWorld().identifiers[methodName], scg.newFunction(icm)); + } + else + thisClass->defineMethod(methodName, new JSFunction(icm)); } } else { @@ -2651,7 +2660,7 @@ void ICodeGenerator::readICode(const char *fileName) } } scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete(&Void_Type))); - +/* if (!hasDefaultConstructor) { TypedRegister thisValue = TypedRegister(0, thisClass); ArgumentList *args = new ArgumentList(0); @@ -2665,6 +2674,7 @@ void ICodeGenerator::readICode(const char *fileName) thisClass->defineConstructor(className); scg.setStatic(thisClass, mContext->getWorld().identifiers[className], scg.newFunction(icg.complete(&Void_Type))); } +*/ thisClass->complete(); if (scg.getICode()->size()) { @@ -2676,7 +2686,24 @@ void ICodeGenerator::readICode(const char *fileName) } else { if (node->name().compare(widenCString("script")) == 0) { - // build an icode module and execute it + String &body = node->body(); + if (body.length()) { + std::string str(body.length(), char()); + std::transform(body.begin(), body.end(), str.begin(), narrow); + ICodeParser icp(mContext); + + stdOut << "(script) Calling ICodeParser with :\n" << str << "\n"; + + icp.parseSourceFromString(str); + + result = new ICodeModule(icp.mInstructions, + NULL, /* VariableList *variables */ + NULL, /* ParameterList *parameters */ + icp.mMaxRegister, + NULL, /* InstructionMap *instructionMap */ + &Void_Type, + NotABanana); /* exception register */ + } } else { if (node->name().compare(widenCString("instance")) == 0) { @@ -2691,7 +2718,7 @@ void ICodeGenerator::readICode(const char *fileName) } } - + return result; } diff --git a/js2/src/icodegenerator.h b/js2/src/icodegenerator.h index 46be19604a1..a9a21cc9b0a 100644 --- a/js2/src/icodegenerator.h +++ b/js2/src/icodegenerator.h @@ -297,7 +297,7 @@ namespace ICG { } ICodeModule *complete(JSType *resultType); - void readICode(const char *fileName); + ICodeModule *readICode(const char *fileName); JSType *extractType(ExprNode *t); JSType *getParameterType(FunctionDefinition &function, int index); diff --git a/js2/src/interpreter.cpp b/js2/src/interpreter.cpp index 886c3780174..43b5b3af05a 100644 --- a/js2/src/interpreter.cpp +++ b/js2/src/interpreter.cpp @@ -162,10 +162,10 @@ ICodeModule* Context::genCode(StmtNode *p, const String &fileName) return icm; } -void Context::loadClass(const char *fileName) +ICodeModule* Context::loadClass(const char *fileName) { ICodeGenerator icg(this); - icg.readICode(fileName); // loads it into the global object + return icg.readICode(fileName); // loads it into the global object } JSValues& Context::getRegisters() { return mActivation->mRegisters; } @@ -604,96 +604,99 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) else { ICodeModule *icm = target->getICode(); ArgumentList *args = op3(call); + ArgumentList *callArgs = NULL; - // if all the parameters are positional -// if (icm->itsParameters->mPositionalCount == icm->itsParameters->size()) + if (icm->itsParameters) { - // the parameter count includes 'this' and any named rest parameter - // - 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 all the parameters are positional + // if (icm->itsParameters->mPositionalCount == icm->itsParameters->size()) - // don't want to count the rest parameter while processing the others - if (icm->itsParameters->mRestParameter != ParameterList::NoRestParameter) pCount--; + // the parameter count includes 'this' and any named rest parameter + // + 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. + callArgs = new ArgumentList(pCount, Argument(TypedRegister(NotARegister, &Null_Type), NULL)); + + // don't want to count the rest parameter while processing the others + if (icm->itsParameters->mRestParameter != ParameterList::NoRestParameter) pCount--; - uint32 i; - JSArray *restArg = NULL; + uint32 i; + JSArray *restArg = NULL; - // first match all named arguments with their intended target locations - for (i = 0; i < args->size(); i++) { + // first match all named arguments with their intended target locations + for (i = 0; i < args->size(); i++) { - const StringAtom *argName = (*args)[i].second; + 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->data(); - const char16 *end; - double d = stringToDouble(c, c + argName->size(), end); - int index = -1; + 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->data(); + const char16 *end; + double d = stringToDouble(c, c + argName->size(), end); + int index = -1; - 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 { // shift the index value down by the number of positional parameters - index = (int)d; - index -= icm->itsParameters->mPositionalCount; - } + 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 { // shift the index value down by the number of positional parameters + index = (int)d; + index -= icm->itsParameters->mPositionalCount; + } - TypedRegister argument = (*args)[i].first; // this is the argument whose name didn't match + 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(); - if (index == -1) - restArg->setProperty(*argName, (*registers)[argument.first]); - else - (*restArg)[uint32(index)] = (*registers)[argument.first]; - (*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 { - if (index == -1) - restArg->setProperty(*argName, (*registers)[argument.first]); - else - (*restArg)[uint32(index)] = (*registers)[argument.first]; + 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(); + if (index == -1) + restArg->setProperty(*argName, (*registers)[argument.first]); + else + (*restArg)[uint32(index)] = (*registers)[argument.first]; + (*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 { + if (index == -1) + restArg->setProperty(*argName, (*registers)[argument.first]); + else + (*restArg)[uint32(index)] = (*registers)[argument.first]; + } } + // else just throw it away } - // else just throw it away + } + else { + 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]; } } - else { - 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]; + + + // 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"); + } } } - - - // 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"); - } - } - mLinkage = new Linkage(mLinkage, ++mPC, mActivation, mGlobal, op1(call), mICode, mCurrentClosure); mICode = icm; mActivation = new Activation(mICode->itsMaxRegister, mActivation, target->getThis(), callArgs); diff --git a/js2/src/interpreter.h b/js2/src/interpreter.h index 7d3cf53dafb..597ac35355e 100644 --- a/js2/src/interpreter.h +++ b/js2/src/interpreter.h @@ -81,7 +81,7 @@ namespace Interpreter { ICodeModule* genCode(StmtNode *p, const String &fileName); JSValue readEvalFile(FILE* in, const String& fileName); - void loadClass(const char *fileName); + ICodeModule* loadClass(const char *fileName); const JSValue findBinaryOverride(JSValue &operand1, JSValue &operand2, ExprNode::Kind op); @@ -142,14 +142,16 @@ namespace Interpreter { // copy caller's parameter list to initial registers. JSValues::iterator dest = mRegisters.begin(); *dest++ = thisArg; - const JSValues& params = caller->mRegisters; - for (ArgumentList::const_iterator src = list->begin(), - end = list->end(); src != end; ++src, ++dest) { - Register r = (*src).first.first; - if (r != NotARegister) - *dest = params[r]; - else - *dest = JSValue(JSValue::uninitialized_tag); + if (list) { + const JSValues& params = caller->mRegisters; + for (ArgumentList::const_iterator src = list->begin(), + end = list->end(); src != end; ++src, ++dest) { + Register r = (*src).first.first; + if (r != NotARegister) + *dest = params[r]; + else + *dest = JSValue(JSValue::uninitialized_tag); + } } } diff --git a/js2/tests/cpp/js2_shell.cpp b/js2/tests/cpp/js2_shell.cpp index 59210fa9340..921c43d8fbf 100644 --- a/js2/tests/cpp/js2_shell.cpp +++ b/js2/tests/cpp/js2_shell.cpp @@ -182,7 +182,9 @@ static JSValue loadxml(Context *cx, const JSValues &argv) JSString& fileName = *val.string; std::string str(fileName.length(), char()); std::transform(fileName.begin(), fileName.end(), str.begin(), narrow); - cx->loadClass(str.c_str()); + ICodeModule *icm = cx->loadClass(str.c_str()); + if (icm) + result = JSValue(new JSFunction(icm)); } } }