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", },
{ -1, "Pop", },
{ -128, "PopN", },
{ -1, "VoidPop", },
{ 0, "GetField", },
{ -1, "SetField", },
{ 1, "GetMethod", },
@ -729,7 +730,8 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
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);
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
if (v->initializer) {
if (isStatic && (static_cg != NULL)) {
@ -903,7 +905,7 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
NOT_REACHED("what else??");
}
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);
addOp(GetPropertyOp);
addStringRef(m_cx->Value_StringAtom);
@ -925,7 +927,7 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
addLong(1);
addByte(Explicit);
iteratorWriteRef->emitCodeSequence(this);
addOp(PopOp);
addOp(VoidPopOp);
setLabel(labelAtTestCondition);
iteratorReadRef->emitCodeSequence(this);
@ -948,16 +950,7 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3
addOp(PopOp);
setLabel(labelAtEnd);
/*
if (valueBaseDepth) {
if (valueBaseDepth > 1) {
addOpAdjustDepth(PopNOp, -valueBaseDepth);
addShort(valueBaseDepth);
}
else
addOp(PopOp);
}
*/
delete objectReadRef;
delete objectWriteRef;
delete iteratorReadRef;
@ -2366,6 +2359,9 @@ BinaryOpEquals:
return Object_Type;
}
break;
case ExprNode::regExp:
m_cx->reportError(Exception::internalError, "Regular expressions nyi", p->pos);
break;
default:
NOT_REACHED("Not Implemented Yet");
}
@ -2409,6 +2405,7 @@ uint32 printInstruction(Formatter &f, uint32 i, const ByteCodeModule& bcm)
case DupOp:
case DupInsertOp:
case PopOp:
case VoidPopOp:
case LoadGlobalObjectOp:
case NewClosureOp:
case ClassOp:

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

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

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

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

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

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

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

