Fixed result value of for..in statement. Handling of user throws & runtime

errors combined. Various reader->pos bugs. Added Error & NativeError types.
Added escape & unescape. Fixed bugs in Array.sort & Date settors.
This commit is contained in:
rogerl%netscape.com 2001-11-05 18:05:37 +00:00
Родитель a88a53aec1
Коммит 033e700dd8
10 изменённых файлов: 471 добавлений и 158 удалений

Просмотреть файл

@ -331,6 +331,7 @@ ByteCodeData gByteCodeData[OpCodeCount] = {
{ -128, "DupInsertN", }, { -128, "DupInsertN", },
{ -1, "Pop", }, { -1, "Pop", },
{ -128, "PopN", }, { -128, "PopN", },
{ -1, "VoidPop", },
{ 0, "GetField", }, { 0, "GetField", },
{ -1, "SetField", }, { -1, "SetField", },
{ 1, "GetMethod", }, { 1, "GetMethod", },
@ -729,7 +730,8 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
VariableBinding *v = vs->bindings; VariableBinding *v = vs->bindings;
bool isStatic = (vs->attributeValue->mTrueFlags & Property::Static) == Property::Static; bool isStatic = (vs->attributeValue->mTrueFlags & Property::Static) == Property::Static;
while (v) { while (v) {
Reference *ref = mScopeChain->getName(*v->name, vs->attributeValue->mNamespaceList, Write); Reference *ref = v->scope->genReference(false, *v->name, vs->attributeValue->mNamespaceList, Write, 0);
// Reference *ref = mScopeChain->getName(*v->name, vs->attributeValue->mNamespaceList, Write);
ASSERT(ref); // must have been added previously by collectNames ASSERT(ref); // must have been added previously by collectNames
if (v->initializer) { if (v->initializer) {
if (isStatic && (static_cg != NULL)) { if (isStatic && (static_cg != NULL)) {
@ -903,7 +905,7 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
NOT_REACHED("what else??"); NOT_REACHED("what else??");
} }
if (value == NULL) if (value == NULL)
m_cx->reportError(Exception::referenceError, "Invalid for..in target"); m_cx->reportError(Exception::referenceError, "Invalid for..in target", p->pos);
iteratorReadRef->emitCodeSequence(this); iteratorReadRef->emitCodeSequence(this);
addOp(GetPropertyOp); addOp(GetPropertyOp);
addStringRef(m_cx->Value_StringAtom); addStringRef(m_cx->Value_StringAtom);
@ -925,7 +927,7 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
addLong(1); addLong(1);
addByte(Explicit); addByte(Explicit);
iteratorWriteRef->emitCodeSequence(this); iteratorWriteRef->emitCodeSequence(this);
addOp(PopOp); addOp(VoidPopOp);
setLabel(labelAtTestCondition); setLabel(labelAtTestCondition);
iteratorReadRef->emitCodeSequence(this); iteratorReadRef->emitCodeSequence(this);
@ -948,16 +950,7 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
addOp(PopOp); addOp(PopOp);
setLabel(labelAtEnd); setLabel(labelAtEnd);
/*
if (valueBaseDepth) {
if (valueBaseDepth > 1) {
addOpAdjustDepth(PopNOp, -valueBaseDepth);
addShort(valueBaseDepth);
}
else
addOp(PopOp);
}
*/
delete objectReadRef; delete objectReadRef;
delete objectWriteRef; delete objectWriteRef;
delete iteratorReadRef; delete iteratorReadRef;
@ -2366,6 +2359,9 @@ BinaryOpEquals:
return Object_Type; return Object_Type;
} }
break; break;
case ExprNode::regExp:
m_cx->reportError(Exception::internalError, "Regular expressions nyi", p->pos);
break;
default: default:
NOT_REACHED("Not Implemented Yet"); NOT_REACHED("Not Implemented Yet");
} }
@ -2409,6 +2405,7 @@ uint32 printInstruction(Formatter &f, uint32 i, const ByteCodeModule& bcm)
case DupOp: case DupOp:
case DupInsertOp: case DupInsertOp:
case PopOp: case PopOp:
case VoidPopOp:
case LoadGlobalObjectOp: case LoadGlobalObjectOp:
case NewClosureOp: case NewClosureOp:
case ClassOp: case ClassOp:

Просмотреть файл

@ -121,6 +121,7 @@ typedef enum {
DupInsertNOp, // <N> <N things> <object2> --> <object2> <N things> <object2> DupInsertNOp, // <N> <N things> <object2> --> <object2> <N things> <object2>
PopOp, // <object> --> PopOp, // <object> -->
PopNOp, // <N> <N things> --> PopNOp, // <N> <N things> -->
VoidPopOp, // <object>--> (doesn't cache result value)
// for instance members // for instance members
GetFieldOp, // <slot> <base> --> <object> GetFieldOp, // <slot> <base> --> <object>
SetFieldOp, // <slot> <base> <object> --> <object> SetFieldOp, // <slot> <base> <object> --> <object>

Просмотреть файл

@ -51,6 +51,7 @@ static const char *const kindStrings[] = {
"Type error", // Yype error "Type error", // Yype error
"Uncaught exception error", // uncaught exception error "Uncaught exception error", // uncaught exception error
"Semantic error", // semantic error "Semantic error", // semantic error
"User exception", // 'throw' from user code
}; };
// Return a null-terminated string describing the exception's kind. // Return a null-terminated string describing the exception's kind.

Просмотреть файл

@ -55,7 +55,8 @@ namespace JavaScript
rangeError, rangeError,
typeError, typeError,
uncaughtError, uncaughtError,
semanticError semanticError,
userException
}; };
Kind kind; // The exception's kind Kind kind; // The exception's kind

Просмотреть файл

