From 61579b22f3359eeee6b947ba795997d80bb3b4ee Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Fri, 2 Feb 2001 01:04:22 +0000 Subject: [PATCH] Re-structuring some ICG details. --- js/js2/icodeEmitter.cpp | 1985 ++++++++++++++++++++++++++++++++++ js/js2/icodegenerator.cpp | 1986 +---------------------------------- js/js2/icodegenerator.h | 132 +-- js/js2/interpreter.cpp | 42 +- js/js2/interpreter.h | 4 + js/js2/js2.cpp | 4 +- js2/src/icodeEmitter.cpp | 1985 ++++++++++++++++++++++++++++++++++ js2/src/icodegenerator.cpp | 1986 +---------------------------------- js2/src/icodegenerator.h | 132 +-- js2/src/interpreter.cpp | 42 +- js2/src/interpreter.h | 4 + js2/tests/cpp/js2_shell.cpp | 4 +- 12 files changed, 4246 insertions(+), 4060 deletions(-) create mode 100644 js/js2/icodeEmitter.cpp create mode 100644 js2/src/icodeEmitter.cpp diff --git a/js/js2/icodeEmitter.cpp b/js/js2/icodeEmitter.cpp new file mode 100644 index 000000000000..ccd21d42be8d --- /dev/null +++ b/js/js2/icodeEmitter.cpp @@ -0,0 +1,1985 @@ +/* -*- 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 "numerics.h" +#include "world.h" +#include "vmtypes.h" +#include "jstypes.h" +#include "jsclasses.h" +#include "icodegenerator.h" +#include "interpreter.h" +#include "exception.h" +#include "icodeasm.h" + +#include +#include + +namespace JavaScript { +namespace ICG { + +using namespace VM; +using namespace JSTypes; +using namespace JSClasses; +using namespace Interpreter; +using namespace ICodeASM; + +inline char narrow(char16 ch) { return char(ch); } + +/************************************************************************/ + + +bool LabelEntry::containsLabel(const StringAtom *label) +{ + if (labelSet) { + for (LabelSet::iterator i = labelSet->begin(); i != labelSet->end(); i++) + if ( (*i) == label ) + return true; + } + return false; +} + +static bool hasAttribute(const IdentifierList* identifiers, Token::Kind tokenKind) +{ + while (identifiers) { + if (identifiers->name.tokenKind == tokenKind) + return true; + identifiers = identifiers->next; + } + return false; +} + +static bool hasAttribute(const IdentifierList* identifiers, StringAtom &name) +{ + while (identifiers) { + if (identifiers->name == name) + return true; + identifiers = identifiers->next; + } + return false; +} + + +/************************************************************************/ + + + +ExprNode::Kind ICodeGenerator::mapICodeOpToExprNode(ICodeOp op) +{ + switch (op) { + case ADD: + return ExprNode::add; + case SUBTRACT: + return ExprNode::subtract; + case MULTIPLY: + return ExprNode::multiply; + case DIVIDE: + return ExprNode::divide; + case REMAINDER: + return ExprNode::modulo; + case SHIFTLEFT: + return ExprNode::leftShift; + case SHIFTRIGHT: + return ExprNode::rightShift; + case USHIFTRIGHT: + return ExprNode::logicalRightShift; + case AND: + return ExprNode::bitwiseAnd; + case OR: + return ExprNode::bitwiseOr; + case XOR: + return ExprNode::bitwiseXor; + case POSATE: + return ExprNode::plus; + case NEGATE: + return ExprNode::minus; + case BITNOT: + return ExprNode::complement; + case COMPARE_EQ: + return ExprNode::equal; + case COMPARE_LT: + return ExprNode::lessThan; + case COMPARE_LE: + return ExprNode::lessThanOrEqual; + case STRICT_EQ: + return ExprNode::identical; + } + return ExprNode::none; +} + + +ICodeOp ICodeGenerator::mapExprNodeToICodeOp(ExprNode::Kind kind) +{ + // can be an array later, when everything has settled down + switch (kind) { + // binary + case ExprNode::add: + case ExprNode::addEquals: + return ADD; + case ExprNode::subtract: + case ExprNode::subtractEquals: + return SUBTRACT; + case ExprNode::multiply: + case ExprNode::multiplyEquals: + return MULTIPLY; + case ExprNode::divide: + case ExprNode::divideEquals: + return DIVIDE; + case ExprNode::modulo: + case ExprNode::moduloEquals: + return REMAINDER; + case ExprNode::leftShift: + case ExprNode::leftShiftEquals: + return SHIFTLEFT; + case ExprNode::rightShift: + case ExprNode::rightShiftEquals: + return SHIFTRIGHT; + case ExprNode::logicalRightShift: + case ExprNode::logicalRightShiftEquals: + return USHIFTRIGHT; + case ExprNode::bitwiseAnd: + case ExprNode::bitwiseAndEquals: + return AND; + case ExprNode::bitwiseXor: + case ExprNode::bitwiseXorEquals: + return XOR; + case ExprNode::bitwiseOr: + case ExprNode::bitwiseOrEquals: + return OR; + // unary + case ExprNode::plus: + return POSATE; + case ExprNode::minus: + return NEGATE; + case ExprNode::complement: + return BITNOT; + + // relational + case ExprNode::In: + return COMPARE_IN; + case ExprNode::Instanceof: + return INSTANCEOF; + + case ExprNode::equal: + return COMPARE_EQ; + case ExprNode::lessThan: + return COMPARE_LT; + case ExprNode::lessThanOrEqual: + return COMPARE_LE; + case ExprNode::identical: + return STRICT_EQ; + + // these get reversed by the generator + case ExprNode::notEqual: + return COMPARE_EQ; + case ExprNode::greaterThan: + return COMPARE_LT; + case ExprNode::greaterThanOrEqual: + return COMPARE_LE; + case ExprNode::notIdentical: + return STRICT_EQ; + + default: + NOT_REACHED("Unimplemented kind"); + return NOP; + } +} + + +static bool generatedBoolean(ExprNode *p) +{ + switch (p->getKind()) { + case ExprNode::parentheses: + { + UnaryExprNode *u = static_cast(p); + return generatedBoolean(u->op); + } + case ExprNode::True: + case ExprNode::False: + case ExprNode::equal: + case ExprNode::notEqual: + case ExprNode::lessThan: + case ExprNode::lessThanOrEqual: + case ExprNode::greaterThan: + case ExprNode::greaterThanOrEqual: + case ExprNode::identical: + case ExprNode::notIdentical: + case ExprNode::In: + case ExprNode::Instanceof: + case ExprNode::logicalAnd: + case ExprNode::logicalXor: + case ExprNode::logicalOr: + return true; + default: + break; + } + return false; +} + +static bool isSlotName(JSType *t, const StringAtom &name, uint32 &slotIndex, JSType *&type, bool lvalue) +{ + JSClass* c = dynamic_cast(t); + while (c) { + if (c->hasSlot(name)) { + const JSSlot &s = c->getSlot(name); + if (lvalue) { + if (s.mActual || s.mSetter) { + slotIndex = s.mIndex; + type = s.mType; + return true; + } + } + else { + if (s.mActual || s.mGetter) { + slotIndex = s.mIndex; + type = s.mType; + return true; + } + } + return false; + } + c = c->getSuperClass(); + } + return false; +} + +static bool isMethodName(JSType *t, const StringAtom &name, uint32 &slotIndex) +{ + JSClass* c = dynamic_cast(t); + return (c && c->hasMethod(name, slotIndex)); +} + + +static bool isStaticName(JSClass *c, const StringAtom &name, JSType*& type, bool &isConstructor) +{ + do { + if (c->hasStatic(name, type, isConstructor)) + return true; + c = c->getSuperClass(); + } while (c); + return false; +} + +ICodeGenerator::LValueKind ICodeGenerator::getVariableByName(const StringAtom &name, TypedRegister &v) +{ + v = variableList->findVariable(name); + if (v.first == NotARegister) + v = parameterList->findVariable(name); + if (v.first != NotARegister) + return Var; + return NoKind; +} + +ICodeGenerator::LValueKind ICodeGenerator::scanForVariable(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base) +{ + LValueKind k = getVariableByName(name, v); + if (k == Var) return k; + + uint32 count = 0; + ICodeGenerator *upper = mContainingFunction; + while (upper) { + k = upper->getVariableByName(name, v); + if (k == Var) { + base = getClosure(count); + slotIndex = v.first; + return Slot; + } + count++; + upper = upper->mContainingFunction; + } + return NoKind; +} + +// find 'name' (unqualified) in the current context. +// for local variable, returns v.first = register number +// for slot/method, returns slotIndex and sets base appropriately +// (note closure vars also get handled this way) +// v.second is set to the type regardless +ICodeGenerator::LValueKind ICodeGenerator::resolveIdentifier(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base, bool lvalue) +{ + if (!isWithinWith()) { + LValueKind k = scanForVariable(name, v, slotIndex, base); + if (k != NoKind) + return k; + else { + if (mClass) { // we're compiling a method of a class + if (!isStaticMethod()) { + if (isSlotName(mClass, name, slotIndex, v.second, lvalue)) { + base = TypedRegister(0, mClass); + return Slot; + } + if (isMethodName(mClass, name, slotIndex)) { + base = TypedRegister(0, mClass); + return Method; + } + } + bool isConstructor = false; + if (isStaticName(mClass, name, v.second, isConstructor)) { + return (isConstructor) ? Constructor : Static; + } + } + // last chance - if it's a generic name in the global scope, try to get a type for it + v.second = mContext->getGlobalObject()->getType(name); + return Name; + } + } + // all bet's off, generic name & type + v.second = &Any_Type; + return Name; +} + +TypedRegister ICodeGenerator::handleIdentifier(IdentifierExprNode *p, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) +{ + ASSERT(p->getKind() == ExprNode::identifier); + + /*JSType *vType = &Any_Type;*/ + uint32 slotIndex; + TypedRegister v; + TypedRegister base; + + const StringAtom &name = (static_cast(p))->name; + LValueKind lValueKind = resolveIdentifier(name, v, slotIndex, base, lvalue); + JSType *targetType = v.second; + + switch (use) { + case ExprNode::addEquals: + case ExprNode::subtractEquals: + case ExprNode::multiplyEquals: + case ExprNode::divideEquals: + case ExprNode::moduloEquals: + case ExprNode::leftShiftEquals: + case ExprNode::rightShiftEquals: + case ExprNode::logicalRightShiftEquals: + case ExprNode::bitwiseAndEquals: + case ExprNode::bitwiseXorEquals: + case ExprNode::bitwiseOrEquals: + switch (lValueKind) { + case Var: + break; + case Name: + v = loadName(name, v.second); + break; + case Slot: + v = getSlot(base, slotIndex); + break; + case Static: + case Constructor: + v = getStatic(mClass, name); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); + // fall thru... + case ExprNode::assignment: + ret = cast(ret, targetType); + switch (lValueKind) { + case Var: + move(v, ret); + break; + case Name: + saveName(name, ret); + break; + case Slot: + setSlot(base, slotIndex, ret); + break; + case Static: + case Constructor: + setStatic(mClass, name, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::identifier: + switch (lValueKind) { + case Var: + ret = v; + break; + case Name: + ret = loadName(name, v.second); + break; + case Slot: + ret = getSlot(base, slotIndex); + break; + case Static: + case Constructor: + ret = getStatic(mClass, name); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::preDecrement: + case ExprNode::preIncrement: + switch (lValueKind) { + case Var: + ret = binaryOp(xcrementOp, v, loadImmediate(1.0)); + break; + case Name: + ret = loadName(name, v.second); + ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); + saveName(name, ret); + break; + case Slot: + ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); + setSlot(base, slotIndex, ret); + break; + case Static: + case Constructor: + ret = binaryOp(xcrementOp, getStatic(mClass, name), loadImmediate(1.0)); + setStatic(mClass, name, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::postDecrement: + case ExprNode::postIncrement: + switch (lValueKind) { + case Var: + ret = varXcr(v, xcrementOp); + break; + case Name: + ret = nameXcr(name, xcrementOp); + break; + case Slot: + ret = slotXcr(base, slotIndex, xcrementOp); + break; + case Static: + case Constructor: + ret = staticXcr(mClass, name, xcrementOp); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::call: + { + switch (lValueKind) { + case Var: + ret = call(v, args); + break; + case Name: + ret = call(loadName(name), args); + break; + case Method: + ret = call(getMethod(base, slotIndex), args); + break; + case Static: + ret = call(getStatic(mClass, name), args); + break; + case Constructor: + ret = newClass(mClass); + call(bindThis(ret, getStatic(mClass, name)), args); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + } + break; + default: + NOT_REACHED("Bad use kind"); + } + return ret; + +} + +TypedRegister ICodeGenerator::handleDot(BinaryExprNode *b, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) +{ + ASSERT(b->getKind() == ExprNode::dot); + + LValueKind lValueKind = Property; + + if (b->op2->getKind() != ExprNode::identifier) { + NOT_REACHED("Implement me"); // turns into a getProperty (but not via any overloaded [] ) + } + else { + // we have . + const StringAtom &fieldName = static_cast(b->op2)->name; + TypedRegister base; + TypedRegister baseBase; + JSClass *clazz = NULL; + JSType *fieldType = &Any_Type; + uint32 slotIndex; + if ((b->op1->getKind() == ExprNode::identifier) && !isWithinWith()) { + // handle . + const StringAtom &baseName = (static_cast(b->op1))->name; + LValueKind baseKind = resolveIdentifier(baseName, base, slotIndex, baseBase, false); + if (baseKind == Slot) { + base = getSlot(baseBase, slotIndex); + } + // + // handle . + // + if (base.second == &Type_Type) { + const JSValue &v = mContext->getGlobalObject()->getVariable(baseName); + bool isConstructor; + ASSERT(v.isType()); // there's no other way that base.second could be &Type_Type, right? + clazz = dynamic_cast(v.type); + if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) { + lValueKind = (isConstructor) ? Constructor : Static; + } + } + if (lValueKind == Property) { + if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) + lValueKind = Slot; + else + if (isMethodName(base.second, fieldName, slotIndex)) + lValueKind = Method; + else { + bool isConstructor; + clazz = dynamic_cast(base.second); + if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) + lValueKind = (isConstructor) ? Constructor : Static; + } + } + if ((lValueKind == Property) || (base.first == NotARegister)) + base = loadName(baseName, base.second); + } + else { + base = genExpr(b->op1); + if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) + lValueKind = Slot; + else + if (isMethodName(base.second, fieldName, slotIndex)) + lValueKind = Method; + else { + bool isConstructor; + clazz = dynamic_cast(base.second); + if (clazz && clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) + lValueKind = (isConstructor) ? Constructor : Static; + } + } + TypedRegister v; + switch (use) { + case ExprNode::call: + switch (lValueKind) { + case Static: + ret = call(getStatic(clazz, fieldName), args); + break; + case Constructor: + ret = newClass(clazz); + call(bindThis(ret, getStatic(clazz, fieldName)), args); + break; + case Property: + ret = call(bindThis(base, getProperty(base, fieldName)), args); + break; + case Method: + ret = call(getMethod(base, slotIndex), args); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::dot: + case ExprNode::addEquals: + case ExprNode::subtractEquals: + case ExprNode::multiplyEquals: + case ExprNode::divideEquals: + case ExprNode::moduloEquals: + case ExprNode::leftShiftEquals: + case ExprNode::rightShiftEquals: + case ExprNode::logicalRightShiftEquals: + case ExprNode::bitwiseAndEquals: + case ExprNode::bitwiseXorEquals: + case ExprNode::bitwiseOrEquals: + switch (lValueKind) { + case Constructor: + case Static: + v = getStatic(clazz, fieldName); + break; + case Property: + v = getProperty(base, fieldName); + break; + case Slot: + v = getSlot(base, slotIndex); + break; + case Method: + v = getMethod(base, slotIndex); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + if (use == ExprNode::dot) { + ret = v; + break; + } + ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); + // fall thru... + case ExprNode::assignment: + ret = cast(ret, fieldType); + switch (lValueKind) { + case Constructor: + case Static: + setStatic(clazz, fieldName, ret); + break; + case Property: + setProperty(base, fieldName, ret); + break; + case Slot: + setSlot(base, slotIndex, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::postDecrement: + case ExprNode::postIncrement: + { +// JSClass *clss = dynamic_cast(fieldType); +// if (clss) { +// clss->findOverloadedOperator(use); +// } + switch (lValueKind) { + case Constructor: + case Static: + ret = staticXcr(clazz, fieldName, xcrementOp); + break; + case Property: + ret = propertyXcr(base, fieldName, xcrementOp); + break; + case Slot: + ret = slotXcr(base, slotIndex, xcrementOp); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + } + break; + case ExprNode::preDecrement: + case ExprNode::preIncrement: + switch (lValueKind) { + case Constructor: + case Static: + ret = binaryOp(xcrementOp, getStatic(clazz, fieldName), loadImmediate(1.0)); + setStatic(clazz, fieldName, ret); + break; + case Property: + ret = binaryOp(xcrementOp, getProperty(base, fieldName), loadImmediate(1.0)); + setProperty(base, fieldName, ret); + break; + case Slot: + ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); + setSlot(base, slotIndex, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::Delete: + if (lValueKind == Property) { + ret = deleteProperty(base, fieldName); + } + break; + default: + NOT_REACHED("unexpected use node"); + } + ret.second = fieldType; + } + return ret; +} + + + +/* + if trueBranch OR falseBranch are not null, the sub-expression should generate + a conditional branch to the appropriate target. If either branch is NULL, it + indicates that the label is immediately forthcoming. +*/ +TypedRegister ICodeGenerator::genExpr(ExprNode *p, + bool needBoolValueInBranch, + Label *trueBranch, + Label *falseBranch) +{ + TypedRegister ret(NotARegister, &None_Type); + ICodeOp xcrementOp = ADD; + switch (p->getKind()) { + case ExprNode::True: + if (trueBranch || falseBranch) { + if (needBoolValueInBranch) + ret = loadBoolean(true); + if (trueBranch) + branch(trueBranch); + } + else + ret = loadBoolean(true); + break; + case ExprNode::False: + if (trueBranch || falseBranch) { + if (needBoolValueInBranch) + ret = loadBoolean(false); + if (falseBranch) + branch(falseBranch); + } + else + ret = loadBoolean(false); + break; + case ExprNode::Null: + ret = loadNull(); + break; + case ExprNode::parentheses: + { + UnaryExprNode *u = static_cast(p); + ret = genExpr(u->op, needBoolValueInBranch, trueBranch, falseBranch); + } + break; + case ExprNode::New: + { + InvokeExprNode *i = static_cast(p); + ArgumentList *args = new ArgumentList(); + ExprPairList *p = i->pairs; + StringFormatter s; + while (p) { + if (p->field && (p->field->getKind() == ExprNode::identifier)) + args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); + else { + if (p->field && (p->field->getKind() == ExprNode::string)) + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); + else { + s << (uint32)args->size(); + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); + s.clear(); + } + } + p = p->next; + } + if (i->op->getKind() == ExprNode::identifier) { + const StringAtom &className = static_cast(i->op)->name; + const JSValue& value = mContext->getGlobalObject()->getVariable(className); + if (value.isType()) { + JSClass* clazz = dynamic_cast(value.type); + if (clazz) { + ret = newClass(clazz); + ret = call(bindThis(ret, getStatic(clazz, className)), args); + } + else { + // + // like 'new Boolean()' - see if the type has a constructor + // + JSFunction *f = value.type->getConstructor(); + if (f) + ret = directCall(f, args); + else + NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. + } + } + else { + if (value.isFunction()) { + TypedRegister f = loadName(className, value.type); + ret = newObject(f); + ret = call(bindThis(ret, f), args); + } + else + NOT_REACHED("new , where is not a function"); // XXX Runtime error. + } + } + else + ret = newObject(TypedRegister(NotARegister, &Any_Type)); // XXX more ? + } + break; + case ExprNode::Delete: + { + UnaryExprNode *d = static_cast(p); + ASSERT(d->op->getKind() == ExprNode::dot); + ret = handleDot(static_cast(d->op), p->getKind(), xcrementOp, ret, NULL, true); + // rather than getProperty(), need to do a deleteProperty(). + } + break; + case ExprNode::call : + { + InvokeExprNode *i = static_cast(p); + ArgumentList *args = new ArgumentList(); + ExprPairList *p = i->pairs; + StringFormatter s; + while (p) { + if (p->field && (p->field->getKind() == ExprNode::identifier)) + args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); + else { + if (p->field && (p->field->getKind() == ExprNode::string)) + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); + else { + s << (uint32)args->size(); + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); + s.clear(); + } + } + p = p->next; + } + + if (i->op->getKind() == ExprNode::dot) { + BinaryExprNode *b = static_cast(i->op); + ret = handleDot(b, ExprNode::call, xcrementOp, ret, args, false); + } + else { + if (i->op->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(i->op), ExprNode::call, xcrementOp, ret, args, false); + } + else { + if (i->op->getKind() == ExprNode::index) { + InvokeExprNode *ii = static_cast(i->op); + TypedRegister base = genExpr(ii->op); + ret = call(bindThis(base, getElement(base, genExpr(ii->pairs->value))), args); // FIXME, only taking first index + } + else + ASSERT("WAH!"); + } + } + } + break; + case ExprNode::index : + { + InvokeExprNode *i = static_cast(p); + TypedRegister base = genExpr(i->op); + JSClass *clazz = dynamic_cast(base.second); + if (clazz) { + // look for operator [] and invoke it + } + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + ret = getElement(base, index); + } + break; + case ExprNode::dot : + { + BinaryExprNode *b = static_cast(p); + ret = handleDot(b, p->getKind(), xcrementOp, ret, NULL, false); + } + break; + case ExprNode::This : + { + ret = TypedRegister(0, mClass ? mClass : &Any_Type); + } + break; + case ExprNode::identifier : + { + ret = handleIdentifier(static_cast(p), ExprNode::identifier, xcrementOp, ret, NULL, false); + } + break; + case ExprNode::number : + ret = loadImmediate((static_cast(p))->value); + break; + case ExprNode::string : + ret = loadString(mContext->getWorld().identifiers[(static_cast(p))->str]); + break; + case ExprNode::preDecrement: + xcrementOp = SUBTRACT; + case ExprNode::preIncrement: + { + UnaryExprNode *u = static_cast(p); + if (u->op->getKind() == ExprNode::dot) { + ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(u->op); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + ret = getElement(base, index); + ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); + setElement(base, index, ret); + } + else + ASSERT("WAH!"); + } + break; + case ExprNode::postDecrement: + xcrementOp = SUBTRACT; + case ExprNode::postIncrement: + { + UnaryExprNode *u = static_cast(p); + if (u->op->getKind() == ExprNode::dot) { + ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(u->op); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + ret = elementXcr(base, index, xcrementOp); + } + else + ASSERT("WAH!"); + } + break; + + case ExprNode::plus: + case ExprNode::minus: + case ExprNode::complement: + { + UnaryExprNode *u = static_cast(p); + TypedRegister r = genExpr(u->op); + ret = op(mapExprNodeToICodeOp(p->getKind()), r); + } + break; + case ExprNode::add: + case ExprNode::subtract: + case ExprNode::multiply: + case ExprNode::divide: + case ExprNode::modulo: + case ExprNode::leftShift: + case ExprNode::rightShift: + case ExprNode::logicalRightShift: + case ExprNode::bitwiseAnd: + case ExprNode::bitwiseXor: + case ExprNode::bitwiseOr: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); + } + break; + case ExprNode::assignment: + { + BinaryExprNode *b = static_cast(p); + ret = genExpr(b->op2); + if (b->op1->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::dot) { + BinaryExprNode *lb = static_cast(b->op1); + ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(b->op1); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + setElement(base, index, ret); + } + else + ASSERT("WAH!"); + } + break; + case ExprNode::addEquals: + case ExprNode::subtractEquals: + case ExprNode::multiplyEquals: + case ExprNode::divideEquals: + case ExprNode::moduloEquals: + case ExprNode::leftShiftEquals: + case ExprNode::rightShiftEquals: + case ExprNode::logicalRightShiftEquals: + case ExprNode::bitwiseAndEquals: + case ExprNode::bitwiseXorEquals: + case ExprNode::bitwiseOrEquals: + { + BinaryExprNode *b = static_cast(p); + ret = genExpr(b->op2); + if (b->op1->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::dot) { + BinaryExprNode *lb = static_cast(b->op1); + ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(b->op1); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + TypedRegister v = getElement(base, index); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), v, ret); + setElement(base, index, ret); + } + else + ASSERT("WAH!"); + } + break; + case ExprNode::equal: + case ExprNode::lessThan: + case ExprNode::lessThanOrEqual: + case ExprNode::identical: + case ExprNode::In: + case ExprNode::Instanceof: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); + if (trueBranch || falseBranch) { + if (trueBranch == NULL) + branchFalse(falseBranch, ret); + else { + branchTrue(trueBranch, ret); + if (falseBranch) + branch(falseBranch); + } + } + } + break; + case ExprNode::greaterThan: + case ExprNode::greaterThanOrEqual: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r2, r1); // will return reverse case + if (trueBranch || falseBranch) { + if (trueBranch == NULL) + branchFalse(falseBranch, ret); + else { + branchTrue(trueBranch, ret); + if (falseBranch) + branch(falseBranch); + } + } + } + break; + + case ExprNode::notEqual: + case ExprNode::notIdentical: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); // will generate equal/identical code + if (trueBranch || falseBranch) { + if (trueBranch == NULL) + branchTrue(falseBranch, ret); + else { + branchFalse(trueBranch, ret); + if (falseBranch) + branch(falseBranch); + } + } + else + ret = logicalNot(ret); + + } + break; + + case ExprNode::logicalAnd: + { + BinaryExprNode *b = static_cast(p); + if (trueBranch || falseBranch) { + genExpr(b->op1, needBoolValueInBranch, NULL, falseBranch); + genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); + } + else { + Label *fBranch = getLabel(); + TypedRegister r1 = genExpr(b->op1, true, NULL, fBranch); + if (!generatedBoolean(b->op1)) { + r1 = test(r1); + branchFalse(fBranch, r1); + } + TypedRegister r2 = genExpr(b->op2); + if (!generatedBoolean(b->op2)) { + r2 = test(r2); + } + if (r1 != r2) // FIXME, need a way to specify a dest??? + move(r1, r2); + setLabel(fBranch); + ret = r1; + } + } + break; + case ExprNode::logicalOr: + { + BinaryExprNode *b = static_cast(p); + if (trueBranch || falseBranch) { + genExpr(b->op1, needBoolValueInBranch, trueBranch, NULL); + genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); + } + else { + Label *tBranch = getLabel(); + TypedRegister r1 = genExpr(b->op1, true, tBranch, NULL); + if (!generatedBoolean(b->op1)) { + r1 = test(r1); + branchTrue(tBranch, r1); + } + TypedRegister r2 = genExpr(b->op2); + if (!generatedBoolean(b->op2)) { + r2 = test(r2); + } + if (r1 != r2) // FIXME, need a way to specify a dest??? + move(r1, r2); + setLabel(tBranch); + ret = r1; + } + } + break; + + case ExprNode::conditional: + { + TernaryExprNode *t = static_cast(p); + Label *fBranch = getLabel(); + Label *beyondBranch = getLabel(); + TypedRegister c = genExpr(t->op1, false, NULL, fBranch); + if (!generatedBoolean(t->op1)) + branchFalse(fBranch, test(c)); + TypedRegister r1 = genExpr(t->op2); + branch(beyondBranch); + setLabel(fBranch); + TypedRegister r2 = genExpr(t->op3); + if (r1 != r2) // FIXME, need a way to specify a dest??? + move(r1, r2); + setLabel(beyondBranch); + ret = r1; + } + break; + + case ExprNode::objectLiteral: + { + ret = newObject(TypedRegister(NotARegister, &Any_Type)); + PairListExprNode *plen = static_cast(p); + ExprPairList *e = plen->pairs; + while (e) { + if (e->field && e->value && (e->field->getKind() == ExprNode::identifier)) + setProperty(ret, (static_cast(e->field))->name, genExpr(e->value)); + e = e->next; + } + } + break; + + case ExprNode::functionLiteral: + { + FunctionExprNode *f = static_cast(p); + ICodeModule *icm = genFunction(f->function, false, false, NULL); + ret = newClosure(icm); + } + break; + + case ExprNode::at: + { + BinaryExprNode *b = static_cast(p); + // for now, just handle simple identifiers on the rhs. + ret = genExpr(b->op1); + if (b->op2->getKind() == ExprNode::identifier) { + TypedRegister t; + const StringAtom &name = (static_cast(b->op2))->name; + ASSERT(t.second == &Type_Type); + const JSValue &v = mContext->getGlobalObject()->getVariable(name); + ASSERT(v.isType()); + JSClass *clazz = dynamic_cast(v.type); + if (clazz) + ret = cast(ret, clazz); + else + ret = cast(ret, t.second); + } + else + NOT_REACHED("Anything more complex than @ is not implemented"); + } + break; + + + default: + { + NOT_REACHED("Unsupported ExprNode kind"); + } + } + return ret; +} + + + +ICodeModule *ICodeGenerator::genFunction(FunctionDefinition &function, bool isStatic, bool isConstructor, JSClass *superclass) +{ + ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; + + ICodeGenerator icg(mContext, this, mClass, flags, mContext->extractType(function.resultType)); + icg.allocateParameter(mContext->getWorld().identifiers["this"], false, (mClass) ? mClass : &Any_Type); // always parameter #0 + VariableBinding *v = function.parameters; + bool unnamed = true; + uint32 positionalCount = 0; + StringFormatter s; + while (v) { + if (unnamed && (v == function.namedParameters)) { // Track when we hit the first named parameter. + icg.parameterList->setPositionalCount(positionalCount); + unnamed = false; + } + + // 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 == function.restParameter) { + icg.parameterList->setRestParameter( (unnamed) ? ParameterList::HasRestParameterBeforeBar : ParameterList::HasRestParameterAfterBar ); + } + else { + if (v->name && (v->name->getKind() == ExprNode::identifier)) { + JSType *pType = mContext->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) { + positionalCount++; + s << r.first - 1; // the first positional parameter is '0' + icg.parameterList->add(mContext->getWorld().identifiers[s.getString()], r, (v->initializer != NULL)); + s.clear(); + } + } + } + v = v->next; + } + if (unnamed) icg.parameterList->setPositionalCount(positionalCount); + + // now allocate the rest parameter + if (function.restParameter) { + v = function.restParameter; + JSType *pType = (v->type == NULL) ? &Array_Type : mContext->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 = function.optParameters; + if (v) { + while (v) { // include the rest parameter, as it may have an initializer + if (v->name && (v->name->getKind() == ExprNode::identifier)) { + icg.addParameterLabel(icg.setLabel(icg.getLabel())); + TypedRegister p = icg.genExpr(v->name); + if (v->initializer) { // might be NULL when we get to the restParameter + Label *l = icg.getLabel(); + icg.branchInitialized(l, p); + icg.move(p, icg.genExpr(v->initializer)); + icg.setLabel(l); + } + else { // an un-initialized rest parameter is still an empty array + if (v == function.restParameter) { + Label *l = icg.getLabel(); + icg.branchInitialized(l, p); + icg.move(p, icg.newArray()); + icg.setLabel(l); + } + } + } + v = v->next; + } + icg.addParameterLabel(icg.setLabel(icg.getLabel())); // to provide the entry-point for the default case + } + + if (isConstructor) { + /* + See if the first statement is an expression statement consisting + of a call to super(). If not we need to add a call to the default + superclass constructor ourselves. + */ + TypedRegister thisValue = TypedRegister(0, mClass); + ArgumentList *args = new ArgumentList(); + if (superclass) { + bool foundSuperCall = false; + BlockStmtNode *b = function.body; + if (b && b->statements && (b->statements->getKind() == StmtNode::expression)) { + ExprStmtNode *e = static_cast(b->statements); + if (e->expr->getKind() == ExprNode::call) { + InvokeExprNode *i = static_cast(e->expr); + if (i->op->getKind() == ExprNode::dot) { + BinaryExprNode *b = static_cast(i->op); + if ((b->op1->getKind() == ExprNode::This) && (b->op2->getKind() == ExprNode::qualify)) { + BinaryExprNode *q = static_cast(b->op2); + if (q->op1->getKind() == ExprNode::Super) { + // XXX verify that q->op2 is either the superclass name or a constructor for it + foundSuperCall = true; + } + } + } + } + } + if (!foundSuperCall) { // invoke the default superclass constructor + icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); + } + } + if (mClass->hasStatic(mInitName)) + icg.call(icg.bindThis(thisValue, icg.getStatic(mClass, mInitName)), args); // ok, so it's mis-named + + } + if (function.body) + icg.genStmt(function.body); + if (isConstructor) { + TypedRegister thisValue = TypedRegister(0, mClass); + icg.returnStmt(thisValue); + } + return icg.complete(); +} + +TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet) +{ + TypedRegister ret(NotARegister, &None_Type); + + startStatement(p->pos); + + switch (p->getKind()) { + case StmtNode::Class: + { + // FIXME: need a semantic check to make sure a class isn't being redefined(?) + ClassStmtNode *classStmt = static_cast(p); + ASSERT(classStmt->name->getKind() == ExprNode::identifier); + IdentifierExprNode* nameExpr = static_cast(classStmt->name); + JSClass* superclass = 0; + if (classStmt->superclass) { + ASSERT(classStmt->superclass->getKind() == ExprNode::identifier); + IdentifierExprNode* superclassExpr = static_cast(classStmt->superclass); + const JSValue& superclassValue = mContext->getGlobalObject()->getVariable(superclassExpr->name); + ASSERT(superclassValue.isObject() && !superclassValue.isNull()); + superclass = static_cast(superclassValue.object); + } + JSClass* thisClass = new JSClass(mContext->getGlobalObject(), nameExpr->name, superclass); + // is it ok for a partially defined class to appear in global scope? this is needed + // to handle recursive types, such as linked list nodes. + mContext->getGlobalObject()->defineVariable(nameExpr->name, &Type_Type, JSValue(thisClass)); + + +/* + Pre-pass to declare all the methods & fields +*/ + bool needsInstanceInitializer = false; + TypedRegister thisRegister = TypedRegister(0, thisClass); + if (classStmt->body) { + StmtNode* s = classStmt->body->statements; + while (s) { + switch (s->getKind()) { + case StmtNode::Const: + case StmtNode::Var: + { + VariableStmtNode *vs = static_cast(s); + bool isStatic = hasAttribute(vs->attributes, Token::Static); + VariableBinding *v = vs->bindings; + while (v) { + if (v->name) { + ASSERT(v->name->getKind() == ExprNode::identifier); + IdentifierExprNode* idExpr = static_cast(v->name); + JSType* type = mContext->extractType(v->type); + if (isStatic) + thisClass->defineStatic(idExpr->name, type); + else { + if (hasAttribute(vs->attributes, mContext->getWorld().identifiers["virtual"])) + thisClass->defineSlot(idExpr->name, type, JSSlot::kIsVirtual); + else + thisClass->defineSlot(idExpr->name, type); + if (v->initializer) + needsInstanceInitializer = true; + } + } + v = v->next; + } + } + break; + case StmtNode::Constructor: + case StmtNode::Function: + { + FunctionStmtNode *f = static_cast(s); + bool isStatic = hasAttribute(f->attributes, Token::Static); + bool isConstructor = (s->getKind() == StmtNode::Constructor); + if (f->function.prefix == FunctionName::Operator) { + thisClass->defineOperator(f->function.op, + mContext->getParameterType(f->function, 0), + mContext->getParameterType(f->function, 1), NULL); + } + else + if (f->function.name->getKind() == ExprNode::identifier) { + const StringAtom& name = (static_cast(f->function.name))->name; + if (isConstructor) + thisClass->defineConstructor(name); + else + if (isStatic) + thisClass->defineStatic(name, &Function_Type); + else { + switch (f->function.prefix) { + case FunctionName::Get: + thisClass->setGetter(name, NULL, mContext->extractType(f->function.resultType)); + break; + case FunctionName::Set: + thisClass->setSetter(name, NULL, mContext->extractType(f->function.resultType)); + break; + case FunctionName::normal: + thisClass->defineMethod(name, NULL); + break; + default: + NOT_REACHED("unexpected prefix"); + break; + } + } + } + } + break; + default: + NOT_REACHED("unimplemented class member statement"); + break; + } + s = s->next; + } + } + if (needsInstanceInitializer) + thisClass->defineStatic(mInitName, &Function_Type); +/* + Now gen code for each +*/ + + bool hasDefaultConstructor = false; + if (classStmt->body) { + JSScope* thisScope = thisClass->getScope(); + ICodeGenerator *ccg = NULL; + Context *classContext = new Context(mContext->getWorld(), thisScope); + if (needsInstanceInitializer) { + // constructor code generator. Slot variable + // initializers get added to this function. + ccg = new ICodeGenerator(classContext, NULL, thisClass, kNoFlags, &Void_Type); + ccg->allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 + } + + // static initializer code generator. + // static field inits, plus code to initialize + // static method slots. + ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod, &Void_Type); + StmtNode* s = classStmt->body->statements; + while (s) { + switch (s->getKind()) { + case StmtNode::Const: + case StmtNode::Var: + { + VariableStmtNode *vs = static_cast(s); + bool isStatic = hasAttribute(vs->attributes, Token::Static); + VariableBinding *v = vs->bindings; + while (v) { + if (v->name) { + ASSERT(v->name->getKind() == ExprNode::identifier); + if (v->initializer) { + IdentifierExprNode* idExpr = static_cast(v->name); + if (isStatic) { + scg.setStatic(thisClass, idExpr->name, scg.genExpr(v->initializer)); + scg.resetStatement(); + } else { + const JSSlot& slot = thisClass->getSlot(idExpr->name); + ccg->setSlot(thisRegister, slot.mIndex, ccg->genExpr(v->initializer)); + ccg->resetStatement(); + } + } + } + v = v->next; + } + } + break; + case StmtNode::Constructor: + case StmtNode::Function: + { + FunctionStmtNode *f = static_cast(s); + bool isStatic = hasAttribute(f->attributes, Token::Static); + bool isConstructor = (s->getKind() == StmtNode::Constructor); + ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; + + ICodeGenerator mcg(classContext, NULL, thisClass, flags); // method code generator. + ICodeModule *icm = mcg.genFunction(f->function, isStatic, isConstructor, superclass); + if (f->function.prefix == FunctionName::Operator) { + thisClass->defineOperator(f->function.op, + mContext->getParameterType(f->function, 0), + mContext->getParameterType(f->function, 1), new JSFunction(icm)); + } + else + if (f->function.name->getKind() == ExprNode::identifier) { + const StringAtom& name = (static_cast(f->function.name))->name; + if (isConstructor) { + if (name == nameExpr->name) + hasDefaultConstructor = true; + scg.setStatic(thisClass, name, scg.newFunction(icm)); + } + else + if (isStatic) + scg.setStatic(thisClass, name, scg.newFunction(icm)); + else { + switch (f->function.prefix) { + case FunctionName::Get: + thisClass->setGetter(name, new JSFunction(icm), icm->mResultType); + break; + case FunctionName::Set: + thisClass->setSetter(name, new JSFunction(icm), icm->mResultType); + break; + case FunctionName::normal: + thisClass->defineMethod(name, new JSFunction(icm)); + break; + default: + NOT_REACHED("unexpected prefix"); + break; + } + } + } + } + break; + default: + NOT_REACHED("unimplemented class member statement"); + break; + } + s = s->next; + } + + // add the instance initializer + if (ccg) { + scg.setStatic(thisClass, mInitName, scg.newFunction(ccg->complete())); + delete ccg; + } + // invent a default constructor if necessary, it just calls the + // initializer and the superclass default constructor + if (!hasDefaultConstructor) { + TypedRegister thisValue = TypedRegister(0, thisClass); + ArgumentList *args = new ArgumentList(); + ICodeGenerator icg(classContext, NULL, thisClass, kIsStaticMethod, &Void_Type); + icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 + if (superclass) + icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); + if (thisClass->hasStatic(mInitName)) + icg.call(icg.bindThis(thisValue, icg.getStatic(thisClass, mInitName)), args); + icg.returnStmt(thisValue); + thisClass->defineConstructor(nameExpr->name); + scg.setStatic(thisClass, nameExpr->name, scg.newFunction(icg.complete())); + } + // freeze the class. + thisClass->complete(); + + + // REVISIT: using the scope of the class to store both methods and statics. + if (scg.getICode()->size()) { + ICodeModule* clinit = scg.complete(); + classContext->interpret(clinit, JSValues()); + delete clinit; + } + delete classContext; + } + } + break; + case StmtNode::Function: + { + FunctionStmtNode *f = static_cast(p); + bool isStatic = hasAttribute(f->attributes, Token::Static); + ICodeModule *icm = genFunction(f->function, isStatic, false, NULL); + JSType *resultType = mContext->extractType(f->function.resultType); + if (f->function.name->getKind() == ExprNode::identifier) { + const StringAtom& name = (static_cast(f->function.name))->name; + switch (f->function.prefix) { + case FunctionName::Get: + if (isTopLevel()) { + mContext->getGlobalObject()->defineVariable(name, resultType); + mContext->getGlobalObject()->setGetter(name, new JSFunction(icm)); + } + else { + // is this legal - a nested getter? + NOT_REACHED("Better check with Waldemar"); + //allocateVariable(name, resultType); + } + break; + case FunctionName::Set: + if (isTopLevel()) { + mContext->getGlobalObject()->defineVariable(name, resultType); + mContext->getGlobalObject()->setSetter(name, new JSFunction(icm)); + } + else { + // is this legal - a nested setter? + NOT_REACHED("Better check with Waldemar"); + //allocateVariable(name, resultType); + } + break; + case FunctionName::normal: + mContext->getGlobalObject()->defineFunction(name, icm); + break; + default: + NOT_REACHED("unexpected prefix"); + break; + } + } + } + break; + case StmtNode::Import: + { + ImportStmtNode *i = static_cast(p); + String *fileName = i->bindings->packageName.str; + if (fileName) { /// if not, build one from the idList instead + std::string str(fileName->length(), char()); + std::transform(fileName->begin(), fileName->end(), str.begin(), narrow); + FILE* f = fopen(str.c_str(), "r"); + if (f) { + (void)mContext->readEvalFile(f, *fileName); + fclose(f); + } + } + } + break; + case StmtNode::Var: + { + VariableStmtNode *vs = static_cast(p); + VariableBinding *v = vs->bindings; + while (v) { + if (v->name && (v->name->getKind() == ExprNode::identifier)) { + JSType *type = mContext->extractType(v->type); + if (isTopLevel()) + mContext->getGlobalObject()->defineVariable((static_cast(v->name))->name, type); + else + allocateVariable((static_cast(v->name))->name, type); + if (v->initializer) { + if (!isTopLevel() && !isWithinWith()) { + TypedRegister r = genExpr(v->name); + TypedRegister val = genExpr(v->initializer); + val = cast(val, type); + move(r, val); + } + else { + TypedRegister val = genExpr(v->initializer); + val = cast(val, type); + saveName((static_cast(v->name))->name, val); + } + } + } + v = v->next; + } + } + break; + case StmtNode::expression: + { + ExprStmtNode *e = static_cast(p); + ret = genExpr(e->expr); + } + break; + case StmtNode::Throw: + { + ExprStmtNode *e = static_cast(p); + throwStmt(genExpr(e->expr)); + } + break; + case StmtNode::Debugger: + { + debuggerStmt(); + } + break; + case StmtNode::Return: + { + ExprStmtNode *e = static_cast(p); + if (e->expr) + returnStmt(ret = genExpr(e->expr)); + else + returnStmt(TypedRegister(NotARegister, &Void_Type)); + } + break; + case StmtNode::If: + { + Label *falseLabel = getLabel(); + UnaryStmtNode *i = static_cast(p); + TypedRegister c = genExpr(i->expr, false, NULL, falseLabel); + if (!generatedBoolean(i->expr)) + branchFalse(falseLabel, test(c)); + genStmt(i->stmt); + setLabel(falseLabel); + } + break; + case StmtNode::IfElse: + { + Label *falseLabel = getLabel(); + Label *trueLabel = getLabel(); + Label *beyondLabel = getLabel(); + BinaryStmtNode *i = static_cast(p); + TypedRegister c = genExpr(i->expr, false, trueLabel, falseLabel); + if (!generatedBoolean(i->expr)) + branchFalse(falseLabel, test(c)); + setLabel(trueLabel); + genStmt(i->stmt); + branch(beyondLabel); + setLabel(falseLabel); + genStmt(i->stmt2); + setLabel(beyondLabel); + } + break; + case StmtNode::With: + { + UnaryStmtNode *w = static_cast(p); + TypedRegister o = genExpr(w->expr); + bool withinWith = isWithinWith(); + setFlag(kIsWithinWith, true); + beginWith(o); + genStmt(w->stmt); + endWith(); + setFlag(kIsWithinWith, withinWith); + } + break; + case StmtNode::Switch: + { + Label *defaultLabel = NULL; + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel()); + mLabelStack.push_back(e); + SwitchStmtNode *sw = static_cast(p); + TypedRegister sc = genExpr(sw->expr); + StmtNode *s = sw->statements; + // ECMA requires case & default statements to be immediate children of switch + // unlike C where they can be arbitrarily deeply nested in other statements. + Label *nextCaseLabel = NULL; + GenericBranch *lastBranch = NULL; + while (s) { + if (s->getKind() == StmtNode::Case) { + ExprStmtNode *c = static_cast(s); + if (c->expr) { + if (nextCaseLabel) + setLabel(nextCaseLabel); + nextCaseLabel = getLabel(); + TypedRegister r = genExpr(c->expr); + TypedRegister eq = binaryOp(COMPARE_EQ, r, sc); + lastBranch = branchFalse(nextCaseLabel, eq); + } + else { + defaultLabel = getLabel(); + setLabel(defaultLabel); + } + } + else + genStmt(s); + s = s->next; + } + if (nextCaseLabel) + setLabel(nextCaseLabel); + if (defaultLabel && lastBranch) + lastBranch->setTarget(defaultLabel); + + setLabel(e->breakLabel); + mLabelStack.pop_back(); + } + break; + case StmtNode::DoWhile: + { + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); + mLabelStack.push_back(e); + UnaryStmtNode *d = static_cast(p); + Label *doBodyTopLabel = getLabel(); + setLabel(doBodyTopLabel); + genStmt(d->stmt); + setLabel(e->continueLabel); + TypedRegister c = genExpr(d->expr, false, doBodyTopLabel, NULL); + if (!generatedBoolean(d->expr)) + branchTrue(doBodyTopLabel, test(c)); + setLabel(e->breakLabel); + mLabelStack.pop_back(); + } + break; + case StmtNode::While: + { + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); + mLabelStack.push_back(e); + branch(e->continueLabel); + + UnaryStmtNode *w = static_cast(p); + + Label *whileBodyTopLabel = getLabel(); + setLabel(whileBodyTopLabel); + genStmt(w->stmt); + + setLabel(e->continueLabel); + TypedRegister c = genExpr(w->expr, false, whileBodyTopLabel, NULL); + if (!generatedBoolean(w->expr)) + branchTrue(whileBodyTopLabel, test(c)); + + setLabel(e->breakLabel); + mLabelStack.pop_back(); + } + break; + case StmtNode::For: + { + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); + mLabelStack.push_back(e); + + ForStmtNode *f = static_cast(p); + if (f->initializer) + genStmt(f->initializer); + Label *forTestLabel = getLabel(); + branch(forTestLabel); + + Label *forBlockTop = getLabel(); + setLabel(forBlockTop); + genStmt(f->stmt); + + setLabel(e->continueLabel); + if (f->expr3) { + (*mInstructionMap)[iCode->size()] = f->expr3->pos; + genExpr(f->expr3); + } + + setLabel(forTestLabel); + if (f->expr2) { + (*mInstructionMap)[iCode->size()] = f->expr2->pos; + TypedRegister c = genExpr(f->expr2, false, forBlockTop, NULL); + if (!generatedBoolean(f->expr2)) + branchTrue(forBlockTop, test(c)); + } + + setLabel(e->breakLabel); + + mLabelStack.pop_back(); + } + break; + case StmtNode::block: + { + BlockStmtNode *b = static_cast(p); + StmtNode *s = b->statements; + while (s) { + genStmt(s); + s = s->next; + } + } + break; + + case StmtNode::label: + { + LabelStmtNode *l = static_cast(p); + // ok, there's got to be a cleverer way of doing this... + if (currentLabelSet == NULL) { + currentLabelSet = new LabelSet(); + currentLabelSet->push_back(&l->name); + genStmt(l->stmt, currentLabelSet); + delete currentLabelSet; + } + else { + currentLabelSet->push_back(&l->name); + genStmt(l->stmt, currentLabelSet); + currentLabelSet->pop_back(); + } + } + break; + + case StmtNode::Break: + { + GoStmtNode *g = static_cast(p); + if (g->label) { + LabelEntry *e = NULL; + for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { + e = (*i); + if (e->containsLabel(g->name)) + break; + } + if (e) { + ASSERT(e->breakLabel); + branch(e->breakLabel); + } + else + NOT_REACHED("break label not in label set"); + } + else { + ASSERT(!mLabelStack.empty()); + LabelEntry *e = mLabelStack.back(); + ASSERT(e->breakLabel); + branch(e->breakLabel); + } + } + break; + case StmtNode::Continue: + { + GoStmtNode *g = static_cast(p); + if (g->label) { + LabelEntry *e = NULL; + for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { + e = (*i); + if (e->containsLabel(g->name)) + break; + } + if (e) { + ASSERT(e->continueLabel); + branch(e->continueLabel); + } + else + NOT_REACHED("continue label not in label set"); + } + else { + ASSERT(!mLabelStack.empty()); + LabelEntry *e = mLabelStack.back(); + ASSERT(e->continueLabel); + branch(e->continueLabel); + } + } + break; + + case StmtNode::Try: + { + /* + The finallyInvoker is a little stub used by the interpreter to + invoke the finally handler on the (exceptional) way out of the + try block assuming there are no catch clauses. + */ + /*Register ex = NotARegister;*/ + TryStmtNode *t = static_cast(p); + Label *catchLabel = (t->catches) ? getLabel() : NULL; + Label *finallyInvoker = (t->finally) ? getLabel() : NULL; + Label *finallyLabel = (t->finally) ? getLabel() : NULL; + Label *beyondLabel = getLabel(); + beginTry(catchLabel, finallyLabel); + genStmt(t->stmt); + endTry(); + if (finallyLabel) + jsr(finallyLabel); + branch(beyondLabel); + if (catchLabel) { + setLabel(catchLabel); + CatchClause *c = t->catches; + while (c) { + // Bind the incoming exception ... + if (mExceptionRegister.first == NotABanana) + mExceptionRegister = allocateRegister(&Any_Type); + allocateVariable(c->name, mExceptionRegister); + + genStmt(c->stmt); + if (finallyLabel) + jsr(finallyLabel); + c = c->next; + } + } + if (finallyLabel) { + setLabel(finallyInvoker); + jsr(finallyLabel); + throwStmt(mExceptionRegister); + + setLabel(finallyLabel); + genStmt(t->finally); + rts(); + } + setLabel(beyondLabel); + } + break; + + case StmtNode::empty: + /* nada */ + break; + + default: + NOT_REACHED("unimplemented statement kind"); + } + resetStatement(); + return ret; +} + + +} // namespace ICG + +} // namespace JavaScript diff --git a/js/js2/icodegenerator.cpp b/js/js2/icodegenerator.cpp index 7a8de7f33e51..cdac5b109a68 100644 --- a/js/js2/icodegenerator.cpp +++ b/js/js2/icodegenerator.cpp @@ -73,7 +73,11 @@ Formatter& operator<<(Formatter &f, ICodeModule &i) // -ICodeGenerator::ICodeGenerator(Context *cx, ICodeGenerator *containingFunction, JSClass *aClass, ICodeGeneratorFlags flags) +ICodeGenerator::ICodeGenerator(Context *cx, + ICodeGenerator *containingFunction, + JSClass *aClass, + ICodeGeneratorFlags flags, + JSType *resultType) : mTopRegister(0), mExceptionRegister(TypedRegister(NotARegister, &None_Type)), variableList(new VariableList()), @@ -84,20 +88,13 @@ ICodeGenerator::ICodeGenerator(Context *cx, ICodeGenerator *containingFunction, mFlags(flags), pLabels(NULL), mInitName(cx->getWorld().identifiers["__init__"]), - mContainingFunction(containingFunction) + mContainingFunction(containingFunction), + mResultType(resultType) { iCode = new InstructionStream(); iCodeOwner = true; } -JSType *ICodeGenerator::findType(const StringAtom& typeName) -{ - const JSValue& type = mContext->getGlobalObject()->getVariable(typeName); - if (type.isType()) - return type.type; - return &Any_Type; -} - /* -Called to allocate parameter and variable registers, aka 'permanent' registers. -mTopRegister is the current high-water mark. @@ -125,8 +122,17 @@ TypedRegister ICodeGenerator::allocateRegister(JSType *type) return result; } +TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name, const StringAtom& typeName) +{ + return allocateVariable(name, mContext->findType(typeName)); +} -ICodeModule *ICodeGenerator::complete(JSType *resultType) +TypedRegister ICodeGenerator::allocateParameter(const StringAtom& name, bool isOptional, const StringAtom& typeName) +{ + return allocateParameter(name, isOptional, mContext->findType(typeName)); +} + +ICodeModule *ICodeGenerator::complete() { #ifdef DEBUG for (LabelList::iterator i = labels.begin(); @@ -169,12 +175,7 @@ ICodeModule *ICodeGenerator::complete(JSType *resultType) } } */ - ICodeModule* module = new ICodeModule(iCode, - variableList, - parameterList, - mPermanentRegister.size(), - mInstructionMap, - resultType, mExceptionRegister.first); + ICodeModule* module = new ICodeModule(*this); if (pLabels) { uint32 i; uint32 parameterInits = pLabels->size() - 1; // there's an extra label at the end for the actual entryPoint @@ -611,1943 +612,6 @@ Label *ICodeGenerator::setLabel(Label *l) return l; } -/************************************************************************/ - -ExprNode::Kind ICodeGenerator::mapICodeOpToExprNode(ICodeOp op) -{ - switch (op) { - case ADD: - return ExprNode::add; - case SUBTRACT: - return ExprNode::subtract; - case MULTIPLY: - return ExprNode::multiply; - case DIVIDE: - return ExprNode::divide; - case REMAINDER: - return ExprNode::modulo; - case SHIFTLEFT: - return ExprNode::leftShift; - case SHIFTRIGHT: - return ExprNode::rightShift; - case USHIFTRIGHT: - return ExprNode::logicalRightShift; - case AND: - return ExprNode::bitwiseAnd; - case OR: - return ExprNode::bitwiseOr; - case XOR: - return ExprNode::bitwiseXor; - case POSATE: - return ExprNode::plus; - case NEGATE: - return ExprNode::minus; - case BITNOT: - return ExprNode::complement; - case COMPARE_EQ: - return ExprNode::equal; - case COMPARE_LT: - return ExprNode::lessThan; - case COMPARE_LE: - return ExprNode::lessThanOrEqual; - case STRICT_EQ: - return ExprNode::identical; - } - return ExprNode::none; -} - - -ICodeOp ICodeGenerator::mapExprNodeToICodeOp(ExprNode::Kind kind) -{ - // can be an array later, when everything has settled down - switch (kind) { - // binary - case ExprNode::add: - case ExprNode::addEquals: - return ADD; - case ExprNode::subtract: - case ExprNode::subtractEquals: - return SUBTRACT; - case ExprNode::multiply: - case ExprNode::multiplyEquals: - return MULTIPLY; - case ExprNode::divide: - case ExprNode::divideEquals: - return DIVIDE; - case ExprNode::modulo: - case ExprNode::moduloEquals: - return REMAINDER; - case ExprNode::leftShift: - case ExprNode::leftShiftEquals: - return SHIFTLEFT; - case ExprNode::rightShift: - case ExprNode::rightShiftEquals: - return SHIFTRIGHT; - case ExprNode::logicalRightShift: - case ExprNode::logicalRightShiftEquals: - return USHIFTRIGHT; - case ExprNode::bitwiseAnd: - case ExprNode::bitwiseAndEquals: - return AND; - case ExprNode::bitwiseXor: - case ExprNode::bitwiseXorEquals: - return XOR; - case ExprNode::bitwiseOr: - case ExprNode::bitwiseOrEquals: - return OR; - // unary - case ExprNode::plus: - return POSATE; - case ExprNode::minus: - return NEGATE; - case ExprNode::complement: - return BITNOT; - - // relational - case ExprNode::In: - return COMPARE_IN; - case ExprNode::Instanceof: - return INSTANCEOF; - - case ExprNode::equal: - return COMPARE_EQ; - case ExprNode::lessThan: - return COMPARE_LT; - case ExprNode::lessThanOrEqual: - return COMPARE_LE; - case ExprNode::identical: - return STRICT_EQ; - - // these get reversed by the generator - case ExprNode::notEqual: - return COMPARE_EQ; - case ExprNode::greaterThan: - return COMPARE_LT; - case ExprNode::greaterThanOrEqual: - return COMPARE_LE; - case ExprNode::notIdentical: - return STRICT_EQ; - - default: - NOT_REACHED("Unimplemented kind"); - return NOP; - } -} - - -static bool generatedBoolean(ExprNode *p) -{ - switch (p->getKind()) { - case ExprNode::parentheses: - { - UnaryExprNode *u = static_cast(p); - return generatedBoolean(u->op); - } - case ExprNode::True: - case ExprNode::False: - case ExprNode::equal: - case ExprNode::notEqual: - case ExprNode::lessThan: - case ExprNode::lessThanOrEqual: - case ExprNode::greaterThan: - case ExprNode::greaterThanOrEqual: - case ExprNode::identical: - case ExprNode::notIdentical: - case ExprNode::In: - case ExprNode::Instanceof: - case ExprNode::logicalAnd: - case ExprNode::logicalXor: - case ExprNode::logicalOr: - return true; - default: - break; - } - return false; -} - -static bool isSlotName(JSType *t, const StringAtom &name, uint32 &slotIndex, JSType *&type, bool lvalue) -{ - JSClass* c = dynamic_cast(t); - while (c) { - if (c->hasSlot(name)) { - const JSSlot &s = c->getSlot(name); - if (lvalue) { - if (s.mActual || s.mSetter) { - slotIndex = s.mIndex; - type = s.mType; - return true; - } - } - else { - if (s.mActual || s.mGetter) { - slotIndex = s.mIndex; - type = s.mType; - return true; - } - } - return false; - } - c = c->getSuperClass(); - } - return false; -} - -static bool isMethodName(JSType *t, const StringAtom &name, uint32 &slotIndex) -{ - JSClass* c = dynamic_cast(t); - return (c && c->hasMethod(name, slotIndex)); -} - - -static bool isStaticName(JSClass *c, const StringAtom &name, JSType*& type, bool &isConstructor) -{ - do { - if (c->hasStatic(name, type, isConstructor)) - return true; - c = c->getSuperClass(); - } while (c); - return false; -} - -ICodeGenerator::LValueKind ICodeGenerator::getVariableByName(const StringAtom &name, TypedRegister &v) -{ - v = variableList->findVariable(name); - if (v.first == NotARegister) - v = parameterList->findVariable(name); - if (v.first != NotARegister) - return Var; - return NoKind; -} - -ICodeGenerator::LValueKind ICodeGenerator::scanForVariable(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base) -{ - LValueKind k = getVariableByName(name, v); - if (k == Var) return k; - - uint32 count = 0; - ICodeGenerator *upper = mContainingFunction; - while (upper) { - k = upper->getVariableByName(name, v); - if (k == Var) { - base = getClosure(count); - slotIndex = v.first; - return Slot; - } - count++; - upper = upper->mContainingFunction; - } - return NoKind; -} - -// find 'name' (unqualified) in the current context. -// for local variable, returns v.first = register number -// for slot/method, returns slotIndex and sets base appropriately -// (note closure vars also get handled this way) -// v.second is set to the type regardless -ICodeGenerator::LValueKind ICodeGenerator::resolveIdentifier(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base, bool lvalue) -{ - if (!isWithinWith()) { - LValueKind k = scanForVariable(name, v, slotIndex, base); - if (k != NoKind) - return k; - else { - if (mClass) { // we're compiling a method of a class - if (!isStaticMethod()) { - if (isSlotName(mClass, name, slotIndex, v.second, lvalue)) { - base = TypedRegister(0, mClass); - return Slot; - } - if (isMethodName(mClass, name, slotIndex)) { - base = TypedRegister(0, mClass); - return Method; - } - } - bool isConstructor = false; - if (isStaticName(mClass, name, v.second, isConstructor)) { - return (isConstructor) ? Constructor : Static; - } - } - // last chance - if it's a generic name in the global scope, try to get a type for it - v.second = mContext->getGlobalObject()->getType(name); - return Name; - } - } - // all bet's off, generic name & type - v.second = &Any_Type; - return Name; -} - -TypedRegister ICodeGenerator::handleIdentifier(IdentifierExprNode *p, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) -{ - ASSERT(p->getKind() == ExprNode::identifier); - - /*JSType *vType = &Any_Type;*/ - uint32 slotIndex; - TypedRegister v; - TypedRegister base; - - const StringAtom &name = (static_cast(p))->name; - LValueKind lValueKind = resolveIdentifier(name, v, slotIndex, base, lvalue); - JSType *targetType = v.second; - - switch (use) { - case ExprNode::addEquals: - case ExprNode::subtractEquals: - case ExprNode::multiplyEquals: - case ExprNode::divideEquals: - case ExprNode::moduloEquals: - case ExprNode::leftShiftEquals: - case ExprNode::rightShiftEquals: - case ExprNode::logicalRightShiftEquals: - case ExprNode::bitwiseAndEquals: - case ExprNode::bitwiseXorEquals: - case ExprNode::bitwiseOrEquals: - switch (lValueKind) { - case Var: - break; - case Name: - v = loadName(name, v.second); - break; - case Slot: - v = getSlot(base, slotIndex); - break; - case Static: - case Constructor: - v = getStatic(mClass, name); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); - // fall thru... - case ExprNode::assignment: - ret = cast(ret, targetType); - switch (lValueKind) { - case Var: - move(v, ret); - break; - case Name: - saveName(name, ret); - break; - case Slot: - setSlot(base, slotIndex, ret); - break; - case Static: - case Constructor: - setStatic(mClass, name, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::identifier: - switch (lValueKind) { - case Var: - ret = v; - break; - case Name: - ret = loadName(name, v.second); - break; - case Slot: - ret = getSlot(base, slotIndex); - break; - case Static: - case Constructor: - ret = getStatic(mClass, name); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::preDecrement: - case ExprNode::preIncrement: - switch (lValueKind) { - case Var: - ret = binaryOp(xcrementOp, v, loadImmediate(1.0)); - break; - case Name: - ret = loadName(name, v.second); - ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); - saveName(name, ret); - break; - case Slot: - ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); - setSlot(base, slotIndex, ret); - break; - case Static: - case Constructor: - ret = binaryOp(xcrementOp, getStatic(mClass, name), loadImmediate(1.0)); - setStatic(mClass, name, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::postDecrement: - case ExprNode::postIncrement: - switch (lValueKind) { - case Var: - ret = varXcr(v, xcrementOp); - break; - case Name: - ret = nameXcr(name, xcrementOp); - break; - case Slot: - ret = slotXcr(base, slotIndex, xcrementOp); - break; - case Static: - case Constructor: - ret = staticXcr(mClass, name, xcrementOp); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::call: - { - switch (lValueKind) { - case Var: - ret = call(v, args); - break; - case Name: - ret = call(loadName(name), args); - break; - case Method: - ret = call(getMethod(base, slotIndex), args); - break; - case Static: - ret = call(getStatic(mClass, name), args); - break; - case Constructor: - ret = newClass(mClass); - call(bindThis(ret, getStatic(mClass, name)), args); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - } - break; - default: - NOT_REACHED("Bad use kind"); - } - return ret; - -} - -TypedRegister ICodeGenerator::handleDot(BinaryExprNode *b, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) -{ - ASSERT(b->getKind() == ExprNode::dot); - - LValueKind lValueKind = Property; - - if (b->op2->getKind() != ExprNode::identifier) { - NOT_REACHED("Implement me"); // turns into a getProperty (but not via any overloaded [] ) - } - else { - // we have . - const StringAtom &fieldName = static_cast(b->op2)->name; - TypedRegister base; - TypedRegister baseBase; - JSClass *clazz = NULL; - JSType *fieldType = &Any_Type; - uint32 slotIndex; - if ((b->op1->getKind() == ExprNode::identifier) && !isWithinWith()) { - // handle . - const StringAtom &baseName = (static_cast(b->op1))->name; - LValueKind baseKind = resolveIdentifier(baseName, base, slotIndex, baseBase, false); - if (baseKind == Slot) { - base = getSlot(baseBase, slotIndex); - } - // - // handle . - // - if (base.second == &Type_Type) { - const JSValue &v = mContext->getGlobalObject()->getVariable(baseName); - bool isConstructor; - ASSERT(v.isType()); // there's no other way that base.second could be &Type_Type, right? - clazz = dynamic_cast(v.type); - if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) { - lValueKind = (isConstructor) ? Constructor : Static; - } - } - if (lValueKind == Property) { - if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) - lValueKind = Slot; - else - if (isMethodName(base.second, fieldName, slotIndex)) - lValueKind = Method; - else { - bool isConstructor; - clazz = dynamic_cast(base.second); - if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) - lValueKind = (isConstructor) ? Constructor : Static; - } - } - if ((lValueKind == Property) || (base.first == NotARegister)) - base = loadName(baseName, base.second); - } - else { - base = genExpr(b->op1); - if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) - lValueKind = Slot; - else - if (isMethodName(base.second, fieldName, slotIndex)) - lValueKind = Method; - else { - bool isConstructor; - clazz = dynamic_cast(base.second); - if (clazz && clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) - lValueKind = (isConstructor) ? Constructor : Static; - } - } - TypedRegister v; - switch (use) { - case ExprNode::call: - switch (lValueKind) { - case Static: - ret = call(getStatic(clazz, fieldName), args); - break; - case Constructor: - ret = newClass(clazz); - call(bindThis(ret, getStatic(clazz, fieldName)), args); - break; - case Property: - ret = call(bindThis(base, getProperty(base, fieldName)), args); - break; - case Method: - ret = call(getMethod(base, slotIndex), args); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::dot: - case ExprNode::addEquals: - case ExprNode::subtractEquals: - case ExprNode::multiplyEquals: - case ExprNode::divideEquals: - case ExprNode::moduloEquals: - case ExprNode::leftShiftEquals: - case ExprNode::rightShiftEquals: - case ExprNode::logicalRightShiftEquals: - case ExprNode::bitwiseAndEquals: - case ExprNode::bitwiseXorEquals: - case ExprNode::bitwiseOrEquals: - switch (lValueKind) { - case Constructor: - case Static: - v = getStatic(clazz, fieldName); - break; - case Property: - v = getProperty(base, fieldName); - break; - case Slot: - v = getSlot(base, slotIndex); - break; - case Method: - v = getMethod(base, slotIndex); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - if (use == ExprNode::dot) { - ret = v; - break; - } - ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); - // fall thru... - case ExprNode::assignment: - ret = cast(ret, fieldType); - switch (lValueKind) { - case Constructor: - case Static: - setStatic(clazz, fieldName, ret); - break; - case Property: - setProperty(base, fieldName, ret); - break; - case Slot: - setSlot(base, slotIndex, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::postDecrement: - case ExprNode::postIncrement: - { -// JSClass *clss = dynamic_cast(fieldType); -// if (clss) { -// clss->findOverloadedOperator(use); -// } - switch (lValueKind) { - case Constructor: - case Static: - ret = staticXcr(clazz, fieldName, xcrementOp); - break; - case Property: - ret = propertyXcr(base, fieldName, xcrementOp); - break; - case Slot: - ret = slotXcr(base, slotIndex, xcrementOp); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - } - break; - case ExprNode::preDecrement: - case ExprNode::preIncrement: - switch (lValueKind) { - case Constructor: - case Static: - ret = binaryOp(xcrementOp, getStatic(clazz, fieldName), loadImmediate(1.0)); - setStatic(clazz, fieldName, ret); - break; - case Property: - ret = binaryOp(xcrementOp, getProperty(base, fieldName), loadImmediate(1.0)); - setProperty(base, fieldName, ret); - break; - case Slot: - ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); - setSlot(base, slotIndex, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::Delete: - if (lValueKind == Property) { - ret = deleteProperty(base, fieldName); - } - break; - default: - NOT_REACHED("unexpected use node"); - } - ret.second = fieldType; - } - return ret; -} - - - -/* - if trueBranch OR falseBranch are not null, the sub-expression should generate - a conditional branch to the appropriate target. If either branch is NULL, it - indicates that the label is immediately forthcoming. -*/ -TypedRegister ICodeGenerator::genExpr(ExprNode *p, - bool needBoolValueInBranch, - Label *trueBranch, - Label *falseBranch) -{ - TypedRegister ret(NotARegister, &None_Type); - ICodeOp xcrementOp = ADD; - switch (p->getKind()) { - case ExprNode::True: - if (trueBranch || falseBranch) { - if (needBoolValueInBranch) - ret = loadBoolean(true); - if (trueBranch) - branch(trueBranch); - } - else - ret = loadBoolean(true); - break; - case ExprNode::False: - if (trueBranch || falseBranch) { - if (needBoolValueInBranch) - ret = loadBoolean(false); - if (falseBranch) - branch(falseBranch); - } - else - ret = loadBoolean(false); - break; - case ExprNode::Null: - ret = loadNull(); - break; - case ExprNode::parentheses: - { - UnaryExprNode *u = static_cast(p); - ret = genExpr(u->op, needBoolValueInBranch, trueBranch, falseBranch); - } - break; - case ExprNode::New: - { - InvokeExprNode *i = static_cast(p); - ArgumentList *args = new ArgumentList(); - ExprPairList *p = i->pairs; - StringFormatter s; - while (p) { - if (p->field && (p->field->getKind() == ExprNode::identifier)) - args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); - else { - if (p->field && (p->field->getKind() == ExprNode::string)) - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); - else { - s << (uint32)args->size(); - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); - s.clear(); - } - } - p = p->next; - } - if (i->op->getKind() == ExprNode::identifier) { - const StringAtom &className = static_cast(i->op)->name; - const JSValue& value = mContext->getGlobalObject()->getVariable(className); - if (value.isType()) { - JSClass* clazz = dynamic_cast(value.type); - if (clazz) { - ret = newClass(clazz); - ret = call(bindThis(ret, getStatic(clazz, className)), args); - } - else { - // - // like 'new Boolean()' - see if the type has a constructor - // - JSFunction *f = value.type->getConstructor(); - if (f) - ret = directCall(f, args); - else - NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. - } - } - else { - if (value.isFunction()) { - TypedRegister f = loadName(className, value.type); - ret = newObject(f); - ret = call(bindThis(ret, f), args); - } - else - NOT_REACHED("new , where is not a function"); // XXX Runtime error. - } - } - else - ret = newObject(TypedRegister(NotARegister, &Any_Type)); // XXX more ? - } - break; - case ExprNode::Delete: - { - UnaryExprNode *d = static_cast(p); - ASSERT(d->op->getKind() == ExprNode::dot); - ret = handleDot(static_cast(d->op), p->getKind(), xcrementOp, ret, NULL, true); - // rather than getProperty(), need to do a deleteProperty(). - } - break; - case ExprNode::call : - { - InvokeExprNode *i = static_cast(p); - ArgumentList *args = new ArgumentList(); - ExprPairList *p = i->pairs; - StringFormatter s; - while (p) { - if (p->field && (p->field->getKind() == ExprNode::identifier)) - args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); - else { - if (p->field && (p->field->getKind() == ExprNode::string)) - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); - else { - s << (uint32)args->size(); - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); - s.clear(); - } - } - p = p->next; - } - - if (i->op->getKind() == ExprNode::dot) { - BinaryExprNode *b = static_cast(i->op); - ret = handleDot(b, ExprNode::call, xcrementOp, ret, args, false); - } - else { - if (i->op->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(i->op), ExprNode::call, xcrementOp, ret, args, false); - } - else { - if (i->op->getKind() == ExprNode::index) { - InvokeExprNode *ii = static_cast(i->op); - TypedRegister base = genExpr(ii->op); - ret = call(bindThis(base, getElement(base, genExpr(ii->pairs->value))), args); // FIXME, only taking first index - } - else - ASSERT("WAH!"); - } - } - } - break; - case ExprNode::index : - { - InvokeExprNode *i = static_cast(p); - TypedRegister base = genExpr(i->op); - JSClass *clazz = dynamic_cast(base.second); - if (clazz) { - // look for operator [] and invoke it - } - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - ret = getElement(base, index); - } - break; - case ExprNode::dot : - { - BinaryExprNode *b = static_cast(p); - ret = handleDot(b, p->getKind(), xcrementOp, ret, NULL, false); - } - break; - case ExprNode::This : - { - ret = TypedRegister(0, mClass ? mClass : &Any_Type); - } - break; - case ExprNode::identifier : - { - ret = handleIdentifier(static_cast(p), ExprNode::identifier, xcrementOp, ret, NULL, false); - } - break; - case ExprNode::number : - ret = loadImmediate((static_cast(p))->value); - break; - case ExprNode::string : - ret = loadString(mContext->getWorld().identifiers[(static_cast(p))->str]); - break; - case ExprNode::preDecrement: - xcrementOp = SUBTRACT; - case ExprNode::preIncrement: - { - UnaryExprNode *u = static_cast(p); - if (u->op->getKind() == ExprNode::dot) { - ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(u->op); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - ret = getElement(base, index); - ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); - setElement(base, index, ret); - } - else - ASSERT("WAH!"); - } - break; - case ExprNode::postDecrement: - xcrementOp = SUBTRACT; - case ExprNode::postIncrement: - { - UnaryExprNode *u = static_cast(p); - if (u->op->getKind() == ExprNode::dot) { - ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(u->op); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - ret = elementXcr(base, index, xcrementOp); - } - else - ASSERT("WAH!"); - } - break; - - case ExprNode::plus: - case ExprNode::minus: - case ExprNode::complement: - { - UnaryExprNode *u = static_cast(p); - TypedRegister r = genExpr(u->op); - ret = op(mapExprNodeToICodeOp(p->getKind()), r); - } - break; - case ExprNode::add: - case ExprNode::subtract: - case ExprNode::multiply: - case ExprNode::divide: - case ExprNode::modulo: - case ExprNode::leftShift: - case ExprNode::rightShift: - case ExprNode::logicalRightShift: - case ExprNode::bitwiseAnd: - case ExprNode::bitwiseXor: - case ExprNode::bitwiseOr: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); - } - break; - case ExprNode::assignment: - { - BinaryExprNode *b = static_cast(p); - ret = genExpr(b->op2); - if (b->op1->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::dot) { - BinaryExprNode *lb = static_cast(b->op1); - ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(b->op1); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - setElement(base, index, ret); - } - else - ASSERT("WAH!"); - } - break; - case ExprNode::addEquals: - case ExprNode::subtractEquals: - case ExprNode::multiplyEquals: - case ExprNode::divideEquals: - case ExprNode::moduloEquals: - case ExprNode::leftShiftEquals: - case ExprNode::rightShiftEquals: - case ExprNode::logicalRightShiftEquals: - case ExprNode::bitwiseAndEquals: - case ExprNode::bitwiseXorEquals: - case ExprNode::bitwiseOrEquals: - { - BinaryExprNode *b = static_cast(p); - ret = genExpr(b->op2); - if (b->op1->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::dot) { - BinaryExprNode *lb = static_cast(b->op1); - ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(b->op1); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - TypedRegister v = getElement(base, index); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), v, ret); - setElement(base, index, ret); - } - else - ASSERT("WAH!"); - } - break; - case ExprNode::equal: - case ExprNode::lessThan: - case ExprNode::lessThanOrEqual: - case ExprNode::identical: - case ExprNode::In: - case ExprNode::Instanceof: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); - if (trueBranch || falseBranch) { - if (trueBranch == NULL) - branchFalse(falseBranch, ret); - else { - branchTrue(trueBranch, ret); - if (falseBranch) - branch(falseBranch); - } - } - } - break; - case ExprNode::greaterThan: - case ExprNode::greaterThanOrEqual: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r2, r1); // will return reverse case - if (trueBranch || falseBranch) { - if (trueBranch == NULL) - branchFalse(falseBranch, ret); - else { - branchTrue(trueBranch, ret); - if (falseBranch) - branch(falseBranch); - } - } - } - break; - - case ExprNode::notEqual: - case ExprNode::notIdentical: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); // will generate equal/identical code - if (trueBranch || falseBranch) { - if (trueBranch == NULL) - branchTrue(falseBranch, ret); - else { - branchFalse(trueBranch, ret); - if (falseBranch) - branch(falseBranch); - } - } - else - ret = logicalNot(ret); - - } - break; - - case ExprNode::logicalAnd: - { - BinaryExprNode *b = static_cast(p); - if (trueBranch || falseBranch) { - genExpr(b->op1, needBoolValueInBranch, NULL, falseBranch); - genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); - } - else { - Label *fBranch = getLabel(); - TypedRegister r1 = genExpr(b->op1, true, NULL, fBranch); - if (!generatedBoolean(b->op1)) { - r1 = test(r1); - branchFalse(fBranch, r1); - } - TypedRegister r2 = genExpr(b->op2); - if (!generatedBoolean(b->op2)) { - r2 = test(r2); - } - if (r1 != r2) // FIXME, need a way to specify a dest??? - move(r1, r2); - setLabel(fBranch); - ret = r1; - } - } - break; - case ExprNode::logicalOr: - { - BinaryExprNode *b = static_cast(p); - if (trueBranch || falseBranch) { - genExpr(b->op1, needBoolValueInBranch, trueBranch, NULL); - genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); - } - else { - Label *tBranch = getLabel(); - TypedRegister r1 = genExpr(b->op1, true, tBranch, NULL); - if (!generatedBoolean(b->op1)) { - r1 = test(r1); - branchTrue(tBranch, r1); - } - TypedRegister r2 = genExpr(b->op2); - if (!generatedBoolean(b->op2)) { - r2 = test(r2); - } - if (r1 != r2) // FIXME, need a way to specify a dest??? - move(r1, r2); - setLabel(tBranch); - ret = r1; - } - } - break; - - case ExprNode::conditional: - { - TernaryExprNode *t = static_cast(p); - Label *fBranch = getLabel(); - Label *beyondBranch = getLabel(); - TypedRegister c = genExpr(t->op1, false, NULL, fBranch); - if (!generatedBoolean(t->op1)) - branchFalse(fBranch, test(c)); - TypedRegister r1 = genExpr(t->op2); - branch(beyondBranch); - setLabel(fBranch); - TypedRegister r2 = genExpr(t->op3); - if (r1 != r2) // FIXME, need a way to specify a dest??? - move(r1, r2); - setLabel(beyondBranch); - ret = r1; - } - break; - - case ExprNode::objectLiteral: - { - ret = newObject(TypedRegister(NotARegister, &Any_Type)); - PairListExprNode *plen = static_cast(p); - ExprPairList *e = plen->pairs; - while (e) { - if (e->field && e->value && (e->field->getKind() == ExprNode::identifier)) - setProperty(ret, (static_cast(e->field))->name, genExpr(e->value)); - e = e->next; - } - } - break; - - case ExprNode::functionLiteral: - { - FunctionExprNode *f = static_cast(p); - ICodeModule *icm = genFunction(f->function, false, false, NULL); - ret = newClosure(icm); - } - break; - - case ExprNode::at: - { - BinaryExprNode *b = static_cast(p); - // for now, just handle simple identifiers on the rhs. - ret = genExpr(b->op1); - if (b->op2->getKind() == ExprNode::identifier) { - TypedRegister t; - const StringAtom &name = (static_cast(b->op2))->name; - ASSERT(t.second == &Type_Type); - const JSValue &v = mContext->getGlobalObject()->getVariable(name); - ASSERT(v.isType()); - JSClass *clazz = dynamic_cast(v.type); - if (clazz) - ret = cast(ret, clazz); - else - ret = cast(ret, t.second); - } - else - NOT_REACHED("Anything more complex than @ is not implemented"); - } - break; - - - default: - { - NOT_REACHED("Unsupported ExprNode kind"); - } - } - return ret; -} - -bool LabelEntry::containsLabel(const StringAtom *label) -{ - if (labelSet) { - for (LabelSet::iterator i = labelSet->begin(); i != labelSet->end(); i++) - if ( (*i) == label ) - return true; - } - return false; -} - -static bool hasAttribute(const IdentifierList* identifiers, Token::Kind tokenKind) -{ - while (identifiers) { - if (identifiers->name.tokenKind == tokenKind) - return true; - identifiers = identifiers->next; - } - return false; -} - -static bool hasAttribute(const IdentifierList* identifiers, StringAtom &name) -{ - while (identifiers) { - if (identifiers->name == name) - return true; - identifiers = identifiers->next; - } - return false; -} - -JSType *ICodeGenerator::extractType(ExprNode *t) -{ - JSType* type = &Any_Type; - // FUTURE: do code generation for type expressions. - if (t && (t->getKind() == ExprNode::identifier)) { - IdentifierExprNode* typeExpr = static_cast(t); - type = findType(typeExpr->name); - } - return type; -} - -JSType *ICodeGenerator::getParameterType(FunctionDefinition &function, int index) -{ - VariableBinding *v = function.parameters; - while (v) { - if (index-- == 0) - return extractType(v->type); - else - v = v->next; - } - return NULL; -} - -ICodeModule *ICodeGenerator::genFunction(FunctionDefinition &function, bool isStatic, bool isConstructor, JSClass *superclass) -{ - ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; - - ICodeGenerator icg(mContext, this, mClass, flags); - icg.allocateParameter(mContext->getWorld().identifiers["this"], false, (mClass) ? mClass : &Any_Type); // always parameter #0 - VariableBinding *v = function.parameters; - bool unnamed = true; - uint32 positionalCount = 0; - StringFormatter s; - while (v) { - if (unnamed && (v == function.namedParameters)) { // Track when we hit the first named parameter. - icg.parameterList->setPositionalCount(positionalCount); - unnamed = false; - } - - // 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 == 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) { - positionalCount++; - s << r.first - 1; // the first positional parameter is '0' - icg.parameterList->add(mContext->getWorld().identifiers[s.getString()], r, (v->initializer != NULL)); - s.clear(); - } - } - } - v = v->next; - } - if (unnamed) icg.parameterList->setPositionalCount(positionalCount); - - // now allocate the rest parameter - if (function.restParameter) { - v = 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 = function.optParameters; - if (v) { - while (v) { // include the rest parameter, as it may have an initializer - if (v->name && (v->name->getKind() == ExprNode::identifier)) { - icg.addParameterLabel(icg.setLabel(icg.getLabel())); - TypedRegister p = icg.genExpr(v->name); - if (v->initializer) { // might be NULL when we get to the restParameter - Label *l = icg.getLabel(); - icg.branchInitialized(l, p); - icg.move(p, icg.genExpr(v->initializer)); - icg.setLabel(l); - } - else { // an un-initialized rest parameter is still an empty array - if (v == function.restParameter) { - Label *l = icg.getLabel(); - icg.branchInitialized(l, p); - icg.move(p, icg.newArray()); - icg.setLabel(l); - } - } - } - v = v->next; - } - icg.addParameterLabel(icg.setLabel(icg.getLabel())); // to provide the entry-point for the default case - } - - if (isConstructor) { - /* - See if the first statement is an expression statement consisting - of a call to super(). If not we need to add a call to the default - superclass constructor ourselves. - */ - TypedRegister thisValue = TypedRegister(0, mClass); - ArgumentList *args = new ArgumentList(); - if (superclass) { - bool foundSuperCall = false; - BlockStmtNode *b = function.body; - if (b && b->statements && (b->statements->getKind() == StmtNode::expression)) { - ExprStmtNode *e = static_cast(b->statements); - if (e->expr->getKind() == ExprNode::call) { - InvokeExprNode *i = static_cast(e->expr); - if (i->op->getKind() == ExprNode::dot) { - BinaryExprNode *b = static_cast(i->op); - if ((b->op1->getKind() == ExprNode::This) && (b->op2->getKind() == ExprNode::qualify)) { - BinaryExprNode *q = static_cast(b->op2); - if (q->op1->getKind() == ExprNode::Super) { - // XXX verify that q->op2 is either the superclass name or a constructor for it - foundSuperCall = true; - } - } - } - } - } - if (!foundSuperCall) { // invoke the default superclass constructor - icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); - } - } - if (mClass->hasStatic(mInitName)) - icg.call(icg.bindThis(thisValue, icg.getStatic(mClass, mInitName)), args); // ok, so it's mis-named - - } - if (function.body) - icg.genStmt(function.body); - if (isConstructor) { - TypedRegister thisValue = TypedRegister(0, mClass); - icg.returnStmt(thisValue); - } - return icg.complete(extractType(function.resultType)); -} - -TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet) -{ - TypedRegister ret(NotARegister, &None_Type); - - startStatement(p->pos); - - switch (p->getKind()) { - case StmtNode::Class: - { - // FIXME: need a semantic check to make sure a class isn't being redefined(?) - ClassStmtNode *classStmt = static_cast(p); - ASSERT(classStmt->name->getKind() == ExprNode::identifier); - IdentifierExprNode* nameExpr = static_cast(classStmt->name); - JSClass* superclass = 0; - if (classStmt->superclass) { - ASSERT(classStmt->superclass->getKind() == ExprNode::identifier); - IdentifierExprNode* superclassExpr = static_cast(classStmt->superclass); - const JSValue& superclassValue = mContext->getGlobalObject()->getVariable(superclassExpr->name); - ASSERT(superclassValue.isObject() && !superclassValue.isNull()); - superclass = static_cast(superclassValue.object); - } - JSClass* thisClass = new JSClass(mContext->getGlobalObject(), nameExpr->name, superclass); - // is it ok for a partially defined class to appear in global scope? this is needed - // to handle recursive types, such as linked list nodes. - mContext->getGlobalObject()->defineVariable(nameExpr->name, &Type_Type, JSValue(thisClass)); - - -/* - Pre-pass to declare all the methods & fields -*/ - bool needsInstanceInitializer = false; - TypedRegister thisRegister = TypedRegister(0, thisClass); - if (classStmt->body) { - StmtNode* s = classStmt->body->statements; - while (s) { - switch (s->getKind()) { - case StmtNode::Const: - case StmtNode::Var: - { - VariableStmtNode *vs = static_cast(s); - bool isStatic = hasAttribute(vs->attributes, Token::Static); - VariableBinding *v = vs->bindings; - while (v) { - if (v->name) { - ASSERT(v->name->getKind() == ExprNode::identifier); - IdentifierExprNode* idExpr = static_cast(v->name); - JSType* type = extractType(v->type); - if (isStatic) - thisClass->defineStatic(idExpr->name, type); - else { - if (hasAttribute(vs->attributes, mContext->getWorld().identifiers["virtual"])) - thisClass->defineSlot(idExpr->name, type, JSSlot::kIsVirtual); - else - thisClass->defineSlot(idExpr->name, type); - if (v->initializer) - needsInstanceInitializer = true; - } - } - v = v->next; - } - } - break; - case StmtNode::Constructor: - case StmtNode::Function: - { - FunctionStmtNode *f = static_cast(s); - bool isStatic = hasAttribute(f->attributes, Token::Static); - bool isConstructor = (s->getKind() == StmtNode::Constructor); - if (f->function.prefix == FunctionName::Operator) { - thisClass->defineOperator(f->function.op, getParameterType(f->function, 0), getParameterType(f->function, 1), NULL); - } - else - if (f->function.name->getKind() == ExprNode::identifier) { - const StringAtom& name = (static_cast(f->function.name))->name; - if (isConstructor) - thisClass->defineConstructor(name); - else - if (isStatic) - thisClass->defineStatic(name, &Function_Type); - else { - switch (f->function.prefix) { - case FunctionName::Get: - thisClass->setGetter(name, NULL, extractType(f->function.resultType)); - break; - case FunctionName::Set: - thisClass->setSetter(name, NULL, extractType(f->function.resultType)); - break; - case FunctionName::normal: - thisClass->defineMethod(name, NULL); - break; - default: - NOT_REACHED("unexpected prefix"); - break; - } - } - } - } - break; - default: - NOT_REACHED("unimplemented class member statement"); - break; - } - s = s->next; - } - } - if (needsInstanceInitializer) - thisClass->defineStatic(mInitName, &Function_Type); -/* - Now gen code for each -*/ - - bool hasDefaultConstructor = false; - if (classStmt->body) { - JSScope* thisScope = thisClass->getScope(); - ICodeGenerator *ccg = NULL; - Context *classContext = new Context(mContext->getWorld(), thisScope); - if (needsInstanceInitializer) { - // constructor code generator. Slot variable - // initializers get added to this function. - ccg = new ICodeGenerator(classContext, NULL, thisClass, kNoFlags); - ccg->allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 - } - - ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod); // static initializer code generator. - // static field inits, plus code to initialize - // static method slots. - StmtNode* s = classStmt->body->statements; - while (s) { - switch (s->getKind()) { - case StmtNode::Const: - case StmtNode::Var: - { - VariableStmtNode *vs = static_cast(s); - bool isStatic = hasAttribute(vs->attributes, Token::Static); - VariableBinding *v = vs->bindings; - while (v) { - if (v->name) { - ASSERT(v->name->getKind() == ExprNode::identifier); - if (v->initializer) { - IdentifierExprNode* idExpr = static_cast(v->name); - if (isStatic) { - scg.setStatic(thisClass, idExpr->name, scg.genExpr(v->initializer)); - scg.resetStatement(); - } else { - const JSSlot& slot = thisClass->getSlot(idExpr->name); - ccg->setSlot(thisRegister, slot.mIndex, ccg->genExpr(v->initializer)); - ccg->resetStatement(); - } - } - } - v = v->next; - } - } - break; - case StmtNode::Constructor: - case StmtNode::Function: - { - FunctionStmtNode *f = static_cast(s); - bool isStatic = hasAttribute(f->attributes, Token::Static); - bool isConstructor = (s->getKind() == StmtNode::Constructor); - ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; - - ICodeGenerator mcg(classContext, NULL, thisClass, flags); // method code generator. - ICodeModule *icm = mcg.genFunction(f->function, isStatic, isConstructor, superclass); - if (f->function.prefix == FunctionName::Operator) { - thisClass->defineOperator(f->function.op, getParameterType(f->function, 0), getParameterType(f->function, 1), new JSFunction(icm)); - } - else - if (f->function.name->getKind() == ExprNode::identifier) { - const StringAtom& name = (static_cast(f->function.name))->name; - if (isConstructor) { - if (name == nameExpr->name) - hasDefaultConstructor = true; - scg.setStatic(thisClass, name, scg.newFunction(icm)); - } - else - if (isStatic) - scg.setStatic(thisClass, name, scg.newFunction(icm)); - else { - switch (f->function.prefix) { - case FunctionName::Get: - thisClass->setGetter(name, new JSFunction(icm), icm->mResultType); - break; - case FunctionName::Set: - thisClass->setSetter(name, new JSFunction(icm), icm->mResultType); - break; - case FunctionName::normal: - thisClass->defineMethod(name, new JSFunction(icm)); - break; - default: - NOT_REACHED("unexpected prefix"); - break; - } - } - } - } - break; - default: - NOT_REACHED("unimplemented class member statement"); - break; - } - s = s->next; - } - - // add the instance initializer - if (ccg) { - scg.setStatic(thisClass, mInitName, scg.newFunction(ccg->complete(&Void_Type))); - delete ccg; - } - // invent a default constructor if necessary, it just calls the - // initializer and the superclass default constructor - if (!hasDefaultConstructor) { - TypedRegister thisValue = TypedRegister(0, thisClass); - ArgumentList *args = new ArgumentList(); - ICodeGenerator icg(classContext, NULL, thisClass, kIsStaticMethod); - icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 - if (superclass) - icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); - if (thisClass->hasStatic(mInitName)) - icg.call(icg.bindThis(thisValue, icg.getStatic(thisClass, mInitName)), args); - icg.returnStmt(thisValue); - thisClass->defineConstructor(nameExpr->name); - scg.setStatic(thisClass, nameExpr->name, scg.newFunction(icg.complete(&Void_Type))); - } - // freeze the class. - thisClass->complete(); - - - // REVISIT: using the scope of the class to store both methods and statics. - if (scg.getICode()->size()) { - ICodeModule* clinit = scg.complete(&Void_Type); - classContext->interpret(clinit, JSValues()); - delete clinit; - } - delete classContext; - } - } - break; - case StmtNode::Function: - { - FunctionStmtNode *f = static_cast(p); - bool isStatic = hasAttribute(f->attributes, Token::Static); - ICodeModule *icm = genFunction(f->function, isStatic, false, NULL); - JSType *resultType = extractType(f->function.resultType); - if (f->function.name->getKind() == ExprNode::identifier) { - const StringAtom& name = (static_cast(f->function.name))->name; - switch (f->function.prefix) { - case FunctionName::Get: - if (isTopLevel()) { - mContext->getGlobalObject()->defineVariable(name, resultType); - mContext->getGlobalObject()->setGetter(name, new JSFunction(icm)); - } - else { - // is this legal - a nested getter? - NOT_REACHED("Better check with Waldemar"); - //allocateVariable(name, resultType); - } - break; - case FunctionName::Set: - if (isTopLevel()) { - mContext->getGlobalObject()->defineVariable(name, resultType); - mContext->getGlobalObject()->setSetter(name, new JSFunction(icm)); - } - else { - // is this legal - a nested setter? - NOT_REACHED("Better check with Waldemar"); - //allocateVariable(name, resultType); - } - break; - case FunctionName::normal: - mContext->getGlobalObject()->defineFunction(name, icm); - break; - default: - NOT_REACHED("unexpected prefix"); - break; - } - } - } - break; - case StmtNode::Import: - { - ImportStmtNode *i = static_cast(p); - String *fileName = i->bindings->packageName.str; - if (fileName) { /// if not, build one from the idList instead - std::string str(fileName->length(), char()); - std::transform(fileName->begin(), fileName->end(), str.begin(), narrow); - FILE* f = fopen(str.c_str(), "r"); - if (f) { - (void)mContext->readEvalFile(f, *fileName); - fclose(f); - } - } - } - break; - case StmtNode::Var: - { - VariableStmtNode *vs = static_cast(p); - VariableBinding *v = vs->bindings; - while (v) { - if (v->name && (v->name->getKind() == ExprNode::identifier)) { - JSType *type = extractType(v->type); - if (isTopLevel()) - mContext->getGlobalObject()->defineVariable((static_cast(v->name))->name, type); - else - allocateVariable((static_cast(v->name))->name, type); - if (v->initializer) { - if (!isTopLevel() && !isWithinWith()) { - TypedRegister r = genExpr(v->name); - TypedRegister val = genExpr(v->initializer); - val = cast(val, type); - move(r, val); - } - else { - TypedRegister val = genExpr(v->initializer); - val = cast(val, type); - saveName((static_cast(v->name))->name, val); - } - } - } - v = v->next; - } - } - break; - case StmtNode::expression: - { - ExprStmtNode *e = static_cast(p); - ret = genExpr(e->expr); - } - break; - case StmtNode::Throw: - { - ExprStmtNode *e = static_cast(p); - throwStmt(genExpr(e->expr)); - } - break; - case StmtNode::Debugger: - { - debuggerStmt(); - } - break; - case StmtNode::Return: - { - ExprStmtNode *e = static_cast(p); - if (e->expr) - returnStmt(ret = genExpr(e->expr)); - else - returnStmt(TypedRegister(NotARegister, &Void_Type)); - } - break; - case StmtNode::If: - { - Label *falseLabel = getLabel(); - UnaryStmtNode *i = static_cast(p); - TypedRegister c = genExpr(i->expr, false, NULL, falseLabel); - if (!generatedBoolean(i->expr)) - branchFalse(falseLabel, test(c)); - genStmt(i->stmt); - setLabel(falseLabel); - } - break; - case StmtNode::IfElse: - { - Label *falseLabel = getLabel(); - Label *trueLabel = getLabel(); - Label *beyondLabel = getLabel(); - BinaryStmtNode *i = static_cast(p); - TypedRegister c = genExpr(i->expr, false, trueLabel, falseLabel); - if (!generatedBoolean(i->expr)) - branchFalse(falseLabel, test(c)); - setLabel(trueLabel); - genStmt(i->stmt); - branch(beyondLabel); - setLabel(falseLabel); - genStmt(i->stmt2); - setLabel(beyondLabel); - } - break; - case StmtNode::With: - { - UnaryStmtNode *w = static_cast(p); - TypedRegister o = genExpr(w->expr); - bool withinWith = isWithinWith(); - setFlag(kIsWithinWith, true); - beginWith(o); - genStmt(w->stmt); - endWith(); - setFlag(kIsWithinWith, withinWith); - } - break; - case StmtNode::Switch: - { - Label *defaultLabel = NULL; - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel()); - mLabelStack.push_back(e); - SwitchStmtNode *sw = static_cast(p); - TypedRegister sc = genExpr(sw->expr); - StmtNode *s = sw->statements; - // ECMA requires case & default statements to be immediate children of switch - // unlike C where they can be arbitrarily deeply nested in other statements. - Label *nextCaseLabel = NULL; - GenericBranch *lastBranch = NULL; - while (s) { - if (s->getKind() == StmtNode::Case) { - ExprStmtNode *c = static_cast(s); - if (c->expr) { - if (nextCaseLabel) - setLabel(nextCaseLabel); - nextCaseLabel = getLabel(); - TypedRegister r = genExpr(c->expr); - TypedRegister eq = binaryOp(COMPARE_EQ, r, sc); - lastBranch = branchFalse(nextCaseLabel, eq); - } - else { - defaultLabel = getLabel(); - setLabel(defaultLabel); - } - } - else - genStmt(s); - s = s->next; - } - if (nextCaseLabel) - setLabel(nextCaseLabel); - if (defaultLabel && lastBranch) - lastBranch->setTarget(defaultLabel); - - setLabel(e->breakLabel); - mLabelStack.pop_back(); - } - break; - case StmtNode::DoWhile: - { - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); - mLabelStack.push_back(e); - UnaryStmtNode *d = static_cast(p); - Label *doBodyTopLabel = getLabel(); - setLabel(doBodyTopLabel); - genStmt(d->stmt); - setLabel(e->continueLabel); - TypedRegister c = genExpr(d->expr, false, doBodyTopLabel, NULL); - if (!generatedBoolean(d->expr)) - branchTrue(doBodyTopLabel, test(c)); - setLabel(e->breakLabel); - mLabelStack.pop_back(); - } - break; - case StmtNode::While: - { - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); - mLabelStack.push_back(e); - branch(e->continueLabel); - - UnaryStmtNode *w = static_cast(p); - - Label *whileBodyTopLabel = getLabel(); - setLabel(whileBodyTopLabel); - genStmt(w->stmt); - - setLabel(e->continueLabel); - TypedRegister c = genExpr(w->expr, false, whileBodyTopLabel, NULL); - if (!generatedBoolean(w->expr)) - branchTrue(whileBodyTopLabel, test(c)); - - setLabel(e->breakLabel); - mLabelStack.pop_back(); - } - break; - case StmtNode::For: - { - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); - mLabelStack.push_back(e); - - ForStmtNode *f = static_cast(p); - if (f->initializer) - genStmt(f->initializer); - Label *forTestLabel = getLabel(); - branch(forTestLabel); - - Label *forBlockTop = getLabel(); - setLabel(forBlockTop); - genStmt(f->stmt); - - setLabel(e->continueLabel); - if (f->expr3) { - (*mInstructionMap)[iCode->size()] = f->expr3->pos; - genExpr(f->expr3); - } - - setLabel(forTestLabel); - if (f->expr2) { - (*mInstructionMap)[iCode->size()] = f->expr2->pos; - TypedRegister c = genExpr(f->expr2, false, forBlockTop, NULL); - if (!generatedBoolean(f->expr2)) - branchTrue(forBlockTop, test(c)); - } - - setLabel(e->breakLabel); - - mLabelStack.pop_back(); - } - break; - case StmtNode::block: - { - BlockStmtNode *b = static_cast(p); - StmtNode *s = b->statements; - while (s) { - genStmt(s); - s = s->next; - } - } - break; - - case StmtNode::label: - { - LabelStmtNode *l = static_cast(p); - // ok, there's got to be a cleverer way of doing this... - if (currentLabelSet == NULL) { - currentLabelSet = new LabelSet(); - currentLabelSet->push_back(&l->name); - genStmt(l->stmt, currentLabelSet); - delete currentLabelSet; - } - else { - currentLabelSet->push_back(&l->name); - genStmt(l->stmt, currentLabelSet); - currentLabelSet->pop_back(); - } - } - break; - - case StmtNode::Break: - { - GoStmtNode *g = static_cast(p); - if (g->label) { - LabelEntry *e = NULL; - for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { - e = (*i); - if (e->containsLabel(g->name)) - break; - } - if (e) { - ASSERT(e->breakLabel); - branch(e->breakLabel); - } - else - NOT_REACHED("break label not in label set"); - } - else { - ASSERT(!mLabelStack.empty()); - LabelEntry *e = mLabelStack.back(); - ASSERT(e->breakLabel); - branch(e->breakLabel); - } - } - break; - case StmtNode::Continue: - { - GoStmtNode *g = static_cast(p); - if (g->label) { - LabelEntry *e = NULL; - for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { - e = (*i); - if (e->containsLabel(g->name)) - break; - } - if (e) { - ASSERT(e->continueLabel); - branch(e->continueLabel); - } - else - NOT_REACHED("continue label not in label set"); - } - else { - ASSERT(!mLabelStack.empty()); - LabelEntry *e = mLabelStack.back(); - ASSERT(e->continueLabel); - branch(e->continueLabel); - } - } - break; - - case StmtNode::Try: - { - /* - The finallyInvoker is a little stub used by the interpreter to - invoke the finally handler on the (exceptional) way out of the - try block assuming there are no catch clauses. - */ - /*Register ex = NotARegister;*/ - TryStmtNode *t = static_cast(p); - Label *catchLabel = (t->catches) ? getLabel() : NULL; - Label *finallyInvoker = (t->finally) ? getLabel() : NULL; - Label *finallyLabel = (t->finally) ? getLabel() : NULL; - Label *beyondLabel = getLabel(); - beginTry(catchLabel, finallyLabel); - genStmt(t->stmt); - endTry(); - if (finallyLabel) - jsr(finallyLabel); - branch(beyondLabel); - if (catchLabel) { - setLabel(catchLabel); - CatchClause *c = t->catches; - while (c) { - // Bind the incoming exception ... - if (mExceptionRegister.first == NotABanana) - mExceptionRegister = allocateRegister(&Any_Type); - allocateVariable(c->name, mExceptionRegister); - - genStmt(c->stmt); - if (finallyLabel) - jsr(finallyLabel); - c = c->next; - } - } - if (finallyLabel) { - setLabel(finallyInvoker); - jsr(finallyLabel); - throwStmt(mExceptionRegister); - - setLabel(finallyLabel); - genStmt(t->finally); - rts(); - } - setLabel(beyondLabel); - } - break; - - case StmtNode::empty: - /* nada */ - break; - - default: - NOT_REACHED("unimplemented statement kind"); - } - resetStatement(); - return ret; -} - - -/************************************************************************/ - Formatter& ICodeGenerator::print(Formatter& f) { @@ -2598,7 +662,7 @@ ICodeModule *ICodeGenerator::readFunction(XMLNode *element, JSClass *thisClass) String parameterTypeName; element->getValue(widenCString("name"), parameterName); element->getValue(widenCString("type"), parameterTypeName); - JSType *parameterType = findType(mContext->getWorld().identifiers[parameterTypeName]); + JSType *parameterType = mContext->findType(mContext->getWorld().identifiers[parameterTypeName]); theParameterList->add(mContext->getWorld().identifiers[parameterName], TypedRegister(pCount, parameterType), false); s << pCount - 1; theParameterList->add(mContext->getWorld().identifiers[s.getString()], TypedRegister(pCount, parameterType), false); @@ -2608,7 +672,7 @@ ICodeModule *ICodeGenerator::readFunction(XMLNode *element, JSClass *thisClass) } theParameterList->setPositionalCount(pCount); - JSType *resultType = findType(mContext->getWorld().identifiers[resultTypeName]); + JSType *resultType = mContext->findType(mContext->getWorld().identifiers[resultTypeName]); String &body = element->body(); if (body.length()) { std::string str(body.length(), char()); @@ -2655,8 +719,8 @@ ICodeModule *ICodeGenerator::readICode(const char *fileName) JSClass* thisClass = new JSClass(mContext->getGlobalObject(), className, superclass); JSScope* thisScope = thisClass->getScope(); Context *classContext = new Context(mContext->getWorld(), thisScope); - ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod); - ICodeGenerator ccg(classContext, NULL, thisClass, kNoFlags); + ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod, &Void_Type); + ICodeGenerator ccg(classContext, NULL, thisClass, kNoFlags, &Void_Type); ccg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); thisClass->defineStatic(mInitName, &Function_Type); @@ -2687,7 +751,7 @@ ICodeModule *ICodeGenerator::readICode(const char *fileName) element->getValue(widenCString("name"), fieldName); element->getValue(widenCString("type"), fieldType); - JSType *type = findType(mContext->getWorld().identifiers[fieldType]); + JSType *type = mContext->findType(mContext->getWorld().identifiers[fieldType]); if (element->hasAttribute(widenCString("static"))) thisClass->defineStatic(fieldName, type); @@ -2696,11 +760,11 @@ ICodeModule *ICodeGenerator::readICode(const char *fileName) } } } - scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete(&Void_Type))); + scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete())); thisClass->complete(); if (scg.getICode()->size()) { - ICodeModule* clinit = scg.complete(&Void_Type); + ICodeModule* clinit = scg.complete(); classContext->interpret(clinit, JSValues()); delete clinit; } diff --git a/js/js2/icodegenerator.h b/js/js2/icodegenerator.h index d68911905a26..4bd15e51bdfb 100644 --- a/js/js2/icodegenerator.h +++ b/js/js2/icodegenerator.h @@ -124,53 +124,10 @@ namespace ICG { }; + class ICodeModule; + typedef std::map > InstructionMap; - class ICodeModule { - public: - ICodeModule(InstructionStream *iCode, VariableList *variables, - ParameterList *parameters, - uint32 maxRegister, - InstructionMap *instructionMap, - JSType *resultType, uint32 exceptionRegister) : - its_iCode(iCode), itsVariables(variables), itsParameters(parameters), - itsMaxRegister(maxRegister), - mID(++sMaxID), mInstructionMap(instructionMap), - mParameterInit(NULL), - mEntryPoint(0), - mResultType(resultType), - mExceptionRegister(exceptionRegister) - { - } - - ~ICodeModule() - { - delete its_iCode; - delete itsVariables; - delete mInstructionMap; - if (mParameterInit) delete mParameterInit; - } - - Formatter& print(Formatter& f); - void setFileName (String aFileName) { mFileName = aFileName; } - String getFileName () { return mFileName; } - - InstructionStream *its_iCode; - VariableList *itsVariables; - ParameterList *itsParameters; - uint32 itsMaxRegister; - uint32 mID; - InstructionMap *mInstructionMap; - String mFileName; - uint32 *mParameterInit; - uint32 mEntryPoint; - JSType *mResultType; - uint32 mExceptionRegister; - - static uint32 sMaxID; - - }; - typedef std::vector LabelSet; class LabelEntry { public: @@ -198,6 +155,7 @@ namespace ICG { class ICodeGenerator { public: + friend ICodeModule; typedef enum { kNoFlags = 0, kIsTopLevel = 0x01, kIsStaticMethod = 0x02, kIsWithinWith = 0x04 } ICodeGeneratorFlags; private: InstructionStream *iCode; @@ -221,6 +179,7 @@ namespace ICG { const StringAtom &mInitName; ICodeGenerator *mContainingFunction;// outer function for nested functions + JSType *mResultType; std::vector mPermanentRegister; @@ -241,7 +200,6 @@ namespace ICG { TypedRegister allocateRegister(JSType *type); - JSType *findType(const StringAtom& typeName); void addParameterLabel(Label *label) { if (pLabels == NULL) pLabels = new LabelList(); pLabels->push_back(label); } @@ -289,7 +247,11 @@ namespace ICG { public: - ICodeGenerator(Context *cx, ICodeGenerator *containingFunction = NULL, JSClass *aClass = NULL, ICodeGeneratorFlags flags = kIsTopLevel); + ICodeGenerator(Context *cx, + ICodeGenerator *containingFunction = NULL, + JSClass *aClass = NULL, + ICodeGeneratorFlags flags = kIsTopLevel, + JSType *resultType = &Any_Type); ~ICodeGenerator() { @@ -300,11 +262,8 @@ namespace ICG { } } - ICodeModule *complete(JSType *resultType); + ICodeModule *complete(); ICodeModule *readICode(const char *fileName); - - JSType *extractType(ExprNode *t); - JSType *getParameterType(FunctionDefinition &function, int index); TypedRegister genExpr(ExprNode *p, bool needBoolValueInBranch = false, @@ -322,10 +281,7 @@ namespace ICG { return allocateVariable(name, &Any_Type); } - TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName) - { - return allocateVariable(name, findType(typeName)); - } + TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName); TypedRegister allocateVariable(const StringAtom& name, JSType *type) { @@ -345,10 +301,7 @@ namespace ICG { 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, const StringAtom& typeName); TypedRegister allocateParameter(const StringAtom& name, bool isOptional, JSType *type) { @@ -421,10 +374,63 @@ namespace ICG { Formatter& operator<<(Formatter &f, ICodeGenerator &i); Formatter& operator<<(Formatter &f, ICodeModule &i); Formatter& operator<<(Formatter &f, std::string &s); - /* - std::ostream &operator<<(std::ostream &s, ICodeGenerator &i); - std::ostream &operator<<(std::ostream &s, StringAtom &str); - */ + + class ICodeModule { + public: + ICodeModule(InstructionStream *iCode, VariableList *variables, + ParameterList *parameters, + uint32 maxRegister, + InstructionMap *instructionMap, + JSType *resultType, uint32 exceptionRegister) : + its_iCode(iCode), itsVariables(variables), itsParameters(parameters), + itsMaxRegister(maxRegister), + mID(++sMaxID), mInstructionMap(instructionMap), + mParameterInit(NULL), + mEntryPoint(0), + mResultType(resultType), + mExceptionRegister(exceptionRegister) + { + } + + ICodeModule(ICodeGenerator &icg) : + its_iCode(icg.iCode), itsVariables(icg.variableList), itsParameters(icg.parameterList), + itsMaxRegister(icg.mPermanentRegister.size()), + mID(++sMaxID), mInstructionMap(icg.mInstructionMap), + mParameterInit(NULL), + mEntryPoint(0), + mResultType(icg.mResultType), + mExceptionRegister(icg.mExceptionRegister.first) + { + } + + + ~ICodeModule() + { + delete its_iCode; + delete itsVariables; + delete mInstructionMap; + if (mParameterInit) delete mParameterInit; + } + + Formatter& print(Formatter& f); + void setFileName (String aFileName) { mFileName = aFileName; } + String getFileName () { return mFileName; } + + InstructionStream *its_iCode; + VariableList *itsVariables; + ParameterList *itsParameters; + uint32 itsMaxRegister; + uint32 mID; + InstructionMap *mInstructionMap; + String mFileName; + uint32 *mParameterInit; + uint32 mEntryPoint; + JSType *mResultType; + uint32 mExceptionRegister; + + static uint32 sMaxID; + + }; diff --git a/js/js2/interpreter.cpp b/js/js2/interpreter.cpp index 63886ef4f0f4..96026f0a06af 100644 --- a/js/js2/interpreter.cpp +++ b/js/js2/interpreter.cpp @@ -82,9 +82,9 @@ ICodeModule* Context::compileFunction(const String &source) String filename = widenCString("Some source source"); Parser p(getWorld(), a, source, filename); ExprNode* e = p.parseExpression(false); - ICodeGenerator icg(this); ASSERT(e->getKind() == ExprNode::functionLiteral); FunctionExprNode* f = static_cast(e); + ICodeGenerator icg(this, NULL, NULL, ICodeGenerator::kIsTopLevel, extractType(f->function.resultType)); icg.allocateParameter(getWorld().identifiers["this"], false); // always parameter #0 VariableBinding* v = f->function.parameters; while (v) { @@ -93,7 +93,7 @@ ICodeModule* Context::compileFunction(const String &source) v = v->next; } icg.genStmt(f->function.body); - ICodeModule* result = icg.complete(icg.extractType(f->function.resultType)); + ICodeModule* result = icg.complete(); result->setFileName(filename); return result; } @@ -143,7 +143,7 @@ JSValue Context::readEvalFile(FILE* in, const String& fileName) ICodeModule* Context::genCode(StmtNode *p, const String &fileName) { - ICodeGenerator icg(this); + ICodeGenerator icg(this, NULL, NULL, ICodeGenerator::kIsTopLevel, &Void_Type); TypedRegister ret(NotARegister, &None_Type); while (p) { @@ -152,7 +152,7 @@ ICodeModule* Context::genCode(StmtNode *p, const String &fileName) } icg.returnStmt(ret); - ICodeModule *icm = icg.complete(&Void_Type); + ICodeModule *icm = icg.complete(); icm->setFileName(fileName); return icm; } @@ -1595,6 +1595,40 @@ void Context::broadcast(Event event) } } + +/* Helper functions for extracting types from expression trees */ +JSType *Context::findType(const StringAtom& typeName) +{ + const JSValue& type = getGlobalObject()->getVariable(typeName); + if (type.isType()) + return type.type; + return &Any_Type; +} + +JSType *Context::extractType(ExprNode *t) +{ + JSType* type = &Any_Type; + if (t && (t->getKind() == ExprNode::identifier)) { + IdentifierExprNode* typeExpr = static_cast(t); + type = findType(typeExpr->name); + } + return type; +} + +JSType *Context::getParameterType(FunctionDefinition &function, int index) +{ + VariableBinding *v = function.parameters; + while (v) { + if (index-- == 0) + return extractType(v->type); + else + v = v->next; + } + return NULL; +} + + + Context::Frame* Context::getFrames() { return mLinkage; diff --git a/js/js2/interpreter.h b/js/js2/interpreter.h index 597ac35355e8..51ac472d2cb2 100644 --- a/js/js2/interpreter.h +++ b/js/js2/interpreter.h @@ -85,6 +85,10 @@ namespace Interpreter { const JSValue findBinaryOverride(JSValue &operand1, JSValue &operand2, ExprNode::Kind op); + JSType *findType(const StringAtom& typeName); + JSType *extractType(ExprNode *t); + JSType *getParameterType(FunctionDefinition &function, int index); + private: void broadcast(Event event); void initOperatorsPackage(); diff --git a/js/js2/js2.cpp b/js/js2/js2.cpp index 375d2338b557..99edbe051d15 100644 --- a/js/js2/js2.cpp +++ b/js/js2/js2.cpp @@ -361,13 +361,13 @@ static void testCompile() Arena a; Parser p(world, a, testScript, widenCString("testCompile")); StmtNode *parsedStatements = p.parseProgram(); - ICodeGenerator icg(&cx); + ICodeGenerator icg(&cx, NULL, NULL, ICodeGenerator::kIsTopLevel, &Void_Type); StmtNode *s = parsedStatements; while (s) { icg.genStmt(s); s = s->next; } - JSValue result = cx.interpret(icg.complete(&Void_Type), JSValues()); + JSValue result = cx.interpret(icg.complete(), JSValues()); stdOut << "result = " << result << "\n"; } } diff --git a/js2/src/icodeEmitter.cpp b/js2/src/icodeEmitter.cpp new file mode 100644 index 000000000000..ccd21d42be8d --- /dev/null +++ b/js2/src/icodeEmitter.cpp @@ -0,0 +1,1985 @@ +/* -*- 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 "numerics.h" +#include "world.h" +#include "vmtypes.h" +#include "jstypes.h" +#include "jsclasses.h" +#include "icodegenerator.h" +#include "interpreter.h" +#include "exception.h" +#include "icodeasm.h" + +#include +#include + +namespace JavaScript { +namespace ICG { + +using namespace VM; +using namespace JSTypes; +using namespace JSClasses; +using namespace Interpreter; +using namespace ICodeASM; + +inline char narrow(char16 ch) { return char(ch); } + +/************************************************************************/ + + +bool LabelEntry::containsLabel(const StringAtom *label) +{ + if (labelSet) { + for (LabelSet::iterator i = labelSet->begin(); i != labelSet->end(); i++) + if ( (*i) == label ) + return true; + } + return false; +} + +static bool hasAttribute(const IdentifierList* identifiers, Token::Kind tokenKind) +{ + while (identifiers) { + if (identifiers->name.tokenKind == tokenKind) + return true; + identifiers = identifiers->next; + } + return false; +} + +static bool hasAttribute(const IdentifierList* identifiers, StringAtom &name) +{ + while (identifiers) { + if (identifiers->name == name) + return true; + identifiers = identifiers->next; + } + return false; +} + + +/************************************************************************/ + + + +ExprNode::Kind ICodeGenerator::mapICodeOpToExprNode(ICodeOp op) +{ + switch (op) { + case ADD: + return ExprNode::add; + case SUBTRACT: + return ExprNode::subtract; + case MULTIPLY: + return ExprNode::multiply; + case DIVIDE: + return ExprNode::divide; + case REMAINDER: + return ExprNode::modulo; + case SHIFTLEFT: + return ExprNode::leftShift; + case SHIFTRIGHT: + return ExprNode::rightShift; + case USHIFTRIGHT: + return ExprNode::logicalRightShift; + case AND: + return ExprNode::bitwiseAnd; + case OR: + return ExprNode::bitwiseOr; + case XOR: + return ExprNode::bitwiseXor; + case POSATE: + return ExprNode::plus; + case NEGATE: + return ExprNode::minus; + case BITNOT: + return ExprNode::complement; + case COMPARE_EQ: + return ExprNode::equal; + case COMPARE_LT: + return ExprNode::lessThan; + case COMPARE_LE: + return ExprNode::lessThanOrEqual; + case STRICT_EQ: + return ExprNode::identical; + } + return ExprNode::none; +} + + +ICodeOp ICodeGenerator::mapExprNodeToICodeOp(ExprNode::Kind kind) +{ + // can be an array later, when everything has settled down + switch (kind) { + // binary + case ExprNode::add: + case ExprNode::addEquals: + return ADD; + case ExprNode::subtract: + case ExprNode::subtractEquals: + return SUBTRACT; + case ExprNode::multiply: + case ExprNode::multiplyEquals: + return MULTIPLY; + case ExprNode::divide: + case ExprNode::divideEquals: + return DIVIDE; + case ExprNode::modulo: + case ExprNode::moduloEquals: + return REMAINDER; + case ExprNode::leftShift: + case ExprNode::leftShiftEquals: + return SHIFTLEFT; + case ExprNode::rightShift: + case ExprNode::rightShiftEquals: + return SHIFTRIGHT; + case ExprNode::logicalRightShift: + case ExprNode::logicalRightShiftEquals: + return USHIFTRIGHT; + case ExprNode::bitwiseAnd: + case ExprNode::bitwiseAndEquals: + return AND; + case ExprNode::bitwiseXor: + case ExprNode::bitwiseXorEquals: + return XOR; + case ExprNode::bitwiseOr: + case ExprNode::bitwiseOrEquals: + return OR; + // unary + case ExprNode::plus: + return POSATE; + case ExprNode::minus: + return NEGATE; + case ExprNode::complement: + return BITNOT; + + // relational + case ExprNode::In: + return COMPARE_IN; + case ExprNode::Instanceof: + return INSTANCEOF; + + case ExprNode::equal: + return COMPARE_EQ; + case ExprNode::lessThan: + return COMPARE_LT; + case ExprNode::lessThanOrEqual: + return COMPARE_LE; + case ExprNode::identical: + return STRICT_EQ; + + // these get reversed by the generator + case ExprNode::notEqual: + return COMPARE_EQ; + case ExprNode::greaterThan: + return COMPARE_LT; + case ExprNode::greaterThanOrEqual: + return COMPARE_LE; + case ExprNode::notIdentical: + return STRICT_EQ; + + default: + NOT_REACHED("Unimplemented kind"); + return NOP; + } +} + + +static bool generatedBoolean(ExprNode *p) +{ + switch (p->getKind()) { + case ExprNode::parentheses: + { + UnaryExprNode *u = static_cast(p); + return generatedBoolean(u->op); + } + case ExprNode::True: + case ExprNode::False: + case ExprNode::equal: + case ExprNode::notEqual: + case ExprNode::lessThan: + case ExprNode::lessThanOrEqual: + case ExprNode::greaterThan: + case ExprNode::greaterThanOrEqual: + case ExprNode::identical: + case ExprNode::notIdentical: + case ExprNode::In: + case ExprNode::Instanceof: + case ExprNode::logicalAnd: + case ExprNode::logicalXor: + case ExprNode::logicalOr: + return true; + default: + break; + } + return false; +} + +static bool isSlotName(JSType *t, const StringAtom &name, uint32 &slotIndex, JSType *&type, bool lvalue) +{ + JSClass* c = dynamic_cast(t); + while (c) { + if (c->hasSlot(name)) { + const JSSlot &s = c->getSlot(name); + if (lvalue) { + if (s.mActual || s.mSetter) { + slotIndex = s.mIndex; + type = s.mType; + return true; + } + } + else { + if (s.mActual || s.mGetter) { + slotIndex = s.mIndex; + type = s.mType; + return true; + } + } + return false; + } + c = c->getSuperClass(); + } + return false; +} + +static bool isMethodName(JSType *t, const StringAtom &name, uint32 &slotIndex) +{ + JSClass* c = dynamic_cast(t); + return (c && c->hasMethod(name, slotIndex)); +} + + +static bool isStaticName(JSClass *c, const StringAtom &name, JSType*& type, bool &isConstructor) +{ + do { + if (c->hasStatic(name, type, isConstructor)) + return true; + c = c->getSuperClass(); + } while (c); + return false; +} + +ICodeGenerator::LValueKind ICodeGenerator::getVariableByName(const StringAtom &name, TypedRegister &v) +{ + v = variableList->findVariable(name); + if (v.first == NotARegister) + v = parameterList->findVariable(name); + if (v.first != NotARegister) + return Var; + return NoKind; +} + +ICodeGenerator::LValueKind ICodeGenerator::scanForVariable(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base) +{ + LValueKind k = getVariableByName(name, v); + if (k == Var) return k; + + uint32 count = 0; + ICodeGenerator *upper = mContainingFunction; + while (upper) { + k = upper->getVariableByName(name, v); + if (k == Var) { + base = getClosure(count); + slotIndex = v.first; + return Slot; + } + count++; + upper = upper->mContainingFunction; + } + return NoKind; +} + +// find 'name' (unqualified) in the current context. +// for local variable, returns v.first = register number +// for slot/method, returns slotIndex and sets base appropriately +// (note closure vars also get handled this way) +// v.second is set to the type regardless +ICodeGenerator::LValueKind ICodeGenerator::resolveIdentifier(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base, bool lvalue) +{ + if (!isWithinWith()) { + LValueKind k = scanForVariable(name, v, slotIndex, base); + if (k != NoKind) + return k; + else { + if (mClass) { // we're compiling a method of a class + if (!isStaticMethod()) { + if (isSlotName(mClass, name, slotIndex, v.second, lvalue)) { + base = TypedRegister(0, mClass); + return Slot; + } + if (isMethodName(mClass, name, slotIndex)) { + base = TypedRegister(0, mClass); + return Method; + } + } + bool isConstructor = false; + if (isStaticName(mClass, name, v.second, isConstructor)) { + return (isConstructor) ? Constructor : Static; + } + } + // last chance - if it's a generic name in the global scope, try to get a type for it + v.second = mContext->getGlobalObject()->getType(name); + return Name; + } + } + // all bet's off, generic name & type + v.second = &Any_Type; + return Name; +} + +TypedRegister ICodeGenerator::handleIdentifier(IdentifierExprNode *p, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) +{ + ASSERT(p->getKind() == ExprNode::identifier); + + /*JSType *vType = &Any_Type;*/ + uint32 slotIndex; + TypedRegister v; + TypedRegister base; + + const StringAtom &name = (static_cast(p))->name; + LValueKind lValueKind = resolveIdentifier(name, v, slotIndex, base, lvalue); + JSType *targetType = v.second; + + switch (use) { + case ExprNode::addEquals: + case ExprNode::subtractEquals: + case ExprNode::multiplyEquals: + case ExprNode::divideEquals: + case ExprNode::moduloEquals: + case ExprNode::leftShiftEquals: + case ExprNode::rightShiftEquals: + case ExprNode::logicalRightShiftEquals: + case ExprNode::bitwiseAndEquals: + case ExprNode::bitwiseXorEquals: + case ExprNode::bitwiseOrEquals: + switch (lValueKind) { + case Var: + break; + case Name: + v = loadName(name, v.second); + break; + case Slot: + v = getSlot(base, slotIndex); + break; + case Static: + case Constructor: + v = getStatic(mClass, name); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); + // fall thru... + case ExprNode::assignment: + ret = cast(ret, targetType); + switch (lValueKind) { + case Var: + move(v, ret); + break; + case Name: + saveName(name, ret); + break; + case Slot: + setSlot(base, slotIndex, ret); + break; + case Static: + case Constructor: + setStatic(mClass, name, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::identifier: + switch (lValueKind) { + case Var: + ret = v; + break; + case Name: + ret = loadName(name, v.second); + break; + case Slot: + ret = getSlot(base, slotIndex); + break; + case Static: + case Constructor: + ret = getStatic(mClass, name); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::preDecrement: + case ExprNode::preIncrement: + switch (lValueKind) { + case Var: + ret = binaryOp(xcrementOp, v, loadImmediate(1.0)); + break; + case Name: + ret = loadName(name, v.second); + ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); + saveName(name, ret); + break; + case Slot: + ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); + setSlot(base, slotIndex, ret); + break; + case Static: + case Constructor: + ret = binaryOp(xcrementOp, getStatic(mClass, name), loadImmediate(1.0)); + setStatic(mClass, name, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::postDecrement: + case ExprNode::postIncrement: + switch (lValueKind) { + case Var: + ret = varXcr(v, xcrementOp); + break; + case Name: + ret = nameXcr(name, xcrementOp); + break; + case Slot: + ret = slotXcr(base, slotIndex, xcrementOp); + break; + case Static: + case Constructor: + ret = staticXcr(mClass, name, xcrementOp); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::call: + { + switch (lValueKind) { + case Var: + ret = call(v, args); + break; + case Name: + ret = call(loadName(name), args); + break; + case Method: + ret = call(getMethod(base, slotIndex), args); + break; + case Static: + ret = call(getStatic(mClass, name), args); + break; + case Constructor: + ret = newClass(mClass); + call(bindThis(ret, getStatic(mClass, name)), args); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + } + break; + default: + NOT_REACHED("Bad use kind"); + } + return ret; + +} + +TypedRegister ICodeGenerator::handleDot(BinaryExprNode *b, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) +{ + ASSERT(b->getKind() == ExprNode::dot); + + LValueKind lValueKind = Property; + + if (b->op2->getKind() != ExprNode::identifier) { + NOT_REACHED("Implement me"); // turns into a getProperty (but not via any overloaded [] ) + } + else { + // we have . + const StringAtom &fieldName = static_cast(b->op2)->name; + TypedRegister base; + TypedRegister baseBase; + JSClass *clazz = NULL; + JSType *fieldType = &Any_Type; + uint32 slotIndex; + if ((b->op1->getKind() == ExprNode::identifier) && !isWithinWith()) { + // handle . + const StringAtom &baseName = (static_cast(b->op1))->name; + LValueKind baseKind = resolveIdentifier(baseName, base, slotIndex, baseBase, false); + if (baseKind == Slot) { + base = getSlot(baseBase, slotIndex); + } + // + // handle . + // + if (base.second == &Type_Type) { + const JSValue &v = mContext->getGlobalObject()->getVariable(baseName); + bool isConstructor; + ASSERT(v.isType()); // there's no other way that base.second could be &Type_Type, right? + clazz = dynamic_cast(v.type); + if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) { + lValueKind = (isConstructor) ? Constructor : Static; + } + } + if (lValueKind == Property) { + if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) + lValueKind = Slot; + else + if (isMethodName(base.second, fieldName, slotIndex)) + lValueKind = Method; + else { + bool isConstructor; + clazz = dynamic_cast(base.second); + if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) + lValueKind = (isConstructor) ? Constructor : Static; + } + } + if ((lValueKind == Property) || (base.first == NotARegister)) + base = loadName(baseName, base.second); + } + else { + base = genExpr(b->op1); + if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) + lValueKind = Slot; + else + if (isMethodName(base.second, fieldName, slotIndex)) + lValueKind = Method; + else { + bool isConstructor; + clazz = dynamic_cast(base.second); + if (clazz && clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) + lValueKind = (isConstructor) ? Constructor : Static; + } + } + TypedRegister v; + switch (use) { + case ExprNode::call: + switch (lValueKind) { + case Static: + ret = call(getStatic(clazz, fieldName), args); + break; + case Constructor: + ret = newClass(clazz); + call(bindThis(ret, getStatic(clazz, fieldName)), args); + break; + case Property: + ret = call(bindThis(base, getProperty(base, fieldName)), args); + break; + case Method: + ret = call(getMethod(base, slotIndex), args); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::dot: + case ExprNode::addEquals: + case ExprNode::subtractEquals: + case ExprNode::multiplyEquals: + case ExprNode::divideEquals: + case ExprNode::moduloEquals: + case ExprNode::leftShiftEquals: + case ExprNode::rightShiftEquals: + case ExprNode::logicalRightShiftEquals: + case ExprNode::bitwiseAndEquals: + case ExprNode::bitwiseXorEquals: + case ExprNode::bitwiseOrEquals: + switch (lValueKind) { + case Constructor: + case Static: + v = getStatic(clazz, fieldName); + break; + case Property: + v = getProperty(base, fieldName); + break; + case Slot: + v = getSlot(base, slotIndex); + break; + case Method: + v = getMethod(base, slotIndex); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + if (use == ExprNode::dot) { + ret = v; + break; + } + ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); + // fall thru... + case ExprNode::assignment: + ret = cast(ret, fieldType); + switch (lValueKind) { + case Constructor: + case Static: + setStatic(clazz, fieldName, ret); + break; + case Property: + setProperty(base, fieldName, ret); + break; + case Slot: + setSlot(base, slotIndex, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::postDecrement: + case ExprNode::postIncrement: + { +// JSClass *clss = dynamic_cast(fieldType); +// if (clss) { +// clss->findOverloadedOperator(use); +// } + switch (lValueKind) { + case Constructor: + case Static: + ret = staticXcr(clazz, fieldName, xcrementOp); + break; + case Property: + ret = propertyXcr(base, fieldName, xcrementOp); + break; + case Slot: + ret = slotXcr(base, slotIndex, xcrementOp); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + } + break; + case ExprNode::preDecrement: + case ExprNode::preIncrement: + switch (lValueKind) { + case Constructor: + case Static: + ret = binaryOp(xcrementOp, getStatic(clazz, fieldName), loadImmediate(1.0)); + setStatic(clazz, fieldName, ret); + break; + case Property: + ret = binaryOp(xcrementOp, getProperty(base, fieldName), loadImmediate(1.0)); + setProperty(base, fieldName, ret); + break; + case Slot: + ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); + setSlot(base, slotIndex, ret); + break; + default: + NOT_REACHED("Bad lvalue kind"); + } + break; + case ExprNode::Delete: + if (lValueKind == Property) { + ret = deleteProperty(base, fieldName); + } + break; + default: + NOT_REACHED("unexpected use node"); + } + ret.second = fieldType; + } + return ret; +} + + + +/* + if trueBranch OR falseBranch are not null, the sub-expression should generate + a conditional branch to the appropriate target. If either branch is NULL, it + indicates that the label is immediately forthcoming. +*/ +TypedRegister ICodeGenerator::genExpr(ExprNode *p, + bool needBoolValueInBranch, + Label *trueBranch, + Label *falseBranch) +{ + TypedRegister ret(NotARegister, &None_Type); + ICodeOp xcrementOp = ADD; + switch (p->getKind()) { + case ExprNode::True: + if (trueBranch || falseBranch) { + if (needBoolValueInBranch) + ret = loadBoolean(true); + if (trueBranch) + branch(trueBranch); + } + else + ret = loadBoolean(true); + break; + case ExprNode::False: + if (trueBranch || falseBranch) { + if (needBoolValueInBranch) + ret = loadBoolean(false); + if (falseBranch) + branch(falseBranch); + } + else + ret = loadBoolean(false); + break; + case ExprNode::Null: + ret = loadNull(); + break; + case ExprNode::parentheses: + { + UnaryExprNode *u = static_cast(p); + ret = genExpr(u->op, needBoolValueInBranch, trueBranch, falseBranch); + } + break; + case ExprNode::New: + { + InvokeExprNode *i = static_cast(p); + ArgumentList *args = new ArgumentList(); + ExprPairList *p = i->pairs; + StringFormatter s; + while (p) { + if (p->field && (p->field->getKind() == ExprNode::identifier)) + args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); + else { + if (p->field && (p->field->getKind() == ExprNode::string)) + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); + else { + s << (uint32)args->size(); + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); + s.clear(); + } + } + p = p->next; + } + if (i->op->getKind() == ExprNode::identifier) { + const StringAtom &className = static_cast(i->op)->name; + const JSValue& value = mContext->getGlobalObject()->getVariable(className); + if (value.isType()) { + JSClass* clazz = dynamic_cast(value.type); + if (clazz) { + ret = newClass(clazz); + ret = call(bindThis(ret, getStatic(clazz, className)), args); + } + else { + // + // like 'new Boolean()' - see if the type has a constructor + // + JSFunction *f = value.type->getConstructor(); + if (f) + ret = directCall(f, args); + else + NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. + } + } + else { + if (value.isFunction()) { + TypedRegister f = loadName(className, value.type); + ret = newObject(f); + ret = call(bindThis(ret, f), args); + } + else + NOT_REACHED("new , where is not a function"); // XXX Runtime error. + } + } + else + ret = newObject(TypedRegister(NotARegister, &Any_Type)); // XXX more ? + } + break; + case ExprNode::Delete: + { + UnaryExprNode *d = static_cast(p); + ASSERT(d->op->getKind() == ExprNode::dot); + ret = handleDot(static_cast(d->op), p->getKind(), xcrementOp, ret, NULL, true); + // rather than getProperty(), need to do a deleteProperty(). + } + break; + case ExprNode::call : + { + InvokeExprNode *i = static_cast(p); + ArgumentList *args = new ArgumentList(); + ExprPairList *p = i->pairs; + StringFormatter s; + while (p) { + if (p->field && (p->field->getKind() == ExprNode::identifier)) + args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); + else { + if (p->field && (p->field->getKind() == ExprNode::string)) + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); + else { + s << (uint32)args->size(); + args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); + s.clear(); + } + } + p = p->next; + } + + if (i->op->getKind() == ExprNode::dot) { + BinaryExprNode *b = static_cast(i->op); + ret = handleDot(b, ExprNode::call, xcrementOp, ret, args, false); + } + else { + if (i->op->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(i->op), ExprNode::call, xcrementOp, ret, args, false); + } + else { + if (i->op->getKind() == ExprNode::index) { + InvokeExprNode *ii = static_cast(i->op); + TypedRegister base = genExpr(ii->op); + ret = call(bindThis(base, getElement(base, genExpr(ii->pairs->value))), args); // FIXME, only taking first index + } + else + ASSERT("WAH!"); + } + } + } + break; + case ExprNode::index : + { + InvokeExprNode *i = static_cast(p); + TypedRegister base = genExpr(i->op); + JSClass *clazz = dynamic_cast(base.second); + if (clazz) { + // look for operator [] and invoke it + } + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + ret = getElement(base, index); + } + break; + case ExprNode::dot : + { + BinaryExprNode *b = static_cast(p); + ret = handleDot(b, p->getKind(), xcrementOp, ret, NULL, false); + } + break; + case ExprNode::This : + { + ret = TypedRegister(0, mClass ? mClass : &Any_Type); + } + break; + case ExprNode::identifier : + { + ret = handleIdentifier(static_cast(p), ExprNode::identifier, xcrementOp, ret, NULL, false); + } + break; + case ExprNode::number : + ret = loadImmediate((static_cast(p))->value); + break; + case ExprNode::string : + ret = loadString(mContext->getWorld().identifiers[(static_cast(p))->str]); + break; + case ExprNode::preDecrement: + xcrementOp = SUBTRACT; + case ExprNode::preIncrement: + { + UnaryExprNode *u = static_cast(p); + if (u->op->getKind() == ExprNode::dot) { + ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(u->op); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + ret = getElement(base, index); + ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); + setElement(base, index, ret); + } + else + ASSERT("WAH!"); + } + break; + case ExprNode::postDecrement: + xcrementOp = SUBTRACT; + case ExprNode::postIncrement: + { + UnaryExprNode *u = static_cast(p); + if (u->op->getKind() == ExprNode::dot) { + ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (u->op->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(u->op); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + ret = elementXcr(base, index, xcrementOp); + } + else + ASSERT("WAH!"); + } + break; + + case ExprNode::plus: + case ExprNode::minus: + case ExprNode::complement: + { + UnaryExprNode *u = static_cast(p); + TypedRegister r = genExpr(u->op); + ret = op(mapExprNodeToICodeOp(p->getKind()), r); + } + break; + case ExprNode::add: + case ExprNode::subtract: + case ExprNode::multiply: + case ExprNode::divide: + case ExprNode::modulo: + case ExprNode::leftShift: + case ExprNode::rightShift: + case ExprNode::logicalRightShift: + case ExprNode::bitwiseAnd: + case ExprNode::bitwiseXor: + case ExprNode::bitwiseOr: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); + } + break; + case ExprNode::assignment: + { + BinaryExprNode *b = static_cast(p); + ret = genExpr(b->op2); + if (b->op1->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::dot) { + BinaryExprNode *lb = static_cast(b->op1); + ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(b->op1); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + setElement(base, index, ret); + } + else + ASSERT("WAH!"); + } + break; + case ExprNode::addEquals: + case ExprNode::subtractEquals: + case ExprNode::multiplyEquals: + case ExprNode::divideEquals: + case ExprNode::moduloEquals: + case ExprNode::leftShiftEquals: + case ExprNode::rightShiftEquals: + case ExprNode::logicalRightShiftEquals: + case ExprNode::bitwiseAndEquals: + case ExprNode::bitwiseXorEquals: + case ExprNode::bitwiseOrEquals: + { + BinaryExprNode *b = static_cast(p); + ret = genExpr(b->op2); + if (b->op1->getKind() == ExprNode::identifier) { + ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::dot) { + BinaryExprNode *lb = static_cast(b->op1); + ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); + } + else + if (b->op1->getKind() == ExprNode::index) { + InvokeExprNode *i = static_cast(b->op1); + TypedRegister base = genExpr(i->op); + TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index + TypedRegister v = getElement(base, index); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), v, ret); + setElement(base, index, ret); + } + else + ASSERT("WAH!"); + } + break; + case ExprNode::equal: + case ExprNode::lessThan: + case ExprNode::lessThanOrEqual: + case ExprNode::identical: + case ExprNode::In: + case ExprNode::Instanceof: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); + if (trueBranch || falseBranch) { + if (trueBranch == NULL) + branchFalse(falseBranch, ret); + else { + branchTrue(trueBranch, ret); + if (falseBranch) + branch(falseBranch); + } + } + } + break; + case ExprNode::greaterThan: + case ExprNode::greaterThanOrEqual: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r2, r1); // will return reverse case + if (trueBranch || falseBranch) { + if (trueBranch == NULL) + branchFalse(falseBranch, ret); + else { + branchTrue(trueBranch, ret); + if (falseBranch) + branch(falseBranch); + } + } + } + break; + + case ExprNode::notEqual: + case ExprNode::notIdentical: + { + BinaryExprNode *b = static_cast(p); + TypedRegister r1 = genExpr(b->op1); + TypedRegister r2 = genExpr(b->op2); + ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); // will generate equal/identical code + if (trueBranch || falseBranch) { + if (trueBranch == NULL) + branchTrue(falseBranch, ret); + else { + branchFalse(trueBranch, ret); + if (falseBranch) + branch(falseBranch); + } + } + else + ret = logicalNot(ret); + + } + break; + + case ExprNode::logicalAnd: + { + BinaryExprNode *b = static_cast(p); + if (trueBranch || falseBranch) { + genExpr(b->op1, needBoolValueInBranch, NULL, falseBranch); + genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); + } + else { + Label *fBranch = getLabel(); + TypedRegister r1 = genExpr(b->op1, true, NULL, fBranch); + if (!generatedBoolean(b->op1)) { + r1 = test(r1); + branchFalse(fBranch, r1); + } + TypedRegister r2 = genExpr(b->op2); + if (!generatedBoolean(b->op2)) { + r2 = test(r2); + } + if (r1 != r2) // FIXME, need a way to specify a dest??? + move(r1, r2); + setLabel(fBranch); + ret = r1; + } + } + break; + case ExprNode::logicalOr: + { + BinaryExprNode *b = static_cast(p); + if (trueBranch || falseBranch) { + genExpr(b->op1, needBoolValueInBranch, trueBranch, NULL); + genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); + } + else { + Label *tBranch = getLabel(); + TypedRegister r1 = genExpr(b->op1, true, tBranch, NULL); + if (!generatedBoolean(b->op1)) { + r1 = test(r1); + branchTrue(tBranch, r1); + } + TypedRegister r2 = genExpr(b->op2); + if (!generatedBoolean(b->op2)) { + r2 = test(r2); + } + if (r1 != r2) // FIXME, need a way to specify a dest??? + move(r1, r2); + setLabel(tBranch); + ret = r1; + } + } + break; + + case ExprNode::conditional: + { + TernaryExprNode *t = static_cast(p); + Label *fBranch = getLabel(); + Label *beyondBranch = getLabel(); + TypedRegister c = genExpr(t->op1, false, NULL, fBranch); + if (!generatedBoolean(t->op1)) + branchFalse(fBranch, test(c)); + TypedRegister r1 = genExpr(t->op2); + branch(beyondBranch); + setLabel(fBranch); + TypedRegister r2 = genExpr(t->op3); + if (r1 != r2) // FIXME, need a way to specify a dest??? + move(r1, r2); + setLabel(beyondBranch); + ret = r1; + } + break; + + case ExprNode::objectLiteral: + { + ret = newObject(TypedRegister(NotARegister, &Any_Type)); + PairListExprNode *plen = static_cast(p); + ExprPairList *e = plen->pairs; + while (e) { + if (e->field && e->value && (e->field->getKind() == ExprNode::identifier)) + setProperty(ret, (static_cast(e->field))->name, genExpr(e->value)); + e = e->next; + } + } + break; + + case ExprNode::functionLiteral: + { + FunctionExprNode *f = static_cast(p); + ICodeModule *icm = genFunction(f->function, false, false, NULL); + ret = newClosure(icm); + } + break; + + case ExprNode::at: + { + BinaryExprNode *b = static_cast(p); + // for now, just handle simple identifiers on the rhs. + ret = genExpr(b->op1); + if (b->op2->getKind() == ExprNode::identifier) { + TypedRegister t; + const StringAtom &name = (static_cast(b->op2))->name; + ASSERT(t.second == &Type_Type); + const JSValue &v = mContext->getGlobalObject()->getVariable(name); + ASSERT(v.isType()); + JSClass *clazz = dynamic_cast(v.type); + if (clazz) + ret = cast(ret, clazz); + else + ret = cast(ret, t.second); + } + else + NOT_REACHED("Anything more complex than @ is not implemented"); + } + break; + + + default: + { + NOT_REACHED("Unsupported ExprNode kind"); + } + } + return ret; +} + + + +ICodeModule *ICodeGenerator::genFunction(FunctionDefinition &function, bool isStatic, bool isConstructor, JSClass *superclass) +{ + ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; + + ICodeGenerator icg(mContext, this, mClass, flags, mContext->extractType(function.resultType)); + icg.allocateParameter(mContext->getWorld().identifiers["this"], false, (mClass) ? mClass : &Any_Type); // always parameter #0 + VariableBinding *v = function.parameters; + bool unnamed = true; + uint32 positionalCount = 0; + StringFormatter s; + while (v) { + if (unnamed && (v == function.namedParameters)) { // Track when we hit the first named parameter. + icg.parameterList->setPositionalCount(positionalCount); + unnamed = false; + } + + // 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 == function.restParameter) { + icg.parameterList->setRestParameter( (unnamed) ? ParameterList::HasRestParameterBeforeBar : ParameterList::HasRestParameterAfterBar ); + } + else { + if (v->name && (v->name->getKind() == ExprNode::identifier)) { + JSType *pType = mContext->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) { + positionalCount++; + s << r.first - 1; // the first positional parameter is '0' + icg.parameterList->add(mContext->getWorld().identifiers[s.getString()], r, (v->initializer != NULL)); + s.clear(); + } + } + } + v = v->next; + } + if (unnamed) icg.parameterList->setPositionalCount(positionalCount); + + // now allocate the rest parameter + if (function.restParameter) { + v = function.restParameter; + JSType *pType = (v->type == NULL) ? &Array_Type : mContext->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 = function.optParameters; + if (v) { + while (v) { // include the rest parameter, as it may have an initializer + if (v->name && (v->name->getKind() == ExprNode::identifier)) { + icg.addParameterLabel(icg.setLabel(icg.getLabel())); + TypedRegister p = icg.genExpr(v->name); + if (v->initializer) { // might be NULL when we get to the restParameter + Label *l = icg.getLabel(); + icg.branchInitialized(l, p); + icg.move(p, icg.genExpr(v->initializer)); + icg.setLabel(l); + } + else { // an un-initialized rest parameter is still an empty array + if (v == function.restParameter) { + Label *l = icg.getLabel(); + icg.branchInitialized(l, p); + icg.move(p, icg.newArray()); + icg.setLabel(l); + } + } + } + v = v->next; + } + icg.addParameterLabel(icg.setLabel(icg.getLabel())); // to provide the entry-point for the default case + } + + if (isConstructor) { + /* + See if the first statement is an expression statement consisting + of a call to super(). If not we need to add a call to the default + superclass constructor ourselves. + */ + TypedRegister thisValue = TypedRegister(0, mClass); + ArgumentList *args = new ArgumentList(); + if (superclass) { + bool foundSuperCall = false; + BlockStmtNode *b = function.body; + if (b && b->statements && (b->statements->getKind() == StmtNode::expression)) { + ExprStmtNode *e = static_cast(b->statements); + if (e->expr->getKind() == ExprNode::call) { + InvokeExprNode *i = static_cast(e->expr); + if (i->op->getKind() == ExprNode::dot) { + BinaryExprNode *b = static_cast(i->op); + if ((b->op1->getKind() == ExprNode::This) && (b->op2->getKind() == ExprNode::qualify)) { + BinaryExprNode *q = static_cast(b->op2); + if (q->op1->getKind() == ExprNode::Super) { + // XXX verify that q->op2 is either the superclass name or a constructor for it + foundSuperCall = true; + } + } + } + } + } + if (!foundSuperCall) { // invoke the default superclass constructor + icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); + } + } + if (mClass->hasStatic(mInitName)) + icg.call(icg.bindThis(thisValue, icg.getStatic(mClass, mInitName)), args); // ok, so it's mis-named + + } + if (function.body) + icg.genStmt(function.body); + if (isConstructor) { + TypedRegister thisValue = TypedRegister(0, mClass); + icg.returnStmt(thisValue); + } + return icg.complete(); +} + +TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet) +{ + TypedRegister ret(NotARegister, &None_Type); + + startStatement(p->pos); + + switch (p->getKind()) { + case StmtNode::Class: + { + // FIXME: need a semantic check to make sure a class isn't being redefined(?) + ClassStmtNode *classStmt = static_cast(p); + ASSERT(classStmt->name->getKind() == ExprNode::identifier); + IdentifierExprNode* nameExpr = static_cast(classStmt->name); + JSClass* superclass = 0; + if (classStmt->superclass) { + ASSERT(classStmt->superclass->getKind() == ExprNode::identifier); + IdentifierExprNode* superclassExpr = static_cast(classStmt->superclass); + const JSValue& superclassValue = mContext->getGlobalObject()->getVariable(superclassExpr->name); + ASSERT(superclassValue.isObject() && !superclassValue.isNull()); + superclass = static_cast(superclassValue.object); + } + JSClass* thisClass = new JSClass(mContext->getGlobalObject(), nameExpr->name, superclass); + // is it ok for a partially defined class to appear in global scope? this is needed + // to handle recursive types, such as linked list nodes. + mContext->getGlobalObject()->defineVariable(nameExpr->name, &Type_Type, JSValue(thisClass)); + + +/* + Pre-pass to declare all the methods & fields +*/ + bool needsInstanceInitializer = false; + TypedRegister thisRegister = TypedRegister(0, thisClass); + if (classStmt->body) { + StmtNode* s = classStmt->body->statements; + while (s) { + switch (s->getKind()) { + case StmtNode::Const: + case StmtNode::Var: + { + VariableStmtNode *vs = static_cast(s); + bool isStatic = hasAttribute(vs->attributes, Token::Static); + VariableBinding *v = vs->bindings; + while (v) { + if (v->name) { + ASSERT(v->name->getKind() == ExprNode::identifier); + IdentifierExprNode* idExpr = static_cast(v->name); + JSType* type = mContext->extractType(v->type); + if (isStatic) + thisClass->defineStatic(idExpr->name, type); + else { + if (hasAttribute(vs->attributes, mContext->getWorld().identifiers["virtual"])) + thisClass->defineSlot(idExpr->name, type, JSSlot::kIsVirtual); + else + thisClass->defineSlot(idExpr->name, type); + if (v->initializer) + needsInstanceInitializer = true; + } + } + v = v->next; + } + } + break; + case StmtNode::Constructor: + case StmtNode::Function: + { + FunctionStmtNode *f = static_cast(s); + bool isStatic = hasAttribute(f->attributes, Token::Static); + bool isConstructor = (s->getKind() == StmtNode::Constructor); + if (f->function.prefix == FunctionName::Operator) { + thisClass->defineOperator(f->function.op, + mContext->getParameterType(f->function, 0), + mContext->getParameterType(f->function, 1), NULL); + } + else + if (f->function.name->getKind() == ExprNode::identifier) { + const StringAtom& name = (static_cast(f->function.name))->name; + if (isConstructor) + thisClass->defineConstructor(name); + else + if (isStatic) + thisClass->defineStatic(name, &Function_Type); + else { + switch (f->function.prefix) { + case FunctionName::Get: + thisClass->setGetter(name, NULL, mContext->extractType(f->function.resultType)); + break; + case FunctionName::Set: + thisClass->setSetter(name, NULL, mContext->extractType(f->function.resultType)); + break; + case FunctionName::normal: + thisClass->defineMethod(name, NULL); + break; + default: + NOT_REACHED("unexpected prefix"); + break; + } + } + } + } + break; + default: + NOT_REACHED("unimplemented class member statement"); + break; + } + s = s->next; + } + } + if (needsInstanceInitializer) + thisClass->defineStatic(mInitName, &Function_Type); +/* + Now gen code for each +*/ + + bool hasDefaultConstructor = false; + if (classStmt->body) { + JSScope* thisScope = thisClass->getScope(); + ICodeGenerator *ccg = NULL; + Context *classContext = new Context(mContext->getWorld(), thisScope); + if (needsInstanceInitializer) { + // constructor code generator. Slot variable + // initializers get added to this function. + ccg = new ICodeGenerator(classContext, NULL, thisClass, kNoFlags, &Void_Type); + ccg->allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 + } + + // static initializer code generator. + // static field inits, plus code to initialize + // static method slots. + ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod, &Void_Type); + StmtNode* s = classStmt->body->statements; + while (s) { + switch (s->getKind()) { + case StmtNode::Const: + case StmtNode::Var: + { + VariableStmtNode *vs = static_cast(s); + bool isStatic = hasAttribute(vs->attributes, Token::Static); + VariableBinding *v = vs->bindings; + while (v) { + if (v->name) { + ASSERT(v->name->getKind() == ExprNode::identifier); + if (v->initializer) { + IdentifierExprNode* idExpr = static_cast(v->name); + if (isStatic) { + scg.setStatic(thisClass, idExpr->name, scg.genExpr(v->initializer)); + scg.resetStatement(); + } else { + const JSSlot& slot = thisClass->getSlot(idExpr->name); + ccg->setSlot(thisRegister, slot.mIndex, ccg->genExpr(v->initializer)); + ccg->resetStatement(); + } + } + } + v = v->next; + } + } + break; + case StmtNode::Constructor: + case StmtNode::Function: + { + FunctionStmtNode *f = static_cast(s); + bool isStatic = hasAttribute(f->attributes, Token::Static); + bool isConstructor = (s->getKind() == StmtNode::Constructor); + ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; + + ICodeGenerator mcg(classContext, NULL, thisClass, flags); // method code generator. + ICodeModule *icm = mcg.genFunction(f->function, isStatic, isConstructor, superclass); + if (f->function.prefix == FunctionName::Operator) { + thisClass->defineOperator(f->function.op, + mContext->getParameterType(f->function, 0), + mContext->getParameterType(f->function, 1), new JSFunction(icm)); + } + else + if (f->function.name->getKind() == ExprNode::identifier) { + const StringAtom& name = (static_cast(f->function.name))->name; + if (isConstructor) { + if (name == nameExpr->name) + hasDefaultConstructor = true; + scg.setStatic(thisClass, name, scg.newFunction(icm)); + } + else + if (isStatic) + scg.setStatic(thisClass, name, scg.newFunction(icm)); + else { + switch (f->function.prefix) { + case FunctionName::Get: + thisClass->setGetter(name, new JSFunction(icm), icm->mResultType); + break; + case FunctionName::Set: + thisClass->setSetter(name, new JSFunction(icm), icm->mResultType); + break; + case FunctionName::normal: + thisClass->defineMethod(name, new JSFunction(icm)); + break; + default: + NOT_REACHED("unexpected prefix"); + break; + } + } + } + } + break; + default: + NOT_REACHED("unimplemented class member statement"); + break; + } + s = s->next; + } + + // add the instance initializer + if (ccg) { + scg.setStatic(thisClass, mInitName, scg.newFunction(ccg->complete())); + delete ccg; + } + // invent a default constructor if necessary, it just calls the + // initializer and the superclass default constructor + if (!hasDefaultConstructor) { + TypedRegister thisValue = TypedRegister(0, thisClass); + ArgumentList *args = new ArgumentList(); + ICodeGenerator icg(classContext, NULL, thisClass, kIsStaticMethod, &Void_Type); + icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 + if (superclass) + icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); + if (thisClass->hasStatic(mInitName)) + icg.call(icg.bindThis(thisValue, icg.getStatic(thisClass, mInitName)), args); + icg.returnStmt(thisValue); + thisClass->defineConstructor(nameExpr->name); + scg.setStatic(thisClass, nameExpr->name, scg.newFunction(icg.complete())); + } + // freeze the class. + thisClass->complete(); + + + // REVISIT: using the scope of the class to store both methods and statics. + if (scg.getICode()->size()) { + ICodeModule* clinit = scg.complete(); + classContext->interpret(clinit, JSValues()); + delete clinit; + } + delete classContext; + } + } + break; + case StmtNode::Function: + { + FunctionStmtNode *f = static_cast(p); + bool isStatic = hasAttribute(f->attributes, Token::Static); + ICodeModule *icm = genFunction(f->function, isStatic, false, NULL); + JSType *resultType = mContext->extractType(f->function.resultType); + if (f->function.name->getKind() == ExprNode::identifier) { + const StringAtom& name = (static_cast(f->function.name))->name; + switch (f->function.prefix) { + case FunctionName::Get: + if (isTopLevel()) { + mContext->getGlobalObject()->defineVariable(name, resultType); + mContext->getGlobalObject()->setGetter(name, new JSFunction(icm)); + } + else { + // is this legal - a nested getter? + NOT_REACHED("Better check with Waldemar"); + //allocateVariable(name, resultType); + } + break; + case FunctionName::Set: + if (isTopLevel()) { + mContext->getGlobalObject()->defineVariable(name, resultType); + mContext->getGlobalObject()->setSetter(name, new JSFunction(icm)); + } + else { + // is this legal - a nested setter? + NOT_REACHED("Better check with Waldemar"); + //allocateVariable(name, resultType); + } + break; + case FunctionName::normal: + mContext->getGlobalObject()->defineFunction(name, icm); + break; + default: + NOT_REACHED("unexpected prefix"); + break; + } + } + } + break; + case StmtNode::Import: + { + ImportStmtNode *i = static_cast(p); + String *fileName = i->bindings->packageName.str; + if (fileName) { /// if not, build one from the idList instead + std::string str(fileName->length(), char()); + std::transform(fileName->begin(), fileName->end(), str.begin(), narrow); + FILE* f = fopen(str.c_str(), "r"); + if (f) { + (void)mContext->readEvalFile(f, *fileName); + fclose(f); + } + } + } + break; + case StmtNode::Var: + { + VariableStmtNode *vs = static_cast(p); + VariableBinding *v = vs->bindings; + while (v) { + if (v->name && (v->name->getKind() == ExprNode::identifier)) { + JSType *type = mContext->extractType(v->type); + if (isTopLevel()) + mContext->getGlobalObject()->defineVariable((static_cast(v->name))->name, type); + else + allocateVariable((static_cast(v->name))->name, type); + if (v->initializer) { + if (!isTopLevel() && !isWithinWith()) { + TypedRegister r = genExpr(v->name); + TypedRegister val = genExpr(v->initializer); + val = cast(val, type); + move(r, val); + } + else { + TypedRegister val = genExpr(v->initializer); + val = cast(val, type); + saveName((static_cast(v->name))->name, val); + } + } + } + v = v->next; + } + } + break; + case StmtNode::expression: + { + ExprStmtNode *e = static_cast(p); + ret = genExpr(e->expr); + } + break; + case StmtNode::Throw: + { + ExprStmtNode *e = static_cast(p); + throwStmt(genExpr(e->expr)); + } + break; + case StmtNode::Debugger: + { + debuggerStmt(); + } + break; + case StmtNode::Return: + { + ExprStmtNode *e = static_cast(p); + if (e->expr) + returnStmt(ret = genExpr(e->expr)); + else + returnStmt(TypedRegister(NotARegister, &Void_Type)); + } + break; + case StmtNode::If: + { + Label *falseLabel = getLabel(); + UnaryStmtNode *i = static_cast(p); + TypedRegister c = genExpr(i->expr, false, NULL, falseLabel); + if (!generatedBoolean(i->expr)) + branchFalse(falseLabel, test(c)); + genStmt(i->stmt); + setLabel(falseLabel); + } + break; + case StmtNode::IfElse: + { + Label *falseLabel = getLabel(); + Label *trueLabel = getLabel(); + Label *beyondLabel = getLabel(); + BinaryStmtNode *i = static_cast(p); + TypedRegister c = genExpr(i->expr, false, trueLabel, falseLabel); + if (!generatedBoolean(i->expr)) + branchFalse(falseLabel, test(c)); + setLabel(trueLabel); + genStmt(i->stmt); + branch(beyondLabel); + setLabel(falseLabel); + genStmt(i->stmt2); + setLabel(beyondLabel); + } + break; + case StmtNode::With: + { + UnaryStmtNode *w = static_cast(p); + TypedRegister o = genExpr(w->expr); + bool withinWith = isWithinWith(); + setFlag(kIsWithinWith, true); + beginWith(o); + genStmt(w->stmt); + endWith(); + setFlag(kIsWithinWith, withinWith); + } + break; + case StmtNode::Switch: + { + Label *defaultLabel = NULL; + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel()); + mLabelStack.push_back(e); + SwitchStmtNode *sw = static_cast(p); + TypedRegister sc = genExpr(sw->expr); + StmtNode *s = sw->statements; + // ECMA requires case & default statements to be immediate children of switch + // unlike C where they can be arbitrarily deeply nested in other statements. + Label *nextCaseLabel = NULL; + GenericBranch *lastBranch = NULL; + while (s) { + if (s->getKind() == StmtNode::Case) { + ExprStmtNode *c = static_cast(s); + if (c->expr) { + if (nextCaseLabel) + setLabel(nextCaseLabel); + nextCaseLabel = getLabel(); + TypedRegister r = genExpr(c->expr); + TypedRegister eq = binaryOp(COMPARE_EQ, r, sc); + lastBranch = branchFalse(nextCaseLabel, eq); + } + else { + defaultLabel = getLabel(); + setLabel(defaultLabel); + } + } + else + genStmt(s); + s = s->next; + } + if (nextCaseLabel) + setLabel(nextCaseLabel); + if (defaultLabel && lastBranch) + lastBranch->setTarget(defaultLabel); + + setLabel(e->breakLabel); + mLabelStack.pop_back(); + } + break; + case StmtNode::DoWhile: + { + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); + mLabelStack.push_back(e); + UnaryStmtNode *d = static_cast(p); + Label *doBodyTopLabel = getLabel(); + setLabel(doBodyTopLabel); + genStmt(d->stmt); + setLabel(e->continueLabel); + TypedRegister c = genExpr(d->expr, false, doBodyTopLabel, NULL); + if (!generatedBoolean(d->expr)) + branchTrue(doBodyTopLabel, test(c)); + setLabel(e->breakLabel); + mLabelStack.pop_back(); + } + break; + case StmtNode::While: + { + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); + mLabelStack.push_back(e); + branch(e->continueLabel); + + UnaryStmtNode *w = static_cast(p); + + Label *whileBodyTopLabel = getLabel(); + setLabel(whileBodyTopLabel); + genStmt(w->stmt); + + setLabel(e->continueLabel); + TypedRegister c = genExpr(w->expr, false, whileBodyTopLabel, NULL); + if (!generatedBoolean(w->expr)) + branchTrue(whileBodyTopLabel, test(c)); + + setLabel(e->breakLabel); + mLabelStack.pop_back(); + } + break; + case StmtNode::For: + { + LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); + mLabelStack.push_back(e); + + ForStmtNode *f = static_cast(p); + if (f->initializer) + genStmt(f->initializer); + Label *forTestLabel = getLabel(); + branch(forTestLabel); + + Label *forBlockTop = getLabel(); + setLabel(forBlockTop); + genStmt(f->stmt); + + setLabel(e->continueLabel); + if (f->expr3) { + (*mInstructionMap)[iCode->size()] = f->expr3->pos; + genExpr(f->expr3); + } + + setLabel(forTestLabel); + if (f->expr2) { + (*mInstructionMap)[iCode->size()] = f->expr2->pos; + TypedRegister c = genExpr(f->expr2, false, forBlockTop, NULL); + if (!generatedBoolean(f->expr2)) + branchTrue(forBlockTop, test(c)); + } + + setLabel(e->breakLabel); + + mLabelStack.pop_back(); + } + break; + case StmtNode::block: + { + BlockStmtNode *b = static_cast(p); + StmtNode *s = b->statements; + while (s) { + genStmt(s); + s = s->next; + } + } + break; + + case StmtNode::label: + { + LabelStmtNode *l = static_cast(p); + // ok, there's got to be a cleverer way of doing this... + if (currentLabelSet == NULL) { + currentLabelSet = new LabelSet(); + currentLabelSet->push_back(&l->name); + genStmt(l->stmt, currentLabelSet); + delete currentLabelSet; + } + else { + currentLabelSet->push_back(&l->name); + genStmt(l->stmt, currentLabelSet); + currentLabelSet->pop_back(); + } + } + break; + + case StmtNode::Break: + { + GoStmtNode *g = static_cast(p); + if (g->label) { + LabelEntry *e = NULL; + for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { + e = (*i); + if (e->containsLabel(g->name)) + break; + } + if (e) { + ASSERT(e->breakLabel); + branch(e->breakLabel); + } + else + NOT_REACHED("break label not in label set"); + } + else { + ASSERT(!mLabelStack.empty()); + LabelEntry *e = mLabelStack.back(); + ASSERT(e->breakLabel); + branch(e->breakLabel); + } + } + break; + case StmtNode::Continue: + { + GoStmtNode *g = static_cast(p); + if (g->label) { + LabelEntry *e = NULL; + for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { + e = (*i); + if (e->containsLabel(g->name)) + break; + } + if (e) { + ASSERT(e->continueLabel); + branch(e->continueLabel); + } + else + NOT_REACHED("continue label not in label set"); + } + else { + ASSERT(!mLabelStack.empty()); + LabelEntry *e = mLabelStack.back(); + ASSERT(e->continueLabel); + branch(e->continueLabel); + } + } + break; + + case StmtNode::Try: + { + /* + The finallyInvoker is a little stub used by the interpreter to + invoke the finally handler on the (exceptional) way out of the + try block assuming there are no catch clauses. + */ + /*Register ex = NotARegister;*/ + TryStmtNode *t = static_cast(p); + Label *catchLabel = (t->catches) ? getLabel() : NULL; + Label *finallyInvoker = (t->finally) ? getLabel() : NULL; + Label *finallyLabel = (t->finally) ? getLabel() : NULL; + Label *beyondLabel = getLabel(); + beginTry(catchLabel, finallyLabel); + genStmt(t->stmt); + endTry(); + if (finallyLabel) + jsr(finallyLabel); + branch(beyondLabel); + if (catchLabel) { + setLabel(catchLabel); + CatchClause *c = t->catches; + while (c) { + // Bind the incoming exception ... + if (mExceptionRegister.first == NotABanana) + mExceptionRegister = allocateRegister(&Any_Type); + allocateVariable(c->name, mExceptionRegister); + + genStmt(c->stmt); + if (finallyLabel) + jsr(finallyLabel); + c = c->next; + } + } + if (finallyLabel) { + setLabel(finallyInvoker); + jsr(finallyLabel); + throwStmt(mExceptionRegister); + + setLabel(finallyLabel); + genStmt(t->finally); + rts(); + } + setLabel(beyondLabel); + } + break; + + case StmtNode::empty: + /* nada */ + break; + + default: + NOT_REACHED("unimplemented statement kind"); + } + resetStatement(); + return ret; +} + + +} // namespace ICG + +} // namespace JavaScript diff --git a/js2/src/icodegenerator.cpp b/js2/src/icodegenerator.cpp index 7a8de7f33e51..cdac5b109a68 100644 --- a/js2/src/icodegenerator.cpp +++ b/js2/src/icodegenerator.cpp @@ -73,7 +73,11 @@ Formatter& operator<<(Formatter &f, ICodeModule &i) // -ICodeGenerator::ICodeGenerator(Context *cx, ICodeGenerator *containingFunction, JSClass *aClass, ICodeGeneratorFlags flags) +ICodeGenerator::ICodeGenerator(Context *cx, + ICodeGenerator *containingFunction, + JSClass *aClass, + ICodeGeneratorFlags flags, + JSType *resultType) : mTopRegister(0), mExceptionRegister(TypedRegister(NotARegister, &None_Type)), variableList(new VariableList()), @@ -84,20 +88,13 @@ ICodeGenerator::ICodeGenerator(Context *cx, ICodeGenerator *containingFunction, mFlags(flags), pLabels(NULL), mInitName(cx->getWorld().identifiers["__init__"]), - mContainingFunction(containingFunction) + mContainingFunction(containingFunction), + mResultType(resultType) { iCode = new InstructionStream(); iCodeOwner = true; } -JSType *ICodeGenerator::findType(const StringAtom& typeName) -{ - const JSValue& type = mContext->getGlobalObject()->getVariable(typeName); - if (type.isType()) - return type.type; - return &Any_Type; -} - /* -Called to allocate parameter and variable registers, aka 'permanent' registers. -mTopRegister is the current high-water mark. @@ -125,8 +122,17 @@ TypedRegister ICodeGenerator::allocateRegister(JSType *type) return result; } +TypedRegister ICodeGenerator::allocateVariable(const StringAtom& name, const StringAtom& typeName) +{ + return allocateVariable(name, mContext->findType(typeName)); +} -ICodeModule *ICodeGenerator::complete(JSType *resultType) +TypedRegister ICodeGenerator::allocateParameter(const StringAtom& name, bool isOptional, const StringAtom& typeName) +{ + return allocateParameter(name, isOptional, mContext->findType(typeName)); +} + +ICodeModule *ICodeGenerator::complete() { #ifdef DEBUG for (LabelList::iterator i = labels.begin(); @@ -169,12 +175,7 @@ ICodeModule *ICodeGenerator::complete(JSType *resultType) } } */ - ICodeModule* module = new ICodeModule(iCode, - variableList, - parameterList, - mPermanentRegister.size(), - mInstructionMap, - resultType, mExceptionRegister.first); + ICodeModule* module = new ICodeModule(*this); if (pLabels) { uint32 i; uint32 parameterInits = pLabels->size() - 1; // there's an extra label at the end for the actual entryPoint @@ -611,1943 +612,6 @@ Label *ICodeGenerator::setLabel(Label *l) return l; } -/************************************************************************/ - -ExprNode::Kind ICodeGenerator::mapICodeOpToExprNode(ICodeOp op) -{ - switch (op) { - case ADD: - return ExprNode::add; - case SUBTRACT: - return ExprNode::subtract; - case MULTIPLY: - return ExprNode::multiply; - case DIVIDE: - return ExprNode::divide; - case REMAINDER: - return ExprNode::modulo; - case SHIFTLEFT: - return ExprNode::leftShift; - case SHIFTRIGHT: - return ExprNode::rightShift; - case USHIFTRIGHT: - return ExprNode::logicalRightShift; - case AND: - return ExprNode::bitwiseAnd; - case OR: - return ExprNode::bitwiseOr; - case XOR: - return ExprNode::bitwiseXor; - case POSATE: - return ExprNode::plus; - case NEGATE: - return ExprNode::minus; - case BITNOT: - return ExprNode::complement; - case COMPARE_EQ: - return ExprNode::equal; - case COMPARE_LT: - return ExprNode::lessThan; - case COMPARE_LE: - return ExprNode::lessThanOrEqual; - case STRICT_EQ: - return ExprNode::identical; - } - return ExprNode::none; -} - - -ICodeOp ICodeGenerator::mapExprNodeToICodeOp(ExprNode::Kind kind) -{ - // can be an array later, when everything has settled down - switch (kind) { - // binary - case ExprNode::add: - case ExprNode::addEquals: - return ADD; - case ExprNode::subtract: - case ExprNode::subtractEquals: - return SUBTRACT; - case ExprNode::multiply: - case ExprNode::multiplyEquals: - return MULTIPLY; - case ExprNode::divide: - case ExprNode::divideEquals: - return DIVIDE; - case ExprNode::modulo: - case ExprNode::moduloEquals: - return REMAINDER; - case ExprNode::leftShift: - case ExprNode::leftShiftEquals: - return SHIFTLEFT; - case ExprNode::rightShift: - case ExprNode::rightShiftEquals: - return SHIFTRIGHT; - case ExprNode::logicalRightShift: - case ExprNode::logicalRightShiftEquals: - return USHIFTRIGHT; - case ExprNode::bitwiseAnd: - case ExprNode::bitwiseAndEquals: - return AND; - case ExprNode::bitwiseXor: - case ExprNode::bitwiseXorEquals: - return XOR; - case ExprNode::bitwiseOr: - case ExprNode::bitwiseOrEquals: - return OR; - // unary - case ExprNode::plus: - return POSATE; - case ExprNode::minus: - return NEGATE; - case ExprNode::complement: - return BITNOT; - - // relational - case ExprNode::In: - return COMPARE_IN; - case ExprNode::Instanceof: - return INSTANCEOF; - - case ExprNode::equal: - return COMPARE_EQ; - case ExprNode::lessThan: - return COMPARE_LT; - case ExprNode::lessThanOrEqual: - return COMPARE_LE; - case ExprNode::identical: - return STRICT_EQ; - - // these get reversed by the generator - case ExprNode::notEqual: - return COMPARE_EQ; - case ExprNode::greaterThan: - return COMPARE_LT; - case ExprNode::greaterThanOrEqual: - return COMPARE_LE; - case ExprNode::notIdentical: - return STRICT_EQ; - - default: - NOT_REACHED("Unimplemented kind"); - return NOP; - } -} - - -static bool generatedBoolean(ExprNode *p) -{ - switch (p->getKind()) { - case ExprNode::parentheses: - { - UnaryExprNode *u = static_cast(p); - return generatedBoolean(u->op); - } - case ExprNode::True: - case ExprNode::False: - case ExprNode::equal: - case ExprNode::notEqual: - case ExprNode::lessThan: - case ExprNode::lessThanOrEqual: - case ExprNode::greaterThan: - case ExprNode::greaterThanOrEqual: - case ExprNode::identical: - case ExprNode::notIdentical: - case ExprNode::In: - case ExprNode::Instanceof: - case ExprNode::logicalAnd: - case ExprNode::logicalXor: - case ExprNode::logicalOr: - return true; - default: - break; - } - return false; -} - -static bool isSlotName(JSType *t, const StringAtom &name, uint32 &slotIndex, JSType *&type, bool lvalue) -{ - JSClass* c = dynamic_cast(t); - while (c) { - if (c->hasSlot(name)) { - const JSSlot &s = c->getSlot(name); - if (lvalue) { - if (s.mActual || s.mSetter) { - slotIndex = s.mIndex; - type = s.mType; - return true; - } - } - else { - if (s.mActual || s.mGetter) { - slotIndex = s.mIndex; - type = s.mType; - return true; - } - } - return false; - } - c = c->getSuperClass(); - } - return false; -} - -static bool isMethodName(JSType *t, const StringAtom &name, uint32 &slotIndex) -{ - JSClass* c = dynamic_cast(t); - return (c && c->hasMethod(name, slotIndex)); -} - - -static bool isStaticName(JSClass *c, const StringAtom &name, JSType*& type, bool &isConstructor) -{ - do { - if (c->hasStatic(name, type, isConstructor)) - return true; - c = c->getSuperClass(); - } while (c); - return false; -} - -ICodeGenerator::LValueKind ICodeGenerator::getVariableByName(const StringAtom &name, TypedRegister &v) -{ - v = variableList->findVariable(name); - if (v.first == NotARegister) - v = parameterList->findVariable(name); - if (v.first != NotARegister) - return Var; - return NoKind; -} - -ICodeGenerator::LValueKind ICodeGenerator::scanForVariable(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base) -{ - LValueKind k = getVariableByName(name, v); - if (k == Var) return k; - - uint32 count = 0; - ICodeGenerator *upper = mContainingFunction; - while (upper) { - k = upper->getVariableByName(name, v); - if (k == Var) { - base = getClosure(count); - slotIndex = v.first; - return Slot; - } - count++; - upper = upper->mContainingFunction; - } - return NoKind; -} - -// find 'name' (unqualified) in the current context. -// for local variable, returns v.first = register number -// for slot/method, returns slotIndex and sets base appropriately -// (note closure vars also get handled this way) -// v.second is set to the type regardless -ICodeGenerator::LValueKind ICodeGenerator::resolveIdentifier(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, TypedRegister &base, bool lvalue) -{ - if (!isWithinWith()) { - LValueKind k = scanForVariable(name, v, slotIndex, base); - if (k != NoKind) - return k; - else { - if (mClass) { // we're compiling a method of a class - if (!isStaticMethod()) { - if (isSlotName(mClass, name, slotIndex, v.second, lvalue)) { - base = TypedRegister(0, mClass); - return Slot; - } - if (isMethodName(mClass, name, slotIndex)) { - base = TypedRegister(0, mClass); - return Method; - } - } - bool isConstructor = false; - if (isStaticName(mClass, name, v.second, isConstructor)) { - return (isConstructor) ? Constructor : Static; - } - } - // last chance - if it's a generic name in the global scope, try to get a type for it - v.second = mContext->getGlobalObject()->getType(name); - return Name; - } - } - // all bet's off, generic name & type - v.second = &Any_Type; - return Name; -} - -TypedRegister ICodeGenerator::handleIdentifier(IdentifierExprNode *p, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) -{ - ASSERT(p->getKind() == ExprNode::identifier); - - /*JSType *vType = &Any_Type;*/ - uint32 slotIndex; - TypedRegister v; - TypedRegister base; - - const StringAtom &name = (static_cast(p))->name; - LValueKind lValueKind = resolveIdentifier(name, v, slotIndex, base, lvalue); - JSType *targetType = v.second; - - switch (use) { - case ExprNode::addEquals: - case ExprNode::subtractEquals: - case ExprNode::multiplyEquals: - case ExprNode::divideEquals: - case ExprNode::moduloEquals: - case ExprNode::leftShiftEquals: - case ExprNode::rightShiftEquals: - case ExprNode::logicalRightShiftEquals: - case ExprNode::bitwiseAndEquals: - case ExprNode::bitwiseXorEquals: - case ExprNode::bitwiseOrEquals: - switch (lValueKind) { - case Var: - break; - case Name: - v = loadName(name, v.second); - break; - case Slot: - v = getSlot(base, slotIndex); - break; - case Static: - case Constructor: - v = getStatic(mClass, name); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); - // fall thru... - case ExprNode::assignment: - ret = cast(ret, targetType); - switch (lValueKind) { - case Var: - move(v, ret); - break; - case Name: - saveName(name, ret); - break; - case Slot: - setSlot(base, slotIndex, ret); - break; - case Static: - case Constructor: - setStatic(mClass, name, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::identifier: - switch (lValueKind) { - case Var: - ret = v; - break; - case Name: - ret = loadName(name, v.second); - break; - case Slot: - ret = getSlot(base, slotIndex); - break; - case Static: - case Constructor: - ret = getStatic(mClass, name); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::preDecrement: - case ExprNode::preIncrement: - switch (lValueKind) { - case Var: - ret = binaryOp(xcrementOp, v, loadImmediate(1.0)); - break; - case Name: - ret = loadName(name, v.second); - ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); - saveName(name, ret); - break; - case Slot: - ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); - setSlot(base, slotIndex, ret); - break; - case Static: - case Constructor: - ret = binaryOp(xcrementOp, getStatic(mClass, name), loadImmediate(1.0)); - setStatic(mClass, name, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::postDecrement: - case ExprNode::postIncrement: - switch (lValueKind) { - case Var: - ret = varXcr(v, xcrementOp); - break; - case Name: - ret = nameXcr(name, xcrementOp); - break; - case Slot: - ret = slotXcr(base, slotIndex, xcrementOp); - break; - case Static: - case Constructor: - ret = staticXcr(mClass, name, xcrementOp); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::call: - { - switch (lValueKind) { - case Var: - ret = call(v, args); - break; - case Name: - ret = call(loadName(name), args); - break; - case Method: - ret = call(getMethod(base, slotIndex), args); - break; - case Static: - ret = call(getStatic(mClass, name), args); - break; - case Constructor: - ret = newClass(mClass); - call(bindThis(ret, getStatic(mClass, name)), args); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - } - break; - default: - NOT_REACHED("Bad use kind"); - } - return ret; - -} - -TypedRegister ICodeGenerator::handleDot(BinaryExprNode *b, ExprNode::Kind use, ICodeOp xcrementOp, TypedRegister ret, ArgumentList *args, bool lvalue) -{ - ASSERT(b->getKind() == ExprNode::dot); - - LValueKind lValueKind = Property; - - if (b->op2->getKind() != ExprNode::identifier) { - NOT_REACHED("Implement me"); // turns into a getProperty (but not via any overloaded [] ) - } - else { - // we have . - const StringAtom &fieldName = static_cast(b->op2)->name; - TypedRegister base; - TypedRegister baseBase; - JSClass *clazz = NULL; - JSType *fieldType = &Any_Type; - uint32 slotIndex; - if ((b->op1->getKind() == ExprNode::identifier) && !isWithinWith()) { - // handle . - const StringAtom &baseName = (static_cast(b->op1))->name; - LValueKind baseKind = resolveIdentifier(baseName, base, slotIndex, baseBase, false); - if (baseKind == Slot) { - base = getSlot(baseBase, slotIndex); - } - // - // handle . - // - if (base.second == &Type_Type) { - const JSValue &v = mContext->getGlobalObject()->getVariable(baseName); - bool isConstructor; - ASSERT(v.isType()); // there's no other way that base.second could be &Type_Type, right? - clazz = dynamic_cast(v.type); - if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) { - lValueKind = (isConstructor) ? Constructor : Static; - } - } - if (lValueKind == Property) { - if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) - lValueKind = Slot; - else - if (isMethodName(base.second, fieldName, slotIndex)) - lValueKind = Method; - else { - bool isConstructor; - clazz = dynamic_cast(base.second); - if (clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) - lValueKind = (isConstructor) ? Constructor : Static; - } - } - if ((lValueKind == Property) || (base.first == NotARegister)) - base = loadName(baseName, base.second); - } - else { - base = genExpr(b->op1); - if (isSlotName(base.second, fieldName, slotIndex, fieldType, lvalue)) - lValueKind = Slot; - else - if (isMethodName(base.second, fieldName, slotIndex)) - lValueKind = Method; - else { - bool isConstructor; - clazz = dynamic_cast(base.second); - if (clazz && clazz && isStaticName(clazz, fieldName, fieldType, isConstructor)) - lValueKind = (isConstructor) ? Constructor : Static; - } - } - TypedRegister v; - switch (use) { - case ExprNode::call: - switch (lValueKind) { - case Static: - ret = call(getStatic(clazz, fieldName), args); - break; - case Constructor: - ret = newClass(clazz); - call(bindThis(ret, getStatic(clazz, fieldName)), args); - break; - case Property: - ret = call(bindThis(base, getProperty(base, fieldName)), args); - break; - case Method: - ret = call(getMethod(base, slotIndex), args); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::dot: - case ExprNode::addEquals: - case ExprNode::subtractEquals: - case ExprNode::multiplyEquals: - case ExprNode::divideEquals: - case ExprNode::moduloEquals: - case ExprNode::leftShiftEquals: - case ExprNode::rightShiftEquals: - case ExprNode::logicalRightShiftEquals: - case ExprNode::bitwiseAndEquals: - case ExprNode::bitwiseXorEquals: - case ExprNode::bitwiseOrEquals: - switch (lValueKind) { - case Constructor: - case Static: - v = getStatic(clazz, fieldName); - break; - case Property: - v = getProperty(base, fieldName); - break; - case Slot: - v = getSlot(base, slotIndex); - break; - case Method: - v = getMethod(base, slotIndex); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - if (use == ExprNode::dot) { - ret = v; - break; - } - ret = binaryOp(mapExprNodeToICodeOp(use), v, ret); - // fall thru... - case ExprNode::assignment: - ret = cast(ret, fieldType); - switch (lValueKind) { - case Constructor: - case Static: - setStatic(clazz, fieldName, ret); - break; - case Property: - setProperty(base, fieldName, ret); - break; - case Slot: - setSlot(base, slotIndex, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::postDecrement: - case ExprNode::postIncrement: - { -// JSClass *clss = dynamic_cast(fieldType); -// if (clss) { -// clss->findOverloadedOperator(use); -// } - switch (lValueKind) { - case Constructor: - case Static: - ret = staticXcr(clazz, fieldName, xcrementOp); - break; - case Property: - ret = propertyXcr(base, fieldName, xcrementOp); - break; - case Slot: - ret = slotXcr(base, slotIndex, xcrementOp); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - } - break; - case ExprNode::preDecrement: - case ExprNode::preIncrement: - switch (lValueKind) { - case Constructor: - case Static: - ret = binaryOp(xcrementOp, getStatic(clazz, fieldName), loadImmediate(1.0)); - setStatic(clazz, fieldName, ret); - break; - case Property: - ret = binaryOp(xcrementOp, getProperty(base, fieldName), loadImmediate(1.0)); - setProperty(base, fieldName, ret); - break; - case Slot: - ret = binaryOp(xcrementOp, getSlot(base, slotIndex), loadImmediate(1.0)); - setSlot(base, slotIndex, ret); - break; - default: - NOT_REACHED("Bad lvalue kind"); - } - break; - case ExprNode::Delete: - if (lValueKind == Property) { - ret = deleteProperty(base, fieldName); - } - break; - default: - NOT_REACHED("unexpected use node"); - } - ret.second = fieldType; - } - return ret; -} - - - -/* - if trueBranch OR falseBranch are not null, the sub-expression should generate - a conditional branch to the appropriate target. If either branch is NULL, it - indicates that the label is immediately forthcoming. -*/ -TypedRegister ICodeGenerator::genExpr(ExprNode *p, - bool needBoolValueInBranch, - Label *trueBranch, - Label *falseBranch) -{ - TypedRegister ret(NotARegister, &None_Type); - ICodeOp xcrementOp = ADD; - switch (p->getKind()) { - case ExprNode::True: - if (trueBranch || falseBranch) { - if (needBoolValueInBranch) - ret = loadBoolean(true); - if (trueBranch) - branch(trueBranch); - } - else - ret = loadBoolean(true); - break; - case ExprNode::False: - if (trueBranch || falseBranch) { - if (needBoolValueInBranch) - ret = loadBoolean(false); - if (falseBranch) - branch(falseBranch); - } - else - ret = loadBoolean(false); - break; - case ExprNode::Null: - ret = loadNull(); - break; - case ExprNode::parentheses: - { - UnaryExprNode *u = static_cast(p); - ret = genExpr(u->op, needBoolValueInBranch, trueBranch, falseBranch); - } - break; - case ExprNode::New: - { - InvokeExprNode *i = static_cast(p); - ArgumentList *args = new ArgumentList(); - ExprPairList *p = i->pairs; - StringFormatter s; - while (p) { - if (p->field && (p->field->getKind() == ExprNode::identifier)) - args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); - else { - if (p->field && (p->field->getKind() == ExprNode::string)) - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); - else { - s << (uint32)args->size(); - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); - s.clear(); - } - } - p = p->next; - } - if (i->op->getKind() == ExprNode::identifier) { - const StringAtom &className = static_cast(i->op)->name; - const JSValue& value = mContext->getGlobalObject()->getVariable(className); - if (value.isType()) { - JSClass* clazz = dynamic_cast(value.type); - if (clazz) { - ret = newClass(clazz); - ret = call(bindThis(ret, getStatic(clazz, className)), args); - } - else { - // - // like 'new Boolean()' - see if the type has a constructor - // - JSFunction *f = value.type->getConstructor(); - if (f) - ret = directCall(f, args); - else - NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. - } - } - else { - if (value.isFunction()) { - TypedRegister f = loadName(className, value.type); - ret = newObject(f); - ret = call(bindThis(ret, f), args); - } - else - NOT_REACHED("new , where is not a function"); // XXX Runtime error. - } - } - else - ret = newObject(TypedRegister(NotARegister, &Any_Type)); // XXX more ? - } - break; - case ExprNode::Delete: - { - UnaryExprNode *d = static_cast(p); - ASSERT(d->op->getKind() == ExprNode::dot); - ret = handleDot(static_cast(d->op), p->getKind(), xcrementOp, ret, NULL, true); - // rather than getProperty(), need to do a deleteProperty(). - } - break; - case ExprNode::call : - { - InvokeExprNode *i = static_cast(p); - ArgumentList *args = new ArgumentList(); - ExprPairList *p = i->pairs; - StringFormatter s; - while (p) { - if (p->field && (p->field->getKind() == ExprNode::identifier)) - args->push_back(Argument(genExpr(p->value), &(static_cast(p->field))->name)); - else { - if (p->field && (p->field->getKind() == ExprNode::string)) - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[(static_cast(p->field))->str])); - else { - s << (uint32)args->size(); - args->push_back(Argument(genExpr(p->value), &mContext->getWorld().identifiers[s.getString()] )); - s.clear(); - } - } - p = p->next; - } - - if (i->op->getKind() == ExprNode::dot) { - BinaryExprNode *b = static_cast(i->op); - ret = handleDot(b, ExprNode::call, xcrementOp, ret, args, false); - } - else { - if (i->op->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(i->op), ExprNode::call, xcrementOp, ret, args, false); - } - else { - if (i->op->getKind() == ExprNode::index) { - InvokeExprNode *ii = static_cast(i->op); - TypedRegister base = genExpr(ii->op); - ret = call(bindThis(base, getElement(base, genExpr(ii->pairs->value))), args); // FIXME, only taking first index - } - else - ASSERT("WAH!"); - } - } - } - break; - case ExprNode::index : - { - InvokeExprNode *i = static_cast(p); - TypedRegister base = genExpr(i->op); - JSClass *clazz = dynamic_cast(base.second); - if (clazz) { - // look for operator [] and invoke it - } - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - ret = getElement(base, index); - } - break; - case ExprNode::dot : - { - BinaryExprNode *b = static_cast(p); - ret = handleDot(b, p->getKind(), xcrementOp, ret, NULL, false); - } - break; - case ExprNode::This : - { - ret = TypedRegister(0, mClass ? mClass : &Any_Type); - } - break; - case ExprNode::identifier : - { - ret = handleIdentifier(static_cast(p), ExprNode::identifier, xcrementOp, ret, NULL, false); - } - break; - case ExprNode::number : - ret = loadImmediate((static_cast(p))->value); - break; - case ExprNode::string : - ret = loadString(mContext->getWorld().identifiers[(static_cast(p))->str]); - break; - case ExprNode::preDecrement: - xcrementOp = SUBTRACT; - case ExprNode::preIncrement: - { - UnaryExprNode *u = static_cast(p); - if (u->op->getKind() == ExprNode::dot) { - ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(u->op); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - ret = getElement(base, index); - ret = binaryOp(xcrementOp, ret, loadImmediate(1.0)); - setElement(base, index, ret); - } - else - ASSERT("WAH!"); - } - break; - case ExprNode::postDecrement: - xcrementOp = SUBTRACT; - case ExprNode::postIncrement: - { - UnaryExprNode *u = static_cast(p); - if (u->op->getKind() == ExprNode::dot) { - ret = handleDot(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(u->op), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (u->op->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(u->op); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - ret = elementXcr(base, index, xcrementOp); - } - else - ASSERT("WAH!"); - } - break; - - case ExprNode::plus: - case ExprNode::minus: - case ExprNode::complement: - { - UnaryExprNode *u = static_cast(p); - TypedRegister r = genExpr(u->op); - ret = op(mapExprNodeToICodeOp(p->getKind()), r); - } - break; - case ExprNode::add: - case ExprNode::subtract: - case ExprNode::multiply: - case ExprNode::divide: - case ExprNode::modulo: - case ExprNode::leftShift: - case ExprNode::rightShift: - case ExprNode::logicalRightShift: - case ExprNode::bitwiseAnd: - case ExprNode::bitwiseXor: - case ExprNode::bitwiseOr: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); - } - break; - case ExprNode::assignment: - { - BinaryExprNode *b = static_cast(p); - ret = genExpr(b->op2); - if (b->op1->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::dot) { - BinaryExprNode *lb = static_cast(b->op1); - ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(b->op1); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - setElement(base, index, ret); - } - else - ASSERT("WAH!"); - } - break; - case ExprNode::addEquals: - case ExprNode::subtractEquals: - case ExprNode::multiplyEquals: - case ExprNode::divideEquals: - case ExprNode::moduloEquals: - case ExprNode::leftShiftEquals: - case ExprNode::rightShiftEquals: - case ExprNode::logicalRightShiftEquals: - case ExprNode::bitwiseAndEquals: - case ExprNode::bitwiseXorEquals: - case ExprNode::bitwiseOrEquals: - { - BinaryExprNode *b = static_cast(p); - ret = genExpr(b->op2); - if (b->op1->getKind() == ExprNode::identifier) { - ret = handleIdentifier(static_cast(b->op1), p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::dot) { - BinaryExprNode *lb = static_cast(b->op1); - ret = handleDot(lb, p->getKind(), xcrementOp, ret, NULL, true); - } - else - if (b->op1->getKind() == ExprNode::index) { - InvokeExprNode *i = static_cast(b->op1); - TypedRegister base = genExpr(i->op); - TypedRegister index = genExpr(i->pairs->value); // FIXME, only taking first index - TypedRegister v = getElement(base, index); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), v, ret); - setElement(base, index, ret); - } - else - ASSERT("WAH!"); - } - break; - case ExprNode::equal: - case ExprNode::lessThan: - case ExprNode::lessThanOrEqual: - case ExprNode::identical: - case ExprNode::In: - case ExprNode::Instanceof: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); - if (trueBranch || falseBranch) { - if (trueBranch == NULL) - branchFalse(falseBranch, ret); - else { - branchTrue(trueBranch, ret); - if (falseBranch) - branch(falseBranch); - } - } - } - break; - case ExprNode::greaterThan: - case ExprNode::greaterThanOrEqual: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r2, r1); // will return reverse case - if (trueBranch || falseBranch) { - if (trueBranch == NULL) - branchFalse(falseBranch, ret); - else { - branchTrue(trueBranch, ret); - if (falseBranch) - branch(falseBranch); - } - } - } - break; - - case ExprNode::notEqual: - case ExprNode::notIdentical: - { - BinaryExprNode *b = static_cast(p); - TypedRegister r1 = genExpr(b->op1); - TypedRegister r2 = genExpr(b->op2); - ret = binaryOp(mapExprNodeToICodeOp(p->getKind()), r1, r2); // will generate equal/identical code - if (trueBranch || falseBranch) { - if (trueBranch == NULL) - branchTrue(falseBranch, ret); - else { - branchFalse(trueBranch, ret); - if (falseBranch) - branch(falseBranch); - } - } - else - ret = logicalNot(ret); - - } - break; - - case ExprNode::logicalAnd: - { - BinaryExprNode *b = static_cast(p); - if (trueBranch || falseBranch) { - genExpr(b->op1, needBoolValueInBranch, NULL, falseBranch); - genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); - } - else { - Label *fBranch = getLabel(); - TypedRegister r1 = genExpr(b->op1, true, NULL, fBranch); - if (!generatedBoolean(b->op1)) { - r1 = test(r1); - branchFalse(fBranch, r1); - } - TypedRegister r2 = genExpr(b->op2); - if (!generatedBoolean(b->op2)) { - r2 = test(r2); - } - if (r1 != r2) // FIXME, need a way to specify a dest??? - move(r1, r2); - setLabel(fBranch); - ret = r1; - } - } - break; - case ExprNode::logicalOr: - { - BinaryExprNode *b = static_cast(p); - if (trueBranch || falseBranch) { - genExpr(b->op1, needBoolValueInBranch, trueBranch, NULL); - genExpr(b->op2, needBoolValueInBranch, trueBranch, falseBranch); - } - else { - Label *tBranch = getLabel(); - TypedRegister r1 = genExpr(b->op1, true, tBranch, NULL); - if (!generatedBoolean(b->op1)) { - r1 = test(r1); - branchTrue(tBranch, r1); - } - TypedRegister r2 = genExpr(b->op2); - if (!generatedBoolean(b->op2)) { - r2 = test(r2); - } - if (r1 != r2) // FIXME, need a way to specify a dest??? - move(r1, r2); - setLabel(tBranch); - ret = r1; - } - } - break; - - case ExprNode::conditional: - { - TernaryExprNode *t = static_cast(p); - Label *fBranch = getLabel(); - Label *beyondBranch = getLabel(); - TypedRegister c = genExpr(t->op1, false, NULL, fBranch); - if (!generatedBoolean(t->op1)) - branchFalse(fBranch, test(c)); - TypedRegister r1 = genExpr(t->op2); - branch(beyondBranch); - setLabel(fBranch); - TypedRegister r2 = genExpr(t->op3); - if (r1 != r2) // FIXME, need a way to specify a dest??? - move(r1, r2); - setLabel(beyondBranch); - ret = r1; - } - break; - - case ExprNode::objectLiteral: - { - ret = newObject(TypedRegister(NotARegister, &Any_Type)); - PairListExprNode *plen = static_cast(p); - ExprPairList *e = plen->pairs; - while (e) { - if (e->field && e->value && (e->field->getKind() == ExprNode::identifier)) - setProperty(ret, (static_cast(e->field))->name, genExpr(e->value)); - e = e->next; - } - } - break; - - case ExprNode::functionLiteral: - { - FunctionExprNode *f = static_cast(p); - ICodeModule *icm = genFunction(f->function, false, false, NULL); - ret = newClosure(icm); - } - break; - - case ExprNode::at: - { - BinaryExprNode *b = static_cast(p); - // for now, just handle simple identifiers on the rhs. - ret = genExpr(b->op1); - if (b->op2->getKind() == ExprNode::identifier) { - TypedRegister t; - const StringAtom &name = (static_cast(b->op2))->name; - ASSERT(t.second == &Type_Type); - const JSValue &v = mContext->getGlobalObject()->getVariable(name); - ASSERT(v.isType()); - JSClass *clazz = dynamic_cast(v.type); - if (clazz) - ret = cast(ret, clazz); - else - ret = cast(ret, t.second); - } - else - NOT_REACHED("Anything more complex than @ is not implemented"); - } - break; - - - default: - { - NOT_REACHED("Unsupported ExprNode kind"); - } - } - return ret; -} - -bool LabelEntry::containsLabel(const StringAtom *label) -{ - if (labelSet) { - for (LabelSet::iterator i = labelSet->begin(); i != labelSet->end(); i++) - if ( (*i) == label ) - return true; - } - return false; -} - -static bool hasAttribute(const IdentifierList* identifiers, Token::Kind tokenKind) -{ - while (identifiers) { - if (identifiers->name.tokenKind == tokenKind) - return true; - identifiers = identifiers->next; - } - return false; -} - -static bool hasAttribute(const IdentifierList* identifiers, StringAtom &name) -{ - while (identifiers) { - if (identifiers->name == name) - return true; - identifiers = identifiers->next; - } - return false; -} - -JSType *ICodeGenerator::extractType(ExprNode *t) -{ - JSType* type = &Any_Type; - // FUTURE: do code generation for type expressions. - if (t && (t->getKind() == ExprNode::identifier)) { - IdentifierExprNode* typeExpr = static_cast(t); - type = findType(typeExpr->name); - } - return type; -} - -JSType *ICodeGenerator::getParameterType(FunctionDefinition &function, int index) -{ - VariableBinding *v = function.parameters; - while (v) { - if (index-- == 0) - return extractType(v->type); - else - v = v->next; - } - return NULL; -} - -ICodeModule *ICodeGenerator::genFunction(FunctionDefinition &function, bool isStatic, bool isConstructor, JSClass *superclass) -{ - ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; - - ICodeGenerator icg(mContext, this, mClass, flags); - icg.allocateParameter(mContext->getWorld().identifiers["this"], false, (mClass) ? mClass : &Any_Type); // always parameter #0 - VariableBinding *v = function.parameters; - bool unnamed = true; - uint32 positionalCount = 0; - StringFormatter s; - while (v) { - if (unnamed && (v == function.namedParameters)) { // Track when we hit the first named parameter. - icg.parameterList->setPositionalCount(positionalCount); - unnamed = false; - } - - // 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 == 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) { - positionalCount++; - s << r.first - 1; // the first positional parameter is '0' - icg.parameterList->add(mContext->getWorld().identifiers[s.getString()], r, (v->initializer != NULL)); - s.clear(); - } - } - } - v = v->next; - } - if (unnamed) icg.parameterList->setPositionalCount(positionalCount); - - // now allocate the rest parameter - if (function.restParameter) { - v = 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 = function.optParameters; - if (v) { - while (v) { // include the rest parameter, as it may have an initializer - if (v->name && (v->name->getKind() == ExprNode::identifier)) { - icg.addParameterLabel(icg.setLabel(icg.getLabel())); - TypedRegister p = icg.genExpr(v->name); - if (v->initializer) { // might be NULL when we get to the restParameter - Label *l = icg.getLabel(); - icg.branchInitialized(l, p); - icg.move(p, icg.genExpr(v->initializer)); - icg.setLabel(l); - } - else { // an un-initialized rest parameter is still an empty array - if (v == function.restParameter) { - Label *l = icg.getLabel(); - icg.branchInitialized(l, p); - icg.move(p, icg.newArray()); - icg.setLabel(l); - } - } - } - v = v->next; - } - icg.addParameterLabel(icg.setLabel(icg.getLabel())); // to provide the entry-point for the default case - } - - if (isConstructor) { - /* - See if the first statement is an expression statement consisting - of a call to super(). If not we need to add a call to the default - superclass constructor ourselves. - */ - TypedRegister thisValue = TypedRegister(0, mClass); - ArgumentList *args = new ArgumentList(); - if (superclass) { - bool foundSuperCall = false; - BlockStmtNode *b = function.body; - if (b && b->statements && (b->statements->getKind() == StmtNode::expression)) { - ExprStmtNode *e = static_cast(b->statements); - if (e->expr->getKind() == ExprNode::call) { - InvokeExprNode *i = static_cast(e->expr); - if (i->op->getKind() == ExprNode::dot) { - BinaryExprNode *b = static_cast(i->op); - if ((b->op1->getKind() == ExprNode::This) && (b->op2->getKind() == ExprNode::qualify)) { - BinaryExprNode *q = static_cast(b->op2); - if (q->op1->getKind() == ExprNode::Super) { - // XXX verify that q->op2 is either the superclass name or a constructor for it - foundSuperCall = true; - } - } - } - } - } - if (!foundSuperCall) { // invoke the default superclass constructor - icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); - } - } - if (mClass->hasStatic(mInitName)) - icg.call(icg.bindThis(thisValue, icg.getStatic(mClass, mInitName)), args); // ok, so it's mis-named - - } - if (function.body) - icg.genStmt(function.body); - if (isConstructor) { - TypedRegister thisValue = TypedRegister(0, mClass); - icg.returnStmt(thisValue); - } - return icg.complete(extractType(function.resultType)); -} - -TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet) -{ - TypedRegister ret(NotARegister, &None_Type); - - startStatement(p->pos); - - switch (p->getKind()) { - case StmtNode::Class: - { - // FIXME: need a semantic check to make sure a class isn't being redefined(?) - ClassStmtNode *classStmt = static_cast(p); - ASSERT(classStmt->name->getKind() == ExprNode::identifier); - IdentifierExprNode* nameExpr = static_cast(classStmt->name); - JSClass* superclass = 0; - if (classStmt->superclass) { - ASSERT(classStmt->superclass->getKind() == ExprNode::identifier); - IdentifierExprNode* superclassExpr = static_cast(classStmt->superclass); - const JSValue& superclassValue = mContext->getGlobalObject()->getVariable(superclassExpr->name); - ASSERT(superclassValue.isObject() && !superclassValue.isNull()); - superclass = static_cast(superclassValue.object); - } - JSClass* thisClass = new JSClass(mContext->getGlobalObject(), nameExpr->name, superclass); - // is it ok for a partially defined class to appear in global scope? this is needed - // to handle recursive types, such as linked list nodes. - mContext->getGlobalObject()->defineVariable(nameExpr->name, &Type_Type, JSValue(thisClass)); - - -/* - Pre-pass to declare all the methods & fields -*/ - bool needsInstanceInitializer = false; - TypedRegister thisRegister = TypedRegister(0, thisClass); - if (classStmt->body) { - StmtNode* s = classStmt->body->statements; - while (s) { - switch (s->getKind()) { - case StmtNode::Const: - case StmtNode::Var: - { - VariableStmtNode *vs = static_cast(s); - bool isStatic = hasAttribute(vs->attributes, Token::Static); - VariableBinding *v = vs->bindings; - while (v) { - if (v->name) { - ASSERT(v->name->getKind() == ExprNode::identifier); - IdentifierExprNode* idExpr = static_cast(v->name); - JSType* type = extractType(v->type); - if (isStatic) - thisClass->defineStatic(idExpr->name, type); - else { - if (hasAttribute(vs->attributes, mContext->getWorld().identifiers["virtual"])) - thisClass->defineSlot(idExpr->name, type, JSSlot::kIsVirtual); - else - thisClass->defineSlot(idExpr->name, type); - if (v->initializer) - needsInstanceInitializer = true; - } - } - v = v->next; - } - } - break; - case StmtNode::Constructor: - case StmtNode::Function: - { - FunctionStmtNode *f = static_cast(s); - bool isStatic = hasAttribute(f->attributes, Token::Static); - bool isConstructor = (s->getKind() == StmtNode::Constructor); - if (f->function.prefix == FunctionName::Operator) { - thisClass->defineOperator(f->function.op, getParameterType(f->function, 0), getParameterType(f->function, 1), NULL); - } - else - if (f->function.name->getKind() == ExprNode::identifier) { - const StringAtom& name = (static_cast(f->function.name))->name; - if (isConstructor) - thisClass->defineConstructor(name); - else - if (isStatic) - thisClass->defineStatic(name, &Function_Type); - else { - switch (f->function.prefix) { - case FunctionName::Get: - thisClass->setGetter(name, NULL, extractType(f->function.resultType)); - break; - case FunctionName::Set: - thisClass->setSetter(name, NULL, extractType(f->function.resultType)); - break; - case FunctionName::normal: - thisClass->defineMethod(name, NULL); - break; - default: - NOT_REACHED("unexpected prefix"); - break; - } - } - } - } - break; - default: - NOT_REACHED("unimplemented class member statement"); - break; - } - s = s->next; - } - } - if (needsInstanceInitializer) - thisClass->defineStatic(mInitName, &Function_Type); -/* - Now gen code for each -*/ - - bool hasDefaultConstructor = false; - if (classStmt->body) { - JSScope* thisScope = thisClass->getScope(); - ICodeGenerator *ccg = NULL; - Context *classContext = new Context(mContext->getWorld(), thisScope); - if (needsInstanceInitializer) { - // constructor code generator. Slot variable - // initializers get added to this function. - ccg = new ICodeGenerator(classContext, NULL, thisClass, kNoFlags); - ccg->allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 - } - - ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod); // static initializer code generator. - // static field inits, plus code to initialize - // static method slots. - StmtNode* s = classStmt->body->statements; - while (s) { - switch (s->getKind()) { - case StmtNode::Const: - case StmtNode::Var: - { - VariableStmtNode *vs = static_cast(s); - bool isStatic = hasAttribute(vs->attributes, Token::Static); - VariableBinding *v = vs->bindings; - while (v) { - if (v->name) { - ASSERT(v->name->getKind() == ExprNode::identifier); - if (v->initializer) { - IdentifierExprNode* idExpr = static_cast(v->name); - if (isStatic) { - scg.setStatic(thisClass, idExpr->name, scg.genExpr(v->initializer)); - scg.resetStatement(); - } else { - const JSSlot& slot = thisClass->getSlot(idExpr->name); - ccg->setSlot(thisRegister, slot.mIndex, ccg->genExpr(v->initializer)); - ccg->resetStatement(); - } - } - } - v = v->next; - } - } - break; - case StmtNode::Constructor: - case StmtNode::Function: - { - FunctionStmtNode *f = static_cast(s); - bool isStatic = hasAttribute(f->attributes, Token::Static); - bool isConstructor = (s->getKind() == StmtNode::Constructor); - ICodeGeneratorFlags flags = (isStatic) ? kIsStaticMethod : kNoFlags; - - ICodeGenerator mcg(classContext, NULL, thisClass, flags); // method code generator. - ICodeModule *icm = mcg.genFunction(f->function, isStatic, isConstructor, superclass); - if (f->function.prefix == FunctionName::Operator) { - thisClass->defineOperator(f->function.op, getParameterType(f->function, 0), getParameterType(f->function, 1), new JSFunction(icm)); - } - else - if (f->function.name->getKind() == ExprNode::identifier) { - const StringAtom& name = (static_cast(f->function.name))->name; - if (isConstructor) { - if (name == nameExpr->name) - hasDefaultConstructor = true; - scg.setStatic(thisClass, name, scg.newFunction(icm)); - } - else - if (isStatic) - scg.setStatic(thisClass, name, scg.newFunction(icm)); - else { - switch (f->function.prefix) { - case FunctionName::Get: - thisClass->setGetter(name, new JSFunction(icm), icm->mResultType); - break; - case FunctionName::Set: - thisClass->setSetter(name, new JSFunction(icm), icm->mResultType); - break; - case FunctionName::normal: - thisClass->defineMethod(name, new JSFunction(icm)); - break; - default: - NOT_REACHED("unexpected prefix"); - break; - } - } - } - } - break; - default: - NOT_REACHED("unimplemented class member statement"); - break; - } - s = s->next; - } - - // add the instance initializer - if (ccg) { - scg.setStatic(thisClass, mInitName, scg.newFunction(ccg->complete(&Void_Type))); - delete ccg; - } - // invent a default constructor if necessary, it just calls the - // initializer and the superclass default constructor - if (!hasDefaultConstructor) { - TypedRegister thisValue = TypedRegister(0, thisClass); - ArgumentList *args = new ArgumentList(); - ICodeGenerator icg(classContext, NULL, thisClass, kIsStaticMethod); - icg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); // always parameter #0 - if (superclass) - icg.call(icg.bindThis(thisValue, icg.getStatic(superclass, superclass->getName())), args); - if (thisClass->hasStatic(mInitName)) - icg.call(icg.bindThis(thisValue, icg.getStatic(thisClass, mInitName)), args); - icg.returnStmt(thisValue); - thisClass->defineConstructor(nameExpr->name); - scg.setStatic(thisClass, nameExpr->name, scg.newFunction(icg.complete(&Void_Type))); - } - // freeze the class. - thisClass->complete(); - - - // REVISIT: using the scope of the class to store both methods and statics. - if (scg.getICode()->size()) { - ICodeModule* clinit = scg.complete(&Void_Type); - classContext->interpret(clinit, JSValues()); - delete clinit; - } - delete classContext; - } - } - break; - case StmtNode::Function: - { - FunctionStmtNode *f = static_cast(p); - bool isStatic = hasAttribute(f->attributes, Token::Static); - ICodeModule *icm = genFunction(f->function, isStatic, false, NULL); - JSType *resultType = extractType(f->function.resultType); - if (f->function.name->getKind() == ExprNode::identifier) { - const StringAtom& name = (static_cast(f->function.name))->name; - switch (f->function.prefix) { - case FunctionName::Get: - if (isTopLevel()) { - mContext->getGlobalObject()->defineVariable(name, resultType); - mContext->getGlobalObject()->setGetter(name, new JSFunction(icm)); - } - else { - // is this legal - a nested getter? - NOT_REACHED("Better check with Waldemar"); - //allocateVariable(name, resultType); - } - break; - case FunctionName::Set: - if (isTopLevel()) { - mContext->getGlobalObject()->defineVariable(name, resultType); - mContext->getGlobalObject()->setSetter(name, new JSFunction(icm)); - } - else { - // is this legal - a nested setter? - NOT_REACHED("Better check with Waldemar"); - //allocateVariable(name, resultType); - } - break; - case FunctionName::normal: - mContext->getGlobalObject()->defineFunction(name, icm); - break; - default: - NOT_REACHED("unexpected prefix"); - break; - } - } - } - break; - case StmtNode::Import: - { - ImportStmtNode *i = static_cast(p); - String *fileName = i->bindings->packageName.str; - if (fileName) { /// if not, build one from the idList instead - std::string str(fileName->length(), char()); - std::transform(fileName->begin(), fileName->end(), str.begin(), narrow); - FILE* f = fopen(str.c_str(), "r"); - if (f) { - (void)mContext->readEvalFile(f, *fileName); - fclose(f); - } - } - } - break; - case StmtNode::Var: - { - VariableStmtNode *vs = static_cast(p); - VariableBinding *v = vs->bindings; - while (v) { - if (v->name && (v->name->getKind() == ExprNode::identifier)) { - JSType *type = extractType(v->type); - if (isTopLevel()) - mContext->getGlobalObject()->defineVariable((static_cast(v->name))->name, type); - else - allocateVariable((static_cast(v->name))->name, type); - if (v->initializer) { - if (!isTopLevel() && !isWithinWith()) { - TypedRegister r = genExpr(v->name); - TypedRegister val = genExpr(v->initializer); - val = cast(val, type); - move(r, val); - } - else { - TypedRegister val = genExpr(v->initializer); - val = cast(val, type); - saveName((static_cast(v->name))->name, val); - } - } - } - v = v->next; - } - } - break; - case StmtNode::expression: - { - ExprStmtNode *e = static_cast(p); - ret = genExpr(e->expr); - } - break; - case StmtNode::Throw: - { - ExprStmtNode *e = static_cast(p); - throwStmt(genExpr(e->expr)); - } - break; - case StmtNode::Debugger: - { - debuggerStmt(); - } - break; - case StmtNode::Return: - { - ExprStmtNode *e = static_cast(p); - if (e->expr) - returnStmt(ret = genExpr(e->expr)); - else - returnStmt(TypedRegister(NotARegister, &Void_Type)); - } - break; - case StmtNode::If: - { - Label *falseLabel = getLabel(); - UnaryStmtNode *i = static_cast(p); - TypedRegister c = genExpr(i->expr, false, NULL, falseLabel); - if (!generatedBoolean(i->expr)) - branchFalse(falseLabel, test(c)); - genStmt(i->stmt); - setLabel(falseLabel); - } - break; - case StmtNode::IfElse: - { - Label *falseLabel = getLabel(); - Label *trueLabel = getLabel(); - Label *beyondLabel = getLabel(); - BinaryStmtNode *i = static_cast(p); - TypedRegister c = genExpr(i->expr, false, trueLabel, falseLabel); - if (!generatedBoolean(i->expr)) - branchFalse(falseLabel, test(c)); - setLabel(trueLabel); - genStmt(i->stmt); - branch(beyondLabel); - setLabel(falseLabel); - genStmt(i->stmt2); - setLabel(beyondLabel); - } - break; - case StmtNode::With: - { - UnaryStmtNode *w = static_cast(p); - TypedRegister o = genExpr(w->expr); - bool withinWith = isWithinWith(); - setFlag(kIsWithinWith, true); - beginWith(o); - genStmt(w->stmt); - endWith(); - setFlag(kIsWithinWith, withinWith); - } - break; - case StmtNode::Switch: - { - Label *defaultLabel = NULL; - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel()); - mLabelStack.push_back(e); - SwitchStmtNode *sw = static_cast(p); - TypedRegister sc = genExpr(sw->expr); - StmtNode *s = sw->statements; - // ECMA requires case & default statements to be immediate children of switch - // unlike C where they can be arbitrarily deeply nested in other statements. - Label *nextCaseLabel = NULL; - GenericBranch *lastBranch = NULL; - while (s) { - if (s->getKind() == StmtNode::Case) { - ExprStmtNode *c = static_cast(s); - if (c->expr) { - if (nextCaseLabel) - setLabel(nextCaseLabel); - nextCaseLabel = getLabel(); - TypedRegister r = genExpr(c->expr); - TypedRegister eq = binaryOp(COMPARE_EQ, r, sc); - lastBranch = branchFalse(nextCaseLabel, eq); - } - else { - defaultLabel = getLabel(); - setLabel(defaultLabel); - } - } - else - genStmt(s); - s = s->next; - } - if (nextCaseLabel) - setLabel(nextCaseLabel); - if (defaultLabel && lastBranch) - lastBranch->setTarget(defaultLabel); - - setLabel(e->breakLabel); - mLabelStack.pop_back(); - } - break; - case StmtNode::DoWhile: - { - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); - mLabelStack.push_back(e); - UnaryStmtNode *d = static_cast(p); - Label *doBodyTopLabel = getLabel(); - setLabel(doBodyTopLabel); - genStmt(d->stmt); - setLabel(e->continueLabel); - TypedRegister c = genExpr(d->expr, false, doBodyTopLabel, NULL); - if (!generatedBoolean(d->expr)) - branchTrue(doBodyTopLabel, test(c)); - setLabel(e->breakLabel); - mLabelStack.pop_back(); - } - break; - case StmtNode::While: - { - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); - mLabelStack.push_back(e); - branch(e->continueLabel); - - UnaryStmtNode *w = static_cast(p); - - Label *whileBodyTopLabel = getLabel(); - setLabel(whileBodyTopLabel); - genStmt(w->stmt); - - setLabel(e->continueLabel); - TypedRegister c = genExpr(w->expr, false, whileBodyTopLabel, NULL); - if (!generatedBoolean(w->expr)) - branchTrue(whileBodyTopLabel, test(c)); - - setLabel(e->breakLabel); - mLabelStack.pop_back(); - } - break; - case StmtNode::For: - { - LabelEntry *e = new LabelEntry(currentLabelSet, getLabel(), getLabel()); - mLabelStack.push_back(e); - - ForStmtNode *f = static_cast(p); - if (f->initializer) - genStmt(f->initializer); - Label *forTestLabel = getLabel(); - branch(forTestLabel); - - Label *forBlockTop = getLabel(); - setLabel(forBlockTop); - genStmt(f->stmt); - - setLabel(e->continueLabel); - if (f->expr3) { - (*mInstructionMap)[iCode->size()] = f->expr3->pos; - genExpr(f->expr3); - } - - setLabel(forTestLabel); - if (f->expr2) { - (*mInstructionMap)[iCode->size()] = f->expr2->pos; - TypedRegister c = genExpr(f->expr2, false, forBlockTop, NULL); - if (!generatedBoolean(f->expr2)) - branchTrue(forBlockTop, test(c)); - } - - setLabel(e->breakLabel); - - mLabelStack.pop_back(); - } - break; - case StmtNode::block: - { - BlockStmtNode *b = static_cast(p); - StmtNode *s = b->statements; - while (s) { - genStmt(s); - s = s->next; - } - } - break; - - case StmtNode::label: - { - LabelStmtNode *l = static_cast(p); - // ok, there's got to be a cleverer way of doing this... - if (currentLabelSet == NULL) { - currentLabelSet = new LabelSet(); - currentLabelSet->push_back(&l->name); - genStmt(l->stmt, currentLabelSet); - delete currentLabelSet; - } - else { - currentLabelSet->push_back(&l->name); - genStmt(l->stmt, currentLabelSet); - currentLabelSet->pop_back(); - } - } - break; - - case StmtNode::Break: - { - GoStmtNode *g = static_cast(p); - if (g->label) { - LabelEntry *e = NULL; - for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { - e = (*i); - if (e->containsLabel(g->name)) - break; - } - if (e) { - ASSERT(e->breakLabel); - branch(e->breakLabel); - } - else - NOT_REACHED("break label not in label set"); - } - else { - ASSERT(!mLabelStack.empty()); - LabelEntry *e = mLabelStack.back(); - ASSERT(e->breakLabel); - branch(e->breakLabel); - } - } - break; - case StmtNode::Continue: - { - GoStmtNode *g = static_cast(p); - if (g->label) { - LabelEntry *e = NULL; - for (LabelStack::reverse_iterator i = mLabelStack.rbegin(); i != mLabelStack.rend(); i++) { - e = (*i); - if (e->containsLabel(g->name)) - break; - } - if (e) { - ASSERT(e->continueLabel); - branch(e->continueLabel); - } - else - NOT_REACHED("continue label not in label set"); - } - else { - ASSERT(!mLabelStack.empty()); - LabelEntry *e = mLabelStack.back(); - ASSERT(e->continueLabel); - branch(e->continueLabel); - } - } - break; - - case StmtNode::Try: - { - /* - The finallyInvoker is a little stub used by the interpreter to - invoke the finally handler on the (exceptional) way out of the - try block assuming there are no catch clauses. - */ - /*Register ex = NotARegister;*/ - TryStmtNode *t = static_cast(p); - Label *catchLabel = (t->catches) ? getLabel() : NULL; - Label *finallyInvoker = (t->finally) ? getLabel() : NULL; - Label *finallyLabel = (t->finally) ? getLabel() : NULL; - Label *beyondLabel = getLabel(); - beginTry(catchLabel, finallyLabel); - genStmt(t->stmt); - endTry(); - if (finallyLabel) - jsr(finallyLabel); - branch(beyondLabel); - if (catchLabel) { - setLabel(catchLabel); - CatchClause *c = t->catches; - while (c) { - // Bind the incoming exception ... - if (mExceptionRegister.first == NotABanana) - mExceptionRegister = allocateRegister(&Any_Type); - allocateVariable(c->name, mExceptionRegister); - - genStmt(c->stmt); - if (finallyLabel) - jsr(finallyLabel); - c = c->next; - } - } - if (finallyLabel) { - setLabel(finallyInvoker); - jsr(finallyLabel); - throwStmt(mExceptionRegister); - - setLabel(finallyLabel); - genStmt(t->finally); - rts(); - } - setLabel(beyondLabel); - } - break; - - case StmtNode::empty: - /* nada */ - break; - - default: - NOT_REACHED("unimplemented statement kind"); - } - resetStatement(); - return ret; -} - - -/************************************************************************/ - Formatter& ICodeGenerator::print(Formatter& f) { @@ -2598,7 +662,7 @@ ICodeModule *ICodeGenerator::readFunction(XMLNode *element, JSClass *thisClass) String parameterTypeName; element->getValue(widenCString("name"), parameterName); element->getValue(widenCString("type"), parameterTypeName); - JSType *parameterType = findType(mContext->getWorld().identifiers[parameterTypeName]); + JSType *parameterType = mContext->findType(mContext->getWorld().identifiers[parameterTypeName]); theParameterList->add(mContext->getWorld().identifiers[parameterName], TypedRegister(pCount, parameterType), false); s << pCount - 1; theParameterList->add(mContext->getWorld().identifiers[s.getString()], TypedRegister(pCount, parameterType), false); @@ -2608,7 +672,7 @@ ICodeModule *ICodeGenerator::readFunction(XMLNode *element, JSClass *thisClass) } theParameterList->setPositionalCount(pCount); - JSType *resultType = findType(mContext->getWorld().identifiers[resultTypeName]); + JSType *resultType = mContext->findType(mContext->getWorld().identifiers[resultTypeName]); String &body = element->body(); if (body.length()) { std::string str(body.length(), char()); @@ -2655,8 +719,8 @@ ICodeModule *ICodeGenerator::readICode(const char *fileName) JSClass* thisClass = new JSClass(mContext->getGlobalObject(), className, superclass); JSScope* thisScope = thisClass->getScope(); Context *classContext = new Context(mContext->getWorld(), thisScope); - ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod); - ICodeGenerator ccg(classContext, NULL, thisClass, kNoFlags); + ICodeGenerator scg(classContext, NULL, thisClass, kIsStaticMethod, &Void_Type); + ICodeGenerator ccg(classContext, NULL, thisClass, kNoFlags, &Void_Type); ccg.allocateParameter(mContext->getWorld().identifiers["this"], false, thisClass); thisClass->defineStatic(mInitName, &Function_Type); @@ -2687,7 +751,7 @@ ICodeModule *ICodeGenerator::readICode(const char *fileName) element->getValue(widenCString("name"), fieldName); element->getValue(widenCString("type"), fieldType); - JSType *type = findType(mContext->getWorld().identifiers[fieldType]); + JSType *type = mContext->findType(mContext->getWorld().identifiers[fieldType]); if (element->hasAttribute(widenCString("static"))) thisClass->defineStatic(fieldName, type); @@ -2696,11 +760,11 @@ ICodeModule *ICodeGenerator::readICode(const char *fileName) } } } - scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete(&Void_Type))); + scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete())); thisClass->complete(); if (scg.getICode()->size()) { - ICodeModule* clinit = scg.complete(&Void_Type); + ICodeModule* clinit = scg.complete(); classContext->interpret(clinit, JSValues()); delete clinit; } diff --git a/js2/src/icodegenerator.h b/js2/src/icodegenerator.h index d68911905a26..4bd15e51bdfb 100644 --- a/js2/src/icodegenerator.h +++ b/js2/src/icodegenerator.h @@ -124,53 +124,10 @@ namespace ICG { }; + class ICodeModule; + typedef std::map > InstructionMap; - class ICodeModule { - public: - ICodeModule(InstructionStream *iCode, VariableList *variables, - ParameterList *parameters, - uint32 maxRegister, - InstructionMap *instructionMap, - JSType *resultType, uint32 exceptionRegister) : - its_iCode(iCode), itsVariables(variables), itsParameters(parameters), - itsMaxRegister(maxRegister), - mID(++sMaxID), mInstructionMap(instructionMap), - mParameterInit(NULL), - mEntryPoint(0), - mResultType(resultType), - mExceptionRegister(exceptionRegister) - { - } - - ~ICodeModule() - { - delete its_iCode; - delete itsVariables; - delete mInstructionMap; - if (mParameterInit) delete mParameterInit; - } - - Formatter& print(Formatter& f); - void setFileName (String aFileName) { mFileName = aFileName; } - String getFileName () { return mFileName; } - - InstructionStream *its_iCode; - VariableList *itsVariables; - ParameterList *itsParameters; - uint32 itsMaxRegister; - uint32 mID; - InstructionMap *mInstructionMap; - String mFileName; - uint32 *mParameterInit; - uint32 mEntryPoint; - JSType *mResultType; - uint32 mExceptionRegister; - - static uint32 sMaxID; - - }; - typedef std::vector LabelSet; class LabelEntry { public: @@ -198,6 +155,7 @@ namespace ICG { class ICodeGenerator { public: + friend ICodeModule; typedef enum { kNoFlags = 0, kIsTopLevel = 0x01, kIsStaticMethod = 0x02, kIsWithinWith = 0x04 } ICodeGeneratorFlags; private: InstructionStream *iCode; @@ -221,6 +179,7 @@ namespace ICG { const StringAtom &mInitName; ICodeGenerator *mContainingFunction;// outer function for nested functions + JSType *mResultType; std::vector mPermanentRegister; @@ -241,7 +200,6 @@ namespace ICG { TypedRegister allocateRegister(JSType *type); - JSType *findType(const StringAtom& typeName); void addParameterLabel(Label *label) { if (pLabels == NULL) pLabels = new LabelList(); pLabels->push_back(label); } @@ -289,7 +247,11 @@ namespace ICG { public: - ICodeGenerator(Context *cx, ICodeGenerator *containingFunction = NULL, JSClass *aClass = NULL, ICodeGeneratorFlags flags = kIsTopLevel); + ICodeGenerator(Context *cx, + ICodeGenerator *containingFunction = NULL, + JSClass *aClass = NULL, + ICodeGeneratorFlags flags = kIsTopLevel, + JSType *resultType = &Any_Type); ~ICodeGenerator() { @@ -300,11 +262,8 @@ namespace ICG { } } - ICodeModule *complete(JSType *resultType); + ICodeModule *complete(); ICodeModule *readICode(const char *fileName); - - JSType *extractType(ExprNode *t); - JSType *getParameterType(FunctionDefinition &function, int index); TypedRegister genExpr(ExprNode *p, bool needBoolValueInBranch = false, @@ -322,10 +281,7 @@ namespace ICG { return allocateVariable(name, &Any_Type); } - TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName) - { - return allocateVariable(name, findType(typeName)); - } + TypedRegister allocateVariable(const StringAtom& name, const StringAtom& typeName); TypedRegister allocateVariable(const StringAtom& name, JSType *type) { @@ -345,10 +301,7 @@ namespace ICG { 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, const StringAtom& typeName); TypedRegister allocateParameter(const StringAtom& name, bool isOptional, JSType *type) { @@ -421,10 +374,63 @@ namespace ICG { Formatter& operator<<(Formatter &f, ICodeGenerator &i); Formatter& operator<<(Formatter &f, ICodeModule &i); Formatter& operator<<(Formatter &f, std::string &s); - /* - std::ostream &operator<<(std::ostream &s, ICodeGenerator &i); - std::ostream &operator<<(std::ostream &s, StringAtom &str); - */ + + class ICodeModule { + public: + ICodeModule(InstructionStream *iCode, VariableList *variables, + ParameterList *parameters, + uint32 maxRegister, + InstructionMap *instructionMap, + JSType *resultType, uint32 exceptionRegister) : + its_iCode(iCode), itsVariables(variables), itsParameters(parameters), + itsMaxRegister(maxRegister), + mID(++sMaxID), mInstructionMap(instructionMap), + mParameterInit(NULL), + mEntryPoint(0), + mResultType(resultType), + mExceptionRegister(exceptionRegister) + { + } + + ICodeModule(ICodeGenerator &icg) : + its_iCode(icg.iCode), itsVariables(icg.variableList), itsParameters(icg.parameterList), + itsMaxRegister(icg.mPermanentRegister.size()), + mID(++sMaxID), mInstructionMap(icg.mInstructionMap), + mParameterInit(NULL), + mEntryPoint(0), + mResultType(icg.mResultType), + mExceptionRegister(icg.mExceptionRegister.first) + { + } + + + ~ICodeModule() + { + delete its_iCode; + delete itsVariables; + delete mInstructionMap; + if (mParameterInit) delete mParameterInit; + } + + Formatter& print(Formatter& f); + void setFileName (String aFileName) { mFileName = aFileName; } + String getFileName () { return mFileName; } + + InstructionStream *its_iCode; + VariableList *itsVariables; + ParameterList *itsParameters; + uint32 itsMaxRegister; + uint32 mID; + InstructionMap *mInstructionMap; + String mFileName; + uint32 *mParameterInit; + uint32 mEntryPoint; + JSType *mResultType; + uint32 mExceptionRegister; + + static uint32 sMaxID; + + }; diff --git a/js2/src/interpreter.cpp b/js2/src/interpreter.cpp index 63886ef4f0f4..96026f0a06af 100644 --- a/js2/src/interpreter.cpp +++ b/js2/src/interpreter.cpp @@ -82,9 +82,9 @@ ICodeModule* Context::compileFunction(const String &source) String filename = widenCString("Some source source"); Parser p(getWorld(), a, source, filename); ExprNode* e = p.parseExpression(false); - ICodeGenerator icg(this); ASSERT(e->getKind() == ExprNode::functionLiteral); FunctionExprNode* f = static_cast(e); + ICodeGenerator icg(this, NULL, NULL, ICodeGenerator::kIsTopLevel, extractType(f->function.resultType)); icg.allocateParameter(getWorld().identifiers["this"], false); // always parameter #0 VariableBinding* v = f->function.parameters; while (v) { @@ -93,7 +93,7 @@ ICodeModule* Context::compileFunction(const String &source) v = v->next; } icg.genStmt(f->function.body); - ICodeModule* result = icg.complete(icg.extractType(f->function.resultType)); + ICodeModule* result = icg.complete(); result->setFileName(filename); return result; } @@ -143,7 +143,7 @@ JSValue Context::readEvalFile(FILE* in, const String& fileName) ICodeModule* Context::genCode(StmtNode *p, const String &fileName) { - ICodeGenerator icg(this); + ICodeGenerator icg(this, NULL, NULL, ICodeGenerator::kIsTopLevel, &Void_Type); TypedRegister ret(NotARegister, &None_Type); while (p) { @@ -152,7 +152,7 @@ ICodeModule* Context::genCode(StmtNode *p, const String &fileName) } icg.returnStmt(ret); - ICodeModule *icm = icg.complete(&Void_Type); + ICodeModule *icm = icg.complete(); icm->setFileName(fileName); return icm; } @@ -1595,6 +1595,40 @@ void Context::broadcast(Event event) } } + +/* Helper functions for extracting types from expression trees */ +JSType *Context::findType(const StringAtom& typeName) +{ + const JSValue& type = getGlobalObject()->getVariable(typeName); + if (type.isType()) + return type.type; + return &Any_Type; +} + +JSType *Context::extractType(ExprNode *t) +{ + JSType* type = &Any_Type; + if (t && (t->getKind() == ExprNode::identifier)) { + IdentifierExprNode* typeExpr = static_cast(t); + type = findType(typeExpr->name); + } + return type; +} + +JSType *Context::getParameterType(FunctionDefinition &function, int index) +{ + VariableBinding *v = function.parameters; + while (v) { + if (index-- == 0) + return extractType(v->type); + else + v = v->next; + } + return NULL; +} + + + Context::Frame* Context::getFrames() { return mLinkage; diff --git a/js2/src/interpreter.h b/js2/src/interpreter.h index 597ac35355e8..51ac472d2cb2 100644 --- a/js2/src/interpreter.h +++ b/js2/src/interpreter.h @@ -85,6 +85,10 @@ namespace Interpreter { const JSValue findBinaryOverride(JSValue &operand1, JSValue &operand2, ExprNode::Kind op); + JSType *findType(const StringAtom& typeName); + JSType *extractType(ExprNode *t); + JSType *getParameterType(FunctionDefinition &function, int index); + private: void broadcast(Event event); void initOperatorsPackage(); diff --git a/js2/tests/cpp/js2_shell.cpp b/js2/tests/cpp/js2_shell.cpp index 375d2338b557..99edbe051d15 100644 --- a/js2/tests/cpp/js2_shell.cpp +++ b/js2/tests/cpp/js2_shell.cpp @@ -361,13 +361,13 @@ static void testCompile() Arena a; Parser p(world, a, testScript, widenCString("testCompile")); StmtNode *parsedStatements = p.parseProgram(); - ICodeGenerator icg(&cx); + ICodeGenerator icg(&cx, NULL, NULL, ICodeGenerator::kIsTopLevel, &Void_Type); StmtNode *s = parsedStatements; while (s) { icg.genStmt(s); s = s->next; } - JSValue result = cx.interpret(icg.complete(&Void_Type), JSValues()); + JSValue result = cx.interpret(icg.complete(), JSValues()); stdOut << "result = " << result << "\n"; } }