diff --git a/js/js2/dependencies b/js/js2/dependencies index 438e991c4eb..98e4302814a 100644 --- a/js/js2/dependencies +++ b/js/js2/dependencies @@ -1,3 +1,6 @@ +debugger.o: debugger.cpp world.h utilities.h systemtypes.h hash.h \ + parser.h interpreter.h jstypes.h gc_allocator.h vmtypes.h numerics.h \ + icodegenerator.h gc_allocator.o: gc_allocator.cpp gc_allocator.h gc_container.h hash.o: hash.cpp hash.h utilities.h systemtypes.h icodegenerator.o: icodegenerator.cpp numerics.h utilities.h \ diff --git a/js/js2/icodegenerator.cpp b/js/js2/icodegenerator.cpp index bb6e0058f73..af130549a2f 100644 --- a/js/js2/icodegenerator.cpp +++ b/js/js2/icodegenerator.cpp @@ -61,8 +61,8 @@ namespace ICG { ASSERT(stitcher.empty()); for (LabelList::iterator i = labels.begin(); i != labels.end(); i++) { - ASSERT((*i)->base == iCode); - ASSERT((*i)->offset <= iCode->size()); + ASSERT((*i)->mBase == iCode); + ASSERT((*i)->mOffset <= iCode->size()); } #endif @@ -217,14 +217,14 @@ namespace ICG { void ICodeGenerator::setLabel(Label *l) { - l->base = iCode; - l->offset = static_cast(iCode->size()); + l->mBase = iCode; + l->mOffset = static_cast(iCode->size()); } void ICodeGenerator::setLabel(InstructionStream *stream, Label *l) { - l->base = stream; - l->offset = static_cast(stream->size()); + l->mBase = stream; + l->mOffset = static_cast(stream->size()); } /********************************************************************/ @@ -236,9 +236,9 @@ namespace ICG { // themselves?) in order to avoid running this loop unnecessarily. for (LabelList::iterator i = labels.begin(); i != labels.end(); i++) { - if ((*i)->base == sideStream) { - (*i)->base = iCode; - (*i)->offset += iCode->size(); + if ((*i)->mBase == sideStream) { + (*i)->mBase = iCode; + (*i)->mOffset += iCode->size(); } } @@ -611,7 +611,7 @@ namespace ICG { for (LabelList::iterator k = labels.begin(); k != labels.end(); k++) - if ((ptrdiff_t)(*k)->offset == (i - iCode->begin())) { + if ((ptrdiff_t)(*k)->mOffset == (i - iCode->begin())) { f << "#" << (uint32)(i - iCode->begin()) << "\t"; isLabel = true; break; diff --git a/js/js2/icodegenerator.h b/js/js2/icodegenerator.h index b0036b2a070..c4e977952b3 100644 --- a/js/js2/icodegenerator.h +++ b/js/js2/icodegenerator.h @@ -173,7 +173,7 @@ namespace ICG { // expression statements void beginStatement(uint32 /*pos*/) { resetTopRegister(); } - void returnStatement() { iCode->push_back(new Return()); } + void returnStatement() { iCode->push_back(new ReturnVoid()); } void returnStatement(Register result) \ { iCode->push_back(new Return(result)); } diff --git a/js/js2/interpreter.cpp b/js/js2/interpreter.cpp index 752425788c9..4f202345004 100644 --- a/js/js2/interpreter.cpp +++ b/js/js2/interpreter.cpp @@ -1,31 +1,41 @@ -// -*- 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) 2000 Netscape Communications Corporation. All -// Rights Reserved. +/* -*- 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 "interpreter.h" #include "world.h" #include "vmtypes.h" namespace JavaScript { -namespace Interpreter { - - using namespace JSTypes; - // operand access macros. #define op1(i) (i->o1()) #define op2(i) (i->o2()) @@ -37,23 +47,8 @@ namespace Interpreter { #define src2(i) op3(i) #define ofs(i) (i->getOffset()) - static JSObject globals; - - JSValue& defineGlobalProperty(const String& name, const JSValue& value) - { - return (globals[name] = value); - } - - // FIXME: need to copy the ICodeModule's instruction stream. - - JSValue& defineFunction(const String& name, ICodeModule* iCode) - { - JSValue value; - value.function = new JSFunction(iCode); - return defineGlobalProperty(name, value); - } - - JSValue interpret(ICodeModule* iCode, const JSValues& args) + JSValue + Context::interpret(ICodeModule* iCode, const JSValues& args) { // stack of JSFrames. // XXX is a linked list of activation's sufficient? @@ -81,10 +76,26 @@ namespace Interpreter { begin_pc = pc = target->its_iCode->begin(); } continue; + + case RETURN_VOID: + { + JSValue result(NotARegister); + if (frames.empty()) + return result; + JSFrame *frame = frames.top(); + frames.pop(); + activation = frame->itsActivation; + registers = &activation->mRegisters; + (*registers)[frame->itsResult] = result; + pc = frame->itsReturnPC; + begin_pc = frame->itsBasePC; + } + continue; + case RETURN: { Return* ret = static_cast(instruction); - JSValue result; + JSValue result(NotARegister); if (op1(ret) != NotARegister) result = (*registers)[op1(ret)]; if (frames.empty()) @@ -107,13 +118,13 @@ namespace Interpreter { case LOAD_NAME: { LoadName* ln = static_cast(instruction); - (*registers)[dst(ln)] = globals[*src1(ln)]; + (*registers)[dst(ln)] = mGlobal[*src1(ln)]; } break; case SAVE_NAME: { SaveName* sn = static_cast(instruction); - globals[*dst(sn)] = (*registers)[src1(sn)]; + mGlobal[*dst(sn)] = (*registers)[src1(sn)]; } break; case NEW_OBJECT: @@ -295,5 +306,4 @@ namespace Interpreter { } /* interpret */ -} /* namespace Interpreter */ } /* namespace JavaScript */ diff --git a/js/js2/interpreter.h b/js/js2/interpreter.h index deb3c95e6e5..41df1c40460 100644 --- a/js/js2/interpreter.h +++ b/js/js2/interpreter.h @@ -23,20 +23,36 @@ #include "utilities.h" #include "jstypes.h" #include "icodegenerator.h" +#include "gc_allocator.h" namespace JavaScript { -namespace Interpreter { - using namespace ICG; using namespace JSTypes; - - JSValue interpret(ICodeModule* iCode, const JSValues& args); - - JSValue& defineGlobalProperty(const String& name, - const JSValue& value); - JSValue& defineFunction(const String& name, ICodeModule* iCode); -} /* namespace Interpreter */ + class Context : public gc_base { + public: + explicit Context(World& /*world */, JSObject& aGlobal) : + mGlobal(aGlobal) {}; + + JSValue interpret(ICodeModule* iCode, const JSValues& args); + + JSObject& setGlobalObject(JSObject& aGlobal) + { + JSObject &t = mGlobal; + mGlobal = aGlobal; + return t; + } + + JSObject& getGlobalObject() + { + return mGlobal; + } + + private: + /* World mWorld; */ + JSObject& mGlobal; + + }; /* class Interpreter */ } /* namespace JavaScript */ #endif /* interpreter_h */ diff --git a/js/js2/js2.cpp b/js/js2/js2.cpp index 77bfb4d3f06..5a99d5d944f 100644 --- a/js/js2/js2.cpp +++ b/js/js2/js2.cpp @@ -55,489 +55,336 @@ static void initConsole(StringPtr consoleName, #endif namespace JavaScript { - namespace Shell { - using namespace Interpreter; - - // Interactively read a line from the input stream in and put it into - // s Return false if reached the end of input before reading anything. - static bool promptLine(LineReader &inReader, string &s, - const char *prompt) - { - if (prompt) { - stdOut << prompt; +namespace Shell { + + // Interactively read a line from the input stream in and put it into + // s Return false if reached the end of input before reading anything. + static bool promptLine(LineReader &inReader, string &s, + const char *prompt) + { + if (prompt) { + stdOut << prompt; #ifdef XP_MAC_MPW - // Print a CR after the prompt because MPW grabs the entire - // line when entering an interactive command. - stdOut << '\n'; -#endif - } - return inReader.readLine(s) != 0; - } - - - const bool showTokens = true; - - static void readEvalPrint(FILE *in, World &world) - { - String buffer; - string line; - String sourceLocation = widenCString("console"); - LineReader inReader(in); - - while (promptLine(inReader, line, buffer.empty() ? "js> " : 0)) { - appendChars(buffer, line.data(), line.size()); - try { - Arena a; - Parser p(world, a, buffer, sourceLocation); - - if (showTokens) { - Lexer &l = p.lexer; - while (true) { - const Token &t = l.get(true); - if (t.hasKind(Token::end)) - break; - stdOut << ' '; - t.print(stdOut, true); - } - } else { - ExprNode *parseTree = p.parsePostfixExpression(); - } - clear(buffer); - stdOut << '\n'; - } catch (Exception &e) { - /* If we got a syntax error on the end of input, - * then wait for a continuation - * of input rather than printing the error message. */ - if (!(e.hasKind(Exception::syntaxError) && - e.lineNum && e.pos == buffer.size() && - e.sourceFile == sourceLocation)) { - stdOut << '\n' << e.fullMessage(); - clear(buffer); - } - } - } + // Print a CR after the prompt because MPW grabs the entire + // line when entering an interactive command. stdOut << '\n'; -#if 0 - do { - bufp = buffer; - *bufp = '\0'; - - /* - * Accumulate lines until we get a 'compilable unit' - one that either - * generates an error (before running out of source) or that compiles - * cleanly. This should be whenever we get a complete statement that - * coincides with the end of a line. - */ - startline = lineNum; - do { - if (!GetLine(cx, bufp, file, - startline == lineNum ? "js> " : "")) { - hitEOF = JS_TRUE; - break; - } - bufp += strlen(bufp); - lineNum++; - } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, - strlen(buffer))); - } while (!hitEOF); - fprintf(stdout, "\n"); - return; #endif } - - -#if 0 - static int ProcessInputFile(JSContext *cx, JSObject *obj, - char *filename) - { - JSBool ok, hitEOF; - JSScript *script; - jsval result; - JSString *str; - char buffer[4096]; - char *bufp; - int startline; - FILE *file; + return inReader.readLine(s) != 0; + } + + + const bool showTokens = true; + + static void readEvalPrint(FILE *in, World &world) + { + String buffer; + string line; + String sourceLocation = widenCString("console"); + LineReader inReader(in); - if (filename && strcmp(filename, "-")) { - file = fopen(filename, "r"); - if (!file) { - fprintf(stderr, "Can't open \"%s\": %s", filename, - strerror(errno)); - return 1; - } - } else { - file = stdin; - } - - if (!isatty(fileno(file))) { - /* - * It's not interactive - just execute it. - * - * Support the UNIX #! shell hack; gobble the first line - * if it starts with '#'. - */ - int ch = fgetc(file); - if (ch == '#') { - while((ch = fgetc(file)) != EOF) - if (ch == '\n' || ch == '\r') + while (promptLine(inReader, line, buffer.empty() ? "js> " : 0)) { + appendChars(buffer, line.data(), line.size()); + try { + Arena a; + Parser p(world, a, buffer, sourceLocation); + + if (showTokens) { + Lexer &l = p.lexer; + while (true) { + const Token &t = l.get(true); + if (t.hasKind(Token::end)) break; - } else - ungetc(ch, file); - script = JS_CompileFileHandle(cx, obj, filename, file); - if (script) - (void)JS_ExecuteScript(cx, obj, script, &result); - return; - } - - /* It's an interactive filehandle; - * drop into read-eval-print loop. */ - int32 lineNum = 1; - hitEOF = JS_FALSE; - do { - bufp = buffer; - *bufp = '\0'; - - /* - * Accumulate lines until we get a 'compilable unit' - - * one that either generates an error (before running out of - * source) or that compiles cleanly. This should be whenever - * we get a complete statement that coincides with the end of a - * line. - */ - startline = lineNum; - do { - if (!GetLine(cx, bufp, file, - startline == lineNum ? "js> " : "")) { - hitEOF = JS_TRUE; - break; - } - bufp += strlen(bufp); - lineNum++; - } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, - strlen(buffer))); - } while (!hitEOF); - fprintf(stdout, "\n"); - return; - } - - - static int - usage(void) - { - stdErr << "usage: js [-s] [-w] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n"; - return 2; - } - - - static int - ProcessArgs(char **argv, int argc) - { - int i; - char *filename = NULL; - jsint length; - jsval *vector; - jsval *p; - JSObject *argsObj; - JSBool isInteractive = JS_TRUE; - - for (i=0; i < argc; i++) { - if (argv[i][0] == '-') { - switch (argv[i][1]) { - case 'f': - if (i+1 == argc) { - return usage(); - } - filename = argv[i+1]; - /* "-f -" means read from stdin */ - if (filename[0] == '-' && filename[1] == '\0') - filename = NULL; - ProcessInputFile(filename); - filename = NULL; - /* XXX: js -f foo.js should interpret foo.js and - * then drop into interactive mode, but that - * breaks the test harness. - */ - isInteractive = JS_FALSE; - i++; - break; - - default: - return usage(); + stdOut << ' '; + t.print(stdOut, true); } } else { - filename = argv[i++]; - isInteractive = JS_FALSE; - break; + /*ExprNode *parseTree = */ p.parsePostfixExpression(); + } + clear(buffer); + stdOut << '\n'; + } catch (Exception &e) { + /* If we got a syntax error on the end of input, + * then wait for a continuation + * of input rather than printing the error message. */ + if (!(e.hasKind(Exception::syntaxError) && + e.lineNum && e.pos == buffer.size() && + e.sourceFile == sourceLocation)) { + stdOut << '\n' << e.fullMessage(); + clear(buffer); } } - - if (filename || isInteractive) - ProcessInputFile(filename); - return gExitCode; } -#endif + stdOut << '\n'; + } #include "icodegenerator.h" - static void testICG(World &world) - { - // - // testing ICG - // - uint32 pos = 0; - ICodeGenerator icg; + static void testICG(World &world) + { + // + // testing ICG + // + uint32 pos = 0; + ICodeGenerator icg; - // var i,j; - // i is bound to var #0, j to var #1 - Register r_i = icg.allocateVariable(world.identifiers[widenCString("i")]); - Register r_j = icg.allocateVariable(world.identifiers[widenCString("j")]); + // var i,j; + // i is bound to var #0, j to var #1 + Register r_i = icg.allocateVariable(world.identifiers[widenCString("i")]); + Register r_j = icg.allocateVariable(world.identifiers[widenCString("j")]); - // i = j + 2; - icg.beginStatement(pos); - Register r1 = icg.loadImmediate(2.0); - icg.move(r_i, icg.op(ADD, r_i, r_j)); + // i = j + 2; + icg.beginStatement(pos); + Register r1 = icg.loadImmediate(2.0); + icg.move(r_i, icg.op(ADD, r_i, r_j)); - // j = a.b - icg.beginStatement(pos); - r1 = icg.loadName(world.identifiers[widenCString("a")]); - r1 = icg.getProperty(r1, world.identifiers[widenCString("b")]); - icg.move(r_j, r1); + // j = a.b + icg.beginStatement(pos); + r1 = icg.loadName(world.identifiers[widenCString("a")]); + r1 = icg.getProperty(r1, world.identifiers[widenCString("b")]); + icg.move(r_j, r1); - // while (i) i = i + j; - icg.beginWhileStatement(pos); - icg.endWhileExpression(r_i); - icg.move(r_i, icg.op(ADD, r_i, r_j)); - icg.endWhileStatement(); + // while (i) i = i + j; + icg.beginWhileStatement(pos); + icg.endWhileExpression(r_i); + icg.move(r_i, icg.op(ADD, r_i, r_j)); + icg.endWhileStatement(); - // if (i) if (j) i = 3; else j = 4; - icg.beginIfStatement(pos, r_i); - icg.beginIfStatement(pos, r_j); - icg.move(r_i, icg.loadImmediate(3)); - icg.beginElseStatement(true); - icg.move(r_j, icg.loadImmediate(4)); - icg.endIfStatement(); - icg.beginElseStatement(false); - icg.endIfStatement(); + // if (i) if (j) i = 3; else j = 4; + icg.beginIfStatement(pos, r_i); + icg.beginIfStatement(pos, r_j); + icg.move(r_i, icg.loadImmediate(3)); + icg.beginElseStatement(true); + icg.move(r_j, icg.loadImmediate(4)); + icg.endIfStatement(); + icg.beginElseStatement(false); + icg.endIfStatement(); - // switch (i) { case 3: case 4: j = 4; break; case 5: j = 5; break; default : j = 6; } - icg.beginSwitchStatement(pos, r_i); - // case 3, note empty case statement (?necessary???) - icg.endCaseCondition(icg.loadImmediate(3)); - icg.beginCaseStatement(); - icg.endCaseStatement(); - // case 4 - icg.endCaseCondition(icg.loadImmediate(4)); - icg.beginCaseStatement(); - icg.beginStatement(pos); - icg.move(r_j, icg.loadImmediate(4)); - icg.breakStatement(); - icg.endCaseStatement(); - // case 5 - icg.endCaseCondition(icg.loadImmediate(5)); - icg.beginCaseStatement(); - icg.beginStatement(pos); - icg.move(r_j, icg.loadImmediate(5)); - icg.breakStatement(); - icg.endCaseStatement(); - // default - icg.beginDefaultStatement(); - icg.beginStatement(pos); - icg.move(r_j, icg.loadImmediate(6)); - icg.endDefaultStatement(); - icg.endSwitchStatement(); + // switch (i) { case 3: case 4: j = 4; break; case 5: j = 5; break; default : j = 6; } + icg.beginSwitchStatement(pos, r_i); + // case 3, note empty case statement (?necessary???) + icg.endCaseCondition(icg.loadImmediate(3)); + icg.beginCaseStatement(); + icg.endCaseStatement(); + // case 4 + icg.endCaseCondition(icg.loadImmediate(4)); + icg.beginCaseStatement(); + icg.beginStatement(pos); + icg.move(r_j, icg.loadImmediate(4)); + icg.breakStatement(); + icg.endCaseStatement(); + // case 5 + icg.endCaseCondition(icg.loadImmediate(5)); + icg.beginCaseStatement(); + icg.beginStatement(pos); + icg.move(r_j, icg.loadImmediate(5)); + icg.breakStatement(); + icg.endCaseStatement(); + // default + icg.beginDefaultStatement(); + icg.beginStatement(pos); + icg.move(r_j, icg.loadImmediate(6)); + icg.endDefaultStatement(); + icg.endSwitchStatement(); - // for ( ; i; i = i + 1 ) j = 99; - icg.beginForStatement(pos); - icg.forCondition(r_i); - icg.move(r_i, icg.op(ADD, r_i, icg.loadImmediate(1))); - icg.forIncrement(); - icg.move(r_j, icg.loadImmediate(99)); - icg.endForStatement(); + // for ( ; i; i = i + 1 ) j = 99; + icg.beginForStatement(pos); + icg.forCondition(r_i); + icg.move(r_i, icg.op(ADD, r_i, icg.loadImmediate(1))); + icg.forIncrement(); + icg.move(r_j, icg.loadImmediate(99)); + icg.endForStatement(); - ICodeModule *icm = icg.complete(); + ICodeModule *icm = icg.complete(); - stdOut << icg; + stdOut << icg; - delete icm; -} - - static float64 testFunctionCall(World &world, float64 n) - { - uint32 position = 0; - StringAtom& global = world.identifiers[widenCString("global")]; - StringAtom& sum = world.identifiers[widenCString("sum")]; - - ICodeGenerator fun; - // function sum(n) { if (n > 1) return 1 + sum(n - 1); else return 1; } - // n is bound to var #0. - Register r_n = fun.allocateVariable(world.identifiers[widenCString("n")]); - fun.beginStatement(position); - Register r1 = fun.op(COMPARE_GT, r_n, fun.loadImmediate(1.0)); - fun.beginIfStatement(position, r1); - fun.beginStatement(position); - r1 = fun.op(SUBTRACT, r_n, fun.loadImmediate(1.0)); - RegisterList args(1); - args[0] = r1; - r1 = fun.call(fun.loadName(sum), args); - fun.returnStatement(fun.op(ADD, fun.loadImmediate(1.0), r1)); - fun.beginElseStatement(true); - fun.beginStatement(position); - fun.returnStatement(fun.loadImmediate(1.0)); - fun.endIfStatement(); - - ICodeModule *funCode = fun.complete(); - stdOut << fun; - - // now a script : - // return sum(n); - ICodeGenerator script; - script.beginStatement(position); - r1 = script.loadName(sum); - RegisterList args_2(1); - args_2[0] = script.loadImmediate(n); - script.returnStatement(script.call(r1, args_2)); - - stdOut << script; - - - // preset the global property "sum" to contain the above function - defineFunction(sum, funCode); - - JSValue result = interpret(script.complete(), JSValues()); - stdOut << "sum(" << n << ") = " << result.f64 << "\n"; - - return result.f64; - } - - static float64 testFactorial(World &world, float64 n) - { - // generate code for factorial, and interpret it. - uint32 position = 0; - ICodeGenerator icg; - - // fact(n) { - // var result = 1; - Register r_n = icg.allocateVariable(world.identifiers[widenCString("n")]); - Register r_result = icg.allocateVariable(world.identifiers[widenCString("result")]); - - icg.beginStatement(position); - icg.move(r_result, icg.loadImmediate(1.0)); - - // while (n > 1) { - // result = result * n; - // n = n - 1; - // } - { - icg.beginWhileStatement(position); - Register r1 = icg.loadImmediate(1.0); - Register r2 = icg.op(COMPARE_GT, r_n, r1); - icg.endWhileExpression(r2); - r2 = icg.op(MULTIPLY, r_result, r_n); - icg.move(r_result, r2); - icg.beginStatement(position); - r1 = icg.loadImmediate(1.0); - r2 = icg.op(SUBTRACT, r_n, r1); - icg.move(r_n, r2); - icg.endWhileStatement(); - } - - // return result; - icg.returnStatement(r_result); - ICodeModule *icm = icg.complete(); - stdOut << icg; - - // preset the global property "fact" to contain the above function - StringAtom& fact = world.identifiers[widenCString("fact")]; - defineFunction(fact, icm); - - // now a script : - // return fact(n); - ICodeGenerator script; - script.beginStatement(position); - RegisterList args(1); - args[0] = script.loadImmediate(n); - script.returnStatement(script.call(script.loadName(fact), args)); - stdOut << script; - - // test the iCode interpreter. - JSValue result = interpret(script.complete(), JSValues()); - stdOut << "fact(" << n << ") = " << result.f64 << "\n"; - - delete icm; - - return result.f64; - } - - static float64 testObjects(World &world, int32 n) - { - // create some objects, put some properties, and retrieve them. - uint32 position = 0; - ICodeGenerator initCG; - - // var global = new Object(); - StringAtom& global = world.identifiers[widenCString("global")]; - initCG.beginStatement(position); - initCG.saveName(global, initCG.newObject()); - - // global.counter = 0; - StringAtom& counter = world.identifiers[widenCString("counter")]; - initCG.beginStatement(position); - initCG.setProperty(initCG.loadName(global), counter, initCG.loadImmediate(0.0)); - - // var array = new Array(); - StringAtom& array = world.identifiers[widenCString("array")]; - initCG.beginStatement(position); - initCG.saveName(array, initCG.newArray()); - initCG.returnStatement(); - - ICodeModule* initCode = initCG.complete(); - - stdOut << initCG; - - // function increment() - // { - // var i = global.counter; - // array[i] = i; - // return ++global.counter; - // } - ICodeGenerator incrCG; - - incrCG.beginStatement(position); - Register robject = incrCG.loadName(global); - Register roldvalue = incrCG.getProperty(robject, counter); - Register rarray = incrCG.loadName(array); - incrCG.setElement(rarray, roldvalue, roldvalue); - Register rvalue = incrCG.op(ADD, roldvalue, incrCG.loadImmediate(1.0)); - incrCG.setProperty(robject, counter, rvalue); - incrCG.returnStatement(rvalue); - - ICodeModule* incrCode = incrCG.complete(); - - stdOut << incrCG; - - // run initialization code. - JSValues args; - interpret(initCode, args); - - // call the increment function some number of times. - JSValue result; - while (n-- > 0) - result = interpret(incrCode, args); - - stdOut << "result = " << result.f64 << "\n"; - - delete initCode; - delete incrCode; - - return result.f64; - } + delete icm; } -} + + static float64 testFunctionCall(World &world, float64 n) + { + JSObject glob; + Context cx(world, glob); + uint32 position = 0; + //StringAtom& global = world.identifiers[widenCString("global")]; + StringAtom& sum = world.identifiers[widenCString("sum")]; + + ICodeGenerator fun; + // function sum(n) { if (n > 1) return 1 + sum(n - 1); else return 1; } + // n is bound to var #0. + Register r_n = + fun.allocateVariable(world.identifiers[widenCString("n")]); + fun.beginStatement(position); + Register r1 = fun.op(COMPARE_GT, r_n, fun.loadImmediate(1.0)); + fun.beginIfStatement(position, r1); + fun.beginStatement(position); + r1 = fun.op(SUBTRACT, r_n, fun.loadImmediate(1.0)); + RegisterList args(1); + args[0] = r1; + r1 = fun.call(fun.loadName(sum), args); + fun.returnStatement(fun.op(ADD, fun.loadImmediate(1.0), r1)); + fun.beginElseStatement(true); + fun.beginStatement(position); + fun.returnStatement(fun.loadImmediate(1.0)); + fun.endIfStatement(); + + ICodeModule *funCode = fun.complete(); + stdOut << fun; + + // now a script : + // return sum(n); + ICodeGenerator script; + script.beginStatement(position); + r1 = script.loadName(sum); + RegisterList args_2(1); + args_2[0] = script.loadImmediate(n); + script.returnStatement(script.call(r1, args_2)); + + stdOut << script; + + // preset the global property "sum" to contain the above function + glob.defineFunction(sum, funCode); + + JSValue result = cx.interpret(script.complete(), JSValues()); + stdOut << "sum(" << n << ") = " << result.f64 << "\n"; + + return result.f64; + } + + static float64 testFactorial(World &world, float64 n) + { + JSObject glob; + Context cx(world, glob); + // generate code for factorial, and interpret it. + uint32 position = 0; + ICodeGenerator icg; + + // fact(n) { + // var result = 1; + Register r_n = icg.allocateVariable(world.identifiers[widenCString("n")]); + Register r_result = icg.allocateVariable(world.identifiers[widenCString("result")]); + + icg.beginStatement(position); + icg.move(r_result, icg.loadImmediate(1.0)); + + // while (n > 1) { + // result = result * n; + // n = n - 1; + // } + { + icg.beginWhileStatement(position); + Register r1 = icg.loadImmediate(1.0); + Register r2 = icg.op(COMPARE_GT, r_n, r1); + icg.endWhileExpression(r2); + r2 = icg.op(MULTIPLY, r_result, r_n); + icg.move(r_result, r2); + icg.beginStatement(position); + r1 = icg.loadImmediate(1.0); + r2 = icg.op(SUBTRACT, r_n, r1); + icg.move(r_n, r2); + icg.endWhileStatement(); + } + + // return result; + icg.returnStatement(r_result); + ICodeModule *icm = icg.complete(); + stdOut << icg; + + // preset the global property "fact" to contain the above function + StringAtom& fact = world.identifiers[widenCString("fact")]; + glob.defineFunction(fact, icm); + + // now a script : + // return fact(n); + ICodeGenerator script; + script.beginStatement(position); + RegisterList args(1); + args[0] = script.loadImmediate(n); + script.returnStatement(script.call(script.loadName(fact), args)); + stdOut << script; + + // test the iCode interpreter. + JSValue result = cx.interpret(script.complete(), JSValues()); + stdOut << "fact(" << n << ") = " << result.f64 << "\n"; + + delete icm; + + return result.f64; + } + + static float64 testObjects(World &world, int32 n) + { + JSObject glob; + Context cx(world, glob); + // create some objects, put some properties, and retrieve them. + uint32 position = 0; + ICodeGenerator initCG; + + // var global = new Object(); + StringAtom& global = world.identifiers[widenCString("global")]; + initCG.beginStatement(position); + initCG.saveName(global, initCG.newObject()); + + // global.counter = 0; + StringAtom& counter = world.identifiers[widenCString("counter")]; + initCG.beginStatement(position); + initCG.setProperty(initCG.loadName(global), counter, initCG.loadImmediate(0.0)); + + // var array = new Array(); + StringAtom& array = world.identifiers[widenCString("array")]; + initCG.beginStatement(position); + initCG.saveName(array, initCG.newArray()); + initCG.returnStatement(); + + ICodeModule* initCode = initCG.complete(); + + stdOut << initCG; + + // function increment() + // { + // var i = global.counter; + // array[i] = i; + // return ++global.counter; + // } + ICodeGenerator incrCG; + + incrCG.beginStatement(position); + Register robject = incrCG.loadName(global); + Register roldvalue = incrCG.getProperty(robject, counter); + Register rarray = incrCG.loadName(array); + incrCG.setElement(rarray, roldvalue, roldvalue); + Register rvalue = incrCG.op(ADD, roldvalue, incrCG.loadImmediate(1.0)); + incrCG.setProperty(robject, counter, rvalue); + incrCG.returnStatement(rvalue); + + ICodeModule* incrCode = incrCG.complete(); + + stdOut << incrCG; + + // run initialization code. + JSValues args; + cx.interpret(initCode, args); + + // call the increment function some number of times. + JSValue result; + while (n-- > 0) + result = cx.interpret(incrCode, args); + + stdOut << "result = " << result.f64 << "\n"; + + delete initCode; + delete incrCode; + + return result.f64; + } + +} /* namespace Shell */ +} /* namespace JavaScript */ -int main(int argc, char **argv) +int main(int /* argc */, char /* **argv */) { #if defined(XP_MAC) && !defined(XP_MAC_MPW) initConsole("\pJavaScript Shell", "Welcome to the js2 shell.\n", argc, argv); @@ -546,10 +393,10 @@ int main(int argc, char **argv) #if 1 assert(JavaScript::Shell::testFactorial(world, 5) == 120); assert(JavaScript::Shell::testObjects(world, 5) == 5); -// testICG(world); + // testICG(world); assert(JavaScript::Shell::testFunctionCall(world, 5) == 5); #endif JavaScript::Shell::readEvalPrint(stdin, world); return 0; - // return ProcessArgs(argv + 1, argc - 1); + // return ProcessArgs(argv + 1, argc - 1); } diff --git a/js/js2/jstypes.h b/js/js2/jstypes.h index 6ab2d7de4ff..edca6fb3ce3 100644 --- a/js/js2/jstypes.h +++ b/js/js2/jstypes.h @@ -89,17 +89,18 @@ namespace JSTypes { #endif /** - * GC-scannable array of values. - */ + * GC-scannable array of values. + */ typedef std::vector > JSValues; /** - * Basic behavior of all JS objects, mapping a name to a value. - * This is provided mainly to avoid having an awkward implementation - * of JSObject & JSArray, which must each define its own - * gc_allocator. This is all in flux. - */ + * Basic behavior of all JS objects, mapping a name to a value. + * This is provided mainly to avoid having an awkward implementation + * of JSObject & JSArray, which must each define its own + * gc_allocator. This is all in flux. + */ class JSMap : public gc_base { + protected: std::map, gc_map_allocator> properties; public: @@ -109,16 +110,37 @@ namespace JSTypes { } }; - /** - * Private representation of a JavaScript object. - * This will change over time, so it is treated as an opaque - * type everywhere else but here. - */ - class JSObject : public JSMap {}; + class JSFunction : public JSMap { + ICodeModule* mICode; + public: + JSFunction(ICodeModule* iCode) : mICode(iCode) {} + ICodeModule* getICode() { return mICode; } + }; /** - * Private representation of a JavaScript array. - */ + * Private representation of a JavaScript object. + * This will change over time, so it is treated as an opaque + * type everywhere else but here. + */ + class JSObject : public JSMap { + public: + JSValue& defineProperty(const String& name, JSValue &v) + { + return (properties[name] = v); + } + + // FIXME: need to copy the ICodeModule's instruction stream. + JSValue& defineFunction(const String& name, ICodeModule* iCode) + { + JSValue value; + value.function = new JSFunction(iCode); + return properties[name] = value; + } + }; + + /** + * Private representation of a JavaScript array. + */ class JSArray : public JSMap { JSValues elements; public: @@ -164,16 +186,9 @@ namespace JSTypes { } }; - class JSFunction : public JSMap { - ICodeModule* mICode; - public: - JSFunction(ICodeModule* iCode) : mICode(iCode) {} - ICodeModule* getICode() { return mICode; } - }; - /** - * Represents the current function's invocation state. - */ + * Represents the current function's invocation state. + */ struct JSActivation : public gc_base { JSValues mRegisters; @@ -202,10 +217,10 @@ namespace JSTypes { } }; -/** - * Stores saved state from the *previous* activation, the current activation is - * alive and well in locals of the interpreter loop. - */ + /** + * Stores saved state from the *previous* activation, the current + * activation is alive and well in locals of the interpreter loop. + */ struct JSFrame : public gc_base { JSFrame(InstructionIterator returnPC, InstructionIterator basePC, JSActivation* activation, Register result) diff --git a/js/js2/tools/gencode.pl b/js/js2/tools/gencode.pl index 8b8eb4cf510..6fc7eb95e8c 100644 --- a/js/js2/tools/gencode.pl +++ b/js/js2/tools/gencode.pl @@ -10,6 +10,7 @@ my $opcode_maxlen = 0; my $compare_op = { super => "Compare", + super_has_print => 1, rem => "dest, source", params => [ ("Register", "Register") ] }; @@ -17,6 +18,7 @@ my $compare_op = my $math_op = { super => "Arithmetic", + super_has_print => 1, rem => "dest, source1, source2", params => [ ("Register", "Register", "Register") ] }; @@ -24,6 +26,7 @@ my $math_op = my $cbranch_op = { super => "GenericBranch", + super_has_print => 1, rem => "target label, condition", params => [ ("Label*", "Register") ] }; @@ -41,18 +44,6 @@ $ops{"MOVE"} = rem => "dest, source", params => [ ("Register", "Register") ] }; -$ops{"LOAD_VAR"} = - { - super => "Instruction_2", - rem => "dest, index of frame slot", - params => [ ("Register", "uint32" ) ] - }; -$ops{"SAVE_VAR"} = - { - super => "Instruction_2", - rem => "index of frame slot, source", - params => [ ("uint32", "Register") ] - }; $ops{"LOAD_IMMEDIATE"} = { super => "Instruction_2", @@ -138,8 +129,13 @@ $ops{"BRANCH_GT"} = $cbranch_op; $ops{"RETURN"} = { super => "Instruction_1", - rem => "return value or NotARegister", - params => [ ("Register = NotARegister") ] + rem => "return value", + params => [ ("Register") ] + }; +$ops{"RETURN_VOID"} = + { + super => "Instruction", + rem => "Return without a value" }; $ops{"CALL"} = { @@ -179,7 +175,6 @@ sub collect { my ($dec_list, $call_list, $template_list) = &get_paramlists(@{$c->{"params"}}); my $params = $call_list ? $opname . ", " . $call_list : $opname; - my $printbody = &get_printbody(split (", ", $template_list)); if ($super =~ /Instruction_\d/) { $super .= "<" . $template_list . ">"; @@ -193,14 +188,22 @@ sub collect { $init_tab . $tab . "$cname ($dec_list) :\n" . $init_tab . $tab . $tab . "$super\n" . "$init_tab$tab$tab($params) " . - "{};\n" . - $init_tab . $tab . - "virtual Formatter& print (Formatter& f) {\n" . - $init_tab . $tab . $tab . "f << opcodeNames[$opname];\n" . - $printbody . - $init_tab . $tab . $tab . "return f;\n" . - $init_tab . $tab . "}\n" . - $init_tab . "};\n\n"); + "{};\n"); + if (!$c->{"super_has_print"}) { + my $printbody = &get_printbody(split (", ", $template_list)); + + $class_decs .= ($init_tab . $tab . + "virtual Formatter& print (Formatter& f) {\n" . + $init_tab . $tab . $tab . "f << opcodeNames[$opname]" . + $printbody . ";\n" . + $init_tab . $tab . $tab . "return f;\n" . + $init_tab . $tab . "}\n"); + } else { + $class_decs .= $init_tab . $tab . + "/* print() inherited from $super */\n"; + } + + $class_decs .= $init_tab . "};\n\n"; } sub spew { @@ -259,10 +262,10 @@ sub get_paramlists { } $pfx = $deref = ""; - $member = "op$op"; + $member = "mOp$op"; - push (@dec, "$type op$op" . "A$default"); - push (@call, "op$op" . "A"); + push (@dec, "$type aOp$op" . "$default"); + push (@call, "aOp$op"); push (@template, $type); $op++; } @@ -281,25 +284,21 @@ sub get_printbody { print "type $type\n"; if ($type eq "Register") { - push (@oplist, $in . "if (op$op == NotARegister) {\n" . - $in . $tab . "f << \"R~\";\n" . - $in . "} else {\n" . - $in . $tab . "f << \"R\" << op$op;\n" . - $in . "}\n"); + push (@oplist, "\"R\" << mOp$op"); } elsif ($type eq "Label*") { - push (@oplist, $in . "f << \"Offset \" << op$op->offset;\n"); + push (@oplist, "\"Offset \" << mOp$op->mOffset"); } elsif ($type eq "StringAtom*") { - push (@oplist, $in . "f << \"'\" << *op$op << \"'\";\n"); + push (@oplist, "\"'\" << *mOp$op << \"'\""); } else { - push (@oplist, $in . "f << op$op;\n"); + push (@oplist, "mOp$op"); } $op++; } - my $rv = join ($in . "f << \", \";\n", @oplist); + my $rv = join (" << \", \" << ", @oplist); if ($rv ne "") { - $rv = $in . "f << \"\\t\";\n" . $rv; + $rv = " << \"\\t\" << " . $rv; } } diff --git a/js/js2/vmtypes.h b/js/js2/vmtypes.h index b89019c3db3..9ea419a401c 100644 --- a/js/js2/vmtypes.h +++ b/js/js2/vmtypes.h @@ -62,24 +62,23 @@ namespace VM { GET_PROP, /* dest, object, prop name */ LOAD_IMMEDIATE, /* dest, immediate value (double) */ LOAD_NAME, /* dest, name */ - LOAD_VAR, /* dest, index of frame slot */ MOVE, /* dest, source */ MULTIPLY, /* dest, source1, source2 */ NEW_ARRAY, /* dest */ NEW_OBJECT, /* dest */ NOP, /* do nothing and like it */ NOT, /* dest, source */ - RETURN, /* return value or NotARegister */ + RETURN, /* return value */ + RETURN_VOID, /* Return without a value */ SAVE_NAME, /* name, source */ - SAVE_VAR, /* index of frame slot, source */ SET_ELEMENT, /* base, source1, source2 */ SET_PROP, /* object, name, source */ - SUBTRACT /* dest, source1, source2 */ + SUBTRACT, /* dest, source1, source2 */ }; + /********************************************************************/ - - + static char *opcodeNames[] = { "ADD ", "BRANCH ", @@ -101,7 +100,6 @@ namespace VM { "GET_PROP ", "LOAD_IMMEDIATE", "LOAD_NAME ", - "LOAD_VAR ", "MOVE ", "MULTIPLY ", "NEW_ARRAY ", @@ -109,11 +107,11 @@ namespace VM { "NOP ", "NOT ", "RETURN ", + "RETURN_VOID ", "SAVE_NAME ", - "SAVE_VAR ", "SET_ELEMENT ", "SET_PROP ", - "SUBTRACT " + "SUBTRACT ", }; /********************************************************************/ @@ -122,20 +120,20 @@ namespace VM { class Instruction { public: - Instruction(ICodeOp opcodeA) : opcode(opcodeA) { } + Instruction(ICodeOp aOpcode) : mOpcode(aOpcode) { } virtual Formatter& print (Formatter& f) { - f << opcodeNames[opcode] << "\t"; + f << opcodeNames[mOpcode] << "\t"; return f; } ICodeOp getBranchOp() \ - { return ((opcode >= COMPARE_EQ) && (opcode <= COMPARE_NE)) ? \ - (ICodeOp)(BRANCH_EQ + (opcode - COMPARE_EQ)) : NOP; } + { return ((mOpcode >= COMPARE_EQ) && (mOpcode <= COMPARE_NE)) ? \ + (ICodeOp)(BRANCH_EQ + (mOpcode - COMPARE_EQ)) : NOP; } - ICodeOp op() { return opcode; } + ICodeOp op() { return mOpcode; } protected: - ICodeOp opcode; + ICodeOp mOpcode; }; @@ -161,11 +159,11 @@ namespace VM { class Label { public: - Label(InstructionStream* baseA) : - base(baseA), offset(NotALabel) {} + Label(InstructionStream* aBase) : + mBase(aBase), mOffset(NotALabel) {} - InstructionStream *base; - uint32 offset; + InstructionStream *mBase; + uint32 mOffset; }; typedef std::vector