Mozilla/mozilla/js2/src/bytecodegen.cpp
rogerl%netscape.com 32a2685e57 Adding Date package.
git-svn-id: svn://10.0.0.236/trunk@102135 18797224-902f-48f8-a5cc-f745e15eee43
2001-09-01 01:10:57 +00:00

2490 lines
77 KiB
C++

/* -*- 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 <string.h>
// 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->addOpAdjustDepth(GetElementOp, -mDepth);
else
bcg->addOpAdjustDepth(SetElementOp, -(mDepth + 1));
bcg->addShort(mDepth);
}
void ElementReference::emitDelete(ByteCodeGen *bcg)
{
bcg->addOpAdjustDepth(DeleteElementOp, -mDepth);
bcg->addShort(mDepth);
}
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", },
{ -128, "GetElement", },
{ -128, "SetElement", },
{ -128, "DeleteElement" },
{ 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, JSFunction *f)
{
mFunction = f;
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<String>::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<float64>::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<uint32>::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<uint32>::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<uint32>::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<IdentifierExprNode *>(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<ExprStmtNode *>(b->statements);
if (e->expr->getKind() == ExprNode::call) {
InvokeExprNode *i = checked_cast<InvokeExprNode *>(e->expr);
if (i->op->getKind() == ExprNode::dot) {
// check for 'this.m()'
BinaryExprNode *b = checked_cast<BinaryExprNode *>(i->op);
if ((b->op1->getKind() == ExprNode::This) && (b->op2->getKind() == ExprNode::identifier)) {
// IdentifierExprNode *i = checked_cast<IdentifierExprNode *>(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<IdentifierExprNode *>(i->op)->name;
if (superClass->mClassName->compare(name) == 0)
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, fnc);
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, NULL);
}
ByteCodeModule *ByteCodeGen::genCodeForExpression(ExprNode *p)
{
genExpr(p);
addOp(PopOp);
return new ByteCodeModule(this, NULL);
}
// 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<ClassStmtNode *>(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, NULL);
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, NULL);
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<VariableStmtNode *>(p);
VariableBinding *v = vs->bindings;
bool isStatic = (vs->attributeValue->mTrueFlags & Property::Static) == Property::Static;
while (v) {
Reference *ref = mScopeChain->getName(*v->name, vs->attributeValue->mNamespaceList, 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<FunctionStmtNode *>(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<UnaryStmtNode *>(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<UnaryStmtNode *>(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<ForStmtNode *>(p);
Reference *value = NULL;
if (f->initializer->getKind() == StmtNode::Var) {
VariableStmtNode *vs = checked_cast<VariableStmtNode *>(f->initializer);
VariableBinding *v = vs->bindings;
value = mScopeChain->getName(*v->name, CURRENT_ATTR, Write);
}
else {
if (f->initializer->getKind() == StmtNode::expression) {
ExprStmtNode *e = checked_cast<ExprStmtNode *>(f->initializer);
value = genReference(e->expr, Write);
}
else
NOT_REACHED("what else??");
}
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
<statement body>
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(m_cx, objectReadRef, objectWriteRef, Object_Type);
mScopeChain->defineTempVariable(m_cx, iteratorReadRef, iteratorWriteRef, Object_Type);
genExpr(f->expr2);
objectWriteRef->emitCodeSequence(this);
addOp(GetInvokePropertyOp);
// addIdentifierRef(widenCString("Iterator"), widenCString("forin"));
addStringRef(widenCString("forin"));
addOpAdjustDepth(InvokeOp, -1);
addLong(0);
addByte(Explicit);
iteratorWriteRef->emitCodeSequence(this);
addOp(PopOp);
addOp(JumpOp);
addFixup(labelAtTestCondition);
setLabel(labelAtTopOfBlock);
iteratorReadRef->emitCodeSequence(this);
addOp(GetPropertyOp);
addStringRef(widenCString("value"));
value->emitCodeSequence(this);
addOp(PopOp);
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(GetInvokePropertyOp);
addStringRef(widenCString("next"));
iteratorReadRef->emitCodeSequence(this);
addOpAdjustDepth(InvokeOp, -2);
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(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;
}
break;
case StmtNode::For:
{
ForStmtNode *f = checked_cast<ForStmtNode *>(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<LabelStmtNode *>(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<GoStmtNode *>(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<GoStmtNode *>(p);
addOp(JumpOp);
if (g->name)
addFixup(getTopLabel(Label::ContinueLabel, g->name));
else
addFixup(getTopLabel(Label::ContinueLabel));
}
break;
case StmtNode::Switch:
{
/*
<swexpr>
SetVarOp <switchTemp>
Pop
// test sequence in source order except
// the default is moved to end.
GetVarOp <switchTemp>
<case1expr>
Equal
JumpTrue --> case1StmtLabel
GetVarOp <switchTemp>
<case2expr>
Equal
JumpTrue --> case2StmtLabel
Jump --> default, if there is one, or break label
case1StmtLabel:
<stmt>
case2StmtLabel:
<stmt>
defaultLabel:
<stmt>
case3StmtLabel:
<stmt>
..etc.. // all in source order
breakLabel:
*/
uint32 breakLabel = getLabel(Label::BreakLabel);
uint32 defaultLabel = toUInt32(-1);
Reference *switchTempReadRef, *switchTempWriteRef;
mScopeChain->defineTempVariable(m_cx, switchTempReadRef, switchTempWriteRef, Object_Type);
SwitchStmtNode *sw = checked_cast<SwitchStmtNode *>(p);
genExpr(sw->expr);
switchTempWriteRef->emitCodeSequence(this);
addOp(PopOp);
StmtNode *s = sw->statements;
while (s) {
if (s->getKind() == StmtNode::Case) {
ExprStmtNode *c = checked_cast<ExprStmtNode *>(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<ExprStmtNode *>(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<UnaryStmtNode *>(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<BinaryStmtNode *>(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<BlockStmtNode *>(p);
StmtNode *s = b->statements;
while (s) {
result = genCodeForStatement(s, static_cg, finallyLabel);
s = s->next;
}
}
break;
case StmtNode::Return:
{
ExprStmtNode *e = checked_cast<ExprStmtNode *>(p);
if (e->expr) {
genExpr(e->expr);
JSFunction *container = mScopeChain->getContainerFunction();
if (container) {
if (container->getResultType() != Object_Type) {
addOp(LoadTypeOp);
addPointer(container->getResultType());
addOp(CastOp);
}
}
ASSERT(mStackTop == 1);
addOpSetDepth(ReturnOp, 0);
}
else {
ASSERT(mStackTop == 0);
addOpSetDepth(ReturnVoidOp, 0);
}
result = true;
}
break;
case StmtNode::expression:
{
ExprStmtNode *e = checked_cast<ExprStmtNode *>(p);
genExpr(e->expr);
addOp(PopOp);
}
break;
case StmtNode::empty:
/* nada */
break;
case StmtNode::Throw:
{
ExprStmtNode *e = checked_cast<ExprStmtNode *>(p);
genExpr(e->expr);
addOpSetDepth(ThrowOp, 0);
}
break;
case StmtNode::With:
{
UnaryStmtNode *w = checked_cast<UnaryStmtNode *>(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
<tryblock>
} // 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<TryStmtNode *>(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<UseStmtNode *>(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<InvokeExprNode *>(p);
genExpr(i->op);
ExprPairList *p = i->pairs;
uint16 dimCount = 0;
while (p) {
genExpr(p->value);
dimCount++;
p = p->next;
}
Reference *ref = new ElementReference(acc, dimCount);
return ref;
}
case ExprNode::identifier:
{
const StringAtom &name = checked_cast<IdentifierExprNode *>(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<BinaryExprNode *>(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<IdentifierExprNode *>(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<QualifyExprNode *>(b->op2);
ASSERT(qe->qualifier->getKind() == ExprNode::identifier); // XXX handle more complex...
const StringAtom &fieldName = checked_cast<IdentifierExprNode *>(b->op2)->name;
const StringAtom &qualifierName = checked_cast<IdentifierExprNode *>(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<IdentifierExprNode *>(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<IdentifierExprNode *>(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<InvokeExprNode *>(p);
genExpr(i->op);
ExprPairList *p = i->pairs;
uint16 dimCount = 0;
while (p) {
genExpr(p->value);
dimCount++;
p = p->next;
}
readRef = new ElementReference(Read, dimCount);
writeRef = new ElementReference(Write, dimCount);
}
break;
case ExprNode::dot:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
JSType *lType = NULL;
if (b->op1->getKind() == ExprNode::identifier) {
const StringAtom &name = checked_cast<IdentifierExprNode *>(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<IdentifierExprNode *>(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<BooleanExprNode *>(p)->value ? LoadConstantTrueOp : LoadConstantFalseOp);
return Boolean_Type;
case ExprNode::Null:
addOp(LoadConstantNullOp);
return Object_Type;
case ExprNode::number :
addOp(LoadConstantNumberOp);
addNumberRef(checked_cast<NumberExprNode *>(p)->value);
return Number_Type;
case ExprNode::string :
addOp(LoadConstantStringOp);
addStringRef(checked_cast<StringExprNode *>(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<BinaryExprNode *>(p);
genExpr(b->op1);
genExpr(b->op2);
addOp(DoOperatorOp);
addByte(op);
return Object_Type;
}
case ExprNode::notEqual:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
genExpr(b->op1);
genExpr(b->op2);
addOp(DoOperatorOp);
addByte(Equal);
addOp(LogicalNotOp);
return Object_Type;
}
case ExprNode::greaterThan:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
genExpr(b->op1);
genExpr(b->op2);
addOp(SwapOp);
addOp(DoOperatorOp);
addByte(Less);
return Object_Type;
}
case ExprNode::greaterThanOrEqual:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
genExpr(b->op1);
genExpr(b->op2);
addOp(SwapOp);
addOp(DoOperatorOp);
addByte(LessEqual);
return Object_Type;
}
case ExprNode::notIdentical:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
genExpr(b->op1);
genExpr(b->op2);
addOp(DoOperatorOp);
addByte(SpittingImage);
addOp(LogicalNotOp);
return Object_Type;
}
case ExprNode::minus:
{
UnaryExprNode *u = checked_cast<UnaryExprNode *>(p);
genExpr(u->op);
addOp(DoUnaryOp);
addByte(Negate);
return Object_Type;
}
case ExprNode::plus:
{
UnaryExprNode *u = checked_cast<UnaryExprNode *>(p);
genExpr(u->op);
addOp(DoUnaryOp);
addByte(Posate);
return Object_Type;
}
case ExprNode::complement:
{
UnaryExprNode *u = checked_cast<UnaryExprNode *>(p);
genExpr(u->op);
addOp(DoUnaryOp);
addByte(Complement);
return Object_Type;
}
case ExprNode::preIncrement:
// op = Increment;
op = Plus;
goto PreXcrement;
case ExprNode::preDecrement:
// op = Decrement;
op = Minus;
goto PreXcrement;
PreXcrement:
{
UnaryExprNode *u = checked_cast<UnaryExprNode *>(p);
Reference *readRef;
Reference *writeRef;
genReferencePair(u->op, readRef, writeRef);
uint16 baseDepth = readRef->baseExpressionDepth();
if (baseDepth) { // duplicate the base expression
if (baseDepth > 1) {
addOpAdjustDepth(DupNOp, -baseDepth);
addShort(baseDepth);
}
else
addOp(DupOp);
}
readRef->emitCodeSequence(this);
addOp(DoUnaryOp);
addByte(Posate);
addOp(LoadConstantNumberOp);
addNumberRef(1.0);
addOp(DoOperatorOp);
addByte(op);
writeRef->emitCodeSequence(this);
delete readRef;
delete writeRef;
return Object_Type;
}
case ExprNode::postIncrement:
// op = Increment;
op = Plus;
goto PostXcrement;
case ExprNode::postDecrement:
// op = Decrement;
op = Minus;
goto PostXcrement;
PostXcrement:
{
UnaryExprNode *u = checked_cast<UnaryExprNode *>(p);
Reference *readRef;
Reference *writeRef;
genReferencePair(u->op, readRef, writeRef);
uint16 baseDepth = readRef->baseExpressionDepth();
if (baseDepth) { // duplicate the base expression
if (baseDepth > 1) {
addOpAdjustDepth(DupNOp, baseDepth);
addShort(baseDepth);
}
else
addOp(DupOp);
readRef->emitCodeSequence(this);
// duplicate the value and bury it
if (baseDepth > 1) {
addOpAdjustDepth(DupInsertNOp, baseDepth);
addShort(baseDepth);
}
else
addOp(DupInsertOp);
}
else {
readRef->emitCodeSequence(this);
addOp(DupOp);
}
addOp(DoUnaryOp);
addByte(Posate);
addOp(LoadConstantNumberOp);
addNumberRef(1.0);
addOp(DoOperatorOp);
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<BinaryExprNode *>(p);
Reference *readRef;
Reference *writeRef;
genReferencePair(b->op1, readRef, writeRef);
uint16 baseDepth = readRef->baseExpressionDepth();
if (baseDepth) { // duplicate the base expression
if (baseDepth > 1) {
addOp(DupNOp);
addShort(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<BinaryExprNode *>(p);
Reference *readRef;
Reference *writeRef;
genReferencePair(b->op1, readRef, writeRef);
uint16 baseDepth = readRef->baseExpressionDepth();
if (baseDepth) { // duplicate the base expression
if (baseDepth > 1) {
addOpAdjustDepth(DupNOp, -baseDepth);
addShort(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<BinaryExprNode *>(p);
Reference *readRef;
Reference *writeRef;
genReferencePair(b->op1, readRef, writeRef);
uint16 baseDepth = readRef->baseExpressionDepth();
if (baseDepth) { // duplicate the base expression
if (baseDepth > 1) {
addOpAdjustDepth(DupNOp, -baseDepth);
addShort(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<BinaryExprNode *>(p);
Reference *readRef;
Reference *writeRef;
genReferencePair(b->op1, readRef, writeRef);
uint16 baseDepth = readRef->baseExpressionDepth();
if (baseDepth) { // duplicate the base expression
if (baseDepth > 1) {
addOpAdjustDepth(DupNOp, -baseDepth);
addShort(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<BinaryExprNode *>(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<BinaryExprNode *>(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<BinaryExprNode *>(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<UnaryExprNode *>(p);
genExpr(u->op);
addOp(ToBooleanOp);
addOp(LogicalNotOp);
return Boolean_Type;
}
case ExprNode::assignment:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(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:
{
JSFunction *f = mScopeChain->getContainerFunction();
JSType *theClass;
if (f)
theClass = f->getClass();
else
theClass = mScopeChain->topClass();
// 'this' is legal in prototype functions
// and at the script top-level
if ( ((f == NULL) && theClass)
|| ((theClass == NULL) && f && !f->isPrototype()) )
m_cx->reportError(Exception::referenceError, "Illegal use of 'this'", p->pos);
addOp(LoadThisOp);
if (theClass)
return theClass;
else
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<UnaryExprNode *>(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<UnaryExprNode *>(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<InvokeExprNode *>(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<InvokeExprNode *>(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<UnaryExprNode *>(p);
return genExpr(u->op);
}
case ExprNode::conditional:
{
uint32 falseConditionExpression = getLabel();
uint32 labelAtBottom = getLabel();
TernaryExprNode *c = checked_cast<TernaryExprNode *>(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<PairListExprNode *>(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<IdentifierExprNode *>(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<PairListExprNode *>(p);
ExprPairList *e = plen->pairs;
int index = 0;
while (e) {
if (e->value) {
addOp(DupOp);
addOp(LoadConstantNumberOp);
addNumberRef(index);
genExpr(e->value);
addOpAdjustDepth(SetElementOp, -2);
addShort(1);
addOp(PopOp);
}
index++;
e = e->next;
}
}
break;
case ExprNode::Is:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
genExpr(b->op1);
genExpr(b->op2);
addOp(IsOp);
}
break;
case ExprNode::Instanceof:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
genExpr(b->op1);
genExpr(b->op2);
addOp(InstanceOfOp);
}
break;
case ExprNode::As:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(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<NumUnitExprNode *>(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<FunctionExprNode *>(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));
if (mScopeChain->isPossibleUncheckedFunction(&f->function))
fnc->setIsPrototype(true);
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<InvokeExprNode *>(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<UnaryExprNode *>(p);
JSType *uType = genExpr(u->op);
addByte(ClassOp);
return uType;
}
break;
case ExprNode::juxtapose:
{
BinaryExprNode *j = checked_cast<BinaryExprNode *>(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 LoadGlobalObjectOp:
case NewClosureOp:
case ClassOp:
case JuxtaposeOp:
case NamedArgOp:
break;
case DeleteElementOp:
case GetElementOp:
case SetElementOp:
{
uint16 u = bcm.getShort(i);
printFormat(f, "%hu", u);
i += 2;
}
break;
case DoUnaryOp:
case DoOperatorOp:
f << bcm.mCodeBase[i];
i++;
break;
case DupNOp:
case DupInsertNOp:
{
uint16 u = bcm.getShort(i);
printFormat(f, "%hu", u);
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;
}
}
}