@ -63,12 +63,6 @@ namespace JavaScript {
namespace JS2Runtime {
class JSException {
public:
};
inline char narrow(char16 ch) { return char(ch); }
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;
Parser p(mWorld, a, mFlags, str, fileName);
Reader *oldReader = mReader;
mReader = &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);
setReader(&p.lexer.reader);
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) {
setReader(NULL);
bcm->setSource(str, fileName);
@ -103,10 +97,11 @@ JSValue Context::readEvalString(const String &str, const String& fileName, Scope
delete bcm;
}
}
catch (JSException *x) {
catch (Exception &x) {
setReader(oldReader);
throw x;
}
setReader(oldReader);
return result;
}
@ -220,7 +215,7 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha
try {
result = interpret(pc, endPC);
}
catch (JSException *x) {
catch (Exception &x) {
Activation *prev = mActivationStack.top();
mActivationStack.pop();
@ -420,7 +415,12 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
switch ((ByteCodeOp)(*pc++)) {
case PopOp:
{
result = popValue(); // XXX debug only? - just decrement top
result = popValue();
}
break;
case VoidPopOp:
{
popValue(); // XXX just decrement top
}
break;
case PopNOp:
@ -627,22 +627,18 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
if (target->isPrototype() && mThis.isNull())
mThis = JSValue(getGlobalObject());
JSValue *argBase = getBase(stackSize() - argCount);
if (!target->isNative()) {
JSValue *argBase = buildArgumentBlock(target, argCount);
// 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.
argBase = buildArgumentBlock(target, argCount);
mActivationStack.push(new Activation(mLocals, mStack, mStackTop - (cleanUp + 1),
mScopeChain,
mArgumentBase, oldThis,
pc, mCurModule));
mScopeChain = target->getScopeChain();
// if (mThis.isObject())
// mScopeChain->addScope(mThis.object);
mScopeChain->addScope(target->getParameterBarrel());
mScopeChain->addScope(target->getActivation());
if (!target->isChecked()) {
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->addScope(target->getParameterBarrel());
mScopeChain->addScope(target->getActivation());
mCurModule = target->getByteCode();
pc = mCurModule->mCodeBase;
endPC = mCurModule->mCodeBase + mCurModule->mLength;
@ -664,6 +657,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
mStackTop = 0;
}
else {
JSValue *argBase = getBase(stackSize() - argCount);
// native functions may still need access to information
// about the currently executing function.
mActivationStack.push(new Activation(mLocals, mStack, mStackTop - (cleanUp + 1),
@ -790,10 +784,10 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
uint32 index = *((uint32 *)pc);
pc += sizeof(uint32);
const String &name = *mCurModule->getString(index);
PropertyIterator it;
if (mScopeChain->hasOwnProperty(name, CURRENT_ATTR, Read, &it))
mScopeChain->deleteProperty(name, CURRENT_ATTR);
pushValue(kTrueValue);
if (mScopeChain->deleteName(this, name, CURRENT_ATTR))
pushValue(kTrueValue);
else
pushValue(kFalseValue);
}
break;
case DeleteOp:
@ -907,8 +901,18 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
else
reportError(Exception::typeError, "InstanceOf couldn't find [[hasInstance]]");
}
else
reportError(Exception::typeError, "InstanceOf needs function object");
else {
// 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;
case GetNameOp:
@ -994,7 +998,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
try {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), argBase[0], argBase, argCount);
}
catch (JSException *x) {
catch (Exception &x) {
delete[] argBase;
throw x;
}
@ -1050,7 +1054,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
try {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), *baseValue, argBase, argCount);
}
catch (JSException *x) {
catch (Exception &x) {
delete[] argBase;
throw x;
}
@ -1105,7 +1109,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
try {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), kNullValue, argBase, argCount);
}
catch (JSException *x) {
catch (Exception &x) {
delete[] argBase;
throw x;
}
@ -1341,12 +1345,12 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this);
for (uint32 i = 0; i < argCount; 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 {
result = interpret(target->getByteCode(), 0, target->getScopeChain(), newThis, argBase, argCount);
}
catch (JSException *x) {
catch (Exception &x) {
delete[] argBase;
throw x;
}
@ -1553,41 +1557,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
break;
case ThrowOp:
{
throw new JSException();
/*
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");
*/
throw Exception(Exception::userException, "");
}
break;
case ClassOp:
@ -1651,11 +1621,38 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
reportError(Exception::internalError, "Bad Opcode");
}
}
catch (JSException *jsx) {
JSValue x = topValue();
catch (Exception &jsx) {
if (mTryStack.size() > 0) {
HandlerData *hndlr = (HandlerData *)mTryStack.top();
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) {
ASSERT(mActivationStack.size() > 0);
Activation *prev;// = mActivationStack.top();
@ -1673,7 +1670,6 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
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;
@ -1684,10 +1680,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
pushValue(x);
}
else
reportError(Exception::uncaughtError, "No handler for throw");
}
catch (Exception x) {
throw x;
throw jsx; //reportError(Exception::uncaughtError, "No handler for throw");
}
}
return result;
@ -2259,7 +2252,7 @@ JSValue JSValue::valueToNumber(Context *cx, const JSValue& value)
return JSValue(stringToNumber(value.string));
case object_tag:
case function_tag:
return value.toPrimitive(cx, NoHint).toNumber(cx);
return value.toPrimitive(cx, NumberHint).toNumber(cx);
case boolean_tag:
return JSValue((value.boolean) ? 1.0 : 0.0);
case undefined_tag:
@ -2326,7 +2319,7 @@ JSValue JSValue::valueToString(Context *cx, const JSValue& value)
}
if (target)
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
}
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;
}

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