@ -63,12 +63,6 @@ namespace JavaScript {
namespace JS2Runtime { namespace JS2Runtime {
class JSException {
public:
};
inline char narrow(char16 ch) { return char(ch); } inline char narrow(char16 ch) { return char(ch); }
JSValue Context::readEvalString(const String &str, const String& fileName, ScopeChain *scopeChain, const JSValue& thisValue) JSValue Context::readEvalString(const String &str, const String& fileName, ScopeChain *scopeChain, const JSValue& thisValue)
@ -78,24 +72,24 @@ JSValue Context::readEvalString(const String &str, const String& fileName, Scope
Arena a; Arena a;
Parser p(mWorld, a, mFlags, str, fileName); Parser p(mWorld, a, mFlags, str, fileName);
Reader *oldReader = mReader; Reader *oldReader = mReader;
mReader = &p.lexer.reader; setReader(&p.lexer.reader);
StmtNode *parsedStatements = p.parseProgram();
ASSERT(p.lexer.peek(true).hasKind(Token::end));
if (mDebugFlag)
{
PrettyPrinter f(stdOut, 30);
{
PrettyPrinter::Block b(f, 2);
f << "Program =";
f.linearBreak(1);
StmtNode::printStatements(f, parsedStatements);
}
f.end();
stdOut << '\n';
}
buildRuntime(parsedStatements);
JS2Runtime::ByteCodeModule* bcm = genCode(parsedStatements, fileName);
try { try {
StmtNode *parsedStatements = p.parseProgram();
ASSERT(p.lexer.peek(true).hasKind(Token::end));
if (mDebugFlag)
{
PrettyPrinter f(stdOut, 30);
{
PrettyPrinter::Block b(f, 2);
f << "Program =";
f.linearBreak(1);
StmtNode::printStatements(f, parsedStatements);
}
f.end();
stdOut << '\n';
}
buildRuntime(parsedStatements);
JS2Runtime::ByteCodeModule* bcm = genCode(parsedStatements, fileName);
if (bcm) { if (bcm) {
setReader(NULL); setReader(NULL);
bcm->setSource(str, fileName); bcm->setSource(str, fileName);
@ -103,10 +97,11 @@ JSValue Context::readEvalString(const String &str, const String& fileName, Scope
delete bcm; delete bcm;
} }
} }
catch (JSException *x) { catch (Exception &x) {
setReader(oldReader); setReader(oldReader);
throw x; throw x;
} }
setReader(oldReader);
return result; return result;
} }
@ -220,7 +215,7 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha
try { try {
result = interpret(pc, endPC); result = interpret(pc, endPC);
} }
catch (JSException *x) { catch (Exception &x) {
Activation *prev = mActivationStack.top(); Activation *prev = mActivationStack.top();
mActivationStack.pop(); mActivationStack.pop();
@ -420,7 +415,12 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
switch ((ByteCodeOp)(*pc++)) { switch ((ByteCodeOp)(*pc++)) {
case PopOp: case PopOp:
{ {
result = popValue(); // XXX debug only? - just decrement top result = popValue();
}
break;
case VoidPopOp:
{
popValue(); // XXX just decrement top
} }
break; break;
case PopNOp: case PopNOp:
@ -627,22 +627,18 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
if (target->isPrototype() && mThis.isNull()) if (target->isPrototype() && mThis.isNull())
mThis = JSValue(getGlobalObject()); mThis = JSValue(getGlobalObject());
JSValue *argBase = getBase(stackSize() - argCount);
if (!target->isNative()) { if (!target->isNative()) {
JSValue *argBase = buildArgumentBlock(target, argCount);
// XXX Optimize the more typical case in which there are no named arguments, no optional arguments // XXX Optimize the more typical case in which there are no named arguments, no optional arguments
// and the appropriate number of arguments have been supplied. // and the appropriate number of arguments have been supplied.
argBase = buildArgumentBlock(target, argCount);
mActivationStack.push(new Activation(mLocals, mStack, mStackTop - (cleanUp + 1), mActivationStack.push(new Activation(mLocals, mStack, mStackTop - (cleanUp + 1),
mScopeChain, mScopeChain,
mArgumentBase, oldThis, mArgumentBase, oldThis,
pc, mCurModule)); pc, mCurModule));
mScopeChain = target->getScopeChain(); mScopeChain = target->getScopeChain();
// if (mThis.isObject()) mScopeChain->addScope(target->getParameterBarrel());
// mScopeChain->addScope(mThis.object); mScopeChain->addScope(target->getActivation());
if (!target->isChecked()) { if (!target->isChecked()) {
JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this); JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this);
@ -651,9 +647,6 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
mScopeChain->defineVariable(this, Arguments_StringAtom, NULL, Array_Type, JSValue(args)); mScopeChain->defineVariable(this, Arguments_StringAtom, NULL, Array_Type, JSValue(args));
} }
mScopeChain->addScope(target->getParameterBarrel());
mScopeChain->addScope(target->getActivation());
mCurModule = target->getByteCode(); mCurModule = target->getByteCode();
pc = mCurModule->mCodeBase; pc = mCurModule->mCodeBase;
endPC = mCurModule->mCodeBase + mCurModule->mLength; endPC = mCurModule->mCodeBase + mCurModule->mLength;
@ -664,6 +657,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
mStackTop = 0; mStackTop = 0;
} }
else { else {
JSValue *argBase = getBase(stackSize() - argCount);
// native functions may still need access to information // native functions may still need access to information
// about the currently executing function. // about the currently executing function.
mActivationStack.push(new Activation(mLocals, mStack, mStackTop - (cleanUp + 1), mActivationStack.push(new Activation(mLocals, mStack, mStackTop - (cleanUp + 1),
@ -790,10 +784,10 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
uint32 index = *((uint32 *)pc); uint32 index = *((uint32 *)pc);
pc += sizeof(uint32); pc += sizeof(uint32);
const String &name = *mCurModule->getString(index); const String &name = *mCurModule->getString(index);
PropertyIterator it; if (mScopeChain->deleteName(this, name, CURRENT_ATTR))
if (mScopeChain->hasOwnProperty(name, CURRENT_ATTR, Read, &it)) pushValue(kTrueValue);
mScopeChain->deleteProperty(name, CURRENT_ATTR); else
pushValue(kTrueValue); pushValue(kFalseValue);
} }
break; break;
case DeleteOp: case DeleteOp:
@ -907,8 +901,18 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
else else
reportError(Exception::typeError, "InstanceOf couldn't find [[hasInstance]]"); reportError(Exception::typeError, "InstanceOf couldn't find [[hasInstance]]");
} }
else else {
reportError(Exception::typeError, "InstanceOf needs function object"); // XXX hack for <= ECMA 3 compatibility
if (t.isType()) {
if ((v.getType() == t.type)
|| v.getType()->derivesFrom(t.type))
pushValue(kTrueValue);
else
pushValue(kFalseValue);
}
else
reportError(Exception::typeError, "InstanceOf needs function object");
}
} }
break; break;
case GetNameOp: case GetNameOp:
@ -994,7 +998,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
try { try {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), argBase[0], argBase, argCount); result = interpret(target->getByteCode(), 0, target->getScopeChain(), argBase[0], argBase, argCount);
} }
catch (JSException *x) { catch (Exception &x) {
delete[] argBase; delete[] argBase;
throw x; throw x;
} }
@ -1050,7 +1054,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
try { try {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), *baseValue, argBase, argCount); result = interpret(target->getByteCode(), 0, target->getScopeChain(), *baseValue, argBase, argCount);
} }
catch (JSException *x) { catch (Exception &x) {
delete[] argBase; delete[] argBase;
throw x; throw x;
} }
@ -1105,7 +1109,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
try { try {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), kNullValue, argBase, argCount); result = interpret(target->getByteCode(), 0, target->getScopeChain(), kNullValue, argBase, argCount);
} }
catch (JSException *x) { catch (Exception &x) {
delete[] argBase; delete[] argBase;
throw x; throw x;
} }
@ -1341,12 +1345,12 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this); JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this);
for (uint32 i = 0; i < argCount; i++) for (uint32 i = 0; i < argCount; i++)
args->setProperty(this, *numberToString(i), NULL, argBase[i]); args->setProperty(this, *numberToString(i), NULL, argBase[i]);
mScopeChain->defineVariable(this, Arguments_StringAtom, NULL, Array_Type, JSValue(args)); target->getScopeChain()->defineVariable(this, Arguments_StringAtom, NULL, Array_Type, JSValue(args));
} }
try { try {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), newThis, argBase, argCount); result = interpret(target->getByteCode(), 0, target->getScopeChain(), newThis, argBase, argCount);
} }
catch (JSException *x) { catch (Exception &x) {
delete[] argBase; delete[] argBase;
throw x; throw x;
} }
@ -1553,41 +1557,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
break; break;
case ThrowOp: case ThrowOp:
{ {
throw new JSException(); throw Exception(Exception::userException, "");
/*
if (mTryStack.size() > 0) {
HandlerData *hndlr = (HandlerData *)mTryStack.top();
Activation *curAct = (mActivationStack.size() > 0) ? mActivationStack.top() : NULL;
if (curAct != hndlr->mActivation) {
ASSERT(mActivationStack.size() > 0);
Activation *prev;// = mActivationStack.top();
do {
prev = curAct;
if (prev->mPC == NULL) {
// Yikes! the exception is getting thrown across a re-invocation
// of the interpreter loop.
ASSERT(false);
}
mActivationStack.pop();
curAct = mActivationStack.top();
} while (hndlr->mActivation != curAct);
mCurModule = prev->mModule;
endPC = mCurModule->mCodeBase + mCurModule->mLength;
mLocals = prev->mLocals;
mStack = prev->mStack;
mStackTop = 1; // just the exception object remains
mStackMax = mCurModule->mStackDepth;
mArgumentBase = prev->mArgumentBase;
mThis = prev->mThis;
}
resizeStack(hndlr->mStackSize);
pc = hndlr->mPC;
pushValue(x);
}
else
reportError(Exception::uncaughtError, "No handler for throw");
*/
} }
break; break;
case ClassOp: case ClassOp:
@ -1651,11 +1621,38 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
reportError(Exception::internalError, "Bad Opcode"); reportError(Exception::internalError, "Bad Opcode");
} }
} }
catch (JSException *jsx) { catch (Exception &jsx) {
JSValue x = topValue();
if (mTryStack.size() > 0) { if (mTryStack.size() > 0) {
HandlerData *hndlr = (HandlerData *)mTryStack.top(); HandlerData *hndlr = (HandlerData *)mTryStack.top();
Activation *curAct = (mActivationStack.size() > 0) ? mActivationStack.top() : NULL; Activation *curAct = (mActivationStack.size() > 0) ? mActivationStack.top() : NULL;
JSValue x;
// make sure there's a JS object for the catch clause to work with
if (!jsx.hasKind(Exception::userException)) {
JSValue argv[1];
argv[0] = JSValue(new String(jsx.fullMessage()));
switch (jsx.kind) {
case Exception::syntaxError:
x = SyntaxError_Constructor(this, kNullValue, argv, 1);
break;
case Exception::referenceError:
x = ReferenceError_Constructor(this, kNullValue, argv, 1);
break;
case Exception::typeError:
x = TypeError_Constructor(this, kNullValue, argv, 1);
break;
case Exception::rangeError:
x = RangeError_Constructor(this, kNullValue, argv, 1);
break;
default:
x = Error_Constructor(this, kNullValue, argv, 1);
break;
}
}
else {
x = popValue();
}
if (curAct != hndlr->mActivation) { if (curAct != hndlr->mActivation) {
ASSERT(mActivationStack.size() > 0); ASSERT(mActivationStack.size() > 0);
Activation *prev;// = mActivationStack.top(); Activation *prev;// = mActivationStack.top();
@ -1673,7 +1670,6 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
endPC = mCurModule->mCodeBase + mCurModule->mLength; endPC = mCurModule->mCodeBase + mCurModule->mLength;
mLocals = prev->mLocals; mLocals = prev->mLocals;
mStack = prev->mStack; mStack = prev->mStack;
mStackTop = 1; // just the exception object remains
mStackMax = mCurModule->mStackDepth; mStackMax = mCurModule->mStackDepth;
mArgumentBase = prev->mArgumentBase; mArgumentBase = prev->mArgumentBase;
mThis = prev->mThis; mThis = prev->mThis;
@ -1684,10 +1680,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
pushValue(x); pushValue(x);
} }
else else
reportError(Exception::uncaughtError, "No handler for throw"); throw jsx; //reportError(Exception::uncaughtError, "No handler for throw");
}
catch (Exception x) {
throw x;
} }
} }
return result; return result;
@ -2259,7 +2252,7 @@ JSValue JSValue::valueToNumber(Context *cx, const JSValue& value)
return JSValue(stringToNumber(value.string)); return JSValue(stringToNumber(value.string));
case object_tag: case object_tag:
case function_tag: case function_tag:
return value.toPrimitive(cx, NoHint).toNumber(cx); return value.toPrimitive(cx, NumberHint).toNumber(cx);
case boolean_tag: case boolean_tag:
return JSValue((value.boolean) ? 1.0 : 0.0); return JSValue((value.boolean) ? 1.0 : 0.0);
case undefined_tag: case undefined_tag:
@ -2326,7 +2319,7 @@ JSValue JSValue::valueToString(Context *cx, const JSValue& value)
} }
if (target) if (target)
return cx->invokeFunction(target, value, NULL, 0); return cx->invokeFunction(target, value, NULL, 0);
throw new Exception(Exception::runtimeError, "toString"); // XXX cx->reportError(Exception::runtimeError, "toString"); // XXX
return kUndefinedValue; // keep compilers happy return kUndefinedValue; // keep compilers happy
} }
else else
@ -2396,7 +2389,7 @@ JSValue JSValue::toPrimitive(Context *cx, Hint hint) const
} }
} }
} }
throw new Exception(Exception::runtimeError, "toPrimitive"); // XXX cx->reportError(Exception::runtimeError, "toPrimitive"); // XXX
return kUndefinedValue; return kUndefinedValue;
} }

