/* -*- 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