diff --git a/js2/src/algo.h b/js2/src/algo.h index bfbc485d90a..cc6936ad57a 100644 --- a/js2/src/algo.h +++ b/js2/src/algo.h @@ -36,16 +36,14 @@ namespace JavaScript { - // // Algorithms // -// Assign zero to every element between first inclusive and last -// exclusive. -// This is equivalent ot fill(first, last, 0) but may be more efficient. + // Assign zero to every element between first inclusive and last exclusive. + // This is equivalent ot fill(first, last, 0) but may be more efficient. template - inline void zero(ForwardIterator first, ForwardIterator last) + inline void zero(ForwardIterator first, ForwardIterator last) { while (first != last) { *first = 0; @@ -53,17 +51,15 @@ namespace JavaScript } } -// Same as find(first, last, value) but may be more efficient because -// it doesn't use a reference for value. - template - inline InputIterator findValue(InputIterator first, InputIterator last, T value) - { - while (first != last && !(*first == value)) - ++first; - return first; - } - + // Same as find(first, last, value) but may be more efficient because + // it doesn't use a reference for value. + template + inline InputIterator findValue(InputIterator first, InputIterator last, T value) + { + while (first != last && !(*first == value)) + ++first; + return first; + } } - #endif /* algo_h___ */ diff --git a/js2/src/bytecodegen.cpp b/js2/src/bytecodegen.cpp new file mode 100644 index 00000000000..03d75fab733 --- /dev/null +++ b/js2/src/bytecodegen.cpp @@ -0,0 +1,2439 @@ +/* -*- 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. + */ + + +#ifdef _WIN32 + // Turn off warnings about identifiers too long in browser information +#pragma warning(disable: 4786) +#pragma warning(disable: 4711) +#pragma warning(disable: 4710) +#endif + +#include "parser.h" +#include "js2runtime.h" +#include "bytecodegen.h" +#include "numerics.h" +#include "formatter.h" + +#include + +// this is the IdentifierList passed to the name lookup routines +#define CURRENT_ATTR mNamespaceList + +namespace JavaScript { +namespace JS2Runtime { + + +void Reference::emitTypeOf(ByteCodeGen *bcg) +{ + emitCodeSequence(bcg); + bcg->addOp(TypeOfOp); +} + +void Reference::emitDelete(ByteCodeGen *bcg) +{ + bcg->addOp(LoadConstantFalseOp); +} + +void AccessorReference::emitCodeSequence(ByteCodeGen * /*bcg*/) +{ + ASSERT(false); // NYI +// bcg->addOp(InvokeOp); +// bcg->addPointer(mFunction); +} + +void LocalVarReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetLocalVarOp); + else + bcg->addOp(SetLocalVarOp); + bcg->addLong(mIndex); +} + +void ParameterReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetArgOp); + else + bcg->addOp(SetArgOp); + bcg->addLong(mIndex); +} + +void ClosureVarReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetClosureVarOp); + else + bcg->addOp(SetClosureVarOp); + bcg->addLong(mDepth); + bcg->addLong(mIndex); +} + +void StaticFieldReference::emitImplicitLoad(ByteCodeGen *bcg) +{ + bcg->addOp(LoadTypeOp); + bcg->addPointer(mClass); +} + + +void FieldReference::emitImplicitLoad(ByteCodeGen *bcg) +{ + bcg->addOp(LoadThisOp); +} + +void FieldReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetFieldOp); + else + bcg->addOp(SetFieldOp); + bcg->addLong(mIndex); +} + + +void StaticFieldReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetPropertyOp); + else + bcg->addOp(SetPropertyOp); + bcg->addStringRef(mName); +} + +void StaticFieldReference::emitInvokeSequence(ByteCodeGen *bcg) +{ + bcg->addOp(GetPropertyOp); + bcg->addStringRef(mName); +} + + + +void MethodReference::emitImplicitLoad(ByteCodeGen *bcg) +{ + bcg->addOp(LoadThisOp); +} + +void MethodReference::emitCodeSequence(ByteCodeGen *bcg) +{ + bcg->addOp(GetMethodRefOp); + bcg->addLong(mIndex); +} + +void MethodReference::emitInvokeSequence(ByteCodeGen *bcg) +{ + bcg->addOp(GetMethodOp); + bcg->addLong(mIndex); +} + +void GetterMethodReference::emitCodeSequence(ByteCodeGen *bcg) +{ + bcg->addOp(GetMethodOp); + bcg->addLong(mIndex); + bcg->addOpAdjustDepth(InvokeOp, -1); // function, 'this' --> result + bcg->addLong(0); + bcg->addByte(Explicit); +} + +void SetterMethodReference::emitCodeSequence(ByteCodeGen *bcg) +{ + bcg->addOpAdjustDepth(InvokeOp, -2); // leaves value on stack + bcg->addLong(1); + bcg->addByte(Explicit); +} + +bool SetterMethodReference::emitPreAssignment(ByteCodeGen *bcg) +{ + bcg->addOp(GetMethodOp); + bcg->addLong(mIndex); + return true; +} + +void FunctionReference::emitCodeSequence(ByteCodeGen *bcg) +{ + bcg->addOp(LoadFunctionOp); + bcg->addPointer(mFunction); +} + +void GetterFunctionReference::emitCodeSequence(ByteCodeGen *bcg) +{ + bcg->addOp(LoadFunctionOp); + bcg->addPointer(mFunction); + bcg->addOpAdjustDepth(InvokeOp, -1); + bcg->addLong(0); + bcg->addByte(Explicit); +} + +void SetterFunctionReference::emitImplicitLoad(ByteCodeGen *bcg) +{ + bcg->addOp(LoadFunctionOp); + bcg->addPointer(mFunction); +} + +void SetterFunctionReference::emitCodeSequence(ByteCodeGen *bcg) +{ + bcg->addOpAdjustDepth(InvokeOp, -1); + bcg->addLong(1); + bcg->addByte(Explicit); +} + +void NameReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetNameOp); + else + bcg->addOp(SetNameOp); + bcg->addStringRef(mName); +} + +void NameReference::emitTypeOf(ByteCodeGen *bcg) +{ + bcg->addOp(GetTypeOfNameOp); + bcg->addStringRef(mName); + bcg->addOp(TypeOfOp); +} + +void NameReference::emitDelete(ByteCodeGen *bcg) +{ + bcg->addOp(DeleteOp); + bcg->addStringRef(mName); +} + + +void PropertyReference::emitImplicitLoad(ByteCodeGen *bcg) +{ + bcg->addOp(LoadThisOp); +} + +void PropertyReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetPropertyOp); + else + bcg->addOp(SetPropertyOp); + bcg->addStringRef(mName); +} + +void PropertyReference::emitInvokeSequence(ByteCodeGen *bcg) +{ + bcg->addOp(GetInvokePropertyOp); + bcg->addStringRef(mName); +} + +void PropertyReference::emitDelete(ByteCodeGen *bcg) +{ + bcg->addOp(DeleteOp); + bcg->addStringRef(mName); +} + +void ElementReference::emitCodeSequence(ByteCodeGen *bcg) +{ + if (mAccess == Read) + bcg->addOp(GetElementOp); + else + bcg->addOp(SetElementOp); +} + +ByteCodeData gByteCodeData[OpCodeCount] = { +{ 1, "LoadConstantUndefined", }, +{ 1, "LoadConstantTrue", }, +{ 1, "LoadConstantFalse", }, +{ 1, "LoadConstantNull", }, +{ 1, "LoadConstantZero", }, +{ 1, "LoadConstantNumber", }, +{ 1, "LoadConstantString", }, +{ 1, "LoadThis", }, +{ 1, "LoadFunction", }, +{ 1, "LoadType", }, +{ -128, "Invoke", }, +{ 0, "GetType", }, +{ -1, "Cast", }, +{ 0, "DoUnary", }, +{ -1, "DoOperator", }, +{ 1, "PushNull", }, +{ 1, "PushInt", }, +{ 1, "PushNum", }, +{ 1, "PushString", }, +{ 1, "PushType", }, +{ -128, "Return", }, +{ -128, "ReturnVoid", }, +{ 0, "GetConstructor", }, +{ 1, "NewObject", }, +{ -1, "NewThis", }, +{ -128, "NewInstance", }, +{ 0, "Delete", }, +{ 0, "TypeOf", }, +{ -1, "InstanceOf", }, +{ -1, "As", }, +{ -1, "Is", }, +{ 0, "ToBoolean", }, +{ -1, "JumpFalse", }, +{ -1, "JumpTrue", }, +{ 0, "Jump", }, +{ 0, "Try", }, +{ 0, "Jsr", }, +{ 0, "Rts", }, +{ -1, "Within", }, +{ 0, "Without", }, +{ -128, "Throw", }, +{ 0, "Handler", }, +{ -3, "LogicalXor", }, +{ 0, "LogicalNot", }, +{ 0, "Swap", }, +{ 1, "Dup", }, +{ 1, "DupInsert", }, +{ -128, "DupN", }, +{ -128, "DupInsertN", }, +{ -1, "Pop", }, +{ 0, "GetField", }, +{ -1, "SetField", }, +{ 1, "GetMethod", }, +{ 0, "GetMethodRef", }, +{ 1, "GetArg", }, +{ 0, "SetArg", }, +{ 1, "GetLocalVar", }, +{ 0, "SetLocalVar", }, +{ 1, "GetClosureVar", }, +{ 0, "SetClosureVar", }, +{ -1, "GetElement", }, +{ -2, "SetElement", }, +{ 0, "GetProperty", }, +{ 1, "GetInvokeProperty", }, +{ -1, "SetProperty", }, +{ 1, "GetName", }, +{ 1, "GetTypeOfName", }, +{ 0, "SetName", }, +{ 1, "LoadGlobalObject", }, +{ 0, "PushScope", }, +{ 0, "PopScope", }, +{ 0, "NewClosure" }, +{ 0, "Class" }, +{ -1, "Juxtapose" }, +{ -1, "NamedArgument" }, + +}; + +ByteCodeModule::ByteCodeModule(ByteCodeGen *bcg) +{ + mLength = bcg->mBuffer->size(); + mCodeBase = new uint8[mLength]; + memcpy(mCodeBase, bcg->mBuffer->begin(), mLength); + + mCodeMapLength = bcg->mPC_Map->size(); + mCodeMap = new PC_Position[mCodeMapLength]; + memcpy(mCodeMap, bcg->mPC_Map->begin(), mCodeMapLength * sizeof(PC_Position)); + + mStringPoolContents = new String[bcg->mStringPoolContents.size()]; + + int index = 0; + for (std::vector::iterator s_i = bcg->mStringPoolContents.begin(), + s_end = bcg->mStringPoolContents.end(); (s_i != s_end); s_i++, index++) + mStringPoolContents[index] = *s_i; + + mNumberPoolContents = new float64[bcg->mNumberPoolContents.size()]; + index = 0; + for (std::vector::iterator f_i = bcg->mNumberPoolContents.begin(), + f_end = bcg->mNumberPoolContents.end(); (f_i != f_end); f_i++, index++) + mNumberPoolContents[index] = *f_i; + + mLocalsCount = bcg->mScopeChain->countVars(); + mStackDepth = toUInt32(bcg->mStackMax); +} + +size_t ByteCodeModule::getPositionForPC(uint32 pc) +{ + if (mCodeMapLength == 0) + return 0; + + for (uint32 i = 0; i < (mCodeMapLength - 1); i++) { + uint32 pos1 = mCodeMap[i].first; + uint32 pos2 = mCodeMap[i + 1].first; + if ((pc >= pos1) && (pc < pos2)) + return mCodeMap[i].second; + } + return mCodeMap[mCodeMapLength - 1].second; +} + + + + + +uint32 ByteCodeGen::getLabel() +{ + uint32 result = mLabelList.size(); + mLabelList.push_back(Label()); + return result; +} + +uint32 ByteCodeGen::getLabel(Label::LabelKind kind) +{ + uint32 result = mLabelList.size(); + mLabelList.push_back(Label(kind)); + return result; +} + +uint32 ByteCodeGen::getLabel(LabelStmtNode *lbl) +{ + uint32 result = mLabelList.size(); + mLabelList.push_back(Label(lbl)); + return result; +} + +uint32 ByteCodeGen::getTopLabel(Label::LabelKind kind, const StringAtom *name) +{ + uint32 result = uint32(-1); + for (std::vector::reverse_iterator i = mLabelStack.rbegin(), + end = mLabelStack.rend(); + (i != end); i++) + { + // find the closest kind of label + if (mLabelList[*i].matches(kind)) + result = *i; + else // and return it when we get the name + if (mLabelList[*i].matches(name)) + return result; + } + NOT_REACHED("label not found"); + return false; +} + +uint32 ByteCodeGen::getTopLabel(Label::LabelKind kind) +{ + for (std::vector::reverse_iterator i = mLabelStack.rbegin(), + end = mLabelStack.rend(); + (i != end); i++) + { + if (mLabelList[*i].matches(kind)) + return *i; + } + NOT_REACHED("label not found"); + return false; +} + +void ByteCodeGen::addOp(uint8 op) +{ + addByte(op); + ASSERT(gByteCodeData[op].stackImpact != -128); + mStackTop += gByteCodeData[op].stackImpact; + if (mStackTop > mStackMax) + mStackMax = mStackTop; + ASSERT(mStackTop >= 0); +} + +void ByteCodeGen::addNumberRef(float64 f) +{ + NumberPool::iterator i = mNumberPool.find(f); + if (i != mNumberPool.end()) + addLong(i->second); + else { + addLong(mNumberPoolContents.size()); + mNumberPool[f] = mNumberPoolContents.size(); + mNumberPoolContents.push_back(f); + } +} + +void ByteCodeGen::addStringRef(const String &str) +{ + StringPool::iterator i = mStringPool.find(str); + if (i != mStringPool.end()) + addLong(i->second); + else { + addLong(mStringPoolContents.size()); + mStringPool[str] = mStringPoolContents.size(); + mStringPoolContents.push_back(str); + } +} + + + + + + + + +void Label::setLocation(ByteCodeGen *bcg, uint32 location) +{ + mHasLocation = true; + mLocation = location; + for (std::vector::iterator i = mFixupList.begin(), end = mFixupList.end(); + (i != end); i++) + { + uint32 branchLocation = *i; + bcg->setOffset(branchLocation, int32(mLocation - branchLocation)); + } +} + +void Label::addFixup(ByteCodeGen *bcg, uint32 branchLocation) +{ + if (mHasLocation) + bcg->addOffset(int32(mLocation - branchLocation)); + else { + mFixupList.push_back(branchLocation); + bcg->addLong(0); + } +} + + + +void ByteCodeGen::genCodeForFunction(FunctionDefinition &f, size_t pos, JSFunction *fnc, bool isConstructor, JSType *topClass) +{ + mScopeChain->addScope(fnc->mParameterBarrel); + mScopeChain->addScope(&fnc->mActivation); + // OPT - no need to push the parameter and function + // scopes if the function doesn't contain any 'eval' + // calls, all other references to the variables mapped + // inside these scopes will have been turned into + // localVar references. +/* + addByte(PushScopeOp); + addPointer(fnc->mParameterBarrel); + addByte(PushScopeOp); + addPointer(&fnc->mActivation); +*/ + +#ifdef DEBUG + if (f.name) { +// const StringAtom& name = checked_cast(f.name)->name; +// stdOut << "gencode for " << name << "\n"; + } +#endif + + if (isConstructor) { + // + // add a code sequence to create a new empty instance if the + // incoming 'this' is null + // + addOp(LoadTypeOp); + addPointer(topClass); + addOp(NewThisOp); + // + // Invoke the super class constructor if there isn't an explicit + // statement to do so. + // + if (topClass->mSuperType) { + JSType *superClass = topClass->mSuperType; + bool foundSuperCall = false; + BlockStmtNode *b = f.body; + if (b && b->statements) { + if (b->statements->getKind() == StmtNode::expression) { + ExprStmtNode *e = checked_cast(b->statements); + if (e->expr->getKind() == ExprNode::call) { + InvokeExprNode *i = checked_cast(e->expr); + if (i->op->getKind() == ExprNode::dot) { + // check for 'this.m()' + BinaryExprNode *b = checked_cast(i->op); + if ((b->op1->getKind() == ExprNode::This) && (b->op2->getKind() == ExprNode::identifier)) { + // IdentifierExprNode *i = checked_cast(b->op2); + // XXX verify that i->name is a constructor in the superclass + foundSuperCall = true; + i->isSuperInvoke = true; + } + } + else { + // look for calls to 'this()' + if (i->op->getKind() == ExprNode::This) { + foundSuperCall = true; + i->isSuperInvoke = true; + } + else { + // otherwise, look for calls to the superclass by name + if (i->op->getKind() == ExprNode::identifier) { + const StringAtom &name = checked_cast(i->op)->name; + if (name == superClass->mClassName) + foundSuperCall = true; + i->isSuperInvoke = true; + } + } + } + } + else { + if (e->expr->getKind() == ExprNode::superStmt) { + foundSuperCall = true; + } + } + } + } + + if (!foundSuperCall) { // invoke the default superclass constructor + if (superClass) { + // Make sure there's a default constructor with 0 (required) parameters + JSFunction *superConstructor = superClass->getDefaultConstructor(); + if (superConstructor) { + if (superConstructor->getRequiredArgumentCount() > 0) + m_cx->reportError(Exception::typeError, "Super class default constructor must be called explicitly - it has required parameters that must be specified", pos); + addOp(LoadThisOp); + addOp(LoadFunctionOp); + addPointer(superConstructor); + addOpAdjustDepth(InvokeOp, -1); + addLong(0); + addByte(Explicit); + addOp(PopOp); + } + } + } + } + } + + bool hasReturn = genCodeForStatement(f.body, NULL, NotALabel); + +/* + // OPT - see above + addByte(PopScopeOp); + addByte(PopScopeOp); +*/ + if (isConstructor) { + ASSERT(!hasReturn); // is this useful? Won't the semantics have done it? + addOp(LoadThisOp); + ASSERT(mStackTop == 1); + addOpSetDepth(ReturnOp, 0); + } + else { + if (!hasReturn) { + addOp(LoadConstantUndefinedOp); + ASSERT(mStackTop == 1); + addOpSetDepth(ReturnOp, 0); + } + } + + VariableBinding *v = f.parameters; + uint32 index = 0; + while (v) { + + if (v->initializer) { + // this code gets executed if the function is called without + // an argument for this parameter. + fnc->setArgumentInitializer(index, currentOffset()); + genExpr(v->initializer); + addOpSetDepth(ReturnOp, 0); + } + index++; + v = v->next; + } + + ByteCodeModule *bcm = new ByteCodeModule(this); + if (m_cx->mReader) + bcm->setSource(m_cx->mReader->source, m_cx->mReader->sourceLocation); + fnc->setByteCode(bcm); + + mScopeChain->popScope(); + mScopeChain->popScope(); +} + + +ByteCodeModule *ByteCodeGen::genCodeForScript(StmtNode *p) +{ + while (p) { + genCodeForStatement(p, NULL, NotALabel); + p = p->next; + } + return new ByteCodeModule(this); +} + +ByteCodeModule *ByteCodeGen::genCodeForExpression(ExprNode *p) +{ + genExpr(p); + addOp(PopOp); + return new ByteCodeModule(this); +} + + +// emit bytecode for the single statement p. Return true if that statement +// was a return statement (or contained only paths leading to a return statement) +bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint32 finallyLabel) +{ + bool result = false; + addPosition(p->pos); + switch (p->getKind()) { + case StmtNode::Class: + { + ClassStmtNode *classStmt = checked_cast(p); + JSType *thisClass = classStmt->mType; + + mScopeChain->addScope(thisClass); + if (classStmt->body) { + ByteCodeGen static_cg(m_cx, mScopeChain); // this will capture the static initializations + ByteCodeGen bcg(m_cx, mScopeChain); // this will capture the instance initializations + StmtNode* s = classStmt->body->statements; + while (s) { + bcg.genCodeForStatement(s, &static_cg, finallyLabel); + s = s->next; + } + JSFunction *f = NULL; + if (static_cg.hasContent()) { + // build a function to be invoked + // when the class is loaded + f = new JSFunction(Void_Type, mScopeChain); + ByteCodeModule *bcm = new ByteCodeModule(&static_cg); + if (m_cx->mReader) + bcm->setSource(m_cx->mReader->source, m_cx->mReader->sourceLocation); + f->setByteCode(bcm); + } + thisClass->setStaticInitializer(m_cx, f); + f = NULL; + if (bcg.hasContent()) { + // execute this function now to form the initial instance + f = new JSFunction(Void_Type, mScopeChain); + ByteCodeModule *bcm = new ByteCodeModule(&bcg); + if (m_cx->mReader) + bcm->setSource(m_cx->mReader->source, m_cx->mReader->sourceLocation); + f->setByteCode(bcm); + } + thisClass->setInstanceInitializer(m_cx, f); + } + mScopeChain->popScope(); + } + break; + case StmtNode::Const: + case StmtNode::Var: + { + VariableStmtNode *vs = checked_cast(p); + VariableBinding *v = vs->bindings; + bool isStatic = (vs->attributeValue->mTrueFlags & Property::Static) == Property::Static; + while (v) { + Reference *ref = mScopeChain->getName(*v->name, CURRENT_ATTR, Write); + ASSERT(ref); // must have been added previously by collectNames + if (v->initializer) { + if (isStatic && (static_cg != NULL)) { + ref->emitImplicitLoad(static_cg); + static_cg->genExpr(v->initializer); + ref->emitCodeSequence(static_cg); + static_cg->addOp(PopOp); + } + else { + ref->emitImplicitLoad(this); + genExpr(v->initializer); + ref->emitCodeSequence(this); + addOp(PopOp); + } + } + else { + // initialize the variable with an appropriate value + JSValue uiv = ref->mType->getUninitializedValue(); + if (!uiv.isUndefined()) { + ref->emitImplicitLoad(this); + if (uiv.isNull()) + addOp(LoadConstantNullOp); + else + if (uiv.isPositiveZero()) + addOp(LoadConstantZeroOp); + else + if (uiv.isFalse()) + addOp(LoadConstantFalseOp); + else + NOT_REACHED("Any more??"); + ref->emitCodeSequence(this); + addOp(PopOp); + } + } + delete ref; + v = v->next; + } + } + break; + case StmtNode::Function: + { + FunctionStmtNode *f = checked_cast(p); + bool isConstructor = (f->attributeValue->mTrueFlags & Property::Constructor) == Property::Constructor; + JSFunction *fnc = f->mFunction; + + ASSERT(f->function.name); + if (mScopeChain->topClass() && (mScopeChain->topClass()->mClassName.compare(*f->function.name) == 0)) + isConstructor = true; + ByteCodeGen bcg(m_cx, mScopeChain); + bcg.genCodeForFunction(f->function, f->pos, fnc, isConstructor, mScopeChain->topClass()); + + if (mScopeChain->isNestedFunction()) { + addOp(LoadFunctionOp); + addPointer(fnc); + addOp(NewClosureOp); + addOp(SetNameOp); + addStringRef(*f->function.name); + addOp(PopOp); + } + + } + break; + case StmtNode::While: + { + UnaryStmtNode *w = checked_cast(p); + addOp(JumpOp); + uint32 labelAtTestCondition = getLabel(Label::ContinueLabel); + addFixup(labelAtTestCondition); + uint32 labelAtTopOfBlock = getLabel(); + setLabel(labelAtTopOfBlock); + uint32 breakLabel = getLabel(Label::BreakLabel); + + mLabelStack.push_back(breakLabel); + mLabelStack.push_back(labelAtTestCondition); + genCodeForStatement(w->stmt, static_cg, finallyLabel); + mLabelStack.pop_back(); + mLabelStack.pop_back(); + + setLabel(labelAtTestCondition); + genExpr(w->expr); + addOp(ToBooleanOp); + addOp(JumpTrueOp); + addFixup(labelAtTopOfBlock); + setLabel(breakLabel); + } + break; + case StmtNode::DoWhile: + { + UnaryStmtNode *d = checked_cast(p); + uint32 breakLabel = getLabel(Label::BreakLabel); + uint32 labelAtTopOfBlock = getLabel(); + uint32 labelAtTestCondition = getLabel(Label::ContinueLabel); + setLabel(labelAtTopOfBlock); + + mLabelStack.push_back(breakLabel); + mLabelStack.push_back(labelAtTestCondition); + genCodeForStatement(d->stmt, static_cg, finallyLabel); + mLabelStack.pop_back(); + mLabelStack.pop_back(); + + setLabel(labelAtTestCondition); + genExpr(d->expr); + addOp(ToBooleanOp); + addOp(JumpTrueOp); + addFixup(labelAtTopOfBlock); + setLabel(breakLabel); + } + break; + case StmtNode::ForIn: + { + ForStmtNode *f = checked_cast(p); + if (f->initializer->getKind() == StmtNode::Var) { + VariableStmtNode *vs = checked_cast(f->initializer); + VariableBinding *v = vs->bindings; + Reference *value = mScopeChain->getName(*v->name, CURRENT_ATTR, Write); + + uint32 breakLabel = getLabel(Label::BreakLabel); + uint32 labelAtTopOfBlock = getLabel(); + uint32 labelAtIncrement = getLabel(Label::ContinueLabel); + uint32 labelAtTestCondition = getLabel(); + uint32 labelAtEnd = getLabel(); +/* + iterator = object.forin() + goto test + top: + v = iterator.value + + continue: + iterator = object.next(iterator) + test: + if (iterator == null) + goto end + goto top + break: + object.done(iterator) + end: +*/ + + // acquire a local from the scopechain, and copy the target object + // into it. + Reference *objectReadRef, *objectWriteRef; + Reference *iteratorReadRef, *iteratorWriteRef; + mScopeChain->defineTempVariable(objectReadRef, objectWriteRef, Object_Type); + mScopeChain->defineTempVariable(iteratorReadRef, iteratorWriteRef, Object_Type); + + + genExpr(f->expr2); + objectWriteRef->emitCodeSequence(this); + addOp(DupOp); + addOp(GetInvokePropertyOp); +// addIdentifierRef(widenCString("Iterator"), widenCString("forin")); + addStringRef(widenCString("forin")); + addOpAdjustDepth(InvokeOp, -1); + addLong(0); + addByte(Explicit); + iteratorWriteRef->emitCodeSequence(this); + + addOp(JumpOp); + addFixup(labelAtTestCondition); + + setLabel(labelAtTopOfBlock); + iteratorReadRef->emitCodeSequence(this); + addOp(GetPropertyOp); + addStringRef(widenCString("value")); + value->emitCodeSequence(this); + + mLabelStack.push_back(breakLabel); + mLabelStack.push_back(labelAtIncrement); + genCodeForStatement(f->stmt, static_cg, finallyLabel); + mLabelStack.pop_back(); + mLabelStack.pop_back(); + + setLabel(labelAtIncrement); + objectReadRef->emitCodeSequence(this); + addOp(DupOp); + addOp(GetInvokePropertyOp); + addStringRef(widenCString("next")); + iteratorReadRef->emitCodeSequence(this); + addOpAdjustDepth(InvokeOp, -1); + addLong(1); + addByte(Explicit); + iteratorWriteRef->emitCodeSequence(this); + addOp(PopOp); + + setLabel(labelAtTestCondition); + iteratorReadRef->emitCodeSequence(this); + addOp(LoadConstantNullOp); + addOp(DoOperatorOp); + addByte(Equal); + addOp(JumpTrueOp); + addFixup(labelAtEnd); + addOp(JumpOp); + addFixup(labelAtTopOfBlock); + + setLabel(breakLabel); + objectReadRef->emitCodeSequence(this); + addOp(DupOp); + addOp(GetInvokePropertyOp); + addStringRef(widenCString("done")); + iteratorReadRef->emitCodeSequence(this); + addOpAdjustDepth(InvokeOp, -2); + addLong(1); + addByte(Explicit); + addOp(PopOp); + + setLabel(labelAtEnd); + + delete objectReadRef; + delete objectWriteRef; + delete iteratorReadRef; + delete iteratorWriteRef; + } + else + NOT_REACHED("implement me"); + } + break; + case StmtNode::For: + { + ForStmtNode *f = checked_cast(p); + uint32 breakLabel = getLabel(Label::BreakLabel); + uint32 labelAtTopOfBlock = getLabel(); + uint32 labelAtIncrement = getLabel(Label::ContinueLabel); + uint32 labelAtTestCondition = getLabel(); + + if (f->initializer) + genCodeForStatement(f->initializer, static_cg, finallyLabel); + addOp(JumpOp); + addFixup(labelAtTestCondition); + + setLabel(labelAtTopOfBlock); + + mLabelStack.push_back(breakLabel); + mLabelStack.push_back(labelAtIncrement); + genCodeForStatement(f->stmt, static_cg, finallyLabel); + mLabelStack.pop_back(); + mLabelStack.pop_back(); + + setLabel(labelAtIncrement); + if (f->expr3) { + genExpr(f->expr3); + addOp(PopOp); + } + + setLabel(labelAtTestCondition); + if (f->expr2) { + genExpr(f->expr2); + addOp(ToBooleanOp); + addOp(JumpTrueOp); + addFixup(labelAtTopOfBlock); + } + + setLabel(breakLabel); + } + break; + case StmtNode::label: + { + LabelStmtNode *l = checked_cast(p); + mLabelStack.push_back(getLabel(l)); + genCodeForStatement(l->stmt, static_cg, finallyLabel); + mLabelStack.pop_back(); + } + break; + case StmtNode::Break: + { + if (finallyLabel != NotALabel) { + addOp(JsrOp); + addFixup(finallyLabel); + } + + GoStmtNode *g = checked_cast(p); + addOp(JumpOp); + if (g->name) + addFixup(getTopLabel(Label::BreakLabel, g->name)); + else + addFixup(getTopLabel(Label::BreakLabel)); + } + break; + case StmtNode::Continue: + { + GoStmtNode *g = checked_cast(p); + addOp(JumpOp); + if (g->name) + addFixup(getTopLabel(Label::ContinueLabel, g->name)); + else + addFixup(getTopLabel(Label::ContinueLabel)); + } + break; + case StmtNode::Switch: + { +/* + + SetVarOp + Pop + + // test sequence in source order except + // the default is moved to end. + + GetVarOp + + Equal + JumpTrue --> case1StmtLabel + GetVarOp + + Equal + JumpTrue --> case2StmtLabel + Jump --> default, if there is one, or break label + + case1StmtLabel: + + case2StmtLabel: + + defaultLabel: + + case3StmtLabel: + + ..etc.. // all in source order + + breakLabel: +*/ + uint32 breakLabel = getLabel(Label::BreakLabel); + uint32 defaultLabel = toUInt32(-1); + + Reference *switchTempReadRef, *switchTempWriteRef; + mScopeChain->defineTempVariable(switchTempReadRef, switchTempWriteRef, Object_Type); + + SwitchStmtNode *sw = checked_cast(p); + genExpr(sw->expr); + switchTempWriteRef->emitCodeSequence(this); + addOp(PopOp); + + StmtNode *s = sw->statements; + while (s) { + if (s->getKind() == StmtNode::Case) { + ExprStmtNode *c = checked_cast(s); + c->label = getLabel(); + if (c->expr) { + switchTempReadRef->emitCodeSequence(this); + genExpr(c->expr); + addOp(DoOperatorOp); + addByte(Equal); + addOp(JumpTrueOp); + addFixup(c->label); + } + else + defaultLabel = c->label; + } + s = s->next; + } + addOp(JumpOp); + if (defaultLabel != toUInt32(-1)) + addFixup(defaultLabel); + else + addFixup(breakLabel); + + s = sw->statements; + mLabelStack.push_back(breakLabel); + while (s) { + if (s->getKind() == StmtNode::Case) { + ExprStmtNode *c = checked_cast(s); + setLabel(c->label); + } + else + genCodeForStatement(s, static_cg, finallyLabel); + s = s->next; + } + mLabelStack.pop_back(); + setLabel(breakLabel); + + delete switchTempReadRef; + delete switchTempWriteRef; + } + break; + case StmtNode::If: + { + UnaryStmtNode *i = checked_cast(p); + genExpr(i->expr); + addOp(ToBooleanOp); + addOp(JumpFalseOp); + uint32 label = getLabel(); + addFixup(label); + genCodeForStatement(i->stmt, static_cg, finallyLabel); + setLabel(label); + } + break; + case StmtNode::IfElse: + { + BinaryStmtNode *i = checked_cast(p); + genExpr(i->expr); + addOp(ToBooleanOp); + addOp(JumpFalseOp); + uint32 elseStatementLabel = getLabel(); + addFixup(elseStatementLabel); + result = genCodeForStatement(i->stmt, static_cg, finallyLabel); + addOp(JumpOp); + uint32 branchAroundElselabel = getLabel(); + addFixup(branchAroundElselabel); + setLabel(elseStatementLabel); + result &= genCodeForStatement(i->stmt2, static_cg, finallyLabel); + setLabel(branchAroundElselabel); + } + break; + case StmtNode::block: + { + BlockStmtNode *b = checked_cast(p); + StmtNode *s = b->statements; + while (s) { + result = genCodeForStatement(s, static_cg, finallyLabel); + s = s->next; + } + } + break; + case StmtNode::Return: + { + ExprStmtNode *e = checked_cast(p); + if (e->expr) { + genExpr(e->expr); + ASSERT(mStackTop == 1); + addOpSetDepth(ReturnOp, 0); + } + else { + ASSERT(mStackTop == 0); + addOpSetDepth(ReturnVoidOp, 0); + } + result = true; + } + break; + case StmtNode::expression: + { + ExprStmtNode *e = checked_cast(p); + genExpr(e->expr); + addOp(PopOp); + } + break; + case StmtNode::empty: + /* nada */ + break; + case StmtNode::Throw: + { + ExprStmtNode *e = checked_cast(p); + genExpr(e->expr); + addOpSetDepth(ThrowOp, 0); + } + break; + case StmtNode::With: + { + UnaryStmtNode *w = checked_cast(p); + JSType *objType = genExpr(w->expr); + addOp(WithinOp); + mScopeChain->addScope(objType); + genCodeForStatement(w->stmt, static_cg, finallyLabel); + mScopeChain->popScope(); + addOp(WithoutOp); + } + break; + case StmtNode::Try: + { +/* + + try { // [catch,finally] handler labels are pushed on try stack + + } // catch handler label is popped off try stack + jsr finally + jump-->finished + + finally: // finally handler label popped off + { // a throw from in here goes to the 'next' handler + } + rts + + finallyInvoker: <--- + push exception | + jsr finally |--- the handler labels + throw exception | + | + catchLabel: <--- + catch (exception) { // catch handler label popped off + // any throw from in here must jump to the finallyInvoker + // (i.e. not the catch handler!) + the incoming exception + is on the top of the stack. it + get stored into the variable + we've associated with the catch clause + + } + // 'normal' fall thru from catch + jsr finally + + finished: +*/ + TryStmtNode *t = checked_cast(p); + + uint32 catchClauseLabel; + uint32 finallyInvokerLabel; + uint32 t_finallyLabel; + addOp(TryOp); + if (t->finally) { + finallyInvokerLabel = getLabel(); + addFixup(finallyInvokerLabel); + t_finallyLabel = getLabel(); + } + else { + finallyInvokerLabel = NotALabel; + addLong(NotALabel); + t_finallyLabel = NotALabel; + } + if (t->catches) { + catchClauseLabel = getLabel(); + addFixup(catchClauseLabel); + } + else { + catchClauseLabel = NotALabel; + addLong(NotALabel); + } + uint32 finishedLabel = getLabel(); + + genCodeForStatement(t->stmt, static_cg, t_finallyLabel); + + if (t->finally) { + addOp(JsrOp); + addFixup(t_finallyLabel); + addOp(JumpOp); + addFixup(finishedLabel); + setLabel(t_finallyLabel); + addOp(HandlerOp); + genCodeForStatement(t->finally, static_cg, finallyLabel); + addOp(RtsOp); + + setLabel(finallyInvokerLabel); + // the exception object is on the top of the stack already + addOp(JsrOp); + addFixup(t_finallyLabel); + addOpSetDepth(ThrowOp, 0); + + } + else { + addOp(JumpOp); + addFixup(finishedLabel); + } + + if (t->catches) { + setLabel(catchClauseLabel); + addOp(HandlerOp); + CatchClause *c = t->catches; + ASSERT(mStackTop == 0); + mStackTop = 1; + if (mStackMax < 1) mStackMax = 1; + while (c) { + Reference *ref = mScopeChain->getName(c->name, CURRENT_ATTR, Write); + ref->emitImplicitLoad(this); + ref->emitCodeSequence(this); + delete ref; + genCodeForStatement(c->stmt, static_cg, t_finallyLabel); + c = c->next; + if (c) { + mStackTop = 1; + Reference *ref = mScopeChain->getName(c->name, CURRENT_ATTR, Read); + ref->emitCodeSequence(this); + delete ref; + } + } + addOp(PopOp); // the exception object has persisted + // on the top of the stack until here + if (t->finally) { + addOp(JsrOp); + addFixup(t_finallyLabel); + } + } + setLabel(finishedLabel); + } + break; + case StmtNode::Use: + { + UseStmtNode *u = checked_cast(p); + ExprList *eList = u->namespaces; + while (eList) { + ExprNode *e = eList->expr; + if (e->getKind() == ExprNode::identifier) { + NOT_REACHED("implement me"); + // ***** What is this supposed to do? id is not used anywhere. + // AttributeList *id = new(m_cx->mArena) AttributeList(e); + // id->next = CURRENT_ATTR; + } + else + NOT_REACHED("implement me"); + eList = eList->next; + } + + } + break; + case StmtNode::Namespace: + { + // do anything at bytecodegen? + } + break; + default: + NOT_REACHED("Not Implemented Yet"); + } + return result; +} + +Reference *ByteCodeGen::genReference(ExprNode *p, Access acc) +{ + switch (p->getKind()) { + case ExprNode::index: + { + InvokeExprNode *i = checked_cast(p); + genExpr(i->op); + ExprPairList *p = i->pairs; + int32 argCount = 0; + while (p) { + genExpr(p->value); + argCount++; + p = p->next; + } + Reference *ref = new ElementReference(acc, argCount); + return ref; + } + case ExprNode::identifier: + { + const StringAtom &name = checked_cast(p)->name; + Reference *ref = mScopeChain->getName(name, CURRENT_ATTR, acc); + if (ref == NULL) + ref = new NameReference(name, acc); + ref->emitImplicitLoad(this); + return ref; + } + case ExprNode::dot: + { + BinaryExprNode *b = checked_cast(p); + + JSType *lType = NULL; + + // Optimize for ClassName.identifier. If we don't + // do this we simply generate a getProperty op + // against the Type_Type object the leftside has found. + // + // If we find it, emit the code to 'load' the class + // (which loads the static instance) and the name + // lookup can then proceed against the static type. + // + + if (b->op1->getKind() == ExprNode::identifier) { + const StringAtom &name = checked_cast(b->op1)->name; + JSValue v = mScopeChain->getCompileTimeValue(name, NULL); + if (v.isType()) { + lType = v.type; + genExpr(b->op1); + } + } + + if (lType == NULL) + lType = genExpr(b->op1); // generate code for leftside of dot + if (b->op2->getKind() == ExprNode::qualify) { + QualifyExprNode *qe = checked_cast(b->op2); + ASSERT(qe->qualifier->getKind() == ExprNode::identifier); // XXX handle more complex... + + const StringAtom &fieldName = checked_cast(b->op2)->name; + const StringAtom &qualifierName = checked_cast(qe->qualifier)->name; + + NamespaceList *oldNS = mNamespaceList; + mNamespaceList = new NamespaceList(&qualifierName, mNamespaceList); + + Reference *ref = lType->genReference(true, fieldName, mNamespaceList, acc, Property::NoAttribute); + if (ref == NULL) + ref = new PropertyReference(fieldName, acc, Object_Type, 0); + + delete mNamespaceList; + mNamespaceList = oldNS; + + return ref; + } + else { + ASSERT(b->op2->getKind() == ExprNode::identifier); + const StringAtom &fieldName = checked_cast(b->op2)->name; + Reference *ref = lType->genReference(true, fieldName, CURRENT_ATTR, acc, 0); + if (ref == NULL) + ref = new PropertyReference(fieldName, acc, Object_Type, Property::NoAttribute); + return ref; + } + + } + default: // return NULL here rather than throwing an exception, + // for (e.g.) typeof foo(), we use the NULL reference to signal + // the distinction between lvalue & rvalue cases. + return NULL; + } + return NULL; +} + +void ByteCodeGen::genReferencePair(ExprNode *p, Reference *&readRef, Reference *&writeRef) +{ + switch (p->getKind()) { + case ExprNode::identifier: + { + const StringAtom &name = checked_cast(p)->name; + readRef = mScopeChain->getName(name, CURRENT_ATTR, Read); + if (readRef == NULL) + readRef = new NameReference(name, Read); + writeRef = mScopeChain->getName(name, CURRENT_ATTR, Write); + if (writeRef == NULL) + writeRef = new NameReference(name, Write); + readRef->emitImplicitLoad(this); + } + break; + case ExprNode::index: + { + InvokeExprNode *i = checked_cast(p); + genExpr(i->op); + ExprPairList *p = i->pairs; + int32 argCount = 0; + while (p) { + genExpr(p->value); + argCount++; + p = p->next; + } + readRef = new ElementReference(Read, argCount); + writeRef = new ElementReference(Write, argCount); + } + break; + case ExprNode::dot: + { + BinaryExprNode *b = checked_cast(p); + + JSType *lType = NULL; + + if (b->op1->getKind() == ExprNode::identifier) { + const StringAtom &name = checked_cast(b->op1)->name; + JSValue v = mScopeChain->getCompileTimeValue(name, NULL); + if (v.isType()) { + lType = v.type; + genExpr(b->op1); + } + } + + if (lType == NULL) + lType = genExpr(b->op1); // generate code for leftside of dot + if (b->op2->getKind() != ExprNode::identifier) { + // this is where we handle n.q::id + } + else { + const StringAtom &fieldName = checked_cast(b->op2)->name; + readRef = lType->genReference(true, fieldName, CURRENT_ATTR, Read, 0); + if (readRef == NULL) + readRef = new PropertyReference(fieldName, Read, Object_Type, Property::NoAttribute); + writeRef = lType->genReference(true, fieldName, CURRENT_ATTR, Write, 0); + if (writeRef == NULL) + writeRef = new PropertyReference(fieldName, Write, Object_Type, Property::NoAttribute); + } + } + break; + default: + NOT_REACHED("Bad genReferencePair op"); + } +} + + +JSType *ByteCodeGen::genExpr(ExprNode *p) +{ + Operator op; + + switch (p->getKind()) { + case ExprNode::boolean: + addOp(checked_cast(p)->value ? LoadConstantTrueOp : LoadConstantFalseOp); + return Boolean_Type; + case ExprNode::Null: + addOp(LoadConstantNullOp); + return Object_Type; + case ExprNode::number : + addOp(LoadConstantNumberOp); + addNumberRef(checked_cast(p)->value); + return Number_Type; + case ExprNode::string : + addOp(LoadConstantStringOp); + addStringRef(checked_cast(p)->str); + return String_Type; + + case ExprNode::add: + op = Plus; + goto BinaryOperator; + case ExprNode::subtract: + op = Minus; + goto BinaryOperator; + case ExprNode::multiply: + op = Multiply; + goto BinaryOperator; + case ExprNode::divide: + op = Divide; + goto BinaryOperator; + case ExprNode::modulo: + op = Remainder; + goto BinaryOperator; + case ExprNode::leftShift: + op = ShiftLeft; + goto BinaryOperator; + case ExprNode::rightShift: + op = ShiftRight; + goto BinaryOperator; + case ExprNode::logicalRightShift: + op = UShiftRight; + goto BinaryOperator; + case ExprNode::bitwiseAnd: + op = BitAnd; + goto BinaryOperator; + case ExprNode::bitwiseXor: + op = BitXor; + goto BinaryOperator; + case ExprNode::bitwiseOr: + op = BitOr; + goto BinaryOperator; + case ExprNode::lessThan: + op = Less; + goto BinaryOperator; + case ExprNode::lessThanOrEqual: + op = LessEqual; + goto BinaryOperator; + case ExprNode::In: + op = In; + goto BinaryOperator; + case ExprNode::equal: + op = Equal; + goto BinaryOperator; + case ExprNode::identical: + op = SpittingImage; + goto BinaryOperator; + +BinaryOperator: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(DoOperatorOp); + addByte(op); + return Object_Type; + } + + case ExprNode::notEqual: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(DoOperatorOp); + addByte(Equal); + addOp(LogicalNotOp); + return Object_Type; + } + case ExprNode::greaterThan: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(SwapOp); + addOp(DoOperatorOp); + addByte(Less); + return Object_Type; + } + case ExprNode::greaterThanOrEqual: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(SwapOp); + addOp(DoOperatorOp); + addByte(LessEqual); + return Object_Type; + } + case ExprNode::notIdentical: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(DoOperatorOp); + addByte(SpittingImage); + addOp(LogicalNotOp); + return Object_Type; + } + + case ExprNode::minus: + { + UnaryExprNode *u = checked_cast(p); + genExpr(u->op); + addOp(DoUnaryOp); + addByte(Negate); + return Object_Type; + } + + case ExprNode::plus: + { + UnaryExprNode *u = checked_cast(p); + genExpr(u->op); + addOp(DoUnaryOp); + addByte(Posate); + return Object_Type; + } + + case ExprNode::complement: + { + UnaryExprNode *u = checked_cast(p); + genExpr(u->op); + addOp(DoUnaryOp); + addByte(Complement); + return Object_Type; + } + + case ExprNode::preIncrement: + op = Increment; + goto PreXcrement; + case ExprNode::preDecrement: + op = Decrement; + goto PreXcrement; + +PreXcrement: + { + UnaryExprNode *u = checked_cast(p); + Reference *readRef; + Reference *writeRef; + genReferencePair(u->op, readRef, writeRef); + int32 baseDepth = readRef->baseExpressionDepth(); + if (baseDepth) { // duplicate the base expression + ASSERT(baseDepth <= MAX_UINT16); + if (baseDepth > 1) { + addOpAdjustDepth(DupNOp, -baseDepth); + addShort((uint16)baseDepth); + } + else + addOp(DupOp); + readRef->emitCodeSequence(this); + addOpStretchStack(DoUnaryOp, 1); + addByte(op); + } + else { + readRef->emitCodeSequence(this); + addOpStretchStack(DoUnaryOp, 1); + addByte(op); + } + writeRef->emitCodeSequence(this); + delete readRef; + delete writeRef; + return Object_Type; + } + + case ExprNode::postIncrement: + op = Increment; + goto PostXcrement; + case ExprNode::postDecrement: + op = Decrement; + goto PostXcrement; + +PostXcrement: + { + UnaryExprNode *u = checked_cast(p); + Reference *readRef; + Reference *writeRef; + genReferencePair(u->op, readRef, writeRef); + int32 baseDepth = readRef->baseExpressionDepth(); + if (baseDepth) { // duplicate the base expression + ASSERT(baseDepth <= MAX_UINT16); + if (baseDepth > 1) { + addOpAdjustDepth(DupNOp, baseDepth); + addShort((uint16)baseDepth); + } + else + addOp(DupOp); + readRef->emitCodeSequence(this); + // duplicate the value and bury it + if (baseDepth > 1) { + addOpAdjustDepth(DupInsertNOp, baseDepth); + addShort((uint16)baseDepth); + } + else + addOp(DupInsertOp); + } + else { + readRef->emitCodeSequence(this); + addOp(DupOp); + } + addOpStretchStack(DoUnaryOp, 1); + addByte(op); + writeRef->emitCodeSequence(this); + addOp(PopOp); // because the SetXXX will propogate the new value + delete readRef; + delete writeRef; + return Object_Type; + } + + case ExprNode::addEquals: + op = Plus; + goto BinaryOpEquals; + case ExprNode::subtractEquals: + op = Minus; + goto BinaryOpEquals; + case ExprNode::multiplyEquals: + op = Multiply; + goto BinaryOpEquals; + case ExprNode::divideEquals: + op = Divide; + goto BinaryOpEquals; + case ExprNode::moduloEquals: + op = Remainder; + goto BinaryOpEquals; + case ExprNode::leftShiftEquals: + op = ShiftLeft; + goto BinaryOpEquals; + case ExprNode::rightShiftEquals: + op = ShiftRight; + goto BinaryOpEquals; + case ExprNode::logicalRightShiftEquals: + op = UShiftRight; + goto BinaryOpEquals; + case ExprNode::bitwiseAndEquals: + op = BitAnd; + goto BinaryOpEquals; + case ExprNode::bitwiseXorEquals: + op = BitXor; + goto BinaryOpEquals; + case ExprNode::bitwiseOrEquals: + op = BitOr; + goto BinaryOpEquals; + +BinaryOpEquals: + { + BinaryExprNode *b = checked_cast(p); + Reference *readRef; + Reference *writeRef; + genReferencePair(b->op1, readRef, writeRef); + + int32 baseDepth = readRef->baseExpressionDepth(); + if (baseDepth) { // duplicate the base expression + ASSERT(baseDepth <= MAX_UINT16); + if (baseDepth > 1) { + addOp(DupNOp); + addShort((uint16)baseDepth); + } + else + addOp(DupOp); + } + if (writeRef->emitPreAssignment(this)) + addOp(SwapOp); + readRef->emitCodeSequence(this); + genExpr(b->op2); + addOp(DoOperatorOp); + addByte(op); + + if (writeRef->mType != Object_Type) { + addOp(LoadTypeOp); + addPointer(writeRef->mType); + addOp(CastOp); + } + + writeRef->emitCodeSequence(this); + delete readRef; + delete writeRef; + return Object_Type; + } + + + case ExprNode::logicalAndEquals: + { + BinaryExprNode *b = checked_cast(p); + Reference *readRef; + Reference *writeRef; + genReferencePair(b->op1, readRef, writeRef); + + int32 baseDepth = readRef->baseExpressionDepth(); + if (baseDepth) { // duplicate the base expression + ASSERT(baseDepth <= MAX_UINT16); + if (baseDepth > 1) { + addOpAdjustDepth(DupNOp, -baseDepth); + addShort((uint16)baseDepth); + } + else + addOp(DupOp); + } + + uint32 labelAfterSecondExpr = getLabel(); + if (writeRef->emitPreAssignment(this)) + addOp(SwapOp); + readRef->emitCodeSequence(this); + addOp(DupOp); + addOp(ToBooleanOp); + addOp(JumpFalseOp); + addFixup(labelAfterSecondExpr); + addOp(PopOp); + genExpr(b->op2); + setLabel(labelAfterSecondExpr); + + if (writeRef->mType != Object_Type) { + addOp(LoadTypeOp); + addPointer(writeRef->mType); + addOp(CastOp); + } + + writeRef->emitCodeSequence(this); + delete readRef; + delete writeRef; + return Object_Type; + } + + case ExprNode::logicalOrEquals: + { + BinaryExprNode *b = checked_cast(p); + Reference *readRef; + Reference *writeRef; + genReferencePair(b->op1, readRef, writeRef); + + int32 baseDepth = readRef->baseExpressionDepth(); + if (baseDepth) { // duplicate the base expression + ASSERT(baseDepth <= MAX_UINT16); + if (baseDepth > 1) { + addOpAdjustDepth(DupNOp, -baseDepth); + addShort((uint16)baseDepth); + } + else + addOp(DupOp); + } + + uint32 labelAfterSecondExpr = getLabel(); + if (writeRef->emitPreAssignment(this)) + addOp(SwapOp); + readRef->emitCodeSequence(this); + addOp(DupOp); + addOp(ToBooleanOp); + addOp(JumpTrueOp); + addFixup(labelAfterSecondExpr); + addOp(PopOp); + genExpr(b->op2); + setLabel(labelAfterSecondExpr); + + if (writeRef->mType != Object_Type) { + addOp(LoadTypeOp); + addPointer(writeRef->mType); + addOp(CastOp); + } + + writeRef->emitCodeSequence(this); + delete readRef; + delete writeRef; + return Object_Type; + } + + case ExprNode::logicalXorEquals: + { + BinaryExprNode *b = checked_cast(p); + Reference *readRef; + Reference *writeRef; + genReferencePair(b->op1, readRef, writeRef); + + int32 baseDepth = readRef->baseExpressionDepth(); + if (baseDepth) { // duplicate the base expression + ASSERT(baseDepth <= MAX_UINT16); + if (baseDepth > 1) { + addOpAdjustDepth(DupNOp, -baseDepth); + addShort((uint16)baseDepth); + } + else + addOp(DupOp); + } + + if (writeRef->emitPreAssignment(this)) + addOp(SwapOp); + readRef->emitCodeSequence(this); + genExpr(b->op2); + addOp(LogicalXorOp); + + if (writeRef->mType != Object_Type) { + addOp(LoadTypeOp); + addPointer(writeRef->mType); + addOp(CastOp); + } + + writeRef->emitCodeSequence(this); + delete readRef; + delete writeRef; + return Object_Type; + } + + case ExprNode::logicalAnd: + { + BinaryExprNode *b = checked_cast(p); + uint32 labelAfterSecondExpr = getLabel(); + genExpr(b->op1); + addOp(DupOp); + addOp(ToBooleanOp); + addOp(JumpFalseOp); + addFixup(labelAfterSecondExpr); + addOp(PopOp); + genExpr(b->op2); + setLabel(labelAfterSecondExpr); + return Object_Type; + } + case ExprNode::logicalXor: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + addOp(DupOp); + addOp(ToBooleanOp); + genExpr(b->op2); + addOp(DupInsertOp); + addOp(ToBooleanOp); + addOp(LogicalXorOp); + return Object_Type; + } + case ExprNode::logicalOr: + { + BinaryExprNode *b = checked_cast(p); + uint32 labelAfterSecondExpr = getLabel(); + genExpr(b->op1); + addOp(DupOp); + addOp(ToBooleanOp); + addOp(JumpTrueOp); + addFixup(labelAfterSecondExpr); + addOp(PopOp); + genExpr(b->op2); + setLabel(labelAfterSecondExpr); + return Object_Type; + } + + case ExprNode::logicalNot: + { + UnaryExprNode *u = checked_cast(p); + genExpr(u->op); + addOp(ToBooleanOp); + addOp(LogicalNotOp); + return Boolean_Type; + } + + case ExprNode::assignment: + { + BinaryExprNode *b = checked_cast(p); + Reference *ref = genReference(b->op1, Write); + if (ref == NULL) + m_cx->reportError(Exception::semanticError, "incomprehensible assignment designate (and error message)", p->pos); + if (ref->isConst()) + m_cx->reportError(Exception::semanticError, "assignment to const not allowed", p->pos); + + ref->emitPreAssignment(this); + genExpr(b->op2); + + if (ref->mType != Object_Type) { + addOp(LoadTypeOp); + addPointer(ref->mType); + addOp(CastOp); + } + + ref->emitCodeSequence(this); + delete ref; + return Object_Type; + } + case ExprNode::identifier: + { + Reference *ref = genReference(p, Read); + ref->emitCodeSequence(this); + JSType *type = ref->mType; + delete ref; + return type; + } + case ExprNode::This: + { + addOp(LoadThisOp); + JSType *theClass = mScopeChain->topClass(); + if (theClass) + return theClass; + else // XXX need to be able to detect illegal references to 'this' in non-prototype, non-member functions + return Object_Type; + } + case ExprNode::dot: + { + Reference *ref = genReference(p, Read); + ref->emitCodeSequence(this); + JSType *type = ref->mType; + delete ref; + return type; + } + case ExprNode::Delete: + { + UnaryExprNode *u = checked_cast(p); + Reference *ref = genReference(u->op, Read); + if (ref == NULL) + addOp(LoadConstantTrueOp); + else { + ref->emitDelete(this); + delete ref; + } + return Boolean_Type; + } + case ExprNode::Typeof: + { + UnaryExprNode *u = checked_cast(p); + Reference *ref = genReference(u->op, Read); + if (ref == NULL) { + genExpr(u->op); + addOp(TypeOfOp); + } + else { + ref->emitTypeOf(this); + delete ref; + } + return String_Type; + } + case ExprNode::New: + { + InvokeExprNode *i = checked_cast(p); + JSType *type = genExpr(i->op); + + ExprPairList *p = i->pairs; + int32 argCount = 0; + while (p) { + genExpr(p->value); + argCount++; + p = p->next; + } + // A NewInstanceOp actually ends up adding one more + // value onto the stack, before removing the arguments + // and type value + stretchStack(1); + addOpAdjustDepth(NewInstanceOp, -argCount); + addLong(toUInt32(argCount)); + return type; + } + case ExprNode::index: + { + Reference *ref = genReference(p, Read); + ref->emitCodeSequence(this); + delete ref; + return Object_Type; + } + case ExprNode::call: + { + InvokeExprNode *i = checked_cast(p); + Reference *ref = genReference(i->op, Read); + + ref->emitInvokeSequence(this); + + uint8 callFlags = 0; + + ExprPairList *p = i->pairs; + int32 argCount = 0; + while (p) { + genExpr(p->value); + if (p->field) { + callFlags |= NamedArguments; + genExpr(p->field); + addOp(NamedArgOp); + } + argCount++; + p = p->next; + } + + if (ref->needsThis()) { + addOpAdjustDepth(InvokeOp, -(argCount + 1)); + addLong(toUInt32(argCount)); + callFlags |= Explicit; + } + else { + addOpAdjustDepth(InvokeOp, -argCount); + addLong(toUInt32(argCount)); + callFlags |= NoThis; + } + if (i->isSuperInvoke) + callFlags |= SuperInvoke; + addByte(callFlags); + JSType *type = ref->mType; + delete ref; + return type; + } + case ExprNode::parentheses: + { + UnaryExprNode *u = checked_cast(p); + return genExpr(u->op); + } + case ExprNode::conditional: + { + uint32 falseConditionExpression = getLabel(); + uint32 labelAtBottom = getLabel(); + + TernaryExprNode *c = checked_cast(p); + genExpr(c->op1); + addOp(ToBooleanOp); + addOp(JumpFalseOp); + addFixup(falseConditionExpression); + genExpr(c->op2); + addOp(JumpOp); + addFixup(labelAtBottom); + setLabel(falseConditionExpression); + adjustStack(-1); // the true case will leave a stack entry pending + // but we can discard it since only path will be taken. + genExpr(c->op3); + setLabel(labelAtBottom); + return Object_Type; + } + case ExprNode::objectLiteral: + { + addOp(NewObjectOp); + PairListExprNode *plen = checked_cast(p); + ExprPairList *e = plen->pairs; + while (e) { + if (e->field && e->value && (e->field->getKind() == ExprNode::identifier)) { + addOp(DupOp); + genExpr(e->value); + addOp(SetPropertyOp); + addStringRef(checked_cast(e->field)->name); + addOp(PopOp); + } + e = e->next; + } + } + break; + case ExprNode::arrayLiteral: + { + addOp(LoadTypeOp); + addPointer(Array_Type); + addOpAdjustDepth(NewInstanceOp, 1); + addLong(0); + PairListExprNode *plen = checked_cast(p); + ExprPairList *e = plen->pairs; + int index = 0; + while (e) { + if (e->value) { + addOp(DupOp); + addOp(LoadConstantNumberOp); + addNumberRef(index); + genExpr(e->value); + addOp(SetElementOp); + addOp(PopOp); + } + index++; + e = e->next; + } + } + break; + case ExprNode::Is: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(IsOp); + } + break; + case ExprNode::Instanceof: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(InstanceOfOp); + } + break; + case ExprNode::As: + { + BinaryExprNode *b = checked_cast(p); + genExpr(b->op1); + genExpr(b->op2); + addOp(AsOp); + } + break; + case ExprNode::numUnit: + { + // turn the unit string into a function call into the + // Unit package, passing the number literal arguments + // Requires winding up a new lexer/parser chunk. For + // now we'll handle single units only + NumUnitExprNode *n = checked_cast(p); + + addOp(LoadTypeOp); + addPointer(Unit_Type); + addOp(GetInvokePropertyOp); + addStringRef(n->str); + addOp(LoadConstantNumberOp); + addNumberRef(n->num); + addOp(LoadConstantStringOp); + addStringRef(n->numStr); + addOpAdjustDepth(InvokeOp, -2); + addLong(2); + addOp(NoThis); + } + break; + case ExprNode::functionLiteral: + { + FunctionExprNode *f = checked_cast(p); + JSFunction *fnc = new JSFunction(NULL, mScopeChain); + + uint32 reqArgCount = 0; + uint32 optArgCount = 0; + + VariableBinding *b = f->function.parameters; + while ((b != f->function.optParameters) && (b != f->function.restParameter)) { + reqArgCount++; + b = b->next; + } + while (b != f->function.restParameter) { + optArgCount++; + b = b->next; + } + fnc->setArgCounts(reqArgCount, optArgCount, (f->function.restParameter != NULL)); + + m_cx->buildRuntimeForFunction(f->function, fnc); + ByteCodeGen bcg(m_cx, mScopeChain); + bcg.genCodeForFunction(f->function, f->pos, fnc, false, NULL); + addOp(LoadFunctionOp); + addPointer(fnc); + addOp(NewClosureOp); + // XXX more needed!!! how does this function get access to the + // local variables/parameters of all the functions above on the + // scope chain? + // Build a list of those functions now, then at 'NewClosureOp' time + // acquire a link for each function on the list to the current + // activation. That activation object has to survive when those functions + // terminate. + } + break; + case ExprNode::superStmt: + { + // we're in a class constructor - turn this into + // an invocation of the default constructor of the + // superclass + InvokeExprNode *i = checked_cast(p); + JSType *currentClass = mScopeChain->topClass(); + ASSERT(currentClass); + + addOp(LoadFunctionOp); + addPointer(currentClass->mSuperType->getDefaultConstructor()); + + ExprPairList *p = i->pairs; + int32 argCount = 1; + addOp(LoadThisOp); + while (p) { + genExpr(p->value); + argCount++; + p = p->next; + } + + addOpAdjustDepth(InvokeOp, -argCount); + addLong(toUInt32(argCount)); + addByte(Explicit | SuperInvoke); + return currentClass; + } + break; + case ExprNode::dotClass: + { + UnaryExprNode *u = checked_cast(p); + JSType *uType = genExpr(u->op); + addByte(ClassOp); + return uType; + } + break; + case ExprNode::juxtapose: + { + BinaryExprNode *j = checked_cast(p); + genExpr(j->op1); + genExpr(j->op2); + addOp(JuxtaposeOp); + } + break; + default: + NOT_REACHED("Not Implemented Yet"); + } + return NULL; +} + +uint32 printInstruction(Formatter &f, uint32 i, const ByteCodeModule& bcm) +{ + int32 offset; + uint8 op = bcm.mCodeBase[i]; + f << gByteCodeData[op].opName << " "; + i++; + switch (op) { + + case LoadConstantUndefinedOp: + case LoadConstantTrueOp: + case LoadConstantFalseOp: + case LoadConstantNullOp: + case LoadConstantZeroOp: + case LoadThisOp: + case GetTypeOp: + case CastOp: + case ReturnOp: + case ReturnVoidOp: + case GetConstructorOp: + case NewObjectOp: + case NewThisOp: + case TypeOfOp: + case InstanceOfOp: + case AsOp: + case IsOp: + case ToBooleanOp: + case RtsOp: + case WithinOp: + case WithoutOp: + case ThrowOp: + case HandlerOp: + case LogicalXorOp: + case LogicalNotOp: + case SwapOp: + case DupOp: + case DupInsertOp: + case PopOp: + case GetElementOp: + case SetElementOp: + case LoadGlobalObjectOp: + case NewClosureOp: + case ClassOp: + case JuxtaposeOp: + case NamedArgOp: + break; + + case DoUnaryOp: + case DoOperatorOp: + f << bcm.mCodeBase[i]; + i++; + break; + + case DupNOp: + case DupInsertNOp: + f << bcm.getShort(i); + i += 2; + break; + + case JumpOp: + case JumpTrueOp: + case JumpFalseOp: + offset = bcm.getOffset(i); + f << offset << " --> " << (i) + offset; + i += 4; + break; + + case InvokeOp: + f << bcm.getLong(i) << " " << bcm.mCodeBase[i + 4]; + i += 5; + break; + + case GetLocalVarOp: + case SetLocalVarOp: + case GetArgOp: + case SetArgOp: + case GetMethodOp: + case GetMethodRefOp: + case GetFieldOp: + case SetFieldOp: + case NewInstanceOp: + f << bcm.getLong(i); + i += 4; + break; + + case GetClosureVarOp: + case SetClosureVarOp: + f << bcm.getLong(i); + i += 4; + f << " " << bcm.getLong(i); + i += 4; + break; + + case GetNameOp: + case GetTypeOfNameOp: + case SetNameOp: + case GetPropertyOp: + case GetInvokePropertyOp: + case SetPropertyOp: + case LoadConstantStringOp: + case DeleteOp: + f << *bcm.getString(bcm.getLong(i)); + i += 4; + break; + + case LoadConstantNumberOp: + f << bcm.getNumber(bcm.getLong(i)); + i += 4; + break; + + case LoadTypeOp: + case LoadFunctionOp: + case PushScopeOp: + printFormat(f, "0x%X", bcm.getLong(i)); + i += 4; + break; + + case JsrOp: + offset = bcm.getOffset(i); + f << offset << " --> " << i + offset; + i += 4; + break; + + case TryOp: + offset = bcm.getOffset(i); + if (offset == -1) + f << "no finally; "; + else + f << "(finally) " << offset << " --> " << i + offset << "; "; + i += 4; + offset = bcm.getOffset(i); + if (offset == -1) + f << "no catch;"; + else + f << "(catch) " << offset << " --> " << i + offset; + i += 4; + break; + default: + printFormat(f, "Unknown Opcode 0x%X", bcm.mCodeBase[i]); + i++; + break; + } + f << "\n"; + return i; +} + +Formatter& operator<<(Formatter& f, const ByteCodeModule& bcm) +{ + uint32 i = 0; + while (i < bcm.mLength) { + printFormat(f, "%.4d ", i); + i = printInstruction(f, i, bcm); + } + return f; +} + + +} +} + diff --git a/js2/src/bytecodegen.h b/js2/src/bytecodegen.h new file mode 100644 index 00000000000..7093262f3d6 --- /dev/null +++ b/js2/src/bytecodegen.h @@ -0,0 +1,396 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the JavaScript 2 Prototype. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (the "GPL"), in which case the + * provisions of the GPL are applicable instead of those above. + * If you wish to allow use of your version of this file only + * under the terms of the GPL and not to allow others to use your + * version of this file under the NPL, indicate your decision by + * deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the NPL or the GPL. + */ + +#ifndef bytecodegen_h___ +#define bytecodegen_h___ + +#ifdef _WIN32 + // Turn off warnings about identifiers too long in browser information +#pragma warning(disable: 4786) +#endif + + +#include +#include + +#include "systemtypes.h" +#include "strings.h" + +#include "tracer.h" + +namespace JavaScript { +namespace JS2Runtime { + + typedef enum { + // 1st 2 bits specify what kind of 'this' exists + NoThis = 0x00, + Inherent = 0x01, + Explicit = 0x02, + ThisFlags = 0x03, + + // bit #3 indicates presence of named arguments + NamedArguments = 0x04, + + // but #4 is set for the invocation of the super constructor + // from inside a constructor + SuperInvoke = 0x08 + + } CallFlag; + +typedef enum { + + LoadConstantUndefinedOp,// --> + LoadConstantTrueOp, // --> + LoadConstantFalseOp, // --> + LoadConstantNullOp, // --> + LoadConstantZeroOp, // --> <+0.0 value object> + LoadConstantNumberOp, // --> + LoadConstantStringOp, // --> + LoadThisOp, // --> + LoadFunctionOp, // XXX !!! XXX + LoadTypeOp, // XXX !!! XXX + InvokeOp, // --> [] + GetTypeOp, // --> + CastOp, // --> + DoUnaryOp, // --> + DoOperatorOp, // --> + PushNullOp, // --> + PushIntOp, // --> + PushNumOp, // --> + PushStringOp, // --> + PushTypeOp, // + ReturnOp, // --> + ReturnVoidOp, // --> + GetConstructorOp, // --> + NewObjectOp, // --> + NewThisOp, // --> + NewInstanceOp, // --> + DeleteOp, // --> + TypeOfOp, // --> + InstanceOfOp, // --> + AsOp, // --> + IsOp, // --> + ToBooleanOp, // --> + JumpFalseOp, // --> + JumpTrueOp, // --> + JumpOp, // + TryOp, // + JsrOp, // + RtsOp, + WithinOp, // --> + WithoutOp, // + ThrowOp, // --> + HandlerOp, + LogicalXorOp, // --> + LogicalNotOp, // --> + SwapOp, // --> + DupOp, // --> + DupInsertOp, // --> + DupNOp, // --> { N times } + DupInsertNOp, // {xN} --> {xN} + PopOp, // --> + // for instance members + GetFieldOp, // --> + SetFieldOp, // --> + // for instance methods + GetMethodOp, // --> + GetMethodRefOp, // --> + // for argumentz + GetArgOp, // --> + SetArgOp, // --> + // for local variables in the immediate scope + GetLocalVarOp, // --> + SetLocalVarOp, // --> + // for local variables in the nth closure scope + GetClosureVarOp, // , --> + SetClosureVarOp, // , --> + // for array elements + GetElementOp, // --> + SetElementOp, // --> + // for properties + GetPropertyOp, // --> + GetInvokePropertyOp, // --> + SetPropertyOp, // --> + // for all generic names + GetNameOp, // --> + GetTypeOfNameOp, // --> + SetNameOp, // --> + LoadGlobalObjectOp, // --> + PushScopeOp, // XXX !!! XXX + PopScopeOp, // XXX !!! XXX + NewClosureOp, // --> + ClassOp, // --> + JuxtaposeOp, // --> + NamedArgOp, // --> + + OpCodeCount + +} ByteCodeOp; + +struct ByteCodeData { + int8 stackImpact; + char *opName; +}; +extern ByteCodeData gByteCodeData[OpCodeCount]; + + typedef std::pair PC_Position; + + + class ByteCodeModule { + public: + + ByteCodeModule(ByteCodeGen *bcg); + +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("ByteCodeModule", s, t); return t; } + void operator delete(void* t) { trace_release("ByteCodeModule", t); STD::free(t); } +#endif + + uint32 getLong(uint32 index) const { return *((uint32 *)&mCodeBase[index]); } + uint16 getShort(uint32 index) const { return *((uint16 *)&mCodeBase[index]); } + int32 getOffset(uint32 index) const { return *((int32 *)&mCodeBase[index]); } + const String *getString(uint32 index) const { return &mStringPoolContents[index]; } + float64 getNumber(uint32 index) const { return mNumberPoolContents[index]; } + + void setSource(const String &source, const String &sourceLocation) + { + mSource = source; + mSourceLocation = sourceLocation; + } + + String mSource; + String mSourceLocation; + + uint32 mLocalsCount; // number of local vars to allocate space for + uint32 mStackDepth; // max. depth of execution stack + + uint8 *mCodeBase; + uint32 mLength; + + String *mStringPoolContents; + float64 *mNumberPoolContents; + + PC_Position *mCodeMap; + uint32 mCodeMapLength; + + size_t getPositionForPC(uint32 pc); + + }; + Formatter& operator<<(Formatter& f, const ByteCodeModule& bcm); + + #define BufferIncrement (32) + +#define NotALabel ((uint32)(-1)) + + class Label { + public: + + typedef enum { InternalLabel, NamedLabel, BreakLabel, ContinueLabel } LabelKind; + + Label() : mKind(InternalLabel), mHasLocation(false) { } + Label(LabelStmtNode *lbl) : mKind(NamedLabel), mHasLocation(false), mLabelStmt(lbl) { } + Label(LabelKind kind) : mKind(kind), mHasLocation(false) { } + + bool matches(const StringAtom *name) + { + return ((mKind == NamedLabel) && (mLabelStmt->name.compare(*name) == 0)); + } + + bool matches(LabelKind kind) + { + return (mKind == kind); + } + + void addFixup(ByteCodeGen *bcg, uint32 branchLocation); + void setLocation(ByteCodeGen *bcg, uint32 location); + + std::vector mFixupList; + + LabelKind mKind; + bool mHasLocation; + LabelStmtNode *mLabelStmt; + + uint32 mLocation; + }; + + class ByteCodeGen { + public: + + ByteCodeGen(Context *cx, ScopeChain *scopeChain) + : mBuffer(new CodeBuffer), + mScopeChain(scopeChain), + mPC_Map(new CodeMap), + m_cx(cx), + mNamespaceList(NULL) , + mStackTop(0), + mStackMax(0) + { } + +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("ByteCodeGen", s, t); return t; } + void operator delete(void* t) { trace_release("ByteCodeGen", t); STD::free(t); } +#endif + + ByteCodeModule *genCodeForScript(StmtNode *p); + bool genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint32 finallyLabel); + void genCodeForFunction(FunctionDefinition &f, + size_t pos, + JSFunction *fnc, + bool isConstructor, + JSType *topClass); + ByteCodeModule *genCodeForExpression(ExprNode *p); + + JSType *genExpr(ExprNode *p); + Reference *genReference(ExprNode *p, Access acc); + void genReferencePair(ExprNode *p, Reference *&readRef, Reference *&writeRef); + + typedef std::vector CodeBuffer; + + typedef std::vector CodeMap; + + // this is the current code buffer + CodeBuffer *mBuffer; + ScopeChain *mScopeChain; + CodeMap *mPC_Map; + + Context *m_cx; + + std::vector