Просмотреть файл

@ -81,6 +81,13 @@ JSType *Attribute_Type;
JSType *NamedArgument_Type; JSType *NamedArgument_Type;
JSArrayType *Array_Type; JSArrayType *Array_Type;
JSType *Date_Type; JSType *Date_Type;
JSType *Error_Type;
JSType *EvalError_Type;
JSType *RangeError_Type;
JSType *ReferenceError_Type;
JSType *SyntaxError_Type;
JSType *TypeError_Type;
JSType *UriError_Type;
Attribute *Context::executeAttributes(ExprNode *attr) Attribute *Context::executeAttributes(ExprNode *attr)
@ -672,6 +679,18 @@ void ScopeChain::setNameValue(Context *cx, const String& name, AttributeStmtNode
cx->getGlobalObject()->defineVariable(cx, name, attr, Object_Type, v); cx->getGlobalObject()->defineVariable(cx, name, attr, Object_Type, v);
} }
bool ScopeChain::deleteName(Context *cx, const String& name, AttributeStmtNode *attr)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++)
{
PropertyIterator i;
if ((*s)->hasOwnProperty(name, names, Read, &i))
return (*s)->deleteProperty(name, names);
}
return true;
}
inline char narrow(char16 ch) { return char(ch); } inline char narrow(char16 ch) { return char(ch); }
JSObject *ScopeChain::getNameValue(Context *cx, const String& name, AttributeStmtNode *attr) JSObject *ScopeChain::getNameValue(Context *cx, const String& name, AttributeStmtNode *attr)
@ -989,8 +1008,8 @@ void ScopeChain::collectNames(StmtNode *p)
} }
} }
break; break;
case StmtNode::If:
case StmtNode::With: case StmtNode::With:
case StmtNode::If:
case StmtNode::DoWhile: case StmtNode::DoWhile:
case StmtNode::While: case StmtNode::While:
{ {
@ -1008,13 +1027,16 @@ void ScopeChain::collectNames(StmtNode *p)
case StmtNode::Try: case StmtNode::Try:
{ {
TryStmtNode *t = checked_cast<TryStmtNode *>(p); TryStmtNode *t = checked_cast<TryStmtNode *>(p);
collectNames(t->stmt);
if (t->catches) { if (t->catches) {
CatchClause *c = t->catches; CatchClause *c = t->catches;
while (c) { while (c) {
collectNames(c->stmt);
c->prop = defineVariable(m_cx, c->name, NULL, NULL); c->prop = defineVariable(m_cx, c->name, NULL, NULL);
c = c->next; c = c->next;
} }
} }
if (t->finally) collectNames(t->finally);
} }
break; break;
case StmtNode::For: case StmtNode::For:
@ -1046,6 +1068,7 @@ void ScopeChain::collectNames(StmtNode *p)
v->prop = defineStaticVariable(m_cx, *v->name, vs, NULL); v->prop = defineStaticVariable(m_cx, *v->name, vs, NULL);
else else
v->prop = defineVariable(m_cx, *v->name, vs, NULL); v->prop = defineVariable(m_cx, *v->name, vs, NULL);
v->scope = mScopeStack.back();
v = v->next; v = v->next;
} }
} }
@ -1590,17 +1613,20 @@ void Context::buildRuntimeForStmt(StmtNode *p)
case StmtNode::Try: case StmtNode::Try:
{ {
TryStmtNode *t = checked_cast<TryStmtNode *>(p); TryStmtNode *t = checked_cast<TryStmtNode *>(p);
buildRuntimeForStmt(t->stmt);
if (t->catches) { if (t->catches) {
CatchClause *c = t->catches; CatchClause *c = t->catches;
while (c) { while (c) {
buildRuntimeForStmt(c->stmt);
c->prop->mType = mScopeChain->extractType(c->type); c->prop->mType = mScopeChain->extractType(c->type);
c = c->next; c = c->next;
} }
} }
if (t->finally) buildRuntimeForStmt(t->finally);
} }
break; break;
case StmtNode::If:
case StmtNode::With: case StmtNode::With:
case StmtNode::If:
case StmtNode::DoWhile: case StmtNode::DoWhile:
case StmtNode::While: case StmtNode::While:
{ {
@ -1745,7 +1771,7 @@ static JSValue Object_Constructor(Context *cx, const JSValue& thisValue, JSValue
JSValue thatValue = thisValue; // incoming 'new this' potentially supplied by constructor sequence JSValue thatValue = thisValue; // incoming 'new this' potentially supplied by constructor sequence
if (argc != 0) { if (argc != 0) {
if (argv[0].isObject()) if (argv[0].isObject() || argv[0].isFunction() || argv[0].isType())
thatValue = argv[0]; thatValue = argv[0];
else else
if (argv[0].isString() || argv[0].isBool() || argv[0].isNumber()) if (argv[0].isString() || argv[0].isBool() || argv[0].isNumber())
@ -2030,6 +2056,14 @@ static JSValue Boolean_Constructor(Context *cx, const JSValue& thisValue, JSValu
return v; return v;
} }
static JSValue Boolean_TypeCast(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc == 0)
return kFalseValue;
else
return argv[0].toBoolean(cx);
}
static JSValue Boolean_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) static JSValue Boolean_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{ {
ASSERT(thisValue.isObject()); ASSERT(thisValue.isObject());
@ -2054,6 +2088,76 @@ static JSValue Boolean_valueOf(Context *cx, const JSValue& thisValue, JSValue *
return kFalseValue; return kFalseValue;
} }
static JSValue GenericError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc, JSType *errType)
{
JSValue v = thisValue;
if (v.isNull())
v = errType->newInstance(cx);
ASSERT(v.isObject());
JSObject *thisObj = v.object;
JSValue msg;
if (argc > 0)
msg = argv[0].toString(cx);
else
msg = JSValue(&cx->Empty_StringAtom);
thisObj->defineVariable(cx, cx->Message_StringAtom, NULL, Property::NoAttribute, String_Type, msg);
thisObj->defineVariable(cx, cx->Name_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(errType->mClassName));
return v;
}
JSValue Error_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, Error_Type);
}
static JSValue Error_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
ContextStackReplacement csr(cx);
ASSERT(thisValue.isObject());
JSObject *thisObj = thisValue.object;
if ((thisObj->mType != Error_Type) && !thisObj->mType->derivesFrom(Error_Type))
cx->reportError(Exception::typeError, "Error.toString can only be applied to Error objects");
thisObj->getProperty(cx, cx->Message_StringAtom, CURRENT_ATTR);
JSValue msg = cx->popValue();
thisObj->getProperty(cx, cx->Name_StringAtom, CURRENT_ATTR);
JSValue name = cx->popValue();
String *result = new String(*name.toString(cx).string + ":" + *msg.toString(cx).string);
return JSValue(result);
}
JSValue EvalError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, EvalError_Type);
}
JSValue RangeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, RangeError_Type);
}
JSValue ReferenceError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, ReferenceError_Type);
}
JSValue SyntaxError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, SyntaxError_Type);
}
JSValue TypeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, TypeError_Type);
}
JSValue UriError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, UriError_Type);
}
static JSValue ExtendAttribute_Invoke(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc) static JSValue ExtendAttribute_Invoke(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{ {
ASSERT(argc == 1); ASSERT(argc == 1);
@ -2065,15 +2169,18 @@ static JSValue ExtendAttribute_Invoke(Context * /*cx*/, const JSValue& /*thisVal
return JSValue(x); return JSValue(x);
} }
static JSValue GlobalObject_Eval(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) static JSValue GlobalObject_Eval(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{ {
if (!argv[0].isString()) if (argc > 0) {
return argv[0]; if (!argv[0].isString())
const String *sourceStr = argv[0].toString(cx).string; return argv[0];
const String *sourceStr = argv[0].toString(cx).string;
Activation *prev = cx->mActivationStack.top(); Activation *prev = cx->mActivationStack.top();
return cx->readEvalString(*sourceStr, widenCString("eval source"), cx->mScopeChain, prev->mThis); return cx->readEvalString(*sourceStr, widenCString("eval source"), cx->mScopeChain, prev->mThis);
}
return kUndefinedValue;
} }
static JSValue GlobalObject_ParseInt(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc) static JSValue GlobalObject_ParseInt(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
@ -2125,6 +2232,149 @@ static JSValue GlobalObject_isFinite(Context *cx, const JSValue& /*thisValue*/,
return kFalseValue; return kFalseValue;
} }
/*
* Stuff to emulate the old libmocha escape, which took a second argument
* giving the type of escape to perform. Retained for compatibility, and
* copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
*/
#define URL_XALPHAS ((uint8) 1)
#define URL_XPALPHAS ((uint8) 2)
#define URL_PATH ((uint8) 4)
static const uint8 urlCharType[256] =
/* Bit 0 xalpha -- the alphas
* Bit 1 xpalpha -- as xalpha but
* converts spaces to plus and plus to %20
* Bit 2 ... path -- as xalphas but doesn't escape '/'
*/
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
0, };
/* This matches the ECMA escape set when mask is 7 (default.) */
#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
/* See ECMA-262 15.1.2.4. */
static JSValue GlobalObject_escape(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc > 0) {
uint32 i, ni, length, newlength;
char16 ch;
const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
uint32 mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
if (argc > 1) {
float64 d = argv[1].toNumber(cx).f64;
if (!JSDOUBLE_IS_FINITE(d) || (mask = (uint32)d) != d || mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
cx->reportError(Exception::runtimeError, "Bad string mask for escape");
}
const String *str = argv[0].toString(cx).string;
const char16 *chars = str->begin();
length = newlength = str->size();
/* Take a first pass and see how big the result string will need to be. */
for (i = 0; i < length; i++) {
if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
continue;
if (ch < 256) {
if (mask == URL_XPALPHAS && ch == ' ')
continue; /* The character will be encoded as '+' */
newlength += 2; /* The character will be encoded as %XX */
} else {
newlength += 5; /* The character will be encoded as %uXXXX */
}
}
char16 *newchars = new char16[newlength + 1];
for (i = 0, ni = 0; i < length; i++) {
if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
newchars[ni++] = ch;
} else if (ch < 256) {
if (mask == URL_XPALPHAS && ch == ' ') {
newchars[ni++] = '+'; /* convert spaces to pluses */
} else {
newchars[ni++] = '%';
newchars[ni++] = digits[ch >> 4];
newchars[ni++] = digits[ch & 0xF];
}
} else {
newchars[ni++] = '%';
newchars[ni++] = 'u';
newchars[ni++] = digits[ch >> 12];
newchars[ni++] = digits[(ch & 0xF00) >> 8];
newchars[ni++] = digits[(ch & 0xF0) >> 4];
newchars[ni++] = digits[ch & 0xF];
}
}
ASSERT(ni == newlength);
newchars[newlength] = 0;
String *result = new String(newchars, newlength);
delete[] newchars;
return JSValue(result);
}
return JSValue(&cx->Undefined_StringAtom);
}
#undef IS_OK
/* See ECMA-262 15.1.2.5 */
static JSValue GlobalObject_unescape(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc > 0) {
uint32 i, ni;
char16 ch;
const String *str = argv[0].toString(cx).string;
const char16 *chars = str->begin();
uint32 length = str->size();
/* Don't bother allocating less space for the new string. */
char16 *newchars = new char16[length + 1];
ni = i = 0;
while (i < length) {
uint hexValue[4];
ch = chars[i++];
if (ch == '%') {
if (i + 1 < length &&
isASCIIHexDigit(chars[i], hexValue[0]) && isASCIIHexDigit(chars[i + 1], hexValue[1]))
{
ch = hexValue[0] * 16 + hexValue[1];
i += 2;
} else if (i + 4 < length && chars[i] == 'u' &&
isASCIIHexDigit(chars[i + 1], hexValue[0]) && isASCIIHexDigit(chars[i + 2], hexValue[1]) &&
isASCIIHexDigit(chars[i + 3], hexValue[2]) && isASCIIHexDigit(chars[i + 4], hexValue[3]))
{
ch = (((((hexValue[0] << 4)
+ hexValue[1]) << 4)
+ hexValue[2]) << 4)
+ hexValue[3];
i += 5;
}
}
newchars[ni++] = ch;
}
newchars[ni] = 0;
String *result = new String(newchars, ni);
delete[] newchars;
return JSValue(result);
}
return JSValue(&cx->Undefined_StringAtom);
}
JSFunction::JSFunction(Context *cx, JSType *resultType, ScopeChain *scopeChain) JSFunction::JSFunction(Context *cx, JSType *resultType, ScopeChain *scopeChain)
: JSObject(Function_Type), : JSObject(Function_Type),
@ -2276,20 +2526,27 @@ void Context::initBuiltins()
{ {
ClassDef builtInClasses[] = ClassDef builtInClasses[] =
{ {
{ "Object", Object_Constructor, &kUndefinedValue }, { "Object", Object_Constructor, &kUndefinedValue },
{ "Type", NULL, &kNullValue }, { "Type", NULL, &kNullValue },
{ "Function", Function_Constructor, &kNullValue }, { "Function", Function_Constructor, &kNullValue },
{ "Number", Number_Constructor, &kPositiveZero }, { "Number", Number_Constructor, &kPositiveZero },
{ "Integer", Integer_Constructor, &kPositiveZero }, { "Integer", Integer_Constructor, &kPositiveZero },
{ "String", String_Constructor, &kNullValue }, { "String", String_Constructor, &kNullValue },
{ "Array", Array_Constructor, &kNullValue }, { "Array", Array_Constructor, &kNullValue },
{ "Boolean", Boolean_Constructor, &kFalseValue }, { "Boolean", Boolean_Constructor, &kFalseValue },
{ "Void", NULL, &kUndefinedValue }, { "Void", NULL, &kUndefinedValue },
{ "Unit", NULL, &kNullValue }, { "Unit", NULL, &kNullValue },
{ "Attribute", NULL, &kNullValue }, { "Attribute", NULL, &kNullValue },
{ "NamedArgument", NULL, &kNullValue }, { "NamedArgument", NULL, &kNullValue },
{ "Date", Date_Constructor, &kPositiveZero }, { "Date", Date_Constructor, &kPositiveZero },
{ "Null", NULL, &kNullValue }, { "Null", NULL, &kNullValue },
{ "Error", Error_Constructor, &kNullValue },
{ "EvalError", EvalError_Constructor, &kNullValue },
{ "RangeError", RangeError_Constructor, &kNullValue },
{ "ReferenceError", ReferenceError_Constructor, &kNullValue },
{ "SyntaxError", SyntaxError_Constructor, &kNullValue },
{ "TypeError", TypeError_Constructor, &kNullValue },
{ "UriError", UriError_Constructor, &kNullValue },
}; };
Object_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[0].name)], NULL); Object_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[0].name)], NULL);
@ -2334,6 +2591,13 @@ void Context::initBuiltins()
NamedArgument_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[11].name)], Object_Type); NamedArgument_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[11].name)], Object_Type);
Date_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[12].name)], Object_Type); Date_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[12].name)], Object_Type);
Null_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[13].name)], Object_Type); Null_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[13].name)], Object_Type);
Error_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[14].name)], Object_Type);
EvalError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[15].name)], Error_Type);
RangeError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[16].name)], Error_Type);
ReferenceError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[17].name)], Error_Type);
SyntaxError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[18].name)], Error_Type);
TypeError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[19].name)], Error_Type);
UriError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[20].name)], Error_Type);
String_Type->defineVariable(this, FromCharCode_StringAtom, NULL, String_Type, JSValue(new JSFunction(this, String_fromCharCode, String_Type))); String_Type->defineVariable(this, FromCharCode_StringAtom, NULL, String_Type, JSValue(new JSFunction(this, String_fromCharCode, String_Type)));
@ -2374,6 +2638,12 @@ void Context::initBuiltins()
{ "valueOf", Number_Type, 0, Boolean_valueOf }, { "valueOf", Number_Type, 0, Boolean_valueOf },
{ NULL } { NULL }
}; };
ProtoFunDef errorProtos[] =
{
{ "toString", String_Type, 0, Error_toString },
{ "toSource", String_Type, 0, Error_toString },
{ NULL }
};
ASSERT(mGlobal); ASSERT(mGlobal);
*mGlobal = Object_Type->newInstance(this); *mGlobal = Object_Type->newInstance(this);
@ -2392,9 +2662,17 @@ void Context::initBuiltins()
initClass(NamedArgument_Type, &builtInClasses[11], NULL); initClass(NamedArgument_Type, &builtInClasses[11], NULL);
initClass(Date_Type, &builtInClasses[12], getDateProtos() ); initClass(Date_Type, &builtInClasses[12], getDateProtos() );
initClass(Null_Type, &builtInClasses[13], NULL); initClass(Null_Type, &builtInClasses[13], NULL);
initClass(Error_Type, &builtInClasses[14], new PrototypeFunctions(&errorProtos[0]));
initClass(EvalError_Type, &builtInClasses[15], new PrototypeFunctions(&errorProtos[0]));
initClass(RangeError_Type, &builtInClasses[16], new PrototypeFunctions(&errorProtos[0]));
initClass(ReferenceError_Type, &builtInClasses[17], new PrototypeFunctions(&errorProtos[0]));
initClass(SyntaxError_Type, &builtInClasses[18], new PrototypeFunctions(&errorProtos[0]));
initClass(TypeError_Type, &builtInClasses[19], new PrototypeFunctions(&errorProtos[0]));
initClass(UriError_Type, &builtInClasses[20], new PrototypeFunctions(&errorProtos[0]));
Type_Type->defineUnaryOperator(Index, new JSFunction(this, arrayMaker, Type_Type)); Type_Type->defineUnaryOperator(Index, new JSFunction(this, arrayMaker, Type_Type));
Object_Type->mTypeCast = new JSFunction(this, Object_Constructor, Object_Type);
Function_Type->mTypeCast = new JSFunction(this, Function_Constructor, Object_Type); Function_Type->mTypeCast = new JSFunction(this, Function_Constructor, Object_Type);
Number_Type->mTypeCast = new JSFunction(this, Number_TypeCast, Number_Type); Number_Type->mTypeCast = new JSFunction(this, Number_TypeCast, Number_Type);
@ -2403,12 +2681,16 @@ void Context::initBuiltins()
Array_Type->defineUnaryOperator(IndexEqual, new JSFunction(this, Array_SetElement, Object_Type)); Array_Type->defineUnaryOperator(IndexEqual, new JSFunction(this, Array_SetElement, Object_Type));
Array_Type->mTypeCast = new JSFunction(this, Array_Constructor, Array_Type); Array_Type->mTypeCast = new JSFunction(this, Array_Constructor, Array_Type);
Boolean_Type->mTypeCast = new JSFunction(this, Boolean_TypeCast, Boolean_Type);
Date_Type->mTypeCast = new JSFunction(this, Date_TypeCast, String_Type); Date_Type->mTypeCast = new JSFunction(this, Date_TypeCast, String_Type);
Date_Type->defineStaticMethod(this, widenCString("parse"), NULL, new JSFunction(this, Date_parse, Number_Type)); Date_Type->defineStaticMethod(this, widenCString("parse"), NULL, new JSFunction(this, Date_parse, Number_Type));
Date_Type->defineStaticMethod(this, widenCString("UTC"), NULL, new JSFunction(this, Date_UTC, Number_Type)); Date_Type->defineStaticMethod(this, widenCString("UTC"), NULL, new JSFunction(this, Date_UTC, Number_Type));
String_Type->mTypeCast = new JSFunction(this, String_TypeCast, String_Type); String_Type->mTypeCast = new JSFunction(this, String_TypeCast, String_Type);
Error_Type->mTypeCast = new JSFunction(this, Error_Constructor, Error_Type);
} }
@ -2509,6 +2791,15 @@ Context::Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags)
Infinity_StringAtom (world.identifiers["Infinity"]), Infinity_StringAtom (world.identifiers["Infinity"]),
Empty_StringAtom (world.identifiers[""]), Empty_StringAtom (world.identifiers[""]),
Arguments_StringAtom (world.identifiers["arguments"]), Arguments_StringAtom (world.identifiers["arguments"]),
Message_StringAtom (world.identifiers["message"]),
Name_StringAtom (world.identifiers["name"]),
Error_StringAtom (world.identifiers["Error"]),
EvalError_StringAtom (world.identifiers["EvalError"]),
RangeError_StringAtom (world.identifiers["RangeError"]),
ReferenceError_StringAtom (world.identifiers["ReferenceError"]),
SyntaxError_StringAtom (world.identifiers["SyntaxError"]),
TypeError_StringAtom (world.identifiers["TypeError"]),
UriError_StringAtom (world.identifiers["UriError"]),
mWorld(world), mWorld(world),
mScopeChain(NULL), mScopeChain(NULL),
@ -2596,6 +2887,8 @@ Context::Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags)
{ "parseFloat", Number_Type, 2, GlobalObject_ParseFloat }, { "parseFloat", Number_Type, 2, GlobalObject_ParseFloat },
{ "isNaN", Boolean_Type, 2, GlobalObject_isNaN }, { "isNaN", Boolean_Type, 2, GlobalObject_isNaN },
{ "isFinite", Boolean_Type, 2, GlobalObject_isFinite }, { "isFinite", Boolean_Type, 2, GlobalObject_isFinite },
{ "escape", String_Type, 1, GlobalObject_escape },
{ "unescape", String_Type, 1, GlobalObject_unescape },
}; };
for (i = 0; i < (sizeof(globalObjectFunctions) / sizeof(ProtoFunDef)); i++) { for (i = 0; i < (sizeof(globalObjectFunctions) / sizeof(ProtoFunDef)); i++) {
@ -2605,18 +2898,6 @@ Context::Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags)
getGlobalObject()->defineVariable(this, widenCString(globalObjectFunctions[i].name), (NamespaceList *)(NULL), Property::NoAttribute, globalObjectFunctions[i].result, JSValue(x)); getGlobalObject()->defineVariable(this, widenCString(globalObjectFunctions[i].name), (NamespaceList *)(NULL), Property::NoAttribute, globalObjectFunctions[i].result, JSValue(x));
} }
/*
x = new JSFunction(GlobalObject_Eval, Object_Type);
x->setArgCounts(1, 0, false);
x->setIsPrototype(true);
getGlobalObject()->defineVariable(this, Eval_StringAtom, (NamespaceList *)(NULL), Object_Type, JSValue(x));
x = new JSFunction(GlobalObject_ParseInt, Number_Type);
x->setArgCounts(2, 0, false);
x->setIsPrototype(true);
getGlobalObject()->defineVariable(this, widenCString("parseInt"), (NamespaceList *)(NULL), Object_Type, JSValue(x));
*/
getGlobalObject()->defineVariable(this, Undefined_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kUndefinedValue); getGlobalObject()->defineVariable(this, Undefined_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kUndefinedValue);
getGlobalObject()->defineVariable(this, NaN_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kNaNValue); getGlobalObject()->defineVariable(this, NaN_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kNaNValue);
getGlobalObject()->defineVariable(this, Infinity_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kPositiveInfinity); getGlobalObject()->defineVariable(this, Infinity_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kPositiveInfinity);