@ -81,6 +81,13 @@ JSType *Attribute_Type;
JSType *NamedArgument_Type;
JSArrayType *Array_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)
@ -672,6 +679,18 @@ void ScopeChain::setNameValue(Context *cx, const String& name, AttributeStmtNode
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); }
JSObject *ScopeChain::getNameValue(Context *cx, const String& name, AttributeStmtNode *attr)
@ -989,8 +1008,8 @@ void ScopeChain::collectNames(StmtNode *p)
}
}
break;
case StmtNode::If:
case StmtNode::With:
case StmtNode::If:
case StmtNode::DoWhile:
case StmtNode::While:
{
@ -1008,13 +1027,16 @@ void ScopeChain::collectNames(StmtNode *p)
case StmtNode::Try:
{
TryStmtNode *t = checked_cast<TryStmtNode *>(p);
collectNames(t->stmt);
if (t->catches) {
CatchClause *c = t->catches;
while (c) {
collectNames(c->stmt);
c->prop = defineVariable(m_cx, c->name, NULL, NULL);
c = c->next;
}
}
if (t->finally) collectNames(t->finally);
}
break;
case StmtNode::For:
@ -1046,6 +1068,7 @@ void ScopeChain::collectNames(StmtNode *p)
v->prop = defineStaticVariable(m_cx, *v->name, vs, NULL);
else
v->prop = defineVariable(m_cx, *v->name, vs, NULL);
v->scope = mScopeStack.back();
v = v->next;
}
}
@ -1590,17 +1613,20 @@ void Context::buildRuntimeForStmt(StmtNode *p)
case StmtNode::Try:
{
TryStmtNode *t = checked_cast<TryStmtNode *>(p);
buildRuntimeForStmt(t->stmt);
if (t->catches) {
CatchClause *c = t->catches;
while (c) {
buildRuntimeForStmt(c->stmt);
c->prop->mType = mScopeChain->extractType(c->type);
c = c->next;
}
}
if (t->finally) buildRuntimeForStmt(t->finally);
}
break;
case StmtNode::If:
case StmtNode::With:
case StmtNode::If:
case StmtNode::DoWhile:
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
if (argc != 0) {
if (argv[0].isObject())
if (argv[0].isObject() || argv[0].isFunction() || argv[0].isType())
thatValue = argv[0];
else
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;
}
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*/)
{
ASSERT(thisValue.isObject());
@ -2054,6 +2088,76 @@ static JSValue Boolean_valueOf(Context *cx, const JSValue& thisValue, JSValue *
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)
{
ASSERT(argc == 1);
@ -2065,15 +2169,18 @@ static JSValue ExtendAttribute_Invoke(Context * /*cx*/, const JSValue& /*thisVal
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())
return argv[0];
const String *sourceStr = argv[0].toString(cx).string;
if (argc > 0) {
if (!argv[0].isString())
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)
@ -2125,6 +2232,149 @@ static JSValue GlobalObject_isFinite(Context *cx, const JSValue& /*thisValue*/,
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)
: JSObject(Function_Type),
@ -2276,20 +2526,27 @@ void Context::initBuiltins()
{
ClassDef builtInClasses[] =
{
{ "Object", Object_Constructor, &kUndefinedValue },
{ "Type", NULL, &kNullValue },
{ "Function", Function_Constructor, &kNullValue },
{ "Number", Number_Constructor, &kPositiveZero },
{ "Integer", Integer_Constructor, &kPositiveZero },
{ "String", String_Constructor, &kNullValue },
{ "Array", Array_Constructor, &kNullValue },
{ "Boolean", Boolean_Constructor, &kFalseValue },
{ "Void", NULL, &kUndefinedValue },
{ "Unit", NULL, &kNullValue },
{ "Attribute", NULL, &kNullValue },
{ "NamedArgument", NULL, &kNullValue },
{ "Date", Date_Constructor, &kPositiveZero },
{ "Null", NULL, &kNullValue },
{ "Object", Object_Constructor, &kUndefinedValue },
{ "Type", NULL, &kNullValue },
{ "Function", Function_Constructor, &kNullValue },
{ "Number", Number_Constructor, &kPositiveZero },
{ "Integer", Integer_Constructor, &kPositiveZero },
{ "String", String_Constructor, &kNullValue },
{ "Array", Array_Constructor, &kNullValue },
{ "Boolean", Boolean_Constructor, &kFalseValue },
{ "Void", NULL, &kUndefinedValue },
{ "Unit", NULL, &kNullValue },
{ "Attribute", NULL, &kNullValue },
{ "NamedArgument", NULL, &kNullValue },
{ "Date", Date_Constructor, &kPositiveZero },
{ "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);
@ -2334,6 +2591,13 @@ void Context::initBuiltins()
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);
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)));
@ -2374,6 +2638,12 @@ void Context::initBuiltins()
{ "valueOf", Number_Type, 0, Boolean_valueOf },
{ NULL }
};
ProtoFunDef errorProtos[] =
{
{ "toString", String_Type, 0, Error_toString },
{ "toSource", String_Type, 0, Error_toString },
{ NULL }
};
ASSERT(mGlobal);
*mGlobal = Object_Type->newInstance(this);
@ -2392,9 +2662,17 @@ void Context::initBuiltins()
initClass(NamedArgument_Type, &builtInClasses[11], NULL);
initClass(Date_Type, &builtInClasses[12], getDateProtos() );
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));
Object_Type->mTypeCast = new JSFunction(this, Object_Constructor, Object_Type);
Function_Type->mTypeCast = new JSFunction(this, Function_Constructor, Object_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->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->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));
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"]),
Empty_StringAtom (world.identifiers[""]),
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),
mScopeChain(NULL),
@ -2596,6 +2887,8 @@ Context::Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags)
{ "parseFloat", Number_Type, 2, GlobalObject_ParseFloat },
{ "isNaN", Boolean_Type, 2, GlobalObject_isNaN },
{ "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++) {
@ -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));
}
/*
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, NaN_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kNaNValue);
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);
}
bool deleteName(Context *cx, const String& name, AttributeStmtNode *attr);
/*
bool hasOwnProperty(const String& name, NamespaceList *names, Access acc, PropertyIterator *p)
{
JSObject *top = mScopeStack.back();
return top->hasOwnProperty(name, names, acc, p);
}
*/
// delete a property from the top object (already know it's there)
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 {
public:
@ -1444,6 +1456,15 @@ XXX ...couldn't get this to work...
StringAtom& Infinity_StringAtom;
StringAtom& Empty_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 initClass(JSType *type, ClassDef *cdef, PrototypeFunctions *pdef);
@ -1580,6 +1601,10 @@ XXX ...couldn't get this to work...
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 readEvalString(const String &str, const String& fileName, ScopeChain *scopeChain, const JSValue& thisValue);

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

@ -407,7 +407,7 @@ typedef struct QSortArgs {
CompareArgs *arg;
} QSortArgs;
static int sort_compare(JSValue *a, JSValue *b, CompareArgs *arg);
static int32 sort_compare(JSValue *a, JSValue *b, CompareArgs *arg);
static void
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);
}
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 bv = *(const JSValue *)b;
CompareArgs *ca = (CompareArgs *) arg;
Context *cx = ca->context;
int32 result;
if (ca->target == NULL) {
int32 result;
if (av.isUndefined() || bv.isUndefined()) {
/* Put undefined properties at the end. */
result = (av.isUndefined()) ? 1 : -1;
@ -496,8 +496,16 @@ static int sort_compare(JSValue *a, JSValue *b, CompareArgs *arg)
JSValue argv[2];
argv[0] = av;
argv[1] = bv;
JSValue result = cx->invokeFunction(ca->target, kNullValue, argv, 2);
return (int32)(result.toInt32(cx).f64);
JSValue v = cx->invokeFunction(ca->target, kNullValue, argv, 2);
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;
ca.context = cx;
ca.target = NULL;
if (argc > 0) {
if (!argv[0].isFunction())
cx->reportError(Exception::typeError, "sort needs a compare function");
ca.target = argv[0].function;
if (!argv[0].isUndefined()) {
if (!argv[0].isFunction())
cx->reportError(Exception::typeError, "sort needs a compare function");
ca.target = argv[0].function;
}
}
else
ca.target = NULL;
JSObject *thisObj = thisValue.object;
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))
return kNaNValue;
if (argc == 0)
argc = 1; /* should be safe, because length of all settors is 1 */
if (argc == 0) {
*date = nan;
return kNaNValue;
}
else if (argc > maxargs)
argc = maxargs; /* clamp argc */
@ -439,8 +441,10 @@ static JSValue Date_makeDate(Context *cx, const JSValue& thisValue, JSValue *arg
result = *date;
/* see complaint about ECMA in date_MakeTime */
if (argc == 0)
argc = 1; /* should be safe, because length of all settors is 1 */
if (argc == 0) {
*date = nan;
return kNaNValue;
}
else if (argc > maxargs)
argc = maxargs; /* clamp argc */

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

@ -126,6 +126,7 @@ namespace JavaScript {
bool constant; // true for const variables and parameters
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):
ParseNode(pos), next(0), name(name), type(type), initializer(initializer), constant(constant) {}