Просмотреть файл

@ -1083,12 +1083,14 @@ XXX ...couldn't get this to work...
return top->hasProperty(name, names, acc, p); return top->hasProperty(name, names, acc, p);
} }
bool deleteName(Context *cx, const String& name, AttributeStmtNode *attr);
/*
bool hasOwnProperty(const String& name, NamespaceList *names, Access acc, PropertyIterator *p) bool hasOwnProperty(const String& name, NamespaceList *names, Access acc, PropertyIterator *p)
{ {
JSObject *top = mScopeStack.back(); JSObject *top = mScopeStack.back();
return top->hasOwnProperty(name, names, acc, p); return top->hasOwnProperty(name, names, acc, p);
} }
*/
// delete a property from the top object (already know it's there) // delete a property from the top object (already know it's there)
bool deleteProperty(const String &name, NamespaceList *names) bool deleteProperty(const String &name, NamespaceList *names)
{ {
@ -1367,8 +1369,18 @@ XXX ...couldn't get this to work...
class Attribute;
// provide access to the Error object constructors so that runtime exceptions
// can be constructed for Javascript catches.
extern JSValue Error_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue EvalError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue RangeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue ReferenceError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue SyntaxError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue TypeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue UriError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
class Attribute;
class Context { class Context {
public: public:
@ -1444,6 +1456,15 @@ XXX ...couldn't get this to work...
StringAtom& Infinity_StringAtom; StringAtom& Infinity_StringAtom;
StringAtom& Empty_StringAtom; StringAtom& Empty_StringAtom;
StringAtom& Arguments_StringAtom; StringAtom& Arguments_StringAtom;
StringAtom& Message_StringAtom;
StringAtom& Name_StringAtom;
StringAtom& Error_StringAtom;
StringAtom& EvalError_StringAtom;
StringAtom& RangeError_StringAtom;
StringAtom& ReferenceError_StringAtom;
StringAtom& SyntaxError_StringAtom;
StringAtom& TypeError_StringAtom;
StringAtom& UriError_StringAtom;
void initBuiltins(); void initBuiltins();
void initClass(JSType *type, ClassDef *cdef, PrototypeFunctions *pdef); void initClass(JSType *type, ClassDef *cdef, PrototypeFunctions *pdef);
@ -1580,6 +1601,10 @@ XXX ...couldn't get this to work...
OperatorList mOperatorTable[OperatorCount]; OperatorList mOperatorTable[OperatorCount];
typedef String PackageName;
typedef std::vector<PackageName> PackageList;
PackageList mPackages; // the currently loaded packages, mPackages.back() is the current package
JSValue readEvalFile(const String& fileName); JSValue readEvalFile(const String& fileName);
JSValue readEvalString(const String &str, const String& fileName, ScopeChain *scopeChain, const JSValue& thisValue); JSValue readEvalString(const String &str, const String& fileName, ScopeChain *scopeChain, const JSValue& thisValue);

Просмотреть файл

@ -407,7 +407,7 @@ typedef struct QSortArgs {
CompareArgs *arg; CompareArgs *arg;
} QSortArgs; } QSortArgs;
static int sort_compare(JSValue *a, JSValue *b, CompareArgs *arg); static int32 sort_compare(JSValue *a, JSValue *b, CompareArgs *arg);
static void static void
js_qsort_r(QSortArgs *qa, int lo, int hi) js_qsort_r(QSortArgs *qa, int lo, int hi)
@ -472,15 +472,15 @@ static void js_qsort(JSValue *vec, size_t nel, CompareArgs *arg)
delete(pivot); delete(pivot);
} }
static int sort_compare(JSValue *a, JSValue *b, CompareArgs *arg) static int32 sort_compare(JSValue *a, JSValue *b, CompareArgs *arg)
{ {
JSValue av = *(const JSValue *)a; JSValue av = *(const JSValue *)a;
JSValue bv = *(const JSValue *)b; JSValue bv = *(const JSValue *)b;
CompareArgs *ca = (CompareArgs *) arg; CompareArgs *ca = (CompareArgs *) arg;
Context *cx = ca->context; Context *cx = ca->context;
int32 result;
if (ca->target == NULL) { if (ca->target == NULL) {
int32 result;
if (av.isUndefined() || bv.isUndefined()) { if (av.isUndefined() || bv.isUndefined()) {
/* Put undefined properties at the end. */ /* Put undefined properties at the end. */
result = (av.isUndefined()) ? 1 : -1; result = (av.isUndefined()) ? 1 : -1;
@ -496,8 +496,16 @@ static int sort_compare(JSValue *a, JSValue *b, CompareArgs *arg)
JSValue argv[2]; JSValue argv[2];
argv[0] = av; argv[0] = av;
argv[1] = bv; argv[1] = bv;
JSValue result = cx->invokeFunction(ca->target, kNullValue, argv, 2); JSValue v = cx->invokeFunction(ca->target, kNullValue, argv, 2);
return (int32)(result.toInt32(cx).f64); float64 f = v.toNumber(cx).f64;
if (JSDOUBLE_IS_NaN(f) || (f == 0))
result = 0;
else
if (f > 0)
result = 1;
else
result = -1;
return result;
} }
} }
@ -509,14 +517,15 @@ static JSValue Array_sort(Context *cx, const JSValue& thisValue, JSValue *argv,
CompareArgs ca; CompareArgs ca;
ca.context = cx; ca.context = cx;
ca.target = NULL;
if (argc > 0) { if (argc > 0) {
if (!argv[0].isFunction()) if (!argv[0].isUndefined()) {
cx->reportError(Exception::typeError, "sort needs a compare function"); if (!argv[0].isFunction())
ca.target = argv[0].function; cx->reportError(Exception::typeError, "sort needs a compare function");
ca.target = argv[0].function;
}
} }
else
ca.target = NULL;
JSObject *thisObj = thisValue.object; JSObject *thisObj = thisValue.object;
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);

Просмотреть файл

@ -375,8 +375,10 @@ static JSValue Date_makeTime(Context *cx, const JSValue& thisValue, JSValue *arg
if (JSDOUBLE_IS_NaN(result)) if (JSDOUBLE_IS_NaN(result))
return kNaNValue; return kNaNValue;
if (argc == 0) if (argc == 0) {
argc = 1; /* should be safe, because length of all settors is 1 */ *date = nan;
return kNaNValue;
}
else if (argc > maxargs) else if (argc > maxargs)
argc = maxargs; /* clamp argc */ argc = maxargs; /* clamp argc */
@ -439,8 +441,10 @@ static JSValue Date_makeDate(Context *cx, const JSValue& thisValue, JSValue *arg
result = *date; result = *date;
/* see complaint about ECMA in date_MakeTime */ /* see complaint about ECMA in date_MakeTime */
if (argc == 0) if (argc == 0) {
argc = 1; /* should be safe, because length of all settors is 1 */ *date = nan;
return kNaNValue;
}
else if (argc > maxargs) else if (argc > maxargs)
argc = maxargs; /* clamp argc */ argc = maxargs; /* clamp argc */

Просмотреть файл

@ -126,6 +126,7 @@ namespace JavaScript {
bool constant; // true for const variables and parameters bool constant; // true for const variables and parameters
JS2Runtime::Property *prop; // the sematics/codegen passes stuff their data in here. JS2Runtime::Property *prop; // the sematics/codegen passes stuff their data in here.
JS2Runtime::JSObject *scope; // ditto
VariableBinding(size_t pos, const StringAtom *name, ExprNode *type, ExprNode *initializer, bool constant): VariableBinding(size_t pos, const StringAtom *name, ExprNode *type, ExprNode *initializer, bool constant):
ParseNode(pos), next(0), name(name), type(type), initializer(initializer), constant(constant) {} ParseNode(pos), next(0), name(name), type(type), initializer(initializer), constant(constant) {}