diff --git a/js2/src/bytecodegen.cpp b/js2/src/bytecodegen.cpp index 6c2fa24f6e12..4e872bf6d515 100644 --- a/js2/src/bytecodegen.cpp +++ b/js2/src/bytecodegen.cpp @@ -99,6 +99,7 @@ void ClosureVarReference::emitCodeSequence(ByteCodeGen *bcg) void StaticFieldReference::emitImplicitLoad(ByteCodeGen *bcg) { bcg->addOp(LoadTypeOp); + ASSERT(mClass); bcg->addPointer(mClass); } @@ -577,6 +578,7 @@ void ByteCodeGen::genCodeForFunction(FunctionDefinition &f, size_t pos, JSFuncti // incoming 'this' is null // addOp(LoadTypeOp); + ASSERT(topClass); addPointer(topClass); addOp(NewThisOp); // @@ -1207,6 +1209,7 @@ bool ByteCodeGen::genCodeForStatement(StmtNode *p, ByteCodeGen *static_cg, uint3 genExpr(e->expr); if (container->getResultType() != Object_Type) { addOp(LoadTypeOp); + ASSERT(container->getResultType()); addPointer(container->getResultType()); addOp(CastOp); } @@ -1892,6 +1895,7 @@ BinaryOpEquals: if (writeRef->mType != Object_Type) { addOp(LoadTypeOp); + ASSERT(writeRef->mType); addPointer(writeRef->mType); addOp(CastOp); } @@ -1934,6 +1938,7 @@ BinaryOpEquals: if (writeRef->mType != Object_Type) { addOp(LoadTypeOp); + ASSERT(writeRef->mType); addPointer(writeRef->mType); addOp(CastOp); } @@ -1975,6 +1980,7 @@ BinaryOpEquals: if (writeRef->mType != Object_Type) { addOp(LoadTypeOp); + ASSERT(writeRef->mType); addPointer(writeRef->mType); addOp(CastOp); } @@ -2010,6 +2016,7 @@ BinaryOpEquals: if (writeRef->mType != Object_Type) { addOp(LoadTypeOp); + ASSERT(writeRef->mType); addPointer(writeRef->mType); addOp(CastOp); } @@ -2084,6 +2091,7 @@ BinaryOpEquals: if (ref->mType != Object_Type) { addOp(LoadTypeOp); + ASSERT(ref->mType); addPointer(ref->mType); addOp(CastOp); } @@ -2279,6 +2287,7 @@ BinaryOpEquals: case ExprNode::arrayLiteral: { addOp(LoadTypeOp); + ASSERT(Array_Type); addPointer(Array_Type); addOpAdjustDepth(NewInstanceOp, 0); addLong(0); @@ -2336,6 +2345,7 @@ BinaryOpEquals: NumUnitExprNode *n = checked_cast(p); addOp(LoadTypeOp); + ASSERT(Unit_Type); addPointer(Unit_Type); addOp(GetInvokePropertyOp); addStringRef(n->str); @@ -2352,6 +2362,7 @@ BinaryOpEquals: { FunctionExprNode *f = checked_cast(p); JSFunction *fnc = new JSFunction(m_cx, NULL, mScopeChain); + fnc->setResultType(Object_Type); fnc->countParameters(m_cx, f->function); if (mScopeChain->isPossibleUncheckedFunction(f->function)) fnc->setIsPrototype(true); @@ -2435,9 +2446,9 @@ BinaryOpEquals: m_cx->mCurModule = &errorHandlingModule; reValue = RegExp_Constructor(m_cx, reValue, args, 2); m_cx->mCurModule = curModule; - ASSERT(reValue.isObject()); + ASSERT(reValue.isInstance()); addOp(LoadConstantRegExpOp); - addPointer(reValue.object); + addPointer(reValue.instance); return RegExp_Type; } break; diff --git a/js2/src/js2execution.cpp b/js2/src/js2execution.cpp index a04ee092a5c7..bf02fffee94a 100644 --- a/js2/src/js2execution.cpp +++ b/js2/src/js2execution.cpp @@ -132,8 +132,8 @@ bool Context::executeOperator(Operator op, JSType *t1, JSType *t2) // look in the operator table for applicable operators OperatorList applicableOperators; - for (OperatorList::iterator oi = mOperatorTable[op].begin(), - end = mOperatorTable[op].end(); + for (OperatorList::iterator oi = mBinaryOperatorTable[op].begin(), + end = mBinaryOperatorTable[op].end(); (oi != end); oi++) { if ((*oi)->isApplicable(t1, t2)) { @@ -146,12 +146,13 @@ bool Context::executeOperator(Operator op, JSType *t1, JSType *t2) OperatorList::iterator candidate = applicableOperators.begin(); for (OperatorList::iterator aoi = applicableOperators.begin() + 1, aend = applicableOperators.end(); - (aoi != aend); aoi++) + (aoi != aend); aoi++) { if ((*aoi)->mType1->derivesFrom((*candidate)->mType1) || ((*aoi)->mType2->derivesFrom((*candidate)->mType2))) candidate = aoi; } + // XXX how to complain if there is more than one best fit? JSFunction *target = (*candidate)->mImp; @@ -320,7 +321,7 @@ JSValue *Context::buildArgumentBlock(JSFunction *target, uint32 &argCount) if (target->parameterIsRequired(pIndex)) { if (inArgIndex < argCount) { JSValue v = getValue(inArgIndex + argStart); - bool isPositionalArg = !(v.isObject() && (v.object->mType == NamedArgument_Type)); + bool isPositionalArg = !v.isNamedArg(); // if the next argument is named, then we've run out of positional args if (!isPositionalArg) { if (!target->isChecked()) @@ -340,7 +341,7 @@ JSValue *Context::buildArgumentBlock(JSFunction *target, uint32 &argCount) bool tookPositionalArg = false; if (inArgIndex < argCount) { JSValue v = getValue(inArgIndex + argStart); - if (!(v.isObject() && (v.object->mType == NamedArgument_Type))) { + if (!v.isNamedArg()) { argBase[pIndex] = v; argUsed[inArgIndex++] = true; needDefault = false; @@ -353,10 +354,9 @@ JSValue *Context::buildArgumentBlock(JSFunction *target, uint32 &argCount) for (uint32 i = inArgIndex; i < argCount; i++) { if (!argUsed[i]) { JSValue v = getValue(i + argStart); - if (v.isObject() && (v.object->mType == NamedArgument_Type)) { - NamedArgument *arg = static_cast(v.object); - if (arg->mName == parameterName) { - argBase[pIndex] = arg->mValue; + if (v.isNamedArg()) { + if (v.namedArg->mName == parameterName) { + argBase[pIndex] = v.namedArg->mValue; argUsed[i] = true; needDefault = false; break; @@ -390,16 +390,16 @@ JSValue *Context::buildArgumentBlock(JSFunction *target, uint32 &argCount) if (target->hasRestParameter() && target->getParameterName(restParameterIndex)) { restArgument = target->getParameterType(restParameterIndex)->newInstance(this); - argBase[restParameterIndex] = JSValue(restArgument); + argBase[restParameterIndex] = restArgument; haveRestArg = true; } inArgIndex = 0; // re-number the non-named arguments that end up in the rest arg for (i = 0; i < argCount; i++) { if (!argUsed[i]) { JSValue v = getValue(i + argStart); - bool isNamedArg = v.isObject() && (v.object->mType == NamedArgument_Type); + bool isNamedArg = v.isNamedArg(); if (isNamedArg) { - NamedArgument *arg = static_cast(v.object); + NamedArgument *arg = v.namedArg; if (haveRestArg) restArgument.object->setProperty(this, *arg->mName, (NamespaceList *)(NULL), arg->mValue); else @@ -414,10 +414,8 @@ JSValue *Context::buildArgumentBlock(JSFunction *target, uint32 &argCount) if (target->isChecked()) reportError(Exception::referenceError, "Extra argument, no rest argument"); else { - if (isNamedArg) { - NamedArgument *arg = static_cast(v.object); - argBase[i] = arg->mValue; - } + if (isNamedArg) + argBase[i] = v.namedArg->mValue; else argBase[i] = v; } @@ -683,10 +681,10 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) mScopeChain->addScope(target->getActivation()); if (!target->isChecked()) { - JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this); + JSValue args = Array_Type->newInstance(this); for (uint32 i = 0; i < argCount; i++) - args->setProperty(this, *numberToString(i), NULL, argBase[i]); - target->getActivation()->setProperty(this, Arguments_StringAtom, NULL, JSValue(args)); + args.instance->setProperty(this, *numberToString(i), NULL, argBase[i]); + target->getActivation()->setProperty(this, Arguments_StringAtom, NULL, args); } mCurModule = target->getByteCode(); @@ -825,8 +823,8 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) break; case LoadConstantRegExpOp: { - JSObject *t = *((JSObject **)pc); - pc += sizeof(JSObject *); + JSInstance *t = *((JSInstance **)pc); + pc += sizeof(JSInstance *); pushValue(JSValue(t)); } break; @@ -844,11 +842,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) case DeleteOp: { JSValue base = popValue(); - JSObject *obj = NULL; - if (!base.isObject() && !base.isType()) - obj = base.toObject(this).object; - else - obj = base.object; + JSObject *obj = base.toObjectValue(this); uint32 index = *((uint32 *)pc); pc += sizeof(uint32); const StringAtom &name = *mCurModule->getString(index); @@ -891,8 +885,8 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) JSValue t = popValue(); JSValue v = popValue(); if (t.isType()) { - if (v.isObject() - && (v.object->getType() == t.type)) + if (v.isInstance() + && (v.instance->getType() == t.type)) pushValue(v); else pushValue(kNullValue); // XXX or throw an exception if @@ -913,9 +907,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) else pushValue(kFalseValue); else - if (v.isObject() - && ((v.object->getType() == t.type) - || (v.object->getType()->derivesFrom(t.type)))) + if (v.isInstance() + && ((v.instance->getType() == t.type) + || (v.instance->getType()->derivesFrom(t.type)))) pushValue(kTrueValue); else { if (v.getType() == t.type) @@ -978,9 +972,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) if (result.function->isConstructor()) // A constructor has to be called with a NULL 'this' in order to prompt it // to construct the instance object. - pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, NULL)))); + pushValue(JSValue((JSFunction *)(new JSBoundFunction(this, result.function, NULL)))); else - pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, parent)))); + pushValue(JSValue((JSFunction *)(new JSBoundFunction(this, result.function, parent)))); } if (useOnceNamespace) { NamespaceList *t = mNamespaceList->mNext; @@ -1033,29 +1027,18 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) pc += sizeof(uint16); mPC = pc; - JSValue *baseValue = getBase(stackSize() - (dimCount + 1)); + JSValue *base = getBase(stackSize() - (dimCount + 1)); // Use the type of the base to dispatch on... - JSObject *obj = NULL; - if (baseValue->isType()) - obj = baseValue->type; - else - if (baseValue->isFunction()) - obj = baseValue->function; - else - if (baseValue->isObject()) - obj = baseValue->object; - else - obj = baseValue->toObject(this).object; - - JSFunction *target = obj->getType()->getUnaryOperator(Index); + JSObject *obj = base->toObjectValue(this); + JSFunction *target = getUnaryOperator(base->getType(), Index); if (target) { JSValue result; if (target->isNative()) { JSValue *argBase = new JSValue[dimCount + 1]; for (uint32 i = 0; i < (dimCount + 1); i++) - argBase[i] = baseValue[i]; + argBase[i] = base[i]; resizeStack(stackSize() - (dimCount + 1)); result = target->getNativeCode()(this, argBase[0], argBase, dimCount + 1); delete[] argBase; @@ -1090,9 +1073,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) if (result.function->isConstructor()) // A constructor has to be called with a NULL 'this' in order to prompt it // to construct the instance object. - pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, NULL)))); + pushValue(JSValue((JSFunction *)(new JSBoundFunction(this, result.function, NULL)))); else - pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, obj)))); + pushValue(JSValue((JSFunction *)(new JSBoundFunction(this, result.function, obj)))); } } break; @@ -1102,15 +1085,11 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) pc += sizeof(uint16); mPC = pc; - JSValue *baseValue = getBase(stackSize() - (dimCount + 2)); // +1 for assigned value + JSValue *base = getBase(stackSize() - (dimCount + 2)); // +1 for assigned value // Use the type of the base to dispatch on... - JSObject *obj = NULL; - if (!baseValue->isObject() && !baseValue->isType()) - obj = baseValue->toObject(this).object; - else - obj = baseValue->object; - JSFunction *target = obj->getType()->getUnaryOperator(IndexEqual); + JSObject *obj = base->toObjectValue(this); + JSFunction *target = getUnaryOperator(base->getType(), IndexEqual); if (target) { JSValue v = popValue(); // need to have this sitting right above the base value @@ -1123,9 +1102,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) if (target->isNative()) { JSValue *argBase = new JSValue[dimCount + 2]; for (uint32 i = 0; i < (dimCount + 2); i++) - argBase[i] = baseValue[i]; + argBase[i] = base[i]; resizeStack(stackSize() - (dimCount + 2)); - result = target->getNativeCode()(this, *baseValue, baseValue, (dimCount + 2)); + result = target->getNativeCode()(this, *base, base, (dimCount + 2)); delete[] argBase; } else { @@ -1133,7 +1112,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) JSValue *argBase = buildArgumentBlock(target, argCount); resizeStack(stackSize() - (dimCount + 2)); try { - result = interpret(target->getByteCode(), 0, target->getScopeChain(), *baseValue, argBase, argCount); + result = interpret(target->getByteCode(), 0, target->getScopeChain(), *base, argBase, argCount); } catch (Exception &x) { delete[] argBase; @@ -1163,22 +1142,18 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) pc += sizeof(uint16); mPC = pc; - JSValue *baseValue = getBase(stackSize() - (dimCount + 1)); + JSValue *base = getBase(stackSize() - (dimCount + 1)); // Use the type of the base to dispatch on... - JSObject *obj = NULL; - if (!baseValue->isObject() && !baseValue->isType()) - obj = baseValue->toObject(this).object; - else - obj = baseValue->object; - JSFunction *target = obj->getType()->getUnaryOperator(DeleteIndex); + JSObject *obj = base->toObjectValue(this); + JSFunction *target = getUnaryOperator(base->getType(), DeleteIndex); if (target) { JSValue result; if (target->isNative()) { JSValue *argBase = new JSValue[dimCount + 1]; for (uint32 i = 0; i < (dimCount + 1); i++) - argBase[i] = baseValue[i]; + argBase[i] = base[i]; resizeStack(stackSize() - (dimCount + 1)); result = target->getNativeCode()(this, argBase[0], argBase, dimCount + 1); delete[] argBase; @@ -1213,11 +1188,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) case GetPropertyOp: { JSValue base = popValue(); - JSObject *obj = NULL; - if (!base.isObject() && !base.isType()) - obj = base.toObject(this).object; - else - obj = base.object; + JSObject *obj = base.toObjectValue(this); uint32 index = *((uint32 *)pc); pc += sizeof(uint32); mPC = pc; @@ -1231,9 +1202,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) if (result.function->isConstructor()) // A constructor has to be called with a NULL 'this' in order to prompt it // to construct the instance object. - pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, NULL)))); + pushValue(JSValue((JSFunction *)(new JSBoundFunction(this, result.function, NULL)))); else - pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, obj)))); + pushValue(JSValue((JSFunction *)(new JSBoundFunction(this, result.function, obj)))); } if (useOnceNamespace) { NamespaceList *t = mNamespaceList->mNext; @@ -1246,21 +1217,17 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) case GetInvokePropertyOp: { JSValue base = topValue(); - JSObject *obj = NULL; - if (!base.isObject() && !base.isType() && !base.isFunction()) { - obj = base.toObject(this).object; - popValue(); - pushValue(JSValue(obj)); // want the "toObject'd" version of base - } - else - obj = base.object; + JSValue baseObject = base.toObject(this); + // XXX really only need to pop/push if the base got modified + popValue(); + pushValue(baseObject); // want the "toObject'd" version of base uint32 index = *((uint32 *)pc); pc += sizeof(uint32); mPC = pc; const StringAtom &name = *mCurModule->getString(index); - obj->getProperty(this, name, mNamespaceList); + baseObject.getObjectValue()->getProperty(this, name, mNamespaceList); if (useOnceNamespace) { NamespaceList *t = mNamespaceList->mNext; delete mNamespaceList; @@ -1276,11 +1243,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) v = JSValue(v.function->getFunction()); } JSValue base = popValue(); - JSObject *obj = NULL; - if (!base.isObject() && !base.isType()) - obj = base.toObject(this).object; - else - obj = base.object; + JSObject *obj = base.toObjectValue(this); uint32 index = *((uint32 *)pc); pc += sizeof(uint32); mPC = pc; @@ -1300,9 +1263,8 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) Operator op = (Operator)(*pc++); mPC = pc; JSValue v = topValue(); - JSFunction *target; - if (v.isObject() && (target = v.object->getType()->getUnaryOperator(op)) ) - { + JSFunction *target = getUnaryOperator(v.getType(), op); + if (target) { uint32 argBase = stackSize() - 1; JSValue newThis = kNullValue; if (!target->isNative()) { @@ -1404,7 +1366,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) PropertyIterator i; if (target->hasProperty(this, Prototype_StringAtom, mNamespaceList, Read, &i)) { JSValue v = target->getPropertyValue(i); - newThis.object->mPrototype = v.toObject(this).object; + newThis.object->mPrototype = v.toObjectValue(this); newThis.object->setProperty(this, UnderbarPrototype_StringAtom, (NamespaceList *)NULL, JSValue(newThis.object->mPrototype)); } } @@ -1415,9 +1377,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) // if the type has an operator "new" use that, // otherwise use the default constructor (and pass NULL // for the this value) - target = typeValue->type->getUnaryOperator(New); + target = getUnaryOperator(typeValue->type, New); if (target) - newThis = JSValue(typeValue->type->newInstance(this)); + newThis = typeValue->type->newInstance(this); else { newThis = kNullValue; target = typeValue->type->getDefaultConstructor(); @@ -1437,10 +1399,10 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) argBase = buildArgumentBlock(target, argCount); resizeStack(stackSize() - cleanUp); if (!target->isChecked()) { - JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this); + JSValue args = Array_Type->newInstance(this); for (uint32 i = 0; i < argCount; i++) - args->setProperty(this, *numberToString(i), NULL, argBase[i]); - target->getActivation()->setProperty(this, Arguments_StringAtom, NULL, JSValue(args)); + args.instance->setProperty(this, *numberToString(i), NULL, argBase[i]); + target->getActivation()->setProperty(this, Arguments_StringAtom, NULL, args); } try { result = interpret(target->getByteCode(), 0, target->getScopeChain(), newThis, argBase, argCount); @@ -1471,13 +1433,13 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) JSValue v = popValue(); if (mThis.isNull()) { ASSERT(v.isType()); - mThis = JSValue(v.type->newInstance(this)); + mThis = v.type->newInstance(this); } } break; case NewObjectOp: { - pushValue(JSValue(Object_Type->newInstance(this))); + pushValue(Object_Type->newInstance(this)); } break; case GetLocalVarOp: @@ -1550,28 +1512,29 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) case GetMethodOp: { JSValue base = topValue(); - ASSERT(dynamic_cast(base.object)); + ASSERT(base.isInstance()); uint32 index = *((uint32 *)pc); pc += sizeof(uint32); - pushValue(JSValue(base.object->mType->mMethods[index])); + pushValue(JSValue(base.instance->mType->mMethods[index])); } break; case GetMethodRefOp: { JSValue base = popValue(); - ASSERT(dynamic_cast(base.object)); + if (!base.isInstance()) + reportError(Exception::semanticError, "Illegal method reference"); uint32 index = *((uint32 *)pc); pc += sizeof(uint32); - pushValue(JSValue(new JSBoundFunction(base.object->mType->mMethods[index], base.object))); + pushValue(JSValue(new JSBoundFunction(this, base.instance->mType->mMethods[index], base.object))); } break; case GetFieldOp: { JSValue base = popValue(); - ASSERT(dynamic_cast(base.object)); + ASSERT(base.isInstance()); uint32 index = *((uint32 *)pc); pc += sizeof(uint32); - pushValue(((JSInstance *)(base.object))->mInstanceValues[index]); + pushValue(base.instance->mInstanceValues[index]); } break; case SetFieldOp: @@ -1581,17 +1544,17 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) v = JSValue(v.function->getFunction()); } JSValue base = popValue(); - ASSERT(dynamic_cast(base.object)); + ASSERT(base.isInstance()); uint32 index = *((uint32 *)pc); pc += sizeof(uint32); - ((JSInstance *)(base.object))->mInstanceValues[index] = v; + base.instance->mInstanceValues[index] = v; pushValue(v); } break; case WithinOp: { JSValue base = popValue(); - mScopeChain->addScope(base.toObject(this).object); + mScopeChain->addScope(base.toObjectValue(this)); } break; case WithoutOp: @@ -1660,11 +1623,11 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) JSValue v2 = popValue(); JSValue v1 = popValue(); - ASSERT(v1.isObject() && (v1.object->getType() == Attribute_Type)); - ASSERT(v2.isObject() && (v2.object->getType() == Attribute_Type)); + ASSERT(v1.isAttribute()); + ASSERT(v2.isAttribute()); - Attribute *a1 = static_cast(v1.object); - Attribute *a2 = static_cast(v2.object); + Attribute *a1 = v1.attribute; + Attribute *a2 = v2.attribute; if ((a1->mTrueFlags & a2->mFalseFlags) != 0) reportError(Exception::semanticError, "Mismatched attributes"); // XXX could supply more detail @@ -1796,40 +1759,73 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) return result; } +// called on a JSValue to return a base object. The value may be a type, function, instance +// or generic object +JSObject *JSValue::getObjectValue() const +{ + switch (tag) { + case object_tag: + return object; + case instance_tag: + return instance; + case function_tag: + return function; + case type_tag: + return type; + case package_tag: + return package; + default: + NOT_REACHED("Bad tag"); + return NULL; + } +} + +// called on a JSValue with type == Number_Type, which could be either a tagged +// value or a Number object (actually an instance). Return the raw value in either case. float64 JSValue::getNumberValue() const { if (isNumber()) return f64; - ASSERT(isObject() && (getType() == Number_Type)); - return *((float64 *)(object->mPrivate)); + ASSERT(isInstance() && (getType() == Number_Type)); + JSNumberInstance *numInst = checked_cast(instance); + return numInst->mValue; } +// called on a JSValue with type == String_Type, which could be either a tagged +// value or a String object (actually an instance). Return the raw value in either case. const String *JSValue::getStringValue() const { if (isString()) return string; - ASSERT(isObject() && (getType() == String_Type)); - return (const String *)(object->mPrivate); + ASSERT(isInstance() && (getType() == String_Type)); + JSStringInstance *strInst = checked_cast(instance); + return strInst->mValue; } +// called on a JSValue with type == Boolean_Type, which could be either a tagged +// value or a Boolean object (actually an instance). Return the raw value in either case. bool JSValue::getBoolValue() const { if (isBool()) return boolean; - ASSERT(isObject() && (getType() == Boolean_Type)); - return (object->mPrivate != 0); + ASSERT(isInstance() && (getType() == Boolean_Type)); + JSBooleanInstance *boolInst = checked_cast(instance); + return boolInst->mValue; } +// Implementation of '+' for two number types static JSValue numberPlus(Context *, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { return JSValue(argv[0].getNumberValue() + argv[1].getNumberValue()); } +// Implementation of '+' for two integer types static JSValue integerPlus(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { return JSValue(argv[0].getNumberValue() + argv[1].getNumberValue()); } +// Implementation of '+' for two 'generic' objects static JSValue objectPlus(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -1873,16 +1869,19 @@ static JSValue objectPlus(Context *cx, const JSValue& /*thisValue*/, JSValue *ar +// Implementation of '-' for two integer types static JSValue integerMinus(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { return JSValue(argv[0].getNumberValue() - argv[1].getNumberValue()); } +// Implementation of '-' for two number types static JSValue numberMinus(Context *, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { return JSValue(argv[0].getNumberValue() - argv[1].getNumberValue()); } +// Implementation of '-' for two 'generic' objects static JSValue objectMinus(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -1892,11 +1891,13 @@ static JSValue objectMinus(Context *cx, const JSValue& /*thisValue*/, JSValue *a +// Implementation of '*' for two integer types static JSValue integerMultiply(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { return JSValue(argv[0].getNumberValue() * argv[1].getNumberValue()); } +// Implementation of '*' for two 'generic' objects static JSValue objectMultiply(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -1905,7 +1906,7 @@ static JSValue objectMultiply(Context *cx, const JSValue& /*thisValue*/, JSValue } - +// Implementation of '/' for two integer types static JSValue integerDivide(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -1917,6 +1918,7 @@ static JSValue integerDivide(Context * /*cx*/, const JSValue& /*thisValue*/, JSV return JSValue(d); } +// Implementation of '/' for two 'generic' objects static JSValue objectDivide(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -1925,7 +1927,7 @@ static JSValue objectDivide(Context *cx, const JSValue& /*thisValue*/, JSValue * } - +// Implementation of '%' for two integer types static JSValue integerRemainder(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -1937,6 +1939,7 @@ static JSValue integerRemainder(Context * /*cx*/, const JSValue& /*thisValue*/, return JSValue(d); } +// Implementation of '%' for two 'generic' objects static JSValue objectRemainder(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -1955,7 +1958,7 @@ static JSValue objectRemainder(Context *cx, const JSValue& /*thisValue*/, JSValu } - +// Implementation of '<<' for two integer types static JSValue integerShiftLeft(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -1963,6 +1966,7 @@ static JSValue integerShiftLeft(Context * /*cx*/, const JSValue& /*thisValue*/, return JSValue((float64)( (int32)(f1) << ( (uint32)(f2) & 0x1F)) ); } +// Implementation of '<<' for two 'generic' objects static JSValue objectShiftLeft(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -1972,6 +1976,7 @@ static JSValue objectShiftLeft(Context *cx, const JSValue& /*thisValue*/, JSValu +// Implementation of '>>' for two integer types static JSValue integerShiftRight(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -1979,6 +1984,7 @@ static JSValue integerShiftRight(Context * /*cx*/, const JSValue& /*thisValue*/, return JSValue((float64) ( (int32)(f1) >> ( (uint32)(f2) & 0x1F)) ); } +// Implementation of '>>' for two 'generic' objects static JSValue objectShiftRight(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -1988,6 +1994,7 @@ static JSValue objectShiftRight(Context *cx, const JSValue& /*thisValue*/, JSVal +// Implementation of '>>>' for two integer types static JSValue integerUShiftRight(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -1995,6 +2002,7 @@ static JSValue integerUShiftRight(Context * /*cx*/, const JSValue& /*thisValue*/ return JSValue((float64) ( (uint32)(f1) >> ( (uint32)(f2) & 0x1F)) ); } +// Implementation of '>>>' for two 'generic' objects static JSValue objectUShiftRight(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -2003,7 +2011,7 @@ static JSValue objectUShiftRight(Context *cx, const JSValue& /*thisValue*/, JSVa } - +// Implementation of '&' for two integer types static JSValue integerBitAnd(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -2011,6 +2019,7 @@ static JSValue integerBitAnd(Context * /*cx*/, const JSValue& /*thisValue*/, JSV return JSValue((float64)( (int32)(f1) & (int32)(f2) )); } +// Implementation of '&' for two 'generic' objects static JSValue objectBitAnd(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -2020,6 +2029,7 @@ static JSValue objectBitAnd(Context *cx, const JSValue& /*thisValue*/, JSValue * +// Implementation of '^' for two integer types static JSValue integerBitXor(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -2027,6 +2037,7 @@ static JSValue integerBitXor(Context * /*cx*/, const JSValue& /*thisValue*/, JSV return JSValue((float64)( (int32)(f1) ^ (int32)(f2) )); } +// Implementation of '^' for two 'generic' objects static JSValue objectBitXor(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -2036,6 +2047,7 @@ static JSValue objectBitXor(Context *cx, const JSValue& /*thisValue*/, JSValue * +// Implementation of '|' for two integer types static JSValue integerBitOr(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { float64 f1 = argv[0].getNumberValue(); @@ -2043,6 +2055,7 @@ static JSValue integerBitOr(Context * /*cx*/, const JSValue& /*thisValue*/, JSVa return JSValue((float64)( (int32)(f1) | (int32)(f2) )); } +// Implementation of '|' for two 'generic' objects static JSValue objectBitOr(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue &r1 = argv[0]; @@ -2095,6 +2108,7 @@ static JSValue objectLessEqual(Context *cx, const JSValue& /*thisValue*/, JSValu return kTrueValue; } +// Compare two values for '==' static JSValue compareEqual(Context *cx, JSValue r1, JSValue r2) { JSType *t1 = r1.getType(); @@ -2124,8 +2138,12 @@ static JSValue compareEqual(Context *cx, JSValue r1, JSValue r2) return kTrueValue; if (r1.isNull()) return kTrueValue; - if (r1.isObject() && r2.isObject()) // because new Boolean()->getType() == Boolean_Type + if (r1.isInstance() && r2.isInstance()) // because new Boolean()->getType() == Boolean_Type + return JSValue(r1.instance == r2.instance); + if (r1.isObject() && r2.isObject()) return JSValue(r1.object == r2.object); + if (r1.isPackage() && r2.isPackage()) + return JSValue(r1.package == r2.package); if (r1.isType()) return JSValue(r1.type == r2.type); if (r1.isFunction()) @@ -2206,7 +2224,7 @@ static JSValue objectSpittingImage(Context * /*cx*/, const JSValue& /*thisValue* } - +// Build the default state of the binary operator table void Context::initOperators() { struct OpTableEntry { @@ -2263,72 +2281,106 @@ void Context::initOperators() for (uint32 i = 0; i < sizeof(OpTable) / sizeof(OpTableEntry); i++) { JSFunction *f = new JSFunction(this, OpTable[i].imp, OpTable[i].resType); OperatorDefinition *op = new OperatorDefinition(OpTable[i].op1, OpTable[i].op2, f); - mOperatorTable[OpTable[i].which].push_back(op); + mBinaryOperatorTable[OpTable[i].which].push_back(op); } } +// Convert a primitive value into an object value, this is a no-op for +// values that are already object-like. + +// XXX for Waldemar - is it possible to modify behaviour for Number, Boolean etc +// so that the behaviour here of constructing a new instance of those types would +// be different? JSValue JSValue::valueToObject(Context *cx, const JSValue& value) { switch (value.tag) { case f64_tag: { - JSObject *obj = Number_Type->newInstance(cx); - JSFunction *defCon = Number_Type->getDefaultConstructor(); - JSValue argv[1]; - JSValue thisValue = JSValue(obj); - argv[0] = value; - if (defCon->isNative()) { - (defCon->getNativeCode())(cx, thisValue, &argv[0], 1); - } - else { - ASSERT(false); // need to throw a hot potato back to - // ye interpreter loop - } - return thisValue; + JSValue result = Number_Type->newInstance(cx); + ((JSNumberInstance *)result.instance)->mValue = value.f64; + return JSValue(result); } case boolean_tag: { - JSObject *obj = Boolean_Type->newInstance(cx); - JSFunction *defCon = Boolean_Type->getDefaultConstructor(); - JSValue argv[1]; - JSValue thisValue = JSValue(obj); - argv[0] = value; - if (defCon->isNative()) { - (defCon->getNativeCode())(cx, thisValue, &argv[0], 1); - } - else { - ASSERT(false); - } - return thisValue; + JSValue result = Boolean_Type->newInstance(cx); + ((JSBooleanInstance *)result.instance)->mValue = value.boolean; + return JSValue(result); } case string_tag: { - JSObject *obj = String_Type->newInstance(cx); - JSFunction *defCon = String_Type->getDefaultConstructor(); - JSValue argv[1]; - JSValue thisValue = JSValue(obj); - argv[0] = value; - if (defCon->isNative()) { - (defCon->getNativeCode())(cx, thisValue, &argv[0], 1); - } - else { - ASSERT(false); - } - return thisValue; + JSValue result = String_Type->newInstance(cx); + ((JSStringInstance *)result.instance)->mValue = value.string; + return JSValue(result); } + case instance_tag: + case package_tag: + case type_tag: case object_tag: case function_tag: return value; case null_tag: + cx->reportError(Exception::typeError, "converting null to object"); case undefined_tag: - cx->reportError(Exception::typeError, "ToObject"); + cx->reportError(Exception::typeError, "converting undefined to object"); default: NOT_REACHED("Bad tag"); return kUndefinedValue; } } +// Convert a value to an object, without constructing a new value +// unless absolutely necessary, which would be the case for using +// valueToObject().getObjectValue() instead. + +// XXX for Waldemar - is it possible to modify behaviour for Number, Boolean etc +// so that the behaviour here of constructing a new instance of those types would +// be different? + + +JSObject *JSValue::toObjectValue(Context *cx) const +{ + switch (tag) { + case f64_tag: + { + JSValue result = Number_Type->newInstance(cx); + ((JSNumberInstance *)result.instance)->mValue = f64; + return result.instance; + } + case boolean_tag: + { + JSValue result = Boolean_Type->newInstance(cx); + ((JSBooleanInstance *)result.instance)->mValue = boolean; + return result.instance; + } + case string_tag: + { + JSValue result = String_Type->newInstance(cx); + ((JSStringInstance *)result.instance)->mValue = string; + return result.instance; + } + case type_tag: + return type; + case object_tag: + return object; + case function_tag: + return function; + case instance_tag: + return instance; + case package_tag: + return package; + case null_tag: + cx->reportError(Exception::typeError, "converting null to object"); + case undefined_tag: + cx->reportError(Exception::typeError, "converting undefined to object"); + default: + NOT_REACHED("Bad tag"); + return NULL; + } +} + +// convert string to double - a wrapper around the numerics routine +// to handle hex literals as well float64 stringToNumber(const String *string) { const char16 *numEnd; @@ -2353,6 +2405,7 @@ float64 stringToNumber(const String *string) return 0.0; } +// Convert a JSValue to number-type JSValue JSValue::valueToNumber(Context *cx, const JSValue& value) { switch (value.tag) { @@ -2361,6 +2414,9 @@ JSValue JSValue::valueToNumber(Context *cx, const JSValue& value) case string_tag: return JSValue(stringToNumber(value.string)); case object_tag: + case instance_tag: + case package_tag: + case type_tag: case function_tag: return value.toPrimitive(cx, NumberHint).toNumber(cx); case boolean_tag: @@ -2375,13 +2431,15 @@ JSValue JSValue::valueToNumber(Context *cx, const JSValue& value) } } +// Convert a double to a string representation (base-10) const String *numberToString(float64 number) { char buf[dtosStandardBufferSize]; const char *chrp = doubleToStr(buf, dtosStandardBufferSize, number, dtosStandard, 0); return new JavaScript::String(widenCString(chrp)); } - + +// Convert a JSValue to a string (ECMA rules) JSValue JSValue::valueToString(Context *cx, const JSValue& value) { const String *strp = NULL; @@ -2409,6 +2467,12 @@ JSValue JSValue::valueToString(Context *cx, const JSValue& value) case null_tag: strp = &cx->Null_StringAtom; break; + case instance_tag: + obj = value.instance; + break; + case package_tag: + obj = value.package; + break; default: NOT_REACHED("Bad tag"); } @@ -2437,6 +2501,7 @@ JSValue JSValue::valueToString(Context *cx, const JSValue& value) } +// Convert a JSValue to a primitive type JSValue JSValue::toPrimitive(Context *cx, Hint hint) const { JSObject *obj; @@ -2447,10 +2512,13 @@ JSValue JSValue::toPrimitive(Context *cx, Hint hint) const case undefined_tag: return *this; + case instance_tag: + obj = instance; + if ((hint == NoHint) && (getType() == Date_Type)) + hint = StringHint; + break; case object_tag: obj = object; - if ((hint == NoHint) && (obj->getType() == Date_Type)) - hint = StringHint; break; case function_tag: obj = function; @@ -2658,6 +2726,8 @@ JSValue JSValue::valueToBoolean(Context * /*cx*/, const JSValue& value) return JSValue(value.string->length() != 0); case boolean_tag: return value; + case type_tag: + case instance_tag: case object_tag: case function_tag: return kTrueValue; @@ -2715,7 +2785,8 @@ JSType *JSValue::getType() const { case f64_tag: return Number_Type; case boolean_tag: return Boolean_Type; case string_tag: return (JSType *)String_Type; - case object_tag: return object->getType(); + case object_tag: return Object_Type; + case instance_tag: return instance->getType(); case undefined_tag: return Void_Type; case null_tag: return Null_Type; case function_tag: return Function_Type; @@ -2727,6 +2798,32 @@ JSType *JSValue::getType() const { } +JSFunction *Context::getUnaryOperator(JSType *dispatchType, Operator which) +{ + // look in the operator table for applicable operators + OperatorList applicableOperators; + + for (OperatorList::iterator oi = mUnaryOperatorTable[which].begin(), + end = mUnaryOperatorTable[which].end(); + (oi != end); oi++) { + if ((*oi)->isApplicable(dispatchType)) { + applicableOperators.push_back(*oi); + } + } + if (applicableOperators.size() == 0) + return NULL; + + OperatorList::iterator candidate = applicableOperators.begin(); + for (OperatorList::iterator aoi = applicableOperators.begin() + 1, + aend = applicableOperators.end(); + (aoi != aend); aoi++) + { + if ((*aoi)->mType1->derivesFrom((*candidate)->mType1) ) + candidate = aoi; + } + + return (*candidate)->mImp; +} } diff --git a/js2/src/js2runtime.cpp b/js2/src/js2runtime.cpp index 1fea5f7a5d1c..a228f7adde95 100644 --- a/js2/src/js2runtime.cpp +++ b/js2/src/js2runtime.cpp @@ -56,7 +56,6 @@ #include "fdlibm_ns.h" -#include "regexp.h" // this is the AttributeList passed to the name lookup routines #define CURRENT_ATTR (NULL) @@ -104,8 +103,8 @@ Attribute *Context::executeAttributes(ExprNode *attr) // stdOut << *bcm; JSValue result = interpret(bcm, 0, NULL, JSValue(getGlobalObject()), NULL, 0); - ASSERT(result.isObject() && (result.object->getType() == Attribute_Type)); - return static_cast(result.object); + ASSERT(result.isAttribute()); + return result.attribute; } @@ -178,8 +177,8 @@ bool JSObject::hasProperty(Context *cx, const String &name, NamespaceList *names if (hasOwnProperty(cx, name, names, acc, p)) return true; else - if (mPrototype) - return mPrototype->hasProperty(cx, name, names, acc, p); + if (!mPrototype.isNull()) + return mPrototype.getObjectValue()->hasProperty(cx, name, names, acc, p); else return false; } @@ -393,18 +392,14 @@ void JSObject::getProperty(Context *cx, const String &name, NamespaceList *names case FunctionPair: cx->pushValue(cx->invokeFunction(prop->mData.fPair.getterF, JSValue(this), NULL, 0)); break; - case Constructor: - case Method: - cx->pushValue(JSValue(mType->mMethods[prop->mData.index])); - break; default: ASSERT(false); // XXX more to implement break; } } else { - if (mPrototype) - mPrototype->getProperty(cx, name, names); + if (!mPrototype.isNull()) + mPrototype.getObjectValue()->getProperty(cx, name, names); else cx->pushValue(kUndefinedValue); } @@ -463,13 +458,13 @@ XXX this path allows an instance to access static fields of it's type void JSObject::setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v) { PropertyIterator i; - if (hasProperty(cx, name, names, Write, &i)) { + if (hasOwnProperty(cx, name, names, Write, &i)) { if (PROPERTY_ATTR(i) & Property::ReadOnly) return; Property *prop = PROPERTY(i); switch (prop->mFlag) { case ValuePointer: if (name.compare(cx->UnderbarPrototype_StringAtom) == 0) - mPrototype = v.toObject(cx).object; + mPrototype = v; *prop->mData.vp = v; break; case FunctionPair: @@ -602,9 +597,8 @@ bool JSArrayInstance::deleteProperty(Context *cx, const String &name, NamespaceL // 'length' by returning the known value void JSStringInstance::getProperty(Context *cx, const String &name, NamespaceList *names) { - if (name.compare(cx->Length_StringAtom) == 0) { - cx->pushValue(JSValue((float64)mLength)); - } + if (name.compare(cx->Length_StringAtom) == 0) + cx->pushValue(JSValue((float64)(mValue->size()))); else JSInstance::getProperty(cx, name, names); } @@ -679,14 +673,24 @@ void JSInstance::initInstance(Context *cx, JSType *type) // Create a new (empty) instance of this class. The prototype // link for this new instance is established from the type's // prototype object. -JSInstance *JSType::newInstance(Context *cx) +JSValue JSType::newInstance(Context *cx) { JSInstance *result = new JSInstance(cx, this); result->mPrototype = mPrototypeObject; - result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototypeObject)); - return result; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); } +JSValue JSObjectType::newInstance(Context *cx) +{ + JSObject *result = new JSObject(); + result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); +} + + + // the function 'f' gets executed each time a new instance is created void JSType::setInstanceInitializer(Context * /*cx*/, JSFunction *f) { @@ -720,20 +724,52 @@ XXX error for classes, right? but not for local variables under what circumstanc return prop; } -JSInstance *JSArrayType::newInstance(Context *cx) +JSValue JSArrayType::newInstance(Context *cx) { - JSInstance *result = new JSArrayInstance(cx, this); + JSInstance *result = new JSArrayInstance(cx); result->mPrototype = mPrototypeObject; - result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototypeObject)); - return result; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); } -JSInstance *JSStringType::newInstance(Context *cx) +JSValue JSStringType::newInstance(Context *cx) { - JSInstance *result = new JSStringInstance(cx, this); + JSInstance *result = new JSStringInstance(cx); result->mPrototype = mPrototypeObject; - result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototypeObject)); - return result; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); +} + +JSValue JSBooleanType::newInstance(Context *cx) +{ + JSInstance *result = new JSBooleanInstance(cx); + result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); +} + +JSValue JSRegExpType::newInstance(Context *cx) +{ + JSInstance *result = new JSRegExpInstance(cx); + result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); +} + +JSValue JSDateType::newInstance(Context *cx) +{ + JSInstance *result = new JSDateInstance(cx); + result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); +} + +JSValue JSNumberType::newInstance(Context *cx) +{ + JSInstance *result = new JSNumberInstance(cx); + result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject); + return JSValue(result); } // don't add to instances etc., climb all the way down (likely to the global object) @@ -1006,7 +1042,7 @@ JSType *ScopeChain::extractType(ExprNode *t) if (element == Object_Type) type = Array_Type; else { - type = new JSArrayType(m_cx, element, NULL, Object_Type); // XXX or is this a descendat of Array[Object]? + type = new JSArrayType(m_cx, element, NULL, Object_Type, kNullValue, kNullValue); // XXX or is this a descendant of Array[Object]? type->setDefaultConstructor(m_cx, Array_Type->getDefaultConstructor()); } } @@ -1156,7 +1192,7 @@ void ScopeChain::collectNames(StmtNode *p) { ClassStmtNode *classStmt = checked_cast(p); const StringAtom *name = &classStmt->name; - JSType *thisClass = new JSType(m_cx, name, NULL); + JSType *thisClass = new JSType(m_cx, name, NULL, kNullValue, kNullValue); m_cx->setAttributeValue(classStmt, 0); // XXX default attribute for a class? @@ -1393,8 +1429,8 @@ void ScopeChain::collectNames(StmtNode *p) m_cx->loadPackage(packageName, packageName + ".js"); JSValue packageValue = getCompileTimeValue(m_cx, packageName, NULL); - ASSERT(packageValue.isObject() && (packageValue.object->mType == Package_Type)); - Package *package = checked_cast(packageValue.object); + ASSERT(packageValue.isPackage()); + Package *package = packageValue.package; if (i->varName) defineVariable(m_cx, *i->varName, NULL, Package_Type, JSValue(package)); @@ -1627,8 +1663,12 @@ Reference *JSType::genReference(Context *cx, bool hasBase, const String& name, N return NULL; } -JSType::JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj, JSObject *typeProto) - : JSObject(Type_Type), +// Construct a type object, hook up the prototype value (mPrototypeObject) and the __proto__ value +// (mPrototype). Special handling throughout for handling the initialization of Object_Type which has +// no super type. (Note though that it's prototype link __proto__ is the Type_Type object and it has +// a prototype object - whose __proto__ is null) +JSType::JSType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) + : JSInstance(cx, Type_Type), mSuperType(super), mVariableCount(0), mInstanceInitializer(NULL), @@ -1637,78 +1677,57 @@ JSType::JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *pro mClassName(name), mIsDynamic(false), mUninitializedValue(kNullValue), - mPrototypeObject(NULL) + mPrototypeObject(kNullValue) { if (mClassName) mPrivateNamespace = &cx->mWorld.identifiers[*mClassName + " private"]; else mPrivateNamespace = &cx->mWorld.identifiers["unique id needed? private"]; // XXX. No, really? - for (uint32 i = 0; i < OperatorCount; i++) - mUnaryOperators[i] = NULL; - // every class gets a prototype object (used to set the __proto__ value of new instances) - if (protoObj) + if (!protoObj.isNull()) mPrototypeObject = protoObj; else { - mPrototypeObject = new JSObject(this); + JSObject *protoObj = new JSObject(); + mPrototypeObject = JSValue(protoObj); // and that object is prototype-linked to the super-type's prototype object if (mSuperType) - mPrototypeObject->mPrototype = mSuperType->mPrototypeObject; - if (mPrototypeObject->mPrototype) - mPrototypeObject->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, this, JSValue(mPrototypeObject->mPrototype)); - else // must be Object_Type being initialized - mPrototypeObject->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, this, kNullValue); + protoObj->mPrototype = mSuperType->mPrototypeObject; + protoObj->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, this, protoObj->mPrototype); } if (mSuperType) - defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::ReadOnly | Property::DontDelete, Object_Type, JSValue(mPrototypeObject)); + defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::ReadOnly | Property::DontDelete, Object_Type, mPrototypeObject); else // must be Object_Type being initialized - defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::ReadOnly | Property::DontDelete, this, JSValue(mPrototypeObject)); + defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::ReadOnly | Property::DontDelete, this, mPrototypeObject); - if (typeProto) + if (!typeProto.isNull()) mPrototype = typeProto; else { - // the __proto__ of a type is the super-type's prototype object, or for 'class Object' type object it's the Object's prototype object + // the __proto__ of a type is the super-type's prototype object, or for the + // 'class Object' type object it's the Object's prototype object if (mSuperType) mPrototype = mSuperType->mPrototypeObject; else // must be Object_Type being initialized mPrototype = mPrototypeObject; } if (mSuperType) { - defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototype)); - mPrototypeObject->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(this)); + defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototype); + mPrototypeObject.getObjectValue()->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(this)); } else { // must be Object_Type being initialized - defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, this, JSValue(mPrototype)); - mPrototypeObject->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, 0, this, JSValue(this)); + defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, this, mPrototype); + mPrototypeObject.getObjectValue()->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, 0, this, JSValue(this)); } } -JSType::JSType(JSType *xClass) // used for constructing the static component type - : JSObject(Type_Type), - mSuperType(xClass), - mVariableCount(0), - mInstanceInitializer(NULL), - mDefaultConstructor(NULL), - mTypeCast(NULL), - mClassName(NULL), - mPrivateNamespace(NULL), - mIsDynamic(false), - mUninitializedValue(kNullValue), - mPrototypeObject(NULL) -{ - for (uint32 i = 0; i < OperatorCount; i++) - mUnaryOperators[i] = NULL; -} - // Establish the super class - connects the prototype's prototype // and accounts for the super class's instance fields & methods void JSType::setSuperType(JSType *super) { mSuperType = super; if (mSuperType) { - mPrototypeObject->mPrototype = mSuperType->mPrototypeObject; + mPrototypeObject.getObjectValue()->mPrototype = mSuperType->mPrototypeObject; // inherit supertype instance field and vtable slots mVariableCount = mSuperType->mVariableCount; mMethods.insert(mMethods.begin(), @@ -1920,16 +1939,14 @@ void Context::buildRuntimeForStmt(StmtNode *p) ASSERT(f->function.name); const StringAtom& name = *f->function.name; Operator op = getOperator(parameterCount, name); - // if it's a unary operator, it just gets added - // as a method with a special name. Binary operators - // get added to the Context's operator table. + // Operators added to the Context's operator table. // The indexing operators are considered unary since // they only dispatch on the base type. if ((parameterCount == 1) || (op == Index) || (op == IndexEqual) || (op == DeleteIndex)) - mScopeChain->defineUnaryOperator(op, fnc); + defineOperator(op, mScopeChain->topClass(), fnc); else defineOperator(op, getParameterType(f->function, 0), getParameterType(f->function, 1), fnc); @@ -2013,7 +2030,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() || argv[0].isFunction() || argv[0].isType()) + if (argv[0].isInstance() || argv[0].isObject() || argv[0].isFunction() || argv[0].isType()) thatValue = argv[0]; else if (argv[0].isString() || argv[0].isBool() || argv[0].isNumber()) @@ -2032,18 +2049,18 @@ static JSValue Object_Constructor(Context *cx, const JSValue& thisValue, JSValue static JSValue Object_toString(Context * /* cx */, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - if (thisValue.isObject()) - return JSValue(new String(widenCString("[object ") + *thisValue.object->getType()->mClassName + widenCString("]"))); + if (thisValue.isObject() || thisValue.isInstance()) + return JSValue(new String(widenCString("[object ") + *thisValue.getType()->mClassName + widenCString("]"))); else - if (thisValue.isType()) - return JSValue(new String(widenCString("[object ") + widenCString("Type") + widenCString("]"))); - else - if (thisValue.isFunction()) - return JSValue(new String(widenCString("[object ") + widenCString("Function") + widenCString("]"))); - else { - NOT_REACHED("Object.prototype.toString on non-object"); - return kUndefinedValue; - } + if (thisValue.isType()) // XXX why wouldn't this get handled by the above? + return JSValue(new String(widenCString("[object ") + widenCString("Type") + widenCString("]"))); + else + if (thisValue.isFunction()) + return JSValue(new String(widenCString("[object ") + widenCString("Function") + widenCString("]"))); + else { + NOT_REACHED("Object.prototype.toString on non-object"); + return kUndefinedValue; + } } static JSValue Object_valueOf(Context * /* cx */, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) @@ -2051,68 +2068,62 @@ static JSValue Object_valueOf(Context * /* cx */, const JSValue& thisValue, JSVa return thisValue; } -struct IteratorDongle { +class JSIteratorInstance : public JSInstance { +public: + JSIteratorInstance(Context *cx) : JSInstance(cx, NULL) { mType = (JSType *)Object_Type; } + virtual ~JSIteratorInstance() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSIteratorInstance", s, t); return t; } + void operator delete(void* t) { trace_release("JSIteratorInstance", t); STD::free(t); } +#endif JSObject *obj; PropertyIterator it; }; static JSValue Object_forin(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - JSObject *obj = NULL; - if (thisValue.isType()) - obj = thisValue.type; - else - if (thisValue.isFunction()) - obj = thisValue.function; - else - if (thisValue.isObject()) - obj = thisValue.object; - else - NOT_REACHED("Non object for for..in"); + JSObject *obj = thisValue.getObjectValue(); - JSObject *iteratorObject = Object_Type->newInstance(cx); - IteratorDongle *itDude = new IteratorDongle(); - itDude->obj = obj; - itDude->it = obj->mProperties.begin(); + JSIteratorInstance *itInst = new JSIteratorInstance(cx); + itInst->obj = obj; + itInst->it = obj->mProperties.begin(); while (true) { - while (itDude->it == itDude->obj->mProperties.end()) { - itDude->obj = itDude->obj->mPrototype; - if (itDude->obj == NULL) + while (itInst->it == itInst->obj->mProperties.end()) { + if (itInst->obj->mPrototype.isNull()) return kNullValue; - itDude->it = itDude->obj->mProperties.begin(); + itInst->obj = itInst->obj->mPrototype.getObjectValue(); + itInst->it = itInst->obj->mProperties.begin(); } - if (PROPERTY_ATTR(itDude->it) & Property::Enumerable) + if (PROPERTY_ATTR(itInst->it) & Property::Enumerable) break; - itDude->it++; + itInst->it++; } - JSValue v(&PROPERTY_NAME(itDude->it)); - iteratorObject->setProperty(cx, cx->mWorld.identifiers["value"], 0, v); - iteratorObject->mPrivate = itDude; - return JSValue(iteratorObject); + JSValue v(&PROPERTY_NAME(itInst->it)); + itInst->setProperty(cx, cx->mWorld.identifiers["value"], 0, v); + return JSValue(itInst); } static JSValue Object_next(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/) { JSValue iteratorValue = argv[0]; - ASSERT(iteratorValue.isObject()); - JSObject *iteratorObject = iteratorValue.object; + ASSERT(iteratorValue.isInstance()); + JSIteratorInstance *itInst = checked_cast(iteratorValue.instance); - IteratorDongle *itDude = (IteratorDongle *)(iteratorObject->mPrivate); - itDude->it++; + itInst->it++; while (true) { - while (itDude->it == itDude->obj->mProperties.end()) { - itDude->obj = itDude->obj->mPrototype; - if (itDude->obj == NULL) + while (itInst->it == itInst->obj->mProperties.end()) { + if (itInst->obj->mPrototype.isNull()) return kNullValue; - itDude->it = itDude->obj->mProperties.begin(); + itInst->obj = itInst->obj->mPrototype.getObjectValue(); + itInst->it = itInst->obj->mProperties.begin(); } - if (PROPERTY_ATTR(itDude->it) & Property::Enumerable) + if (PROPERTY_ATTR(itInst->it) & Property::Enumerable) break; - itDude->it++; + itInst->it++; } - JSValue v(&PROPERTY_NAME(itDude->it)); - iteratorObject->setProperty(cx, cx->mWorld.identifiers["value"], 0, v); + JSValue v(&PROPERTY_NAME(itInst->it)); + itInst->setProperty(cx, cx->mWorld.identifiers["value"], 0, v); return iteratorValue; } @@ -2129,7 +2140,7 @@ static JSValue Function_Constructor(Context *cx, const JSValue& thisValue, JSVal JSValue v = thisValue; if (v.isNull()) v = Function_Type->newInstance(cx); - ASSERT(v.isObject()); + ASSERT(v.isInstance()); String s; if (argc == 0) @@ -2177,16 +2188,17 @@ static JSValue Function_Constructor(Context *cx, const JSValue& thisValue, JSVal } /***************************************************************/ - JSObject *fncPrototype = Object_Type->newInstance(cx); - fncPrototype->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, Property::Enumerable, Object_Type, JSValue(fnc)); - fnc->defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::Enumerable, Object_Type, JSValue(fncPrototype)); + JSValue fncPrototype = Object_Type->newInstance(cx); + ASSERT(fncPrototype.isObject()); + fncPrototype.object->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, Property::Enumerable, Object_Type, JSValue(fnc)); + fnc->defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::Enumerable, Object_Type, fncPrototype); v = JSValue(fnc); return v; } static JSValue Function_toString(Context *, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isFunction() || (thisValue.isObject() && (thisValue.object->getType() == Function_Type)) ); + ASSERT(thisValue.getType() == Function_Type); return JSValue(new String(widenCString("function () { }"))); } @@ -2204,11 +2216,11 @@ static JSValue Function_hasInstance(Context *cx, const JSValue& thisValue, JSVal if (!p.isObject()) cx->reportError(Exception::typeError, "HasInstance: Function has non-object prototype"); - JSObject *V = v.object->mPrototype; - while (V) { - if (V == p.object) + JSValue V = v.object->mPrototype; + while (!V.isNull()) { + if (V.getObjectValue() == p.object) return kTrueValue; - V = V->mPrototype; + V = V.getObjectValue()->mPrototype; } return kFalseValue; } @@ -2280,12 +2292,12 @@ static JSValue Number_Constructor(Context *cx, const JSValue& thisValue, JSValue JSValue v = thisValue; if (v.isNull()) v = Number_Type->newInstance(cx); - ASSERT(v.isObject()); - JSObject *thisObj = v.object; + ASSERT(v.isInstance()); + JSNumberInstance *numInst = checked_cast(v.instance); if (argc > 0) - thisObj->mPrivate = (void *)(new float64(argv[0].toNumber(cx).f64)); + numInst->mValue = argv[0].toNumber(cx).f64; else - thisObj->mPrivate = (void *)(new float64(0.0)); + numInst->mValue = 0.0; return v; } @@ -2299,18 +2311,18 @@ static JSValue Number_TypeCast(Context *cx, const JSValue& /*thisValue*/, JSValu static JSValue Number_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - if (!thisValue.isObject() || (thisValue.getType() != Number_Type)) + if (thisValue.getType() != Number_Type) cx->reportError(Exception::typeError, "Number.toString called on something other than a Number object"); - JSObject *thisObj = thisValue.object; - return JSValue(numberToString(*((float64 *)(thisObj->mPrivate)))); + JSNumberInstance *numInst = checked_cast(thisValue.instance); + return JSValue(numberToString(numInst->mValue)); } static JSValue Number_valueOf(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - if (!thisValue.isObject() || (thisValue.getType() != Number_Type)) + if (thisValue.getType() != Number_Type) cx->reportError(Exception::typeError, "Number.valueOf called on something other than a Number object"); - JSObject *thisObj = thisValue.object; - return JSValue(*((float64 *)(thisObj->mPrivate))); + JSNumberInstance *numInst = checked_cast(thisValue.instance); + return JSValue(numberToString(numInst->mValue)); } static JSValue Integer_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) @@ -2318,25 +2330,24 @@ static JSValue Integer_Constructor(Context *cx, const JSValue& thisValue, JSValu JSValue v = thisValue; if (v.isNull()) v = Integer_Type->newInstance(cx); - ASSERT(v.isObject()); - JSObject *thisObj = v.object; + ASSERT(v.isInstance()); + JSNumberInstance *numInst = checked_cast(v.instance); if (argc > 0) { float64 d = argv[0].toNumber(cx).f64; bool neg = (d < 0); d = fd::floor(neg ? -d : d); d = neg ? -d : d; - thisObj->mPrivate = (void *)(new double(d)); + numInst->mValue = d; } else - thisObj->mPrivate = (void *)(new double(0.0)); + numInst->mValue = 0.0; return v; } static JSValue Integer_toString(Context *, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - return JSValue(numberToString(*((double *)(thisObj->mPrivate)))); + JSNumberInstance *numInst = checked_cast(thisValue.instance); + return JSValue(numberToString(numInst->mValue)); } @@ -2346,12 +2357,12 @@ static JSValue Boolean_Constructor(Context *cx, const JSValue& thisValue, JSValu JSValue v = thisValue; if (v.isNull()) v = Boolean_Type->newInstance(cx); - ASSERT(v.isObject()); - JSObject *thisObj = v.object; + ASSERT(v.isInstance()); + JSBooleanInstance *thisObj = checked_cast(v.instance); if (argc > 0) - thisObj->mPrivate = (void *)(argv[0].toBoolean(cx).boolean); + thisObj->mValue = argv[0].toBoolean(cx).boolean; else - thisObj->mPrivate = (void *)(false); + thisObj->mValue = false; return v; } @@ -2365,11 +2376,11 @@ static JSValue Boolean_TypeCast(Context *cx, const JSValue& /*thisValue*/, JSVal static JSValue Boolean_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - if (thisObj->mType != Boolean_Type) + if (thisValue.getType() != Boolean_Type) cx->reportError(Exception::typeError, "Boolean.toString can only be applied to Boolean objects"); - if (thisObj->mPrivate != 0) + ASSERT(thisValue.isInstance()); + JSBooleanInstance *thisObj = checked_cast(thisValue.instance); + if (thisObj->mValue) return JSValue(&cx->True_StringAtom); else return JSValue(&cx->False_StringAtom); @@ -2377,11 +2388,11 @@ static JSValue Boolean_toString(Context *cx, const JSValue& thisValue, JSValue * static JSValue Boolean_valueOf(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - if (thisObj->mType != Boolean_Type) + if (thisValue.getType() != Boolean_Type) cx->reportError(Exception::typeError, "Boolean.valueOf can only be applied to Boolean objects"); - if (thisObj->mPrivate != 0) + ASSERT(thisValue.isInstance()); + JSBooleanInstance *thisObj = checked_cast(thisValue.instance); + if (thisObj->mValue) return kTrueValue; else return kFalseValue; @@ -2392,15 +2403,15 @@ static JSValue GenericError_Constructor(Context *cx, const JSValue& thisValue, J JSValue v = thisValue; if (v.isNull()) v = errType->newInstance(cx); - ASSERT(v.isObject()); - JSObject *thisObj = v.object; + ASSERT(v.isInstance()); + JSObject *thisInst = v.instance; 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)); + thisInst->defineVariable(cx, cx->Message_StringAtom, NULL, Property::NoAttribute, String_Type, msg); + thisInst->defineVariable(cx, cx->Name_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(errType->mClassName)); return v; } @@ -2409,53 +2420,55 @@ JSValue RegExp_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, JSValue thatValue = thisValue; if (thatValue.isNull()) thatValue = RegExp_Type->newInstance(cx); - ASSERT(thatValue.isObject()); - JSObject *thisObj = thatValue.object; + ASSERT(thatValue.isInstance()); + JSRegExpInstance *thisInst = checked_cast(thatValue.instance); + REuint32 flags = 0; const String *regexpStr = &cx->Empty_StringAtom; const String *flagStr = &cx->Empty_StringAtom; if (argc > 0) { - if (argv[0].isObject() && (argv[0].object->mType == RegExp_Type)) { + if (argv[0].getType() == RegExp_Type) { + ASSERT(argv[0].isInstance()); if ((argc == 1) || argv[1].isUndefined()) { ContextStackReplacement csr(cx); - argv[0].object->getProperty(cx, cx->Source_StringAtom, CURRENT_ATTR); + argv[0].instance->getProperty(cx, cx->Source_StringAtom, CURRENT_ATTR); JSValue src = cx->popValue(); ASSERT(src.isString()); regexpStr = src.string; - REParseState *other = (REParseState *)(argv[0].object->mPrivate); - String *newFlagStr = new String; - if (other->flags & GLOBAL) *newFlagStr += "g"; - if (other->flags & IGNORECASE) *newFlagStr += "i"; - if (other->flags & MULTILINE) *newFlagStr += "m"; - flagStr = newFlagStr; + REState *other = (checked_cast(argv[0].instance))->mRegExp; + flags = other->flags; } else cx->reportError(Exception::typeError, "Illegal RegExp constructor args"); } else regexpStr = argv[0].toString(cx).string; - if ((argc > 1) && !argv[1].isUndefined()) + if ((argc > 1) && !argv[1].isUndefined()) { flagStr = argv[1].toString(cx).string; + if (parseFlags(flagStr->begin(), flagStr->length(), &flags) != RE_NO_ERROR) { + cx->reportError(Exception::syntaxError, "Failed to parse RegExp : '{0}'", *regexpStr + "/" + *flagStr); // XXX error message? + } + } } - REParseState *parseResult = REParse(regexpStr->begin(), regexpStr->length(), flagStr->begin(), flagStr->length(), false); - if (parseResult) { - thisObj->mPrivate = parseResult; + REState *pState = REParse(regexpStr->begin(), regexpStr->length(), flags, RE_VERSION_1); + if (pState) { + thisInst->mRegExp = pState; // XXX ECMA spec says these are DONTENUM, but SpiderMonkey and test suite disagree /* - thisObj->defineVariable(cx, cx->Source_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), String_Type, JSValue(regexpStr)); - thisObj->defineVariable(cx, cx->Global_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (parseResult->flags & GLOBAL) ? kTrueValue : kFalseValue); - thisObj->defineVariable(cx, cx->IgnoreCase_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (parseResult->flags & IGNORECASE) ? kTrueValue : kFalseValue); - thisObj->defineVariable(cx, cx->Multiline_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (parseResult->flags & MULTILINE) ? kTrueValue : kFalseValue); - thisObj->defineVariable(cx, cx->LastIndex_StringAtom, NULL, Property::DontDelete, Number_Type, kPositiveZero); + thisInst->defineVariable(cx, cx->Source_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), String_Type, JSValue(regexpStr)); + thisInst->defineVariable(cx, cx->Global_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (pState->flags & GLOBAL) ? kTrueValue : kFalseValue); + thisInst->defineVariable(cx, cx->IgnoreCase_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (pState->flags & IGNORECASE) ? kTrueValue : kFalseValue); + thisInst->defineVariable(cx, cx->Multiline_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (pState->flags & MULTILINE) ? kTrueValue : kFalseValue); + thisInst->defineVariable(cx, cx->LastIndex_StringAtom, NULL, Property::DontDelete, Number_Type, kPositiveZero); */ - thisObj->defineVariable(cx, cx->Source_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), String_Type, JSValue(regexpStr)); - thisObj->defineVariable(cx, cx->Global_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (parseResult->flags & GLOBAL) ? kTrueValue : kFalseValue); - thisObj->defineVariable(cx, cx->IgnoreCase_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (parseResult->flags & IGNORECASE) ? kTrueValue : kFalseValue); - thisObj->defineVariable(cx, cx->Multiline_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (parseResult->flags & MULTILINE) ? kTrueValue : kFalseValue); - thisObj->defineVariable(cx, cx->LastIndex_StringAtom, NULL, (Property::DontDelete | Property::Enumerable), Number_Type, kPositiveZero); + thisInst->defineVariable(cx, cx->Source_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), String_Type, JSValue(regexpStr)); + thisInst->defineVariable(cx, cx->Global_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (pState->flags & RE_GLOBAL) ? kTrueValue : kFalseValue); + thisInst->defineVariable(cx, cx->IgnoreCase_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (pState->flags & RE_IGNORECASE) ? kTrueValue : kFalseValue); + thisInst->defineVariable(cx, cx->Multiline_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (pState->flags & RE_MULTILINE) ? kTrueValue : kFalseValue); + thisInst->defineVariable(cx, cx->LastIndex_StringAtom, NULL, (Property::DontDelete | Property::Enumerable), Number_Type, kPositiveZero); } else { - cx->reportError(Exception::syntaxError, "Failed to parse RegExp : '{0}'", *regexpStr + "/" + *flagStr); // XXX error message? + cx->reportError(Exception::syntaxError, "Failed to parse RegExp : '{0}'", "/" + *regexpStr + "/" + *flagStr); // XXX error message? } return thatValue; } @@ -2463,7 +2476,7 @@ JSValue RegExp_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, static JSValue RegExp_TypeCast(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { if (argc > 0) { - if ((argv[0].isObject() && (argv[0].object->mType == RegExp_Type)) + if ((argv[0].getType() == RegExp_Type) && ((argc == 1) || argv[1].isUndefined())) return argv[0]; } @@ -2473,76 +2486,85 @@ static JSValue RegExp_TypeCast(Context *cx, const JSValue& thisValue, JSValue *a static JSValue RegExp_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - if (thisObj->mType != RegExp_Type) + if (thisValue.getType() != RegExp_Type) cx->reportError(Exception::typeError, "RegExp.toString can only be applied to RegExp objects"); - thisObj->getProperty(cx, cx->Source_StringAtom, CURRENT_ATTR); + ASSERT(thisValue.isInstance()); + JSRegExpInstance *thisInst = checked_cast(thisValue.instance); + thisInst->getProperty(cx, cx->Source_StringAtom, CURRENT_ATTR); JSValue src = cx->popValue(); - // XXX not ever expected this except in the one case of RegExp.prototype, which isn't a fully formed + // XXX not ever expecting this except in the one case of RegExp.prototype, which isn't a fully formed // RegExp instance, but has the appropriate type pointer. if (src.isUndefined()) { - ASSERT(thisObj == RegExp_Type->mPrototypeObject); + ASSERT(thisInst == RegExp_Type->mPrototypeObject.instance); return JSValue(&cx->Empty_StringAtom); } String *result = new String("/" + *src.toString(cx).string + "/"); - REParseState *state = (REParseState *)thisObj->mPrivate; - if (state->flags & GLOBAL) *result += "g"; - if (state->flags & IGNORECASE) *result += "i"; - if (state->flags & MULTILINE) *result += "m"; + REState *state = (REState *)thisInst->mRegExp; + if (state->flags & RE_GLOBAL) *result += "g"; + if (state->flags & RE_IGNORECASE) *result += "i"; + if (state->flags & RE_MULTILINE) *result += "m"; return JSValue(result); } JSValue RegExp_exec(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - if (thisObj->mType != RegExp_Type) + if (thisValue.getType() != RegExp_Type) cx->reportError(Exception::typeError, "RegExp.exec can only be applied to RegExp objects"); + ASSERT(thisValue.isInstance()); + JSRegExpInstance *thisInst = checked_cast(thisValue.instance); + JSValue result = kNullValue; if (argc > 0) { ContextStackReplacement csr(cx); - - thisObj->getProperty(cx, cx->LastIndex_StringAtom, CURRENT_ATTR); - JSValue lastIndex = cx->popValue(); - - REParseState *parseResult = (REParseState *)(thisObj->mPrivate); - parseResult->lastIndex = (int)(lastIndex.toInteger(cx).f64); + int32 index = 0; const String *str = argv[0].toString(cx).string; - REState *regexp_result = REExecute(parseResult, str->begin(), str->length()); - if (regexp_result) { - JSArrayInstance *resultArray = (JSArrayInstance *)Array_Type->newInstance(cx); - String *matchStr = new String(str->substr(regexp_result->startIndex, regexp_result->endIndex - regexp_result->startIndex)); - resultArray->setProperty(cx, *numberToString(0), NULL, JSValue(matchStr)); + + RegExp_Type->getProperty(cx, cx->Multiline_StringAtom, CURRENT_ATTR); + JSValue globalMultiline = cx->popValue(); + + if (thisInst->mRegExp->flags & RE_GLOBAL) { + // XXX implement lastIndex as a setter/getter pair instead ??? + thisInst->getProperty(cx, cx->LastIndex_StringAtom, CURRENT_ATTR); + JSValue lastIndex = cx->popValue(); + index = (int32)(lastIndex.toInteger(cx).f64); + } + + REMatchState *match = REExecute(thisInst->mRegExp, str->begin(), index, str->length(), globalMultiline.toBoolean(cx).boolean); + if (match) { + result = Array_Type->newInstance(cx); + String *matchStr = new String(str->substr(match->startIndex, match->endIndex - match->startIndex)); + result.instance->setProperty(cx, *numberToString(0), NULL, JSValue(matchStr)); String *parenStr = &cx->Empty_StringAtom; - for (int32 i = 0; i < regexp_result->n; i++) { - if (regexp_result->parens[i].index != -1) { - String *parenStr = new String(str->substr((uint32)(regexp_result->parens[i].index), (uint32)(regexp_result->parens[i].length))); - resultArray->setProperty(cx, *numberToString(i + 1), NULL, JSValue(parenStr)); + for (int32 i = 0; i < match->parenCount; i++) { + if (match->parens[i].index != -1) { + String *parenStr = new String(str->substr((uint32)(match->parens[i].index), (uint32)(match->parens[i].length))); + result.instance->setProperty(cx, *numberToString(i + 1), NULL, JSValue(parenStr)); } - else - resultArray->setProperty(cx, *numberToString(i + 1), NULL, kUndefinedValue); - + result.instance->setProperty(cx, *numberToString(i + 1), NULL, kUndefinedValue); } // XXX SpiderMonkey also adds 'index' and 'input' properties to the result - resultArray->setProperty(cx, cx->Index_StringAtom, CURRENT_ATTR, JSValue((float64)(regexp_result->startIndex))); - resultArray->setProperty(cx, cx->Input_StringAtom, CURRENT_ATTR, JSValue(str)); - result = JSValue(resultArray); + result.instance->setProperty(cx, cx->Index_StringAtom, CURRENT_ATTR, JSValue((float64)(match->startIndex))); + result.instance->setProperty(cx, cx->Input_StringAtom, CURRENT_ATTR, JSValue(str)); // XXX Set up the SpiderMonkey 'RegExp statics' RegExp_Type->setProperty(cx, cx->LastMatch_StringAtom, CURRENT_ATTR, JSValue(matchStr)); RegExp_Type->setProperty(cx, cx->LastParen_StringAtom, CURRENT_ATTR, JSValue(parenStr)); - String *contextStr = new String(str->substr(0, regexp_result->startIndex)); + String *contextStr = new String(str->substr(0, match->startIndex)); RegExp_Type->setProperty(cx, cx->LeftContext_StringAtom, CURRENT_ATTR, JSValue(contextStr)); - contextStr = new String(str->substr(regexp_result->endIndex, str->length() - regexp_result->endIndex)); + contextStr = new String(str->substr(match->endIndex, str->length() - match->endIndex)); RegExp_Type->setProperty(cx, cx->RightContext_StringAtom, CURRENT_ATTR, JSValue(contextStr)); + + if (thisInst->mRegExp->flags & RE_GLOBAL) { + index = match->endIndex; + thisInst->setProperty(cx, cx->LastIndex_StringAtom, CURRENT_ATTR, JSValue((float64)index)); + } + } - thisObj->setProperty(cx, cx->LastIndex_StringAtom, CURRENT_ATTR, JSValue((float64)(parseResult->lastIndex))); } return result; @@ -2550,14 +2572,17 @@ JSValue RegExp_exec(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 static JSValue RegExp_test(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - if (thisObj->mType != RegExp_Type) + if (thisValue.getType() != RegExp_Type) cx->reportError(Exception::typeError, "RegExp.test can only be applied to RegExp objects"); + ASSERT(thisValue.isInstance()); + JSRegExpInstance *thisInst = checked_cast(thisValue.instance); if (argc > 0) { + ContextStackReplacement csr(cx); const String *str = argv[0].toString(cx).string; - REState *regexp_result = REExecute((REParseState *)(thisObj->mPrivate), str->begin(), str->length()); - if (regexp_result) + RegExp_Type->getProperty(cx, cx->Multiline_StringAtom, CURRENT_ATTR); + JSValue globalMultiline = cx->popValue(); + REMatchState *match = REExecute(thisInst->mRegExp, str->begin(), 0, str->length(), globalMultiline.toBoolean(cx).boolean); + if (match) return kTrueValue; } return kFalseValue; @@ -2573,13 +2598,13 @@ static JSValue Error_toString(Context *cx, const JSValue& thisValue, JSValue * / { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - if ((thisObj->mType != Error_Type) && !thisObj->mType->derivesFrom(Error_Type)) + if ((thisValue.getType() != Error_Type) && !thisValue.getType()->derivesFrom(Error_Type)) cx->reportError(Exception::typeError, "Error.toString can only be applied to Error objects"); - thisObj->getProperty(cx, cx->Message_StringAtom, CURRENT_ATTR); + ASSERT(thisValue.isInstance()); + JSInstance *thisInstance = thisValue.instance; + thisInstance->getProperty(cx, cx->Message_StringAtom, CURRENT_ATTR); JSValue msg = cx->popValue(); - thisObj->getProperty(cx, cx->Name_StringAtom, CURRENT_ATTR); + thisInstance->getProperty(cx, cx->Name_StringAtom, CURRENT_ATTR); JSValue name = cx->popValue(); String *result = new String(*name.toString(cx).string + ":" + *msg.toString(cx).string); @@ -2836,7 +2861,7 @@ static JSValue GlobalObject_unescape(Context *cx, const JSValue& /*thisValue*/, JSFunction::JSFunction(Context *cx, JSType *resultType, ScopeChain *scopeChain) - : JSObject(Function_Type), + : JSInstance(cx, Function_Type), mParameterBarrel(NULL), mActivation(), mByteCode(NULL), @@ -2861,12 +2886,12 @@ JSFunction::JSFunction(Context *cx, JSType *resultType, ScopeChain *scopeChain) mPrototype = Function_Type->mPrototypeObject; mActivation.mContainer = this; defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(Object_Type->newInstance(cx))); - if (mPrototype) - defineVariable(cx, cx->UnderbarPrototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(mPrototype)); + if (!mPrototype.isNull()) + defineVariable(cx, cx->UnderbarPrototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, mPrototype); } JSFunction::JSFunction(Context *cx, NativeCode *code, JSType *resultType) - : JSObject(Function_Type), + : JSInstance(cx, Function_Type), mParameterBarrel(NULL), mActivation(), mByteCode(NULL), @@ -2888,7 +2913,7 @@ JSFunction::JSFunction(Context *cx, NativeCode *code, JSType *resultType) mPrototype = Function_Type->mPrototypeObject; mActivation.mContainer = this; defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(Object_Type->newInstance(cx))); - defineVariable(cx, cx->UnderbarPrototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(mPrototype)); + defineVariable(cx, cx->UnderbarPrototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, mPrototype); } JSValue JSFunction::runParameterInitializer(Context *cx, uint32 a, const JSValue& thisValue, JSValue *argv, uint32 argc) @@ -2943,11 +2968,11 @@ void Context::initClass(JSType *type, ClassDef *cdef, PrototypeFunctions *pdef) StringAtom *name = &mWorld.identifiers[widenCString(pdef->mDef[i].name)]; fun->setFunctionName(name); fun->setParameterCounts(this, pdef->mDef[i].length, 0, 0, false); - type->mPrototypeObject->defineVariable(this, *name, - (NamespaceList *)(NULL), - Property::NoAttribute, - pdef->mDef[i].result, - JSValue(fun)); + type->mPrototypeObject.getObjectValue()->defineVariable(this, *name, + (NamespaceList *)(NULL), + Property::NoAttribute, + pdef->mDef[i].result, + JSValue(fun)); } } type->completeClass(this, mScopeChain); @@ -2966,7 +2991,7 @@ static JSValue arrayMaker(Context *cx, const JSValue& /*thisValue*/, JSValue *ar if ((baseType == Array_Type) && argv[1].isType()) { JSType *elementType = argv[1].type; - JSType *result = new JSArrayType(cx, elementType, NULL, Object_Type); + JSType *result = new JSArrayType(cx, elementType, NULL, Object_Type, kNullValue, kNullValue); result->setDefaultConstructor(cx, Array_Type->getDefaultConstructor()); return JSValue(result); } @@ -3009,11 +3034,11 @@ void Context::initBuiltins() }; - Object_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[0].name)], NULL); - Object_Type->mPrototype->mType = Object_Type; + Object_Type = new JSObjectType(this, &mWorld.identifiers[widenCString(builtInClasses[0].name)], NULL, kNullValue, kNullValue); +// Object_Type->mPrototype->mType = Object_Type; Object_Type->mIsDynamic = true; // XXX aren't all the built-ins thus? - Type_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[1].name)], Object_Type); + Type_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[1].name)], Object_Type, kNullValue, kNullValue); Object_Type->mType = Type_Type; // @@ -3025,56 +3050,62 @@ void Context::initBuiltins() JSFunction *funProto = new JSFunction(this, Object_Type, NULL); funProto->mPrototype = Object_Type->mPrototypeObject; - funProto->defineVariable(this, UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(funProto->mPrototype)); - Function_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[2].name)], Object_Type, funProto); + funProto->defineVariable(this, UnderbarPrototype_StringAtom, NULL, 0, Object_Type, funProto->mPrototype); + Function_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[2].name)], Object_Type, JSValue(funProto), kNullValue); Function_Type->mPrototype = Function_Type->mPrototypeObject; funProto->mType = Function_Type; // now we can bootstrap the Object prototype (__proto__) to be the Function prototype Object_Type->mPrototype = Function_Type->mPrototypeObject; - Object_Type->setProperty(this, UnderbarPrototype_StringAtom, (NamespaceList *)NULL, JSValue(Object_Type->mPrototype)); + Object_Type->setProperty(this, UnderbarPrototype_StringAtom, (NamespaceList *)NULL, Object_Type->mPrototype); - JSObject *numProto = Object_Type->newInstance(this); - Number_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[3].name)], Object_Type, numProto, Function_Type->mPrototypeObject); - numProto->mType = Number_Type; - numProto->mPrivate = (void *)(new float64(0.0)); + JSNumberInstance *numProto = new JSNumberInstance(this); + Number_Type = new JSNumberType(this, &mWorld.identifiers[widenCString(builtInClasses[3].name)], Object_Type, JSValue(numProto), Function_Type->mPrototypeObject); + numProto->mType = Number_Type; - Integer_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[4].name)], Object_Type); + Integer_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[4].name)], Object_Type, kNullValue, kNullValue); - JSObject *strProto = new JSStringInstance(this, NULL); - String_Type = new JSStringType(this, &mWorld.identifiers[widenCString(builtInClasses[5].name)], Object_Type, strProto, Function_Type->mPrototypeObject); - strProto->mType = String_Type; - strProto->mPrivate = (void *)(&Empty_StringAtom); + JSStringInstance *strProto = new JSStringInstance(this); + String_Type = new JSStringType(this, &mWorld.identifiers[widenCString(builtInClasses[5].name)], Object_Type, JSValue(strProto), Function_Type->mPrototypeObject); + strProto->mValue = &Empty_StringAtom; strProto->mPrototype = Function_Type->mPrototypeObject; + strProto->mType = String_Type; - JSArrayInstance *arrayProto = new JSArrayInstance(this, NULL); - Array_Type = new JSArrayType(this, Object_Type, &mWorld.identifiers[widenCString(builtInClasses[6].name)], Object_Type, arrayProto, Function_Type->mPrototypeObject); - arrayProto->mType = Array_Type; + JSArrayInstance *arrayProto = new JSArrayInstance(this); + Array_Type = new JSArrayType(this, Object_Type, &mWorld.identifiers[widenCString(builtInClasses[6].name)], Object_Type, JSValue(arrayProto), Function_Type->mPrototypeObject); + arrayProto->mType = Array_Type; - JSObject *boolProto = Object_Type->newInstance(this); - Boolean_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[7].name)], Object_Type, boolProto, Function_Type->mPrototypeObject); - boolProto->mType = Boolean_Type; - boolProto->mPrivate = 0; + JSBooleanInstance *boolProto = new JSBooleanInstance(this); + Boolean_Type = new JSBooleanType(this, &mWorld.identifiers[widenCString(builtInClasses[7].name)], Object_Type, JSValue(boolProto), Function_Type->mPrototypeObject); + boolProto->mValue = false; + boolProto->mType = Boolean_Type; - Void_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[8].name)], Object_Type); - Unit_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[9].name)], Object_Type); - Attribute_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[10].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, NULL, Function_Type->mPrototypeObject); - 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); + JSDateInstance *dateProto = new JSDateInstance(this); + Date_Type = new JSDateType(this, &mWorld.identifiers[widenCString(builtInClasses[12].name)], Object_Type, JSValue(dateProto), Function_Type->mPrototypeObject); + dateProto->mType = Date_Type; + + Void_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[8].name)], Object_Type, kNullValue, kNullValue); + Unit_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[9].name)], Object_Type, kNullValue, kNullValue); + Attribute_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[10].name)], Object_Type, kNullValue, kNullValue); + NamedArgument_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[11].name)], Object_Type, kNullValue, kNullValue); + Null_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[13].name)], Object_Type, kNullValue, kNullValue); + Error_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[14].name)], Object_Type, kNullValue, kNullValue); + EvalError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[15].name)], Error_Type, kNullValue, kNullValue); + RangeError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[16].name)], Error_Type, kNullValue, kNullValue); + ReferenceError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[17].name)], Error_Type, kNullValue, kNullValue); + SyntaxError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[18].name)], Error_Type, kNullValue, kNullValue); + TypeError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[19].name)], Error_Type, kNullValue, kNullValue); + UriError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[20].name)], Error_Type, kNullValue, kNullValue); // XXX RegExp.prototype is set to a RegExp instance, which isn't ECMA (it's supposed to be an Object instance) but // is SpiderMonkey compatible. - RegExp_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[21].name)], Object_Type); - Package_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[22].name)], Object_Type); + JSRegExpInstance *regExpProto = new JSRegExpInstance(this); + RegExp_Type = new JSRegExpType(this, &mWorld.identifiers[widenCString(builtInClasses[21].name)], Object_Type, JSValue(regExpProto), Function_Type->mPrototypeObject); + regExpProto->mPrototype = Object_Type->mPrototypeObject; + regExpProto->mType = RegExp_Type; + + Package_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[22].name)], Object_Type, kNullValue, kNullValue); @@ -3134,7 +3165,7 @@ void Context::initBuiltins() }; ASSERT(mGlobal); - *mGlobal = Object_Type->newInstance(this); + *mGlobal = Object_Type->newInstance(this).object; initClass(Object_Type, &builtInClasses[0], new PrototypeFunctions(&objectProtos[0]) ); initClass(Type_Type, &builtInClasses[1], NULL ); @@ -3159,15 +3190,15 @@ void Context::initBuiltins() initClass(UriError_Type, &builtInClasses[20], new PrototypeFunctions(&errorProtos[0])); initClass(RegExp_Type, &builtInClasses[21], new PrototypeFunctions(®expProtos[0])); - Type_Type->defineUnaryOperator(Index, new JSFunction(this, arrayMaker, Type_Type)); + defineOperator(Index, Type_Type, 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); - Array_Type->defineUnaryOperator(Index, new JSFunction(this, Array_GetElement, Object_Type)); - Array_Type->defineUnaryOperator(IndexEqual, new JSFunction(this, Array_SetElement, Object_Type)); + defineOperator(Index, Array_Type, new JSFunction(this, Array_GetElement, Object_Type)); + defineOperator(IndexEqual, Array_Type, new JSFunction(this, Array_SetElement, Object_Type)); Array_Type->mTypeCast = new JSFunction(this, Array_Constructor, Array_Type); Array_Type->defineVariable(this, Length_StringAtom, NULL, Property::NoAttribute, Number_Type, JSValue(1.0)); @@ -3345,8 +3376,8 @@ Context::Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags) initBuiltins(); } - JSType *MathType = new JSType(this, &mWorld.identifiers[widenCString("Math")], Object_Type, Object_Type->mPrototypeObject); - JSObject *mathObj = MathType->newInstance(this); + JSType *MathType = new JSType(this, &mWorld.identifiers[widenCString("Math")], Object_Type, Object_Type->mPrototypeObject, kNullValue); + JSObject *mathObj = MathType->newInstance(this).instance; getGlobalObject()->defineVariable(this, Math_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Object_Type, JSValue(mathObj)); initMathObject(this, mathObj); diff --git a/js2/src/js2runtime.h b/js2/src/js2runtime.h index 226d35b8d3f0..153aa11de8e1 100644 --- a/js2/src/js2runtime.h +++ b/js2/src/js2runtime.h @@ -50,6 +50,7 @@ #include "tracer.h" #include "collector.h" +#include "regexp.h" namespace JavaScript { namespace JS2Runtime { @@ -99,6 +100,10 @@ static const double two31 = 2147483648.0; class JSArrayType; class JSStringType; class Context; + class NamedArgument; + class JSInstance; + class Attribute; + class Package; extern JSType *Object_Type; // the base type for all types @@ -117,7 +122,6 @@ static const double two31 = 2147483648.0; extern JSType *Attribute_Type; // used to define 'prototype' 'static' etc & Namespace values extern JSType *Package_Type; - extern JSType *NamedArgument_Type; extern JSType *Date_Type; extern JSType *RegExp_Type; @@ -132,46 +136,63 @@ static const double two31 = 2147483648.0; union { float64 f64; JSObject *object; + JSInstance *instance; JSFunction *function; const String *string; JSType *type; bool boolean; + NamedArgument *namedArg; + Attribute *attribute; + Package *package; }; typedef enum { undefined_tag, f64_tag, object_tag, + instance_tag, function_tag, type_tag, boolean_tag, string_tag, - null_tag + null_tag, + namedArg_tag, + attribute_tag, + package_tag } Tag; Tag tag; JSValue() : f64(0.0), tag(undefined_tag) {} explicit JSValue(float64 f64) : f64(f64), tag(f64_tag) {} explicit JSValue(JSObject *object) : object(object), tag(object_tag) { ASSERT(object); } + explicit JSValue(JSInstance *instance) : instance(instance), tag(instance_tag) { ASSERT(instance); } explicit JSValue(JSFunction *function) : function(function), tag(function_tag) { ASSERT(function); } explicit JSValue(JSType *type) : type(type), tag(type_tag) { ASSERT(type); } explicit JSValue(const String *string) : string(string), tag(string_tag) { ASSERT(string); } explicit JSValue(bool boolean) : boolean(boolean), tag(boolean_tag) {} + explicit JSValue(NamedArgument *arg) : namedArg(arg), tag(namedArg_tag) { ASSERT(arg); } + explicit JSValue(Attribute *attr) : attribute(attr), tag(attribute_tag) { ASSERT(attribute); } + explicit JSValue(Package *pkg) : package(pkg), tag(package_tag) { ASSERT(pkg); } explicit JSValue(Tag tag) : tag(tag) {} float64& operator=(float64 f64) { return (tag = f64_tag, this->f64 = f64); } JSObject*& operator=(JSObject* object) { return (tag = object_tag, this->object = object); } + JSInstance*& operator=(JSInstance* instance) { return (tag = instance_tag, this->instance = instance); } JSType*& operator=(JSType* type) { return (tag = type_tag, this->type = type); } JSFunction*& operator=(JSFunction* function) { return (tag = function_tag, this->function = function); } bool& operator=(bool boolean) { return (tag = boolean_tag, this->boolean = boolean); } bool isObject() const { return (tag == object_tag); } + bool isInstance() const { return (tag == instance_tag); } bool isNumber() const { return (tag == f64_tag); } bool isBool() const { return (tag == boolean_tag); } bool isType() const { return (tag == type_tag); } bool isFunction() const { return (tag == function_tag); } bool isString() const { return (tag == string_tag); } - bool isPrimitive() const { return (tag != object_tag) && (tag != type_tag) && (tag != function_tag); } + bool isPrimitive() const { return isNumber() || isBool() || isString() || isUndefined() || isNull(); } + bool isNamedArg() const { return (tag == namedArg_tag); } + bool isAttribute() const { return (tag == attribute_tag); } + bool isPackage() const { return (tag == package_tag); } bool isUndefined() const { return (tag == undefined_tag); } bool isNull() const { return (tag == null_tag); } @@ -192,13 +213,16 @@ static const double two31 = 2147483648.0; JSValue toUInt32(Context *cx) const { return valueToUInt32(cx, *this); } JSValue toUInt16(Context *cx) const { return valueToUInt16(cx, *this); } JSValue toInt32(Context *cx) const { return valueToInt32(cx, *this); } - JSValue toObject(Context *cx) const { return ((isObject() || isType() || isFunction()) ? + JSValue toObject(Context *cx) const { return ((isObject() || isType() || isFunction() || isInstance() || isPackage()) ? *this : valueToObject(cx, *this)); } JSValue toBoolean(Context *cx) const { return (isBool() ? *this : valueToBoolean(cx, *this)); } float64 getNumberValue() const; const String *getStringValue() const; bool getBoolValue() const; + JSObject *getObjectValue() const; + + JSObject *toObjectValue(Context *cx) const; /* These are for use in 'toPrimitive' calls */ enum Hint { @@ -524,23 +548,15 @@ XXX ...couldn't get this to work... class JSObject { public: // The generic Javascript object. Every JS2 object is one of these - JSObject(JSType *type = Object_Type) : mType(type), mPrivate(NULL), mPrototype(NULL) { } + JSObject() : mPrototype(kNullValue) { } virtual ~JSObject() { } // keeping gcc happy - // every object has a type - JSType *mType; - // the property data is kept (or referenced from) here PropertyMap mProperties; - // Every JSObject has a private part - void *mPrivate; - // Every JSObject (except the Ur-object) has a prototype - JSObject *mPrototype; - - JSType *getType() const { return mType; } + JSValue mPrototype; virtual bool isDynamic() { return true; } @@ -638,11 +654,8 @@ XXX ...couldn't get this to work... */ Collector::size_type scan(Collector* collector) { - mType = (JSType*) collector->copy(mType); // enumerate property map elements. - // what is mPrivate? - mPrivate = collector->copy(mPrivate); - mPrototype = (JSObject*) collector->copy(mPrototype); + // scan mPrototype. return sizeof(JSObject); } @@ -681,7 +694,7 @@ XXX ...couldn't get this to work... public: JSInstance(Context *cx, JSType *type) - : JSObject(type), mInstanceValues(NULL) { if (type) initInstance(cx, type); } + : JSObject(), mType(type), mInstanceValues(NULL) { if (type) initInstance(cx, type); } virtual ~JSInstance() { } // keeping gcc happy @@ -700,8 +713,12 @@ XXX ...couldn't get this to work... mInstanceValues[index] = v; } - virtual bool isDynamic(); + JSType *getType() const { return mType; } + + bool isDynamic(); + // the class that created this instance + JSType *mType; JSValue *mInstanceValues; @@ -714,6 +731,7 @@ XXX ...couldn't get this to work... Collector::size_type scan(Collector* collector) { JSObject::scan(collector); + mType = (JSType*) collector->copy(mType); // FIXME: need some kind of array operator new[] (gc) thing. // this will have to use an extra word to keep track of the // element count. @@ -751,13 +769,20 @@ XXX ...couldn't get this to work... - class JSType : public JSObject { + class JSType : public JSInstance { + protected: + // XXX these initializations are for ParameterBarrel & Activation which are 'types' only + // because they take advantage of the slotted variable handling - maybe an interim class + // for just that purpose would be better... + JSType() : JSInstance(NULL, NULL), mSuperType(NULL), mIsDynamic(false), mVariableCount(0) { } + public: - JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL); - JSType(JSType *xClass); // used for constructing the static component type + JSType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto); virtual ~JSType() { } // keeping gcc happy + + void setSuperType(JSType *super); void setStaticInitializer(Context *cx, JSFunction *f); @@ -767,7 +792,7 @@ XXX ...couldn't get this to work... // construct a new (empty) instance of this class - virtual JSInstance *newInstance(Context *cx); + virtual JSValue newInstance(Context *cx); Property *defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type); @@ -794,16 +819,6 @@ XXX ...couldn't get this to work... void defineSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f); - void defineUnaryOperator(Operator which, JSFunction *f) - { - mUnaryOperators[which] = f; - } - - JSFunction *getUnaryOperator(Operator which) - { - return mUnaryOperators[which]; // XXX Umm, aren't these also getting inherited? - } - void setDefaultConstructor(Context * /*cx*/, JSFunction *f) { mDefaultConstructor = f; @@ -827,16 +842,17 @@ XXX ...couldn't get this to work... JSFunction *getDefaultConstructor() { return mDefaultConstructor; } JSFunction *getTypeCastFunction() { return mTypeCast; } - JSValue getUninitializedValue() { return mUninitializedValue; } + JSValue getUninitializedValue() { return mUninitializedValue; } + // Generates defaultConstructor if one doesn't exist - // assumes that the super types have been completed already void completeClass(Context *cx, ScopeChain *scopeChain); - virtual bool isDynamic() { return mIsDynamic; } + virtual bool isDynamic() { return mIsDynamic; } - JSType *mSuperType; // NULL implies that this is the base Object + JSType *mSuperType; // NULL implies that this is the base Object - uint32 mVariableCount; + uint32 mVariableCount; // number of instance variables JSFunction *mInstanceInitializer; JSFunction *mDefaultConstructor; JSFunction *mTypeCast; @@ -847,14 +863,12 @@ XXX ...couldn't get this to work... const StringAtom *mClassName; const StringAtom *mPrivateNamespace; - JSFunction *mUnaryOperators[OperatorCount]; // XXX too wasteful - bool mIsDynamic; JSValue mUninitializedValue; // the value for uninitialized vars - JSObject *mPrototypeObject; // becomes the prototype for any instance - + JSValue mPrototypeObject; // becomes the prototype for any instance + // DEBUG void printSlotsNStuff(Formatter& f) const; protected: @@ -873,11 +887,8 @@ XXX ...couldn't get this to work... // scan mMethods. // scan mClassName. // scan mPrivateNamespace. - uint32 i; - for (i = 0; i < OperatorCount; ++i) - mUnaryOperators[i] = (JSFunction*) collector->copy(mUnaryOperators[i]); // scan mUninitializedValue. - mPrototypeObject = (JSObject*) collector->copy(mPrototypeObject); + // scan mPrototypeObject. return sizeof(JSType); } @@ -897,17 +908,27 @@ XXX ...couldn't get this to work... Formatter& operator<<(Formatter& f, const JSType& obj); + // + // we have to have unique instance classes whenever the instance requires + // extra data - otherwise where else does this data go? + // + // XXX could instead have dynamically constructed the various classes with + // the appropriate number of instance slots and used the generic newInstance + // mechanism. Then the extra data would just be instance->slot[0,1...] + // + // XXX maybe could have implemented length (for string) as a getter/setter pair + // (would still require StringType, but the new instances would all get + // the pair of methods for free) + // class JSArrayInstance : public JSInstance { public: - JSArrayInstance(Context *cx, JSType * /*type*/) : JSInstance(cx, NULL), mLength(0) { mType = (JSType *)Array_Type; mPrototype = Object_Type->mPrototypeObject; } + JSArrayInstance(Context *cx) : JSInstance(cx, NULL), mLength(0) { mType = (JSType *)Array_Type; mPrototype = Object_Type->mPrototypeObject; } virtual ~JSArrayInstance() { } // keeping gcc happy #ifdef DEBUG void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSArrayInstance", s, t); return t; } void operator delete(void* t) { trace_release("JSArrayInstance", t); STD::free(t); } #endif - - // XXX maybe could have implemented length as a getter/setter pair? void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v); void getProperty(Context *cx, const String &name, NamespaceList *names); @@ -915,12 +936,11 @@ XXX ...couldn't get this to work... bool deleteProperty(Context *cx, const String &name, NamespaceList *names); uint32 mLength; - }; class JSArrayType : public JSType { public: - JSArrayType(Context *cx, JSType *elementType, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL) + JSArrayType(Context *cx, JSType *elementType, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) : JSType(cx, name, super, protoObj, typeProto), mElementType(elementType) { } @@ -931,51 +951,154 @@ XXX ...couldn't get this to work... void operator delete(void* t) { trace_release("JSArrayType", t); STD::free(t); } #endif - JSInstance *newInstance(Context *cx); - + JSValue newInstance(Context *cx); JSType *mElementType; - }; class JSStringInstance : public JSInstance { public: - JSStringInstance(Context *cx, JSType * /*type*/) : JSInstance(cx, NULL), mLength(0) { mType = (JSType *)String_Type; } + JSStringInstance(Context *cx) : JSInstance(cx, NULL), mValue(NULL) { mType = (JSType *)String_Type; } virtual ~JSStringInstance() { } // keeping gcc happy - #ifdef DEBUG void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSStringInstance", s, t); return t; } void operator delete(void* t) { trace_release("JSStringInstance", t); STD::free(t); } #endif - void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v); void getProperty(Context *cx, const String &name, NamespaceList *names); bool hasOwnProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p); bool deleteProperty(Context *cx, const String &name, NamespaceList *names); - - uint32 mLength; - + const String *mValue; }; class JSStringType : public JSType { public: - JSStringType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL) + JSStringType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) : JSType(cx, name, super, protoObj, typeProto) { } virtual ~JSStringType() { } // keeping gcc happy - #ifdef DEBUG void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSStringType", s, t); return t; } void operator delete(void* t) { trace_release("JSStringType", t); STD::free(t); } #endif - - JSInstance *newInstance(Context *cx); - + JSValue newInstance(Context *cx); }; + class JSBooleanInstance : public JSInstance { + public: + JSBooleanInstance(Context *cx) : JSInstance(cx, NULL), mValue(false) { mType = (JSType *)Boolean_Type; } + virtual ~JSBooleanInstance() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSBooleanInstance", s, t); return t; } + void operator delete(void* t) { trace_release("JSBooleanInstance", t); STD::free(t); } +#endif + bool mValue; + }; + class JSBooleanType : public JSType { + public: + JSBooleanType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) + : JSType(cx, name, super, protoObj, typeProto) + { + } + virtual ~JSBooleanType() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSBooleanType", s, t); return t; } + void operator delete(void* t) { trace_release("JSBooleanType", t); STD::free(t); } +#endif + JSValue newInstance(Context *cx); + }; + class JSNumberInstance : public JSInstance { + public: + JSNumberInstance(Context *cx) : JSInstance(cx, NULL), mValue(0.0) { mType = (JSType *)Number_Type; } + virtual ~JSNumberInstance() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSNumberInstance", s, t); return t; } + void operator delete(void* t) { trace_release("JSNumberInstance", t); STD::free(t); } +#endif + float64 mValue; + }; + + class JSNumberType : public JSType { + public: + JSNumberType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) + : JSType(cx, name, super, protoObj, typeProto) + { + } + virtual ~JSNumberType() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSNumberType", s, t); return t; } + void operator delete(void* t) { trace_release("JSNumberType", t); STD::free(t); } +#endif + JSValue newInstance(Context *cx); + }; + + class JSDateInstance : public JSInstance { + public: + JSDateInstance(Context *cx) : JSInstance(cx, NULL), mValue(0.0) { mType = (JSType *)Date_Type; } + virtual ~JSDateInstance() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSDateInstance", s, t); return t; } + void operator delete(void* t) { trace_release("JSDateInstance", t); STD::free(t); } +#endif + float64 mValue; + }; + + class JSDateType : public JSType { + public: + JSDateType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) + : JSType(cx, name, super, protoObj, typeProto) + { + } + virtual ~JSDateType() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSDateType", s, t); return t; } + void operator delete(void* t) { trace_release("JSDateType", t); STD::free(t); } +#endif + JSValue newInstance(Context *cx); + }; + + class JSRegExpInstance : public JSInstance { + public: + JSRegExpInstance(Context *cx) : JSInstance(cx, NULL), mLastIndex(0), mRegExp(NULL) { mType = (JSType *)RegExp_Type; } + virtual ~JSRegExpInstance() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSRegExpInstance", s, t); return t; } + void operator delete(void* t) { trace_release("JSRegExpInstance", t); STD::free(t); } +#endif + uint32 mLastIndex; + REState *mRegExp; + }; + + class JSRegExpType : public JSType { + public: + JSRegExpType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) + : JSType(cx, name, super, protoObj, typeProto) + { + } + virtual ~JSRegExpType() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSBooleanType", s, t); return t; } + void operator delete(void* t) { trace_release("JSBooleanType", t); STD::free(t); } +#endif + JSValue newInstance(Context *cx); + }; + + class JSObjectType : public JSType { + public: + JSObjectType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto) + : JSType(cx, name, super, protoObj, typeProto) + { + } + virtual ~JSObjectType() { } // keeping gcc happy +#ifdef DEBUG + void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSObjectType", s, t); return t; } + void operator delete(void* t) { trace_release("JSObjectType", t); STD::free(t); } +#endif + JSValue newInstance(Context *cx); + }; // captures the Parameter names scope @@ -984,7 +1107,7 @@ XXX ...couldn't get this to work... class ParameterBarrel : public JSType { public: - ParameterBarrel() : JSType(NULL) + ParameterBarrel() : JSType() { } virtual ~ParameterBarrel() { } // keeping gcc happy @@ -1020,8 +1143,7 @@ XXX ...couldn't get this to work... public: Activation() - : JSType(NULL), - mLocals(NULL), + : mLocals(NULL), mStack(NULL), mStackTop(0), mPC(0), @@ -1037,8 +1159,7 @@ XXX ...couldn't get this to work... uint8 *pc, ByteCodeModule *module, NamespaceList *namespaceList ) - : JSType(NULL), - mLocals(locals), + : mLocals(locals), mStack(stack), mStackTop(stackTop), mScopeChain(scopeChain), @@ -1186,12 +1307,7 @@ XXX ...couldn't get this to work... ASSERT(dynamic_cast(top)); top->defineStaticSetterMethod(cx, name, attr, f); } - void defineUnaryOperator(Operator which, JSFunction *f) - { - JSObject *top = mScopeStack.back(); - ASSERT(dynamic_cast(top)); - ((JSType *)top)->defineUnaryOperator(which, f); - } + void defineUnaryOperator(Context *cx, Operator which, JSFunction *f); // see if the current scope contains a name already bool hasProperty(Context *cx, const String& name, NamespaceList *names, Access acc, PropertyIterator *p) @@ -1295,9 +1411,14 @@ XXX ...couldn't get this to work... }; - class JSFunction : public JSObject { + class JSFunction : public JSInstance { protected: - JSFunction() : JSObject(Function_Type), mActivation() { mActivation.mContainer = this; mPrototype = Function_Type->mPrototypeObject; } // for JSBoundFunction (XXX ask Patrick about this structure) + JSFunction(Context *cx) : JSInstance(cx, NULL), mActivation() + { + mType = (JSType *)Function_Type; + mActivation.mContainer = this; + mPrototype = Function_Type->mPrototypeObject; + } // for JSBoundFunction (XXX ask Patrick about this structure) public: typedef enum { Invalid, RequiredParameter, OptionalParameter, RestParameter, NamedParameter } ParameterFlag; @@ -1421,7 +1542,7 @@ XXX ...couldn't get this to work... mParameterBarrel = (ParameterBarrel*) collector->copy(mParameterBarrel); mResultType = (JSType*) collector->copy(mResultType); mClass = (JSType*) collector->copy(mClass); - mPrototype = (JSObject*) collector->copy(mPrototype); + // scan mPrototype. return sizeof(JSFunction); } @@ -1444,8 +1565,8 @@ XXX ...couldn't get this to work... JSFunction *mFunction; JSObject *mThis; public: - JSBoundFunction(JSFunction *f, JSObject *thisObj) - : mFunction(NULL), mThis(thisObj) { if (f->hasBoundThis()) mFunction = f->getFunction(); else mFunction = f; } + JSBoundFunction(Context *cx, JSFunction *f, JSObject *thisObj) + : JSFunction(cx), mFunction(NULL), mThis(thisObj) { if (f->hasBoundThis()) mFunction = f->getFunction(); else mFunction = f; } ~JSBoundFunction() { } // keeping gcc happy @@ -1530,7 +1651,7 @@ XXX ...couldn't get this to work... #endif }; - // This is for binary operators, it collects together the operand + // This is for unary & binary operators, it collects together the operand // types and the function pointer for the given operand. See also // Context::initOperators where the default operators are set up. class OperatorDefinition { @@ -1539,6 +1660,8 @@ XXX ...couldn't get this to work... OperatorDefinition(JSType *type1, JSType *type2, JSFunction *imp) : mType1(type1), mType2(type2), mImp(imp) { ASSERT(mType1); ASSERT(mType2); } + OperatorDefinition(JSType *type1, JSFunction *imp) + : mType1(type1), mImp(imp) { ASSERT(mType1); } JSType *mType1; JSType *mType2; @@ -1553,13 +1676,15 @@ XXX ...couldn't get this to work... ((ty == mType2) || ty->derivesFrom(mType2)) ); } + bool isApplicable(JSType *tx) + { + return ( (tx == mType1) || tx->derivesFrom(mType1) ); + } }; - - // 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); @@ -1576,13 +1701,12 @@ XXX ...couldn't get this to work... // called directly by String.match extern JSValue RegExp_exec(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc); - class Attribute; - class Package : public JSObject { + class Package : public JSObject { public: typedef enum { OnItsWay, InHand } PackageStatus; - Package(const String &name) : JSObject(Package_Type), mName(name), mStatus(OnItsWay) { } + Package(const String &name) : JSObject(), mName(name), mStatus(OnItsWay) { } String mName; PackageStatus mStatus; @@ -1635,7 +1759,12 @@ XXX ...couldn't get this to work... void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("Context", s, t); return t; } void operator delete(void* t) { trace_release("Context", t); STD::free(t); } #endif - + // + // Initialize a bunch of useful string atoms to save + // having to look them up during execution. + // + // Should these be in World instead? + // StringAtom& Virtual_StringAtom; StringAtom& Constructor_StringAtom; StringAtom& Operator_StringAtom; @@ -1699,9 +1828,18 @@ XXX ...couldn't get this to work... void defineOperator(Operator which, JSType *t1, JSType *t2, JSFunction *imp) { OperatorDefinition *op = new OperatorDefinition(t1, t2, imp); - mOperatorTable[which].push_back(op); + mBinaryOperatorTable[which].push_back(op); } + void defineOperator(Operator which, JSType *t, JSFunction *imp) + { + OperatorDefinition *op = new OperatorDefinition(t, imp); + mUnaryOperatorTable[which].push_back(op); + } + + // Construct an array of argument values, updating argCount to + // reflect the final size. Pulls incoming args from the top of + // the stack. JSValue *buildArgumentBlock(JSFunction *target, uint32 &argCount); @@ -1711,10 +1849,11 @@ XXX ...couldn't get this to work... Attribute *executeAttributes(ExprNode *attr); - + // Run the binary operator designated, after using the types to do the dispatch bool executeOperator(Operator op, JSType *t1, JSType *t2); JSValue mapValueToType(JSValue v, JSType *t); + // Run the interpreter loop on the function JSValue invokeFunction(JSFunction *target, const JSValue& thisValue, JSValue *argv, uint32 argc); // This reader is used to generate source information @@ -1786,13 +1925,7 @@ XXX ...couldn't get this to work... mStack[index] = v; mStackTop++; } -/* - void setValue(uint32 n, JSValue v) - { - ASSERT(n < mStackTop); - mStack[n] = v; - } -*/ + JSValue *getBase(uint32 n) { ASSERT(n <= mStackTop); // s'ok to point beyond the end @@ -1825,7 +1958,9 @@ XXX ...couldn't get this to work... typedef std::vector OperatorList; - OperatorList mOperatorTable[OperatorCount]; + // XXX bigger than necessary... + OperatorList mBinaryOperatorTable[OperatorCount]; + OperatorList mUnaryOperatorTable[OperatorCount]; PackageList mPackages; // the currently loaded packages, mPackages.back() is the current package @@ -1848,6 +1983,8 @@ XXX ...couldn't get this to work... void reportError(Exception::Kind kind, char *message, const String& name); void reportError(Exception::Kind kind, char *message, size_t pos, const String& name); + JSFunction *getUnaryOperator(JSType *dispatchType, Operator which); + /* utility routines */ @@ -1907,9 +2044,9 @@ XXX ...couldn't get this to work... }; - class NamedArgument : public JSObject { + class NamedArgument { public: - NamedArgument(JSValue &v, const String *n) : JSObject(NamedArgument_Type), mValue(v), mName(n) { } + NamedArgument(JSValue &v, const String *n) : mValue(v), mName(n) { } JSValue mValue; const String *mName; @@ -1922,7 +2059,6 @@ XXX ...couldn't get this to work... */ Collector::size_type scan(Collector* collector) { - JSObject::scan(collector); mValue.scan(collector); return sizeof(NamedArgument); } @@ -1942,10 +2078,10 @@ XXX ...couldn't get this to work... }; - class Attribute : public JSObject { + class Attribute { public: Attribute(PropertyAttribute t, PropertyAttribute f) - : JSObject(Attribute_Type), mTrueFlags(t), mFalseFlags(f), mExtendArgument(NULL), mNamespaceList(NULL) { } + : mTrueFlags(t), mFalseFlags(f), mExtendArgument(NULL), mNamespaceList(NULL) { } PropertyAttribute mTrueFlags; PropertyAttribute mFalseFlags; @@ -1961,7 +2097,6 @@ XXX ...couldn't get this to work... */ Collector::size_type scan(Collector* collector) { - JSObject::scan(collector); mExtendArgument = (JSType*) collector->copy(mExtendArgument); return sizeof(Attribute); } @@ -2018,8 +2153,17 @@ XXX ...couldn't get this to work... defineMethod(cx, name, attr, f); } + inline bool JSInstance::isDynamic() + { + return mType->isDynamic(); + } - inline bool JSInstance::isDynamic() { return mType->isDynamic(); } + inline void ScopeChain::defineUnaryOperator(Context *cx, Operator which, JSFunction *f) + { + JSObject *top = mScopeStack.back(); + ASSERT(dynamic_cast(top)); + cx->defineOperator(which, (JSType *)top, f); + } } } diff --git a/js2/src/jsarray.cpp b/js2/src/jsarray.cpp index db6e2291163a..e5bfa26dc26e 100644 --- a/js2/src/jsarray.cpp +++ b/js2/src/jsarray.cpp @@ -57,9 +57,8 @@ JSValue Array_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, JSValue thatValue = thisValue; if (thatValue.isNull()) thatValue = Array_Type->newInstance(cx); - ASSERT(thatValue.isObject()); - JSObject *thisObj = thatValue.object; - JSArrayInstance *arrInst = checked_cast(thisObj); + ASSERT(thatValue.isInstance()); + JSArrayInstance *arrInst = checked_cast(thatValue.instance); if (argc > 0) { if (argc == 1) { if (argv[0].isNumber()) { @@ -89,9 +88,11 @@ JSValue Array_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, static JSValue Array_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - JSArrayInstance *arrInst = checked_cast(thisObj); + if (thisValue.getType() != Array_Type) + cx->reportError(Exception::typeError, "Array.prototype.toString called on a non Array"); + + ASSERT(thisValue.isInstance()); + JSArrayInstance *arrInst = checked_cast(thisValue.instance); ContextStackReplacement csr(cx); @@ -107,6 +108,7 @@ static JSValue Array_toString(Context *cx, const JSValue& thisValue, JSValue * / s->append(*result.toString(cx).string); if (i < (arrInst->mLength - 1)) s->append(widenCString(",")); + delete id; } return JSValue(s); } @@ -115,9 +117,11 @@ static JSValue Array_toString(Context *cx, const JSValue& thisValue, JSValue * / static JSValue Array_toSource(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - JSArrayInstance *arrInst = checked_cast(thisObj); + if (thisValue.getType() != Array_Type) + cx->reportError(Exception::typeError, "Array.prototype.toSource called on a non Array"); + + ASSERT(thisValue.isInstance()); + JSArrayInstance *arrInst = checked_cast(thisValue.instance); ContextStackReplacement csr(cx); @@ -133,6 +137,7 @@ static JSValue Array_toSource(Context *cx, const JSValue& thisValue, JSValue * / s->append(*result.toString(cx).string); if (i < (arrInst->mLength - 1)) s->append(widenCString(", ")); + delete id; } s->append(widenCString("]")); return JSValue(s); @@ -142,33 +147,40 @@ static JSValue Array_toSource(Context *cx, const JSValue& thisValue, JSValue * / static JSValue Array_push(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - JSArrayInstance *arrInst = checked_cast(thisObj); + ContextStackReplacement csr(cx); + + JSObject *thisObj = thisValue.getObjectValue(); + thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); + JSValue result = cx->popValue(); + uint32 length = (uint32)(result.toUInt32(cx).f64); for (uint32 i = 0; i < argc; i++) { - const String *id = numberToString(i + arrInst->mLength); - arrInst->defineVariable(cx, *id, (NamespaceList *)(NULL), Property::NoAttribute, Object_Type, argv[i]); + const String *id = numberToString(i + length); + thisObj->defineVariable(cx, *id, (NamespaceList *)(NULL), Property::NoAttribute, Object_Type, argv[i]); delete id; } - arrInst->mLength += argc; - return JSValue((float64)arrInst->mLength); + length += argc; + result = JSValue((float64)length); + thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, result); + return result; } static JSValue Array_pop(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - JSArrayInstance *arrInst = checked_cast(thisObj); - ContextStackReplacement csr(cx); - if (arrInst->mLength > 0) { - const String *id = numberToString(arrInst->mLength - 1); - arrInst->getProperty(cx, *id, NULL); + JSObject *thisObj = thisValue.getObjectValue(); + thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); + JSValue result = cx->popValue(); + uint32 length = (uint32)(result.toUInt32(cx).f64); + + if (length > 0) { + const String *id = numberToString(length - 1); + thisObj->getProperty(cx, *id, NULL); JSValue result = cx->popValue(); - arrInst->deleteProperty(cx, *id, NULL); - --arrInst->mLength; + thisObj->deleteProperty(cx, *id, NULL); + --length; + thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)length)); delete id; return result; } @@ -180,7 +192,8 @@ JSValue Array_concat(Context *cx, const JSValue& thisValue, JSValue *argv, uint3 { JSValue E = thisValue; - JSArrayInstance *A = (JSArrayInstance *)(Array_Type->newInstance(cx)); + JSValue result = Array_Type->newInstance(cx); + JSArrayInstance *A = checked_cast(result.instance); uint32 n = 0; uint32 i = 0; @@ -189,31 +202,35 @@ JSValue Array_concat(Context *cx, const JSValue& thisValue, JSValue *argv, uint3 do { if (E.getType() != Array_Type) { const String *id = numberToString(n++); - A->setProperty(cx, *id, CURRENT_ATTR, E); + A->setProperty(cx, *id, CURRENT_ATTR, E); + delete id; } else { - ASSERT(E.isObject()); - JSArrayInstance *arrInst = checked_cast(E.object); - for (uint32 k = 0; k < arrInst->mLength; k++) { + JSObject *arrObj = E.getObjectValue(); + arrObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); + JSValue result = cx->popValue(); + uint32 length = (uint32)(result.toUInt32(cx).f64); + for (uint32 k = 0; k < length; k++) { const String *id = numberToString(k); - arrInst->getProperty(cx, *id, NULL); - JSValue result = cx->popValue(); + arrObj->getProperty(cx, *id, NULL); + delete id; id = numberToString(n++); - A->setProperty(cx, *id, CURRENT_ATTR, result); + JSValue result = cx->popValue(); + A->setProperty(cx, *id, CURRENT_ATTR, result); + delete id; } } E = argv[i++]; } while (i <= argc); - return JSValue(A); + return result; } static JSValue Array_join(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; + JSObject *thisObj = thisValue.getObjectValue(); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); JSValue result = cx->popValue(); @@ -230,19 +247,23 @@ static JSValue Array_join(Context *cx, const JSValue& thisValue, JSValue *argv, else separator = argv[0].toString(cx).string; - thisObj->getProperty(cx, *numberToString(0), CURRENT_ATTR); + const String *id = numberToString(0); + thisObj->getProperty(cx, *id, CURRENT_ATTR); + delete id; result = cx->popValue(); String *S = new String(); for (uint32 k = 0; k < length; k++) { - thisObj->getProperty(cx, *numberToString(k), CURRENT_ATTR); + id = numberToString(k); + thisObj->getProperty(cx, *id, CURRENT_ATTR); result = cx->popValue(); if (!result.isUndefined() && !result.isNull()) *S += *result.toString(cx).string; if (k < (length - 1)) *S += *separator; + delete id; } return JSValue(S); @@ -252,8 +273,7 @@ static JSValue Array_reverse(Context *cx, const JSValue& thisValue, JSValue * /* { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; + JSObject *thisObj = thisValue.getObjectValue(); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); JSValue result = cx->popValue(); @@ -290,6 +310,8 @@ static JSValue Array_reverse(Context *cx, const JSValue& thisValue, JSValue * /* thisObj->deleteProperty(cx, *id2, CURRENT_ATTR); } } + delete id1; + delete id2; } return thisValue; @@ -299,8 +321,7 @@ static JSValue Array_shift(Context *cx, const JSValue& thisValue, JSValue * /*ar { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; + JSObject *thisObj = thisValue.getObjectValue(); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); JSValue result = cx->popValue(); @@ -311,8 +332,10 @@ static JSValue Array_shift(Context *cx, const JSValue& thisValue, JSValue * /*ar return kUndefinedValue; } - thisObj->getProperty(cx, *numberToString(0), CURRENT_ATTR); + const String *id = numberToString(0); + thisObj->getProperty(cx, *id, CURRENT_ATTR); result = cx->popValue(); + delete id; for (uint32 k = 1; k < length; k++) { const String *id1 = numberToString(k); @@ -325,9 +348,13 @@ static JSValue Array_shift(Context *cx, const JSValue& thisValue, JSValue * /*ar } else thisObj->deleteProperty(cx, *id2, CURRENT_ATTR); + delete id1; + delete id2; } - thisObj->deleteProperty(cx, *numberToString(length - 1), CURRENT_ATTR); + id = numberToString(length - 1); + thisObj->deleteProperty(cx, *id, CURRENT_ATTR); + delete id; thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)(length - 1)) ); return result; @@ -337,14 +364,14 @@ static JSValue Array_slice(Context *cx, const JSValue& thisValue, JSValue *argv, { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; + JSObject *thisObj = thisValue.getObjectValue(); - JSArrayInstance *A = (JSArrayInstance *)Array_Type->newInstance(cx); + JSValue result = Array_Type->newInstance(cx); + JSArrayInstance *A = checked_cast(result.instance); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); - JSValue result = cx->popValue(); - uint32 length = (uint32)(result.toUInt32(cx).f64); + JSValue lengthValue = cx->popValue(); + uint32 length = (uint32)(lengthValue.toUInt32(cx).f64); uint32 start, end; if (argc < 1) @@ -396,6 +423,7 @@ static JSValue Array_slice(Context *cx, const JSValue& thisValue, JSValue *argv, } n++; start++; + delete id1; } A->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)n) ); return JSValue(A); @@ -517,7 +545,6 @@ static int32 sort_compare(JSValue *a, JSValue *b, CompareArgs *arg) static JSValue Array_sort(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject()); ContextStackReplacement csr(cx); CompareArgs ca; @@ -532,7 +559,7 @@ static JSValue Array_sort(Context *cx, const JSValue& thisValue, JSValue *argv, } } - JSObject *thisObj = thisValue.object; + JSObject *thisObj = thisValue.getObjectValue(); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); JSValue result = cx->popValue(); uint32 length = (uint32)(result.toUInt32(cx).f64); @@ -566,13 +593,13 @@ static JSValue Array_splice(Context *cx, const JSValue& thisValue, JSValue *argv uint32 k; ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; + JSObject *thisObj = thisValue.getObjectValue(); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); - JSValue result = cx->popValue(); - uint32 length = (uint32)(result.toUInt32(cx).f64); + JSValue lengthValue = cx->popValue(); + uint32 length = (uint32)(lengthValue.toUInt32(cx).f64); - JSArrayInstance *A = (JSArrayInstance *)Array_Type->newInstance(cx); + JSValue result = Array_Type->newInstance(cx); + JSArrayInstance *A = checked_cast(result.instance); int32 arg0 = (int32)(argv[0].toInt32(cx).f64); uint32 start; @@ -608,6 +635,7 @@ static JSValue Array_splice(Context *cx, const JSValue& thisValue, JSValue *argv thisObj->getProperty(cx, *id1, CURRENT_ATTR); A->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue()); } + delete id1; } A->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)deleteCount) ); @@ -623,6 +651,8 @@ static JSValue Array_splice(Context *cx, const JSValue& thisValue, JSValue *argv } else thisObj->deleteProperty(cx, *id2, CURRENT_ATTR); + delete id1; + delete id2; } for (k = length; k > (length - deleteCount + newItemCount); k--) { const String *id1 = numberToString(k - 1); @@ -641,6 +671,8 @@ static JSValue Array_splice(Context *cx, const JSValue& thisValue, JSValue *argv } else thisObj->deleteProperty(cx, *id2, CURRENT_ATTR); + delete id1; + delete id2; } } } @@ -648,10 +680,11 @@ static JSValue Array_splice(Context *cx, const JSValue& thisValue, JSValue *argv for (uint32 i = 2; i < argc; i++) { const String *id1 = numberToString(k++); thisObj->setProperty(cx, *id1, CURRENT_ATTR, argv[i]); + delete id1; } thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)(length - deleteCount + newItemCount)) ); - return JSValue(A); + return result; } return kUndefinedValue; } @@ -660,8 +693,7 @@ static JSValue Array_unshift(Context *cx, const JSValue& thisValue, JSValue *arg { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; + JSObject *thisObj = thisValue.getObjectValue(); thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); JSValue result = cx->popValue(); uint32 length = (uint32)(result.toUInt32(cx).f64); @@ -677,11 +709,14 @@ static JSValue Array_unshift(Context *cx, const JSValue& thisValue, JSValue *arg } else thisObj->deleteProperty(cx, *id2, CURRENT_ATTR); + delete id1; + delete id2; } for (k = 0; k < argc; k++) { const String *id1 = numberToString(k); thisObj->setProperty(cx, *id1, CURRENT_ATTR, argv[k]); + delete id1; } thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)(length + argc)) ); @@ -712,10 +747,7 @@ JSValue Array_GetElement(Context *cx, const JSValue& thisValue, JSValue *argv, u if (argc != 2) cx->reportError(Exception::referenceError, "[] only supports single dimension"); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - JSArrayInstance *arrInst = checked_cast(thisObj); - ASSERT(thisObj->mType == Array_Type); + JSArrayInstance *arrInst = checked_cast(thisValue.instance); JSValue index = argv[1]; const String *name = index.toString(cx).string; @@ -730,10 +762,7 @@ JSValue Array_SetElement(Context *cx, const JSValue& thisValue, JSValue *argv, u if (argc != 3) cx->reportError(Exception::referenceError, "[]= only supports single dimension"); - ASSERT(thisValue.isObject()); - JSObject *thisObj = thisValue.object; - JSArrayInstance *arrInst = checked_cast(thisObj); - ASSERT(thisObj->mType == Array_Type); + JSArrayInstance *arrInst = checked_cast(thisValue.instance); JSValue index = argv[2]; const String *name = index.toString(cx).string; @@ -741,9 +770,9 @@ JSValue Array_SetElement(Context *cx, const JSValue& thisValue, JSValue *argv, u if (index.isNumber()) { uint32 intIndex; if (isArrayIndex(cx, index, intIndex)) { - PropertyIterator it = thisObj->findNamespacedProperty(*name, NULL); - if (it == thisObj->mProperties.end()) - thisObj->insertNewProperty(*name, NULL, Property::Enumerable, Object_Type, argv[1]); + PropertyIterator it = arrInst->findNamespacedProperty(*name, NULL); + if (it == arrInst->mProperties.end()) + arrInst->insertNewProperty(*name, NULL, Property::Enumerable, Object_Type, argv[1]); else { Property *prop = PROPERTY(it); ASSERT(prop->mFlag == ValuePointer); diff --git a/js2/src/jsdate.cpp b/js2/src/jsdate.cpp index 91f3316e4305..90dcf63f7703 100644 --- a/js2/src/jsdate.cpp +++ b/js2/src/jsdate.cpp @@ -291,11 +291,12 @@ static int32 WeekDay(float64 t) static float64 *Date_getProlog(Context *cx, const JSValue& thisValue) { - ASSERT(thisValue.isObject()); if (thisValue.getType() != Date_Type) cx->reportError(Exception::typeError, "You need a date"); + ASSERT(thisValue.isInstance()); + JSDateInstance *dateInst = checked_cast(thisValue.instance); - return (float64 *)(thisValue.object->mPrivate); + return &dateInst->mValue; } @@ -869,9 +870,8 @@ JSValue Date_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, u JSValue thatValue = thisValue; if (thatValue.isNull()) thatValue = Date_Type->newInstance(cx); - ASSERT(thatValue.isObject()); - JSObject *thisObj = thatValue.object; - thisObj->mPrivate = new float64; + ASSERT(thatValue.isInstance()); + JSDateInstance *thisInst = checked_cast(thatValue.instance); /* Date called as constructor */ if (argc == 0) { @@ -883,20 +883,20 @@ JSValue Date_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, u JSLL_DIV(ms, us, us2ms); JSLL_L2D(msec_time, ms); - *((float64 *)(thisObj->mPrivate)) = msec_time; + thisInst->mValue = msec_time; } else { if (argc == 1) { if (!argv[0].isString()) { /* the argument is a millisecond number */ float64 d = argv[0].toNumber(cx).f64; - *((float64 *)(thisObj->mPrivate)) = TIMECLIP(d); + thisInst->mValue = TIMECLIP(d); } else { /* the argument is a string; parse it. */ const String *str = argv[0].toString(cx).string; float64 d = date_parseString(*str); - *((float64 *)(thisObj->mPrivate)) = TIMECLIP(d); + thisInst->mValue = TIMECLIP(d); } } else { @@ -911,7 +911,7 @@ JSValue Date_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, u /* if any arg is NaN, make a NaN date object and return */ if (!JSDOUBLE_IS_FINITE(double_arg)) { - *((float64 *)(thisObj->mPrivate)) = nan; + thisInst->mValue = nan; return thatValue; } array[loop] = JSValue::float64ToInteger(double_arg); @@ -931,7 +931,7 @@ JSValue Date_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, u msec_time = MakeTime(array[3], array[4], array[5], array[6]); msec_time = MakeDate(day, msec_time); msec_time = UTC(msec_time); - *((float64 *)(thisObj->mPrivate)) = TIMECLIP(msec_time); + thisInst->mValue = TIMECLIP(msec_time); } } return thatValue; diff --git a/js2/src/jsstring.cpp b/js2/src/jsstring.cpp index 220eab658b74..875094e77412 100644 --- a/js2/src/jsstring.cpp +++ b/js2/src/jsstring.cpp @@ -45,7 +45,6 @@ #include "js2runtime.h" #include "jsstring.h" -#include "regexp.h" namespace JavaScript { namespace JS2Runtime { @@ -56,15 +55,13 @@ JSValue String_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, JSValue thatValue = thisValue; if (thatValue.isNull()) thatValue = String_Type->newInstance(cx); - ASSERT(thatValue.isObject()); - JSObject *thisObj = thatValue.object; - JSStringInstance *strInst = checked_cast(thisObj); + ASSERT(thatValue.isInstance()); + JSStringInstance *strInst = checked_cast(thatValue.instance); if (argc > 0) - thisObj->mPrivate = (void *)(new String(*argv[0].toString(cx).string)); + strInst->mValue = new String(*argv[0].toString(cx).string); else - thisObj->mPrivate = (void *)(&cx->Empty_StringAtom); - strInst->mLength = ((String *)(thisObj->mPrivate))->size(); + strInst->mValue = &cx->Empty_StringAtom; return thatValue; } @@ -89,85 +86,105 @@ JSValue String_fromCharCode(Context *cx, const JSValue& /*thisValue*/, JSValue * static JSValue String_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); if (thisValue.getType() != String_Type) cx->reportError(Exception::typeError, "String.toString called on something other than a string thing"); - JSObject *thisObj = thisValue.object; - return JSValue((String *)thisObj->mPrivate); + ASSERT(thisValue.isInstance()); + JSStringInstance *strInst = checked_cast(thisValue.instance); + return JSValue(strInst->mValue); } static JSValue String_valueOf(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject()); if (thisValue.getType() != String_Type) cx->reportError(Exception::typeError, "String.valueOf called on something other than a string thing"); - JSObject *thisObj = thisValue.object; - return JSValue((String *)thisObj->mPrivate); + ASSERT(thisValue.isInstance()); + JSStringInstance *strInst = checked_cast(thisValue.instance); + return JSValue(strInst->mValue); } +/* + * 15.5.4.12 String.prototype.search (regexp) + * + * If regexp is not an object whose [[Class]] property is "RegExp", it is replaced with the result of the expression new + * RegExp(regexp). Let string denote the result of converting the this value to a string. + * The value string is searched from its beginning for an occurrence of the regular expression pattern regexp. The + * result is a number indicating the offset within the string where the pattern matched, or -1 if there was no match. + * NOTE This method ignores the lastIndex and global properties of regexp. The lastIndex property of regexp is left + * unchanged. +*/ static JSValue String_search(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); JSValue S = thisValue.toString(cx); JSValue regexp = argv[0]; - if ((argc == 0) || !regexp.isObject() || (regexp.object->mType != RegExp_Type)) { + if ((argc == 0) || (regexp.getType() != RegExp_Type)) { regexp = kNullValue; regexp = RegExp_Constructor(cx, regexp, argv, 1); } - REParseState *parseResult = (REParseState *)(regexp.object->mPrivate); + REState *pState = (checked_cast(regexp.instance))->mRegExp; - /* save & restore lastIndex as it's not to be modified */ - uint32 lastIndex = parseResult->lastIndex; - parseResult->lastIndex = 0; - REState *regexp_result = REExecute(parseResult, S.string->begin(), S.string->length()); - parseResult->lastIndex = lastIndex; - - if (regexp_result) - return JSValue((float64)(regexp_result->startIndex)); + REMatchState *match = REExecute(pState, S.string->begin(), 0, S.string->length(), false); + if (match) + return JSValue((float64)(match->startIndex)); else return JSValue(-1.0); } +/* + * 15.5.4.10 String.prototype.match (regexp) + * + * If regexp is not an object whose [[Class]] property is "RegExp", it is replaced with the result of the expression new + * RegExp(regexp). Let string denote the result of converting the this value to a string. Then do one of the following: + * - If regexp.global is false: Return the result obtained by invoking RegExp.prototype.exec (see section + * 15.10.6.2) on regexp with string as parameter. + * - If regexp.global is true: Set the regexp.lastIndex property to 0 and invoke RegExp.prototype.exec + * repeatedly until there is no match. If there is a match with an empty string (in other words, if the value of + * regexp.lastIndex is left unchanged), increment regexp.lastIndex by 1. Let n be the number of matches. The + * value returned is an array with the length property set to n and properties 0 through n-1 corresponding to the + * first elements of the results of all matching invocations of RegExp.prototype.exec. + */ + static JSValue String_match(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); JSValue S = thisValue.toString(cx); JSValue regexp = argv[0]; - if ((argc == 0) || !regexp.isObject() || (regexp.object->mType != RegExp_Type)) { + if ((argc == 0) || (regexp.getType() != RegExp_Type)) { regexp = kNullValue; regexp = RegExp_Constructor(cx, regexp, argv, 1); } - REParseState *parseResult = (REParseState *)(regexp.object->mPrivate); - if ((parseResult->flags & GLOBAL) == 0) { + REState *pState = (checked_cast(regexp.instance))->mRegExp; + if ((pState->flags & RE_GLOBAL) == 0) { return RegExp_exec(cx, regexp, &S, 1); } else { - JSArrayInstance *A = (JSArrayInstance *)Array_Type->newInstance(cx); - parseResult->lastIndex = 0; + JSValue result = Array_Type->newInstance(cx); + JSArrayInstance *A = checked_cast(result.instance); int32 index = 0; + int32 lastIndex = 0; while (true) { - REState *regexp_result = REExecute(parseResult, S.string->begin(), S.string->length()); - if (regexp_result == NULL) + REMatchState *match = REExecute(pState, S.string->begin(), lastIndex, S.string->length(), false); + if (match == NULL) break; - if (parseResult->lastIndex == index) - parseResult->lastIndex++; - String *matchStr = new String(S.string->substr(regexp_result->startIndex, regexp_result->endIndex - regexp_result->startIndex)); + if (lastIndex == match->endIndex) + lastIndex++; + else + lastIndex = match->endIndex; + String *matchStr = new String(S.string->substr(match->startIndex, match->endIndex - match->startIndex)); A->setProperty(cx, *numberToString(index++), NULL, JSValue(matchStr)); } - regexp.object->setProperty(cx, cx->LastIndex_StringAtom, NULL, JSValue((float64)(parseResult->lastIndex))); - return JSValue(A); + regexp.instance->setProperty(cx, cx->LastIndex_StringAtom, NULL, JSValue((float64)lastIndex)); + return result; } } -static const String interpretDollar(Context *cx, const String *replaceStr, uint32 dollarPos, const String *searchStr, REState *regexp_result, uint32 &skip) +static const String interpretDollar(Context *cx, const String *replaceStr, uint32 dollarPos, const String *searchStr, REMatchState *match, uint32 &skip) { skip = 2; const char16 *dollarValue = replaceStr->begin() + dollarPos + 1; @@ -175,11 +192,11 @@ static const String interpretDollar(Context *cx, const String *replaceStr, uint3 case '$': return cx->Dollar_StringAtom; case '&': - return searchStr->substr(regexp_result->startIndex, regexp_result->endIndex - regexp_result->startIndex); + return searchStr->substr(match->startIndex, match->endIndex - match->startIndex); case '`': - return searchStr->substr(0, regexp_result->startIndex); + return searchStr->substr(0, match->startIndex); case '\'': - return searchStr->substr(regexp_result->endIndex, searchStr->length() - regexp_result->endIndex); + return searchStr->substr(match->endIndex, searchStr->length() - match->endIndex); case '0': case '1': case '2': @@ -192,16 +209,16 @@ static const String interpretDollar(Context *cx, const String *replaceStr, uint3 case '9': { int32 num = (uint32)(*dollarValue - '0'); - if (num <= regexp_result->n) { + if (num <= match->parenCount) { if ((dollarPos < (replaceStr->length() - 2)) && (dollarValue[1] >= '0') && (dollarValue[1] <= '9')) { int32 tmp = (num * 10) + (dollarValue[1] - '0'); - if (tmp <= regexp_result->n) { + if (tmp <= match->parenCount) { num = tmp; skip = 3; } } - return searchStr->substr((uint32)(regexp_result->parens[num - 1].index), (uint32)(regexp_result->parens[num - 1].length)); + return searchStr->substr((uint32)(match->parens[num - 1].index), (uint32)(match->parens[num - 1].length)); } } // fall thru @@ -211,9 +228,37 @@ static const String interpretDollar(Context *cx, const String *replaceStr, uint3 } } +/* + * 15.5.4.11 String.prototype.replace (searchValue, replaceValue) + * + * Let string denote the result of converting the this value to a string. + * + * If searchValue is a regular expression (an object whose [[Class]] property is "RegExp"), do the following: If + * searchValue.global is false, then search string for the first match of the regular expression searchValue. If + * searchValue.global is true, then search string for all matches of the regular expression searchValue. Do the search + * in the same manner as in String.prototype.match, including the update of searchValue.lastIndex. Let m + * be the number of left capturing parentheses in searchValue (NCapturingParens as specified in section 15.10.2.1). + * + * If searchValue is not a regular expression, let searchString be ToString(searchValue) and search string for the first + * occurrence of searchString. Let m be 0. + * + * If replaceValue is a function, then for each matched substring, call the function with the following m + 3 arguments. + * Argument 1 is the substring that matched. If searchValue is a regular expression, the next m arguments are all of + * the captures in the MatchResult (see section 15.10.2.1). Argument m + 2 is the offset within string where the match + * occurred, and argument m + 3 is string. The result is a string value derived from the original input by replacing each + * matched substring with the corresponding return value of the function call, converted to a string if need be. + * + * Otherwise, let newstring denote the result of converting replaceValue to a string. The result is a string value derived + * from the original input string by replacing each matched substring with a string derived from newstring by replacing + * characters in newstring by replacement text as specified in the following table. These $ replacements are done left-to- + * right, and, once such a replacement is performed, the new replacement text is not subject to further + * replacements. For example, "$1,$2".replace(/(\$(\d))/g, "$$1-$1$2") returns "$1-$11,$1-$22". A + * $ in newstring that does not match any of the forms below is left as is. + */ + + static JSValue String_replace(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); JSValue S = thisValue.toString(cx); JSValue searchValue; @@ -223,54 +268,58 @@ static JSValue String_replace(Context *cx, const JSValue& thisValue, JSValue *ar if (argc > 1) replaceValue = argv[1]; const String *replaceStr = replaceValue.toString(cx).string; - if (searchValue.isObject() && (searchValue.object->mType == RegExp_Type)) { - REParseState *parseResult = (REParseState *)(searchValue.object->mPrivate); - REState *regexp_result; - uint32 m = parseResult->parenCount; + if (searchValue.getType() == RegExp_Type) { + REState *pState = (checked_cast(searchValue.instance))->mRegExp; + REMatchState *match; + uint32 m = pState->parenCount; String newString; - uint32 index = 0; + int32 lastIndex = 0; while (true) { - if (parseResult->flags & GLOBAL) - parseResult->lastIndex = (int32)index; - regexp_result = REExecute(parseResult, S.string->begin(), S.string->length()); - if (regexp_result) { + match = REExecute(pState, S.string->begin(), lastIndex, S.string->length(), false); + if (match) { String insertString; uint32 start = 0; while (true) { + // look for '$' in the replacement string and interpret it as necessary uint32 dollarPos = replaceStr->find('$', start); if ((dollarPos != String::npos) && (dollarPos < (replaceStr->length() - 1))) { uint32 skip; insertString += replaceStr->substr(start, dollarPos - start); - insertString += interpretDollar(cx, replaceStr, dollarPos, S.string, regexp_result, skip); + insertString += interpretDollar(cx, replaceStr, dollarPos, S.string, match, skip); start = dollarPos + skip; } else { + // otherwise, absorb the entire replacement string insertString += replaceStr->substr(start, replaceStr->length() - start); break; } } - newString += S.string->substr(index, regexp_result->startIndex - index); + // grab everything preceding the match + newString += S.string->substr(lastIndex, match->startIndex - lastIndex); + // and then add the replacement string newString += insertString; } else break; - index = regexp_result->endIndex; - if ((parseResult->flags & GLOBAL) == 0) + lastIndex = match->endIndex; // use lastIndex to grab remainder after break + if ((pState->flags & RE_GLOBAL) == 0) break; } - newString += S.string->substr(index, S.string->length() - index); + newString += S.string->substr(lastIndex, S.string->length() - lastIndex); + if ((pState->flags & RE_GLOBAL) == 0) + searchValue.instance->setProperty(cx, cx->LastIndex_StringAtom, NULL, JSValue((float64)lastIndex)); return JSValue(new String(newString)); } else { const String *searchStr = searchValue.toString(cx).string; - REState regexp_result; + REMatchState match; uint32 pos = S.string->find(*searchStr, 0); if (pos == String::npos) return JSValue(S.string); - regexp_result.startIndex = (int32)pos; - regexp_result.endIndex = regexp_result.startIndex + searchStr->length(); - regexp_result.n = 0; + match.startIndex = (int32)pos; + match.endIndex = match.startIndex + searchStr->length(); + match.parenCount = 0; String insertString; String newString; uint32 start = 0; @@ -279,7 +328,7 @@ static JSValue String_replace(Context *cx, const JSValue& thisValue, JSValue *ar if ((dollarPos != String::npos) && (dollarPos < (replaceStr->length() - 1))) { uint32 skip; insertString += replaceStr->substr(start, dollarPos - start); - insertString += interpretDollar(cx, replaceStr, dollarPos, S.string, ®exp_result, skip); + insertString += interpretDollar(cx, replaceStr, dollarPos, S.string, &match, skip); start = dollarPos + skip; } else { @@ -287,9 +336,9 @@ static JSValue String_replace(Context *cx, const JSValue& thisValue, JSValue *ar break; } } - newString += S.string->substr(0, regexp_result.startIndex); + newString += S.string->substr(0, match.startIndex); newString += insertString; - uint32 index = regexp_result.endIndex; + uint32 index = match.endIndex; newString += S.string->substr(index, S.string->length() - index); return JSValue(new String(newString)); } @@ -320,23 +369,23 @@ static void strSplitMatch(const String *S, uint32 q, const String *R, MatchResul result.failure = false; } -static void regexpSplitMatch(const String *S, uint32 q, REParseState *RE, MatchResult &result) +static void regexpSplitMatch(const String *S, uint32 q, REState *RE, MatchResult &result) { result.failure = true; result.captures = NULL; - REState *regexp_result = REMatch(RE, S->begin() + q, S->length() - q); + REMatchState *match = REMatch(RE, S->begin() + q, S->length() - q); - if (regexp_result) { - result.endIndex = regexp_result->startIndex + q; + if (match) { + result.endIndex = match->startIndex + q; result.failure = false; - result.capturesCount = regexp_result->n; - if (regexp_result->n) { - result.captures = new JSValue[regexp_result->n]; - for (int32 i = 0; i < regexp_result->n; i++) { - if (regexp_result->parens[i].index != -1) { - String *parenStr = new String(S->substr((uint32)(regexp_result->parens[i].index + q), - (uint32)(regexp_result->parens[i].length))); + result.capturesCount = match->parenCount; + if (match->parenCount) { + result.captures = new JSValue[match->parenCount]; + for (int32 i = 0; i < match->parenCount; i++) { + if (match->parens[i].index != -1) { + String *parenStr = new String(S->substr((uint32)(match->parens[i].index + q), + (uint32)(match->parens[i].length))); result.captures[i] = JSValue(parenStr); } else @@ -351,10 +400,10 @@ static JSValue String_split(Context *cx, const JSValue& thisValue, JSValue *argv { ContextStackReplacement csr(cx); - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); JSValue S = thisValue.toString(cx); - JSArrayInstance *A = (JSArrayInstance *)Array_Type->newInstance(cx); + JSValue result = Array_Type->newInstance(cx); + JSArrayInstance *A = checked_cast(result.instance); uint32 lim; JSValue separatorV = (argc > 0) ? argv[0] : kUndefinedValue; JSValue limitV = (argc > 1) ? argv[1] : kUndefinedValue; @@ -367,15 +416,15 @@ static JSValue String_split(Context *cx, const JSValue& thisValue, JSValue *argv uint32 s = S.string->size(); uint32 p = 0; - REParseState *RE = NULL; + REState *RE = NULL; const String *R = NULL; - if (separatorV.isObject() && (separatorV.object->mType == RegExp_Type)) - RE = (REParseState *)(separatorV.object->mPrivate); + if (separatorV.getType() == RegExp_Type) + RE = (checked_cast(separatorV.instance))->mRegExp; else R = separatorV.toString(cx).string; if (lim == 0) - return JSValue(A); + return result; /* XXX standard requires this, but Monkey doesn't do it and the tests break @@ -391,9 +440,9 @@ static JSValue String_split(Context *cx, const JSValue& thisValue, JSValue *argv else strSplitMatch(S.string, 0, R, z); if (!z.failure) - return JSValue(A); + return result; A->setProperty(cx, widenCString("0"), NULL, S); - return JSValue(A); + return result; } while (true) { @@ -403,7 +452,7 @@ step11: String *T = new String(*S.string, p, (s - p)); JSValue v(T); A->setProperty(cx, *numberToString(A->mLength), NULL, v); - return JSValue(A); + return result; } MatchResult z; if (RE) @@ -423,20 +472,19 @@ step11: JSValue v(T); A->setProperty(cx, *numberToString(A->mLength), NULL, v); if (A->mLength == lim) - return JSValue(A); + return result; p = e; for (uint32 i = 0; i < z.capturesCount; i++) { A->setProperty(cx, *numberToString(A->mLength), NULL, JSValue(z.captures[i])); if (A->mLength == lim) - return JSValue(A); + return result; } } } static JSValue String_charAt(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); const String *str = thisValue.toString(cx).string; uint32 pos = 0; @@ -452,7 +500,6 @@ static JSValue String_charAt(Context *cx, const JSValue& thisValue, JSValue *arg static JSValue String_charCodeAt(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); const String *str = thisValue.toString(cx).string; uint32 pos = 0; @@ -467,7 +514,6 @@ static JSValue String_charCodeAt(Context *cx, const JSValue& thisValue, JSValue static JSValue String_concat(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); const String *str = thisValue.toString(cx).string; String *result = new String(*str); @@ -480,7 +526,6 @@ static JSValue String_concat(Context *cx, const JSValue& thisValue, JSValue *arg static JSValue String_indexOf(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); if (argc == 0) return JSValue(-1.0); @@ -508,7 +553,6 @@ static JSValue String_indexOf(Context *cx, const JSValue& thisValue, JSValue *ar static JSValue String_lastIndexOf(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); if (argc == 0) return JSValue(-1.0); @@ -543,7 +587,6 @@ static JSValue String_localeCompare(Context * /*cx*/, const JSValue& /*thisValue static JSValue String_toLowerCase(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); JSValue S = thisValue.toString(cx); String *result = new String(*S.string); @@ -555,7 +598,6 @@ static JSValue String_toLowerCase(Context *cx, const JSValue& thisValue, JSValue static JSValue String_toUpperCase(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); JSValue S = thisValue.toString(cx); String *result = new String(*S.string); @@ -565,9 +607,29 @@ static JSValue String_toUpperCase(Context *cx, const JSValue& thisValue, JSValue return JSValue(result); } +/* + * 15.5.4.13 String.prototype.slice (start, end) + * + * The slice method takes two arguments, start and end, and returns a substring of the result of converting this + * object to a string, starting from character position start and running to, but not including, character position end (or + * through the end of the string if end is undefined). If start is negative, it is treated as (sourceLength+start) where + * sourceLength is the length of the string. If end is negative, it is treated as (sourceLength+end) where sourceLength + * is the length of the string. The result is a string value, not a String object. + * + * The following steps are taken: + * 1. Call ToString, giving it the this value as its argument. + * 2. Compute the number of characters in Result(1). + * 3. Call ToInteger(start). + * 4. If end is undefined, use Result(2); else use ToInteger(end). + * 5. If Result(3) is negative, use max(Result(2)+Result(3),0); else use min(Result(3),Result(2)). + * 6. If Result(4) is negative, use max(Result(2)+Result(4),0); else use min(Result(4),Result(2)). + * 7. Compute max(Result(6)–Result(5),0). + * 8. Return a string containing Result(7) consecutive characters from Result(1) beginning with the character at + * position Result(5). + */ + static JSValue String_slice(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); const String *sourceString = thisValue.toString(cx).string; uint32 sourceLength = sourceString->size(); @@ -616,9 +678,30 @@ static JSValue String_slice(Context *cx, const JSValue& thisValue, JSValue *argv return JSValue(new String(sourceString->substr(start, end - start))); } +/* + * 15.5.4.15 String.prototype.substring (start, end) + * The substring method takes two arguments, start and end, and returns a substring of the result of converting this + * object to a string, starting from character position start and running to, but not including, character position end of + * the string (or through the end of the string is end is undefined). The result is a string value, not a String object. + * If either argument is NaN or negative, it is replaced with zero; if either argument is larger than the length of the + * string, it is replaced with the length of the string. + * If start is larger than end, they are swapped. + * + * The following steps are taken: + * 1. Call ToString, giving it the this value as its argument. + * 2. Compute the number of characters in Result(1). + * 3. Call ToInteger(start). + * 4. If end is undefined, use Result(2); else use ToInteger(end). + * 5. Compute min(max(Result(3), 0), Result(2)). + * 6. Compute min(max(Result(4), 0), Result(2)). + * 7. Compute min(Result(5), Result(6)). + * 8. Compute max(Result(5), Result(6)). + * 9. Return a string whose length is the difference between Result(8) and Result(7), containing characters from + * Result(1), namely the characters with indices Result(7) through Result(8)-1, in ascending order. + */ + static JSValue String_substring(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { - ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); const String *sourceString = thisValue.toString(cx).string; uint32 sourceLength = sourceString->size(); diff --git a/js2/src/regexp/regexp.c b/js2/src/regexp/regexp.c index 4327b1a73bd5..f0df1f3a268a 100644 --- a/js2/src/regexp/regexp.c +++ b/js2/src/regexp/regexp.c @@ -47,13 +47,16 @@ #include "regexp.h" +typedef unsigned char REbool; +enum { RE_FALSE, RE_TRUE }; + typedef enum REOp { REOP_EMPTY, /* an empty alternative */ REOP_ALT, /* a tree of alternates */ - REOP_NEXTALT, /* continuation into thereof */ + REOP_ENDALT, /* flags end of alternate, signals jump to next */ REOP_BOL, /* start of line or input string '^' */ REOP_EOL, /* end of line or input string '$' */ @@ -69,10 +72,16 @@ typedef enum REOp { REOP_UNWBND, /* not a word boundary '\B' */ REOP_ASSERT, /* '(?= ... )' */ + REOP_ASSERTTEST, /* end of child for above */ REOP_ASSERTNOT, /* '(?! ... )' */ - REOP_ASSERTTEST, /* continuation point for above */ + REOP_ASSERTNOTTEST, /* end of child for above */ REOP_FLAT, /* literal characters (data.length count) */ + /* tree node of FLAT gets transformed into : */ + REOP_FLATNi, /* 'n' literals characters, ignore case */ + REOP_FLATN, /* 'n' literals characters, case sensitive */ + REOP_FLAT1i, /* 1 literal characters, ignore case */ + REOP_FLAT1, /* 1 literal characters, case sensitive */ REOP_DEC, /* decimal digit '\d' */ REOP_UNDEC, /* not a decimal digit '\D' */ @@ -83,34 +92,27 @@ typedef enum REOp { REOP_LETDIG, /* letter or digit or '_' '\w' */ REOP_UNLETDIG, /* not letter or digit or '_' '\W' */ - REOP_QUANT, /* '+', '*', '?' or '{..}' */ - REOP_REPEAT, /* continuation into quant */ - REOP_MINIMALREPEAT, /* continuation into quant */ - - - REOP_ENDALT, - REOP_STAR, + REOP_QUANT, /* '+', '*', '?' as tree node or '{..}' */ + REOP_STAR, /* Bytecode versions, to save space ... */ REOP_OPT, REOP_PLUS, REOP_MINIMALSTAR, REOP_MINIMALOPT, REOP_MINIMALPLUS, REOP_MINIMALQUANT, - REOP_FLATNi, - REOP_FLATN, - REOP_FLAT1i, - REOP_FLAT1, - REOP_ENDCHILD, - REOP_ASSERTNOTTEST, - REOP_END + REOP_REPEAT, /* intermediate op for processing quant */ + REOP_MINIMALREPEAT, /* and ditto for non greedy case */ + + REOP_ENDCHILD, /* end of child for quantifier */ + + REOP_END /* the holy grail */ } REOp; #define RE_ISDEC(c) ( (c >= '0') && (c <= '9') ) #define RE_UNDEC(c) (c - '0') -#define RE_ISWS(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') ) #define RE_ISLETTER(c) ( ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ) #define RE_ISLETDIG(c) ( RE_ISLETTER(c) || RE_ISDEC(c) ) @@ -118,31 +120,31 @@ typedef enum REOp { typedef struct REContinuationData { - REOp op; /* not necessarily the same as node->kind */ + REOp op; /* not necessarily the same as *pc */ REuint8 *pc; } REContinuationData; typedef struct RENode { REOp kind; - RENode *next; /* links consecutive terms */ + RENode *next; /* links consecutive terms */ void *child; - REuint32 parenIndex; + REuint32 parenIndex; /* for QUANT, PAREN, BACKREF */ union { - void *child2; + void *child2; /* for ALT */ struct { REint32 min; - REint32 max; /* -1 for infinity */ + REint32 max; /* -1 for infinity */ REbool greedy; - REint32 parenCount; /* #parens in quantified term */ + REint32 parenCount; /* #parens in quantified term */ } quantifier; struct { - REchar ch; - REuint32 length; + REchar ch; /* for FLAT1 */ + REuint32 length; /* for FLATN */ } flat; struct { - REint32 classIndex; - const REchar *end; - REuint32 length; + REint32 classIndex; /* index into classList in REState */ + const REchar *end; /* last character of source */ + REuint32 length; /* calculated bitmap length */ } chclass; } data; } RENode; @@ -162,16 +164,17 @@ REuint32 stateStackTop; REuint32 maxStateStack; typedef struct REGlobalData { - REParseState *regexp; /* the RE in execution */ + REState *regexp; /* the RE in execution */ REint32 length; /* length of input string */ const REchar *input; /* the input string */ - REError error; /* runtime error code (out_of_memory only?) */ + RE_Error error; /* runtime error code (out_of_memory only?) */ REint32 lastParen; /* highest paren set so far */ + REbool globalMultiline; /* as specified for current execution */ } REGlobalData; typedef struct REBackTrackData { REContinuationData continuation; /* where to backtrack to */ - REState *state; /* the state of the match */ + REMatchState *state; /* the state of the match */ REint32 lastParen; REProgState *precedingState; REint32 precedingStateTop; @@ -185,34 +188,35 @@ REint32 backTrackStackTop; /* Allocate space for a state and copy x into it. */ -static REState *copyState(REState *x) +static REMatchState *copyState(REMatchState *x) { - REState *result = (REState *)malloc(sizeof(REState) - + (x->n * sizeof(RECapture))); - memcpy(result, x, sizeof(REState) + (x->n * sizeof(RECapture))); + REuint32 sz = sizeof(REMatchState) + (x->parenCount * sizeof(RECapture)); + REMatchState *result = (REMatchState *)malloc(sz); + memcpy(result, x, sz); return result; } /* Copy state. */ -static void recoverState(REState *into, REState *from) +static void recoverState(REMatchState *into, REMatchState *from) { - memcpy(into, from, sizeof(REState) + (from->n * sizeof(RECapture))); + memcpy(into, from, sizeof(REMatchState) + + (from->parenCount * sizeof(RECapture))); } /* Bottleneck for any errors. */ -static void reportRegExpError(REError *errP, REError err) +static void reportRegExpError(RE_Error *errP, RE_Error err) { *errP = err; } /* forward declarations for parser routines */ -REbool parseDisjunction(REParseState *parseState); -REbool parseAlternative(REParseState *parseState); -REbool parseTerm(REParseState *parseState); +REbool parseDisjunction(REState *parseState); +REbool parseAlternative(REState *parseState); +REbool parseTerm(REState *parseState); static REbool isASCIIHexDigit(REchar c, REuint32 *digit) @@ -220,28 +224,28 @@ static REbool isASCIIHexDigit(REchar c, REuint32 *digit) REuint32 cv = c; if (cv < '0') - return false; + return RE_FALSE; if (cv <= '9') { *digit = cv - '0'; - return true; + return RE_TRUE; } cv |= 0x20; if (cv >= 'a' && cv <= 'f') { *digit = cv - 'a' + 10; - return true; + return RE_TRUE; } - return false; + return RE_FALSE; } /* Allocate & initialize a new node. */ -static RENode *newRENode(REParseState *pState, REOp kind) +static RENode *newRENode(REState *pState, REOp kind) { RENode *result = (RENode *)malloc(sizeof(RENode)); if (result == NULL) { - reportRegExpError(&pState->error, OUT_OF_MEMORY); + reportRegExpError(&pState->error, RE_OUT_OF_MEMORY); return NULL; } result->kind = kind; @@ -251,23 +255,23 @@ static RENode *newRENode(REParseState *pState, REOp kind) return result; } -REbool parseDisjunction(REParseState *parseState) +REbool parseDisjunction(REState *parseState) { - if (!parseAlternative(parseState)) return false; + if (!parseAlternative(parseState)) return RE_FALSE; if ((parseState->src != parseState->srcEnd) && (*parseState->src == '|')) { RENode *altResult; ++parseState->src; altResult = newRENode(parseState, REOP_ALT); - if (!altResult) return false; + if (!altResult) return RE_FALSE; altResult->child = parseState->result; - if (!parseDisjunction(parseState)) return false; + if (!parseDisjunction(parseState)) return RE_FALSE; altResult->data.child2 = parseState->result; parseState->result = altResult; parseState->codeLength += 9; /* alt, , ..., goto, */ } - return true; + return RE_TRUE; } /* @@ -276,23 +280,23 @@ REbool parseDisjunction(REParseState *parseState) * linked list for a list of terms for more than one term. * Consecutive FLAT1 nodes get combined into a single FLATN */ -REbool parseAlternative(REParseState *parseState) +REbool parseAlternative(REState *parseState) { RENode *headTerm = NULL; RENode *tailTerm = NULL; - while (true) { + while (RE_TRUE) { if ((parseState->src == parseState->srcEnd) || (*parseState->src == ')') || (*parseState->src == '|')) { if (!headTerm) { parseState->result = newRENode(parseState, REOP_EMPTY); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; } else parseState->result = headTerm; - return true; + return RE_TRUE; } - if (!parseTerm(parseState)) return false; + if (!parseTerm(parseState)) return RE_FALSE; if (headTerm == NULL) headTerm = parseState->result; else { @@ -309,6 +313,7 @@ REbool parseAlternative(REParseState *parseState) else { headTerm->next = parseState->result; tailTerm = parseState->result; + while (tailTerm->next) tailTerm = tailTerm->next; } } else { @@ -324,13 +329,14 @@ REbool parseAlternative(REParseState *parseState) else { tailTerm->next = parseState->result; tailTerm = tailTerm->next; + while (tailTerm->next) tailTerm = tailTerm->next; } } } } } -static REint32 getDecimalValue(REchar c, REParseState *parseState) +static REint32 getDecimalValue(REchar c, REState *parseState) { REint32 value = RE_UNDEC(c); while (parseState->src < parseState->srcEnd) { @@ -347,7 +353,7 @@ static REint32 getDecimalValue(REchar c, REParseState *parseState) /* calculate the total size of the bitmap required for a class expression */ -static REbool calculateBitmapSize(REParseState *pState, RENode *target) +static REbool calculateBitmapSize(REState *pState, RENode *target) { @@ -359,12 +365,12 @@ static REbool calculateBitmapSize(REParseState *pState, RENode *target) REuint32 nDigits; REuint32 i; REuint32 max = 0; - REbool inRange = false; + REbool inRange = RE_FALSE; target->data.chclass.length = 0; if (src == end) - return true; + return RE_TRUE; if (*src == '^') ++src; @@ -426,8 +432,8 @@ lexHex: break; case 'd': if (inRange) { - reportRegExpError(&pState->error, WRONG_RANGE); - return false; + reportRegExpError(&pState->error, RE_WRONG_RANGE); + return RE_FALSE; } localMax = '9'; break; @@ -437,11 +443,11 @@ lexHex: case 'w': case 'W': if (inRange) { - reportRegExpError(&pState->error, WRONG_RANGE); - return false; + reportRegExpError(&pState->error, RE_WRONG_RANGE); + return RE_FALSE; } target->data.chclass.length = 65536; - return true; + return RE_TRUE; default: localMax = c; break; @@ -453,22 +459,22 @@ lexHex: } if (inRange) { if (rangeStart > localMax) { - reportRegExpError(&pState->error, WRONG_RANGE); - return false; + reportRegExpError(&pState->error, RE_WRONG_RANGE); + return RE_FALSE; } - inRange = false; + inRange = RE_FALSE; } else { if (src < (end - 1)) { if (*src == '-') { ++src; - inRange = true; + inRange = RE_TRUE; rangeStart = (REchar)localMax; continue; } } } - if (pState->flags & IGNORECASE) { + if (pState->flags & RE_IGNORECASE) { c = canonicalize((REchar)localMax); if (c > localMax) localMax = c; @@ -477,28 +483,29 @@ lexHex: max = localMax; } target->data.chclass.length = max + 1; - return true; + return RE_TRUE; } -REbool parseTerm(REParseState *parseState) +REbool parseTerm(REState *parseState) { REchar c = *parseState->src++; REuint32 nDigits; REuint32 parenBaseCount = parseState->parenCount; REuint32 num, tmp; RENode *term; + REchar *numStart; switch (c) { /* assertions and atoms */ case '^': parseState->result = newRENode(parseState, REOP_BOL); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case '$': parseState->result = newRENode(parseState, REOP_EOL); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case '\\': @@ -508,36 +515,40 @@ REbool parseTerm(REParseState *parseState) /* assertion escapes */ case 'b' : parseState->result = newRENode(parseState, REOP_WBND); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case 'B': parseState->result = newRENode(parseState, REOP_UNWBND); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; /* Decimal escape */ case '0': - if (parseState->oldSyntax) { - /* octal escape (supported for backward compatibility) */ + if (parseState->version == RE_VERSION_1) { + /* octal escape */ +doOctal: num = 0; - while ((parseState->src < parseState->srcEnd) - && ('0' <= (c = *parseState->src++)) - && (c <= '7')) { - tmp = 8 * num + (REuint32)RE_UNDEC(c); - if (tmp > 0377) + while (parseState->src < parseState->srcEnd) { + c = *parseState->src; + if ((c >= '0') && (c <= '7')) { + parseState->src++; + tmp = 8 * num + (REuint32)RE_UNDEC(c); + if (tmp > 0377) + break; + num = tmp; + } + else break; - num = tmp; } - parseState->src--; parseState->result = newRENode(parseState, REOP_FLAT); parseState->codeLength += 3; - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = (REchar)(num); } else { parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = 0; parseState->codeLength += 3; } @@ -551,47 +562,76 @@ REbool parseTerm(REParseState *parseState) case '7': case '8': case '9': - parseState->result = newRENode(parseState, REOP_BACKREF); - if (!parseState->result) return false; - parseState->result->parenIndex - = (REuint32)(getDecimalValue(c, parseState) - 1); - parseState->codeLength += 3; + numStart = parseState->src - 1; + num = (REuint32)getDecimalValue(c, parseState); + if (parseState->version == RE_VERSION_1) { + /* + * n in [8-9] and > count of parentheses, + * then revert to '8' or '9', ignoring the '\' + */ + if (((num == 8) || (num == 9)) + && (num > parseState->parenCount)) { + parseState->result = newRENode(parseState, REOP_FLAT); + parseState->codeLength += 3; + if (!parseState->result) return RE_FALSE; + parseState->result->data.flat.ch = (REchar)(num + '0'); + } + /* + * more than 1 digit, or a number greater than + * the count of parentheses => it's an octal + */ + if (((parseState->src - numStart) > 1) + || (num > parseState->parenCount)) { + parseState->src = numStart; + goto doOctal; + } + parseState->result = newRENode(parseState, REOP_BACKREF); + if (!parseState->result) return RE_FALSE; + parseState->result->parenIndex = num - 1; + parseState->codeLength += 3; + } + else { + parseState->result = newRENode(parseState, REOP_BACKREF); + if (!parseState->result) return RE_FALSE; + parseState->result->parenIndex = num - 1; + parseState->codeLength += 3; + } break; /* Control escape */ case 'f': parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = 0xC; parseState->codeLength += 3; break; case 'n': parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = 0xA; parseState->codeLength += 3; break; case 'r': parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = 0xD; parseState->codeLength += 3; break; case 't': parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = 0x9; parseState->codeLength += 3; break; case 'v': parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = 0xB; parseState->codeLength += 3; break; /* Control letter */ case 'c': parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; if (((parseState->src + 1) < parseState->srcEnd) && RE_ISLETTER(parseState->src[1])) parseState->result->data.flat.ch @@ -613,7 +653,7 @@ REbool parseTerm(REParseState *parseState) nDigits = 4; lexHex: parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; { REuint32 n = 0; REuint32 i; @@ -622,8 +662,9 @@ lexHex: REuint32 digit; c = *parseState->src++; if (!isASCIIHexDigit(c, &digit)) { - /* back off to accepting the original - *'u' or 'x' as a literal + /* + * back off to accepting the original + * 'u' or 'x' as a literal */ parseState->src -= (i + 2); n = *parseState->src++; @@ -638,38 +679,38 @@ lexHex: /* Character class escapes */ case 'd': parseState->result = newRENode(parseState, REOP_DEC); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case 'D': parseState->result = newRENode(parseState, REOP_UNDEC); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case 's': parseState->result = newRENode(parseState, REOP_WS); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case 'S': parseState->result = newRENode(parseState, REOP_UNWS); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case 'w': parseState->result = newRENode(parseState, REOP_LETDIG); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; case 'W': parseState->result = newRENode(parseState, REOP_UNLETDIG); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; /* IdentityEscape */ default: parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = c; parseState->result->child = (void *)(parseState->src - 1); parseState->codeLength += 3; @@ -679,8 +720,8 @@ lexHex: } else { /* a trailing '\' is an error */ - reportRegExpError(&parseState->error, TRAILING_SLASH); - return false; + reportRegExpError(&parseState->error, RE_TRAILING_SLASH); + return RE_FALSE; } case '(': { @@ -693,13 +734,13 @@ lexHex: switch (*parseState->src++) { case '=': result = newRENode(parseState, REOP_ASSERT); - if (!result) return false; + if (!result) return RE_FALSE; /* ASSERT, , ... ASSERTTEST */ parseState->codeLength += 4; break; case '!': result = newRENode(parseState, REOP_ASSERTNOT); - if (!result) return false; + if (!result) return RE_FALSE; /* ASSERTNOT, , ... ASSERTNOTTEST */ parseState->codeLength += 4; break; @@ -709,14 +750,14 @@ lexHex: result = newRENode(parseState, REOP_PAREN); /* PAREN, , ... CLOSEPAREN, */ parseState->codeLength += 6; - if (!result) return false; + if (!result) return RE_FALSE; result->parenIndex = parseState->parenCount++; } - if (!parseDisjunction(parseState)) return false; + if (!parseDisjunction(parseState)) return RE_FALSE; if ((parseState->src == parseState->srcEnd) || (*parseState->src != ')')) { - reportRegExpError(&parseState->error, UNCLOSED_PAREN); - return false; + reportRegExpError(&parseState->error, RE_UNCLOSED_PAREN); + return RE_FALSE; } else { ++parseState->src; @@ -729,18 +770,18 @@ lexHex: } case '[': parseState->result = newRENode(parseState, REOP_CLASS); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->child = (void *)(parseState->src); - while (true) { + while (RE_TRUE) { if (parseState->src == parseState->srcEnd) { - reportRegExpError(&parseState->error, UNCLOSED_CLASS); - return false; + reportRegExpError(&parseState->error, RE_UNCLOSED_CLASS); + return RE_FALSE; } if (*parseState->src == '\\') { ++parseState->src; if (RE_ISDEC(*parseState->src)) { - reportRegExpError(&parseState->error, BACKREF_IN_CLASS); - return false; + reportRegExpError(&parseState->error, RE_BACKREF_IN_CLASS); + return RE_FALSE; } } else { @@ -755,18 +796,18 @@ lexHex: /* Call calculateBitmapSize now as we want any errors it finds to be reported during the parse phase, not at execution */ if (!calculateBitmapSize(parseState, parseState->result)) - return false; + return RE_FALSE; parseState->codeLength += 3; /* CLASS, */ break; case '.': parseState->result = newRENode(parseState, REOP_DOT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->codeLength++; break; default: parseState->result = newRENode(parseState, REOP_FLAT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.flat.ch = c; parseState->result->child = (void *)(parseState->src - 1); parseState->codeLength += 3; @@ -778,7 +819,7 @@ lexHex: switch (*parseState->src) { case '+': parseState->result = newRENode(parseState, REOP_QUANT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.quantifier.min = 1; parseState->result->data.quantifier.max = -1; /* , , , ... */ @@ -786,19 +827,17 @@ lexHex: goto quantifier; case '*': parseState->result = newRENode(parseState, REOP_QUANT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.quantifier.min = 0; parseState->result->data.quantifier.max = -1; - parseState->codeLength++; /* , , , ... */ parseState->codeLength += 8; goto quantifier; case '?': parseState->result = newRENode(parseState, REOP_QUANT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; parseState->result->data.quantifier.min = 0; parseState->result->data.quantifier.max = 1; - parseState->codeLength++; /* , , , ... */ parseState->codeLength += 8; goto quantifier; @@ -810,7 +849,7 @@ lexHex: ++parseState->src; parseState->result = newRENode(parseState, REOP_QUANT); - if (!parseState->result) return false; + if (!parseState->result) return RE_FALSE; c = *parseState->src; if (RE_ISDEC(c)) { @@ -836,13 +875,13 @@ lexHex: if (c == '}') goto quantifier; else { - reportRegExpError(&parseState->error, UNCLOSED_BRACKET); - return false; + reportRegExpError(&parseState->error, RE_UNCLOSED_BRACKET); + return RE_FALSE; } } } } - return true; + return RE_TRUE; quantifier: ++parseState->src; @@ -852,11 +891,11 @@ quantifier: = parseState->parenCount - parenBaseCount; if ((parseState->src < parseState->srcEnd) && (*parseState->src == '?')) { ++parseState->src; - parseState->result->data.quantifier.greedy = false; + parseState->result->data.quantifier.greedy = RE_FALSE; } else - parseState->result->data.quantifier.greedy = true; - return true; + parseState->result->data.quantifier.greedy = RE_TRUE; + return RE_TRUE; } @@ -866,17 +905,18 @@ quantifier: /* 1. Let e be x's endIndex. -2. If e is zero, return true. -3. If Multiline is false, return false. +2. If e is zero, return RE_TRUE. +3. If Multiline is RE_FALSE, return RE_FALSE. 4. If the character Input[e-1] is one of the line terminator characters , - , , or , return true. -5. Return false. + , , or , return RE_TRUE. +5. Return RE_FALSE. */ -static REState *bolMatcher(REGlobalData *gData, REState *x) +static REMatchState *bolMatcher(REGlobalData *gData, REMatchState *x) { REuint32 e = x->endIndex; if (e != 0) { - if (gData->regexp->flags & MULTILINE) { + if (gData->globalMultiline || + (gData->regexp->flags & RE_MULTILINE)) { if (!RE_ISLINETERM(gData->input[e - 1])) return NULL; } @@ -888,17 +928,18 @@ static REState *bolMatcher(REGlobalData *gData, REState *x) /* 1. Let e be x's endIndex. -2. If e is equal to InputLength, return true. -3. If multiline is false, return false. +2. If e is equal to InputLength, return RE_TRUE. +3. If multiline is RE_FALSE, return RE_FALSE. 4. If the character Input[e] is one of the line terminator characters , - , , or , return true. -5. Return false. + , , or , return RE_TRUE. +5. Return RE_FALSE. */ -static REState *eolMatcher(REGlobalData *gData, REState *x) +static REMatchState *eolMatcher(REGlobalData *gData, REMatchState *x) { REint32 e = x->endIndex; if (e != gData->length) { - if (gData->regexp->flags & MULTILINE) { + if (gData->globalMultiline || + (gData->regexp->flags & RE_MULTILINE)) { if (!RE_ISLINETERM(gData->input[e])) return NULL; } @@ -910,23 +951,23 @@ static REState *eolMatcher(REGlobalData *gData, REState *x) /* -1. If e == -1 or e == InputLength, return false. +1. If e == -1 or e == InputLength, return RE_FALSE. 2. Let c be the character Input[e]. -3. If c is one of the sixty-three characters in the table below, return true. +3. If c is one of the sixty-three characters in the table below, return RE_TRUE. a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ -4. Return false. +4. Return RE_FALSE. */ static REbool isWordChar(REint32 e, REGlobalData *gData) { REchar c; if ((e == -1) || (e == (REint32)(gData->length))) - return false; + return RE_FALSE; c = gData->input[e]; if (RE_ISLETDIG(c) || (c == '_')) - return true; - return false; + return RE_TRUE; + return RE_FALSE; } /* @@ -934,16 +975,16 @@ static REbool isWordChar(REint32 e, REGlobalData *gData) 2. Call IsWordChar(e-1) and let a be the boolean result. 3. Call IsWordChar(e) and let b be the boolean result. for '\b' -4. If a is true and b is false, return true. -5. If a is false and b is true, return true. -6. Return false. +4. If a is RE_TRUE and b is RE_FALSE, return RE_TRUE. +5. If a is RE_FALSE and b is RE_TRUE, return RE_TRUE. +6. Return RE_FALSE. for '\B' -4. If a is true and b is false, return false. -5. If a is false and b is true, return false. -6. Return true. +4. If a is RE_TRUE and b is RE_FALSE, return RE_FALSE. +5. If a is RE_FALSE and b is RE_TRUE, return RE_FALSE. +6. Return RE_TRUE. */ -static REState *wbndMatcher(REGlobalData *gData, REState *x, REbool sense) +static REMatchState *wbndMatcher(REGlobalData *gData, REMatchState *x, REbool sense) { REint32 e = (REint32)(x->endIndex); @@ -967,9 +1008,9 @@ static REState *wbndMatcher(REGlobalData *gData, REState *x, REbool sense) /* 1. Let A be the set of all characters except the four line terminator characters , , , or . -2. Call CharacterSetMatcher(A, false) and return its Matcher result. +2. Call CharacterSetMatcher(A, RE_FALSE) and return its Matcher result. */ -static REState *dotMatcher(REGlobalData *gData, REState *x) +static REMatchState *dotMatcher(REGlobalData *gData, REMatchState *x) { REchar ch; REint32 e = x->endIndex; @@ -988,7 +1029,7 @@ static REState *dotMatcher(REGlobalData *gData, REState *x) \D evaluates by returning the set of all characters not included in the set returned by \d. */ -static REState *decMatcher(REGlobalData *gData, REState *x, REbool sense) +static REMatchState *decMatcher(REGlobalData *gData, REMatchState *x, REbool sense) { REchar ch; REint32 e = x->endIndex; @@ -1008,14 +1049,14 @@ static REState *decMatcher(REGlobalData *gData, REState *x, REbool sense) \S evaluates by returning the set of all characters not included in the set returned by \s. */ -static REState *wsMatcher(REGlobalData *gData, REState *x, REbool sense) +static REMatchState *wsMatcher(REGlobalData *gData, REMatchState *x, REbool sense) { REchar ch; REint32 e = x->endIndex; if (e == gData->length) return NULL; ch = gData->input[e]; - if (RE_ISWS(ch) != sense) + if (RE_ISSPACE(ch) != sense) return NULL; x->endIndex++; return x; @@ -1030,14 +1071,14 @@ static REState *wsMatcher(REGlobalData *gData, REState *x, REbool sense) \W evaluates by returning the set of all characters not included in the set returned by \w. */ -static REState *letdigMatcher(REGlobalData *gData, REState *x, REbool sense) +static REMatchState *letdigMatcher(REGlobalData *gData, REMatchState *x, REbool sense) { REchar ch; REint32 e = x->endIndex; if (e == gData->length) return NULL; ch = gData->input[e]; - if (RE_ISLETDIG(ch) != sense) + if ((RE_ISLETDIG(ch) || (ch == '_')) != sense) return NULL; x->endIndex++; return x; @@ -1050,7 +1091,7 @@ and a Continuation c, and performs the following: 2. If e == InputLength, return failure. 3. Let c be the character Input[e]. 4. Let cc be the result of Canonicalize(c). - 5. If invert is true, go to step 8. + 5. If invert is RE_TRUE, go to step 8. 6. If there does not exist a member a of set A such that Canonicalize(a) == cc, then return failure. 7. Go to step 9. @@ -1060,7 +1101,7 @@ and a Continuation c, and performs the following: 10. Let y be the State (e+1, cap). 11. Call c(y) and return its result. */ -static REState *flatMatcher(REGlobalData *gData, REState *x, REchar matchCh) +static REMatchState *flatMatcher(REGlobalData *gData, REMatchState *x, REchar matchCh) { REchar ch; REint32 e = x->endIndex; @@ -1074,7 +1115,7 @@ static REState *flatMatcher(REGlobalData *gData, REState *x, REchar matchCh) return x; } -static REState *flatIMatcher(REGlobalData *gData, REState *x, REchar matchCh) +static REMatchState *flatIMatcher(REGlobalData *gData, REMatchState *x, REchar matchCh) { REchar ch; REint32 e = x->endIndex; @@ -1091,7 +1132,7 @@ static REState *flatIMatcher(REGlobalData *gData, REState *x, REchar matchCh) /* Consecutive literal characters. */ -static REState *flatNMatcher(REGlobalData *gData, REState *x, +static REMatchState *flatNMatcher(REGlobalData *gData, REMatchState *x, REchar *matchChars, REint32 length) { REint32 e = x->endIndex; @@ -1106,7 +1147,7 @@ static REState *flatNMatcher(REGlobalData *gData, REState *x, return x; } -static REState *flatNIMatcher(REGlobalData *gData, REState *x, +static REMatchState *flatNIMatcher(REGlobalData *gData, REMatchState *x, REchar *matchChars, REint32 length) { REint32 e = x->endIndex; @@ -1122,9 +1163,9 @@ static REState *flatNIMatcher(REGlobalData *gData, REState *x, return x; } -/* Add a single character to the CharSet */ +/* Add a single character to the RECharSet */ -static void addCharacterToCharSet(CharSet *cs, REchar c) +static void addCharacterToCharSet(RECharSet *cs, REchar c) { REuint32 byteIndex = (REuint32)(c / 8); @@ -1133,9 +1174,9 @@ static void addCharacterToCharSet(CharSet *cs, REchar c) } -/* Add a character range, c1 to c2 (inclusive) to the CharSet */ +/* Add a character range, c1 to c2 (inclusive) to the RECharSet */ -static void addCharacterRangeToCharSet(CharSet *cs, REchar c1, REchar c2) +static void addCharacterRangeToCharSet(RECharSet *cs, REchar c1, REchar c2) { REuint32 i; @@ -1160,9 +1201,9 @@ static void addCharacterRangeToCharSet(CharSet *cs, REchar c1, REchar c2) } -/* Compile the source of the class into a CharSet */ +/* Compile the source of the class into a RECharSet */ -static REbool processCharSet(REParseState *pState, RENode *target) +static REbool processCharSet(REState *pState, RENode *target) { REchar rangeStart, thisCh; const REchar *src = (const REchar *)(target->child); @@ -1172,23 +1213,23 @@ static REbool processCharSet(REParseState *pState, RENode *target) REchar c; REint32 nDigits; REint32 i; - REbool inRange = false; + REbool inRange = RE_FALSE; - CharSet *charSet = &pState->classList[target->data.chclass.classIndex]; + RECharSet *charSet = &pState->classList[target->data.chclass.classIndex]; charSet->length = target->data.chclass.length; - charSet->sense = true; + charSet->sense = RE_TRUE; byteLength = (charSet->length / 8) + 1; charSet->bits = (REuint8 *)malloc(byteLength); if (!charSet->bits) - return false; + return RE_FALSE; memset(charSet->bits, 0, byteLength); if (src == end) { - return true; + return RE_TRUE; } if (*src == '^') { - charSet->sense = false; + charSet->sense = RE_FALSE; ++src; } @@ -1259,12 +1300,12 @@ lexHex: continue; case 's': for (i = (REint32)(charSet->length - 1); i >= 0; i--) - if (RE_ISWS(i)) + if (RE_ISSPACE(i)) addCharacterToCharSet(charSet, (REchar)(i)); continue; case 'S': for (i = (REint32)(charSet->length - 1); i >= 0; i--) - if (!RE_ISWS(i)) + if (!RE_ISSPACE(i)) addCharacterToCharSet(charSet, (REchar)(i)); continue; case 'w': @@ -1290,7 +1331,7 @@ lexHex: } if (inRange) { - if (pState->flags & IGNORECASE) { + if (pState->flags & RE_IGNORECASE) { REchar minch = (REchar)65535; REchar maxch = 0; /* @@ -1313,22 +1354,22 @@ lexHex: } else addCharacterRangeToCharSet(charSet, rangeStart, thisCh); - inRange = false; + inRange = RE_FALSE; } else { - if (pState->flags & IGNORECASE) + if (pState->flags & RE_IGNORECASE) addCharacterToCharSet(charSet, canonicalize(thisCh)); addCharacterToCharSet(charSet, thisCh); if (src < (end - 1)) { if (*src == '-') { ++src; - inRange = true; + inRange = RE_TRUE; rangeStart = thisCh; } } } } - return true; + return RE_TRUE; } @@ -1336,10 +1377,10 @@ lexHex: Initialize the character set if it this is the first call. Test the bit - if the ^ flag was specified, non-inclusion is a success */ -static REState *classMatcher(REGlobalData *gData, REState *x, REint32 index) +static REMatchState *classMatcher(REGlobalData *gData, REMatchState *x, REint32 index) { REchar ch; - CharSet *charSet; + RECharSet *charSet; REint32 byteIndex; REint32 e = x->endIndex; if (e == gData->length) @@ -1380,8 +1421,8 @@ static REState *classMatcher(REGlobalData *gData, REState *x, REint32 index) 1. Evaluate DecimalEscape to obtain an EscapeValue E. 2. If E is not a character then go to step 6. 3. Let ch be E's character. -4. Let A be a one-element CharSet containing the character ch. -5. Call CharacterSetMatcher(A, false) and return its Matcher result. +4. Let A be a one-element RECharSet containing the character ch. +5. Call CharacterSetMatcher(A, RE_FALSE) and return its Matcher result. 6. E must be an integer. Let n be that integer. 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. 8. Return an internal Matcher closure that takes two arguments, a State x @@ -1400,8 +1441,8 @@ static REState *classMatcher(REGlobalData *gData, REState *x, REint32 index) 10. Call c(y) and return its result. */ -static REState *backrefMatcher(REGlobalData *gData, - REState *x, REuint32 parenIndex) +static REMatchState *backrefMatcher(REGlobalData *gData, + REMatchState *x, REuint32 parenIndex) { REuint32 e; REuint32 len; @@ -1419,7 +1460,7 @@ static REState *backrefMatcher(REGlobalData *gData, return NULL; parenContent = &gData->input[s->index]; - if (gData->regexp->flags & IGNORECASE) { + if (gData->regexp->flags & RE_IGNORECASE) { for (i = 0; i < len; i++) { if (canonicalize(parenContent[i]) != canonicalize(gData->input[e + i])) @@ -1474,7 +1515,7 @@ static void freeRENode(RENode *t) #define EMIT_FIXUP(branch, target) (EMIT_ARG((branch), (target) - (branch))) #define GET_ARG(pc) ((REuint32)(((pc)[0] << 8) | (pc)[1])) -REuint8 *emitREBytecode(REParseState *pState, REuint8 *pc, RENode *t) +REuint8 *emitREBytecode(REState *pState, REuint8 *pc, RENode *t) { RENode *nextAlt; REuint8 *nextAltFixup, *nextTermFixup; @@ -1509,7 +1550,7 @@ REuint8 *emitREBytecode(REParseState *pState, REuint8 *pc, RENode *t) break; case REOP_FLAT: if (t->child && (t->data.flat.length > 1)) { - if (pState->flags & IGNORECASE) + if (pState->flags & RE_IGNORECASE) pc[-1] = REOP_FLATNi; else pc[-1] = REOP_FLATN; @@ -1517,7 +1558,7 @@ REuint8 *emitREBytecode(REParseState *pState, REuint8 *pc, RENode *t) EMIT_ARG(pc, t->data.flat.length); } else { /* XXX original Monkey code separated ASCII and Unicode cases to save extra byte */ - if (pState->flags & IGNORECASE) + if (pState->flags & RE_IGNORECASE) pc[-1] = REOP_FLAT1i; else pc[-1] = REOP_FLAT1; @@ -1586,7 +1627,7 @@ REuint8 *emitREBytecode(REParseState *pState, REuint8 *pc, RENode *t) } REBackTrackData *pushBackTrackState(REGlobalData *gData, REOp op, - REuint8 *target, REState *x) + REuint8 *target, REMatchState *x) { REBackTrackData *result; if (backTrackStackTop == maxBackTrack) { @@ -1594,7 +1635,7 @@ REBackTrackData *pushBackTrackState(REGlobalData *gData, REOp op, backTrackStack = (REBackTrackData *)realloc(backTrackStack, sizeof(REBackTrackData) * maxBackTrack); if (!backTrackStack) { - reportRegExpError(&gData->error, OUT_OF_MEMORY); + reportRegExpError(&gData->error, RE_OUT_OF_MEMORY); return NULL; } } @@ -1609,7 +1650,7 @@ REBackTrackData *pushBackTrackState(REGlobalData *gData, REOp op, result->precedingState = (REProgState *)malloc(sizeof(REProgState) * stateStackTop); if (!result->precedingState) { - reportRegExpError(&gData->error, OUT_OF_MEMORY); + reportRegExpError(&gData->error, RE_OUT_OF_MEMORY); return NULL; } memcpy(result->precedingState, stateStack, sizeof(REProgState) @@ -1621,14 +1662,14 @@ REBackTrackData *pushBackTrackState(REGlobalData *gData, REOp op, return result; } -static REState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REState *x) +static REMatchState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REMatchState *x) { REOp op = (REOp)(*pc++); REContinuationData currentContinuation; - REState *result; + REMatchState *result; REBackTrackData *backTrackData; REint32 k, length, offset, parenIndex, index; - REbool anchor = false; + REbool anchor = RE_FALSE; REchar anchorCh; REchar matchCh; REuint8 *nextpc; @@ -1646,25 +1687,25 @@ static REState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REState *x) case REOP_FLAT1: case REOP_FLAT1i: anchorCh = GET_ARG(pc); - anchor = true; + anchor = RE_TRUE; break; case REOP_FLATN: case REOP_FLATNi: k = GET_ARG(pc); anchorCh = gData->regexp->srcStart[k]; - anchor = true; + anchor = RE_TRUE; break; } if (anchor) { - anchor = false; + anchor = RE_FALSE; for (k = x->endIndex; k < gData->length; k++) { matchCh = gData->input[k]; if ((matchCh == anchorCh) || - ((gData->regexp->flags & IGNORECASE) + ((gData->regexp->flags & RE_IGNORECASE) && (canonicalize(matchCh) == canonicalize(anchorCh)))) { x->endIndex = k; x->startIndex = k; /* inform caller that we bumped along */ - anchor = true; + anchor = RE_TRUE; break; } } @@ -1672,7 +1713,7 @@ static REState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REState *x) return NULL; } - while (true) { + while (RE_TRUE) { switch (op) { case REOP_EMPTY: result = x; @@ -1684,31 +1725,31 @@ static REState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REState *x) result = eolMatcher(gData, x); break; case REOP_WBND: - result = wbndMatcher(gData, x, true); + result = wbndMatcher(gData, x, RE_TRUE); break; case REOP_UNWBND: - result = wbndMatcher(gData, x, false); + result = wbndMatcher(gData, x, RE_FALSE); break; case REOP_DOT: result = dotMatcher(gData, x); break; case REOP_DEC: - result = decMatcher(gData, x, true); + result = decMatcher(gData, x, RE_TRUE); break; case REOP_UNDEC: - result = decMatcher(gData, x, false); + result = decMatcher(gData, x, RE_FALSE); break; case REOP_WS: - result = wsMatcher(gData, x, true); + result = wsMatcher(gData, x, RE_TRUE); break; case REOP_UNWS: - result = wsMatcher(gData, x, false); + result = wsMatcher(gData, x, RE_FALSE); break; case REOP_LETDIG: - result = letdigMatcher(gData, x, true); + result = letdigMatcher(gData, x, RE_TRUE); break; case REOP_UNLETDIG: - result = letdigMatcher(gData, x, false); + result = letdigMatcher(gData, x, RE_FALSE); break; case REOP_FLATN: offset = GET_ARG(pc); @@ -1802,7 +1843,13 @@ static REState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REState *x) continue; case REOP_ASSERTTEST: --stateStackTop; - x->endIndex = stateStack[stateStackTop].index; + x->endIndex = stateStack[stateStackTop].index; + for (k = stateStack[stateStackTop].parenCount; + k < backTrackStackTop; k++) { + if (backTrackStack[k].precedingState) + free(backTrackStack[k].precedingState); + free(backTrackStack[k].state); + } backTrackStackTop = stateStack[stateStackTop].parenCount; currentContinuation = stateStack[stateStackTop].continuation; if (result != NULL) @@ -1810,7 +1857,13 @@ static REState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REState *x) break; case REOP_ASSERTNOTTEST: --stateStackTop; - x->endIndex = stateStack[stateStackTop].index; + x->endIndex = stateStack[stateStackTop].index; + for (k = stateStack[stateStackTop].parenCount; + k < backTrackStackTop; k++) { + if (backTrackStack[k].precedingState) + free(backTrackStack[k].precedingState); + free(backTrackStack[k].state); + } backTrackStackTop = stateStack[stateStackTop].parenCount; currentContinuation = stateStack[stateStackTop].continuation; if (result == NULL) @@ -1823,7 +1876,7 @@ static REState *executeREBytecode(REuint8 *pc, REGlobalData *gData, REState *x) index = GET_ARG(pc); pc += ARG_LEN; result = classMatcher(gData, x, index); - if (gData->error != NO_ERROR) return NULL; + if (gData->error != RE_NO_ERROR) return NULL; break; case REOP_END: @@ -2068,32 +2121,50 @@ minimalquantcommon: /* * Throw away the RegExp and all data associated with it. */ -void freeRegExp(REParseState *pState) +void REfreeRegExp(REState *pState) { REuint32 i; if (pState->result) freeRENode(pState->result); - if (pState->srcStart) free(pState->srcStart); if (pState->pc) free(pState->pc); for (i = 0; i < pState->classCount; i++) { free(pState->classList[i].bits); } + if (pState->srcStart) free(pState->srcStart); + free(pState->classList); free(pState); } +RE_Error parseFlags(const REchar *flagsSource, REint32 flagsLength, REuint32 *flags) +{ + REint32 i; + *flags = 0; + + for (i = 0; i < flagsLength; i++) { + switch (flagsSource[i]) { + case 'g': + *flags |= RE_GLOBAL; break; + case 'i': + *flags |= RE_IGNORECASE; break; + case 'm': + *flags |= RE_MULTILINE; break; + default: + return RE_BAD_FLAG; + } + } + return RE_NO_ERROR; +} /* * Parse the regexp - errors are reported via the registered error function * and NULL is returned. Otherwise the regexp is compiled and the completed * ParseState returned. */ -REParseState *REParse(const REchar *source, REint32 sourceLength, - const REchar *flags, REint32 flagsLength, - REbool oldSyntax) +REState *REParse(const REchar *source, REint32 sourceLength, + REuint32 flags, RE_Version version) { REuint8 *endPC; - REint32 i; RENode *t; - REParseState *pState = (REParseState *)malloc(sizeof(REParseState)); + REState *pState = (REState *)malloc(sizeof(REState)); if (!pState) return NULL; pState->srcStart = (REchar *)malloc(sizeof(REchar) * sourceLength); if (!pState->srcStart) goto fail; @@ -2101,26 +2172,12 @@ REParseState *REParse(const REchar *source, REint32 sourceLength, pState->srcEnd = pState->srcStart + sourceLength; pState->src = pState->srcStart; pState->parenCount = 0; - pState->lastIndex = 0; - pState->flags = 0; - pState->oldSyntax = oldSyntax; + pState->flags = flags; + pState->version = version; pState->classList = NULL; pState->classCount = 0; pState->codeLength = 0; - for (i = 0; i < flagsLength; i++) { - switch (flags[i]) { - case 'g': - pState->flags |= GLOBAL; break; - case 'i': - pState->flags |= IGNORECASE; break; - case 'm': - pState->flags |= MULTILINE; break; - default: - reportRegExpError(&pState->error, BAD_FLAG); - goto fail; - } - } if (parseDisjunction(pState)) { t = pState->result; if (t) { @@ -2132,7 +2189,7 @@ REParseState *REParse(const REchar *source, REint32 sourceLength, else pState->result = newRENode(pState, REOP_END); if (pState->classCount) { - pState->classList = (CharSet *)malloc(sizeof(CharSet) + pState->classList = (RECharSet *)malloc(sizeof(RECharSet) * pState->classCount); if (!pState->classList) goto fail; } @@ -2151,10 +2208,10 @@ fail: return NULL; } -static REState *initMatch(REGlobalData *gData, REParseState *parseState, - const REchar *text, REint32 length) +static REMatchState *initMatch(REGlobalData *gData, REState *pState, + const REchar *text, REint32 length, int globalMultiline) { - REState *result; + REMatchState *result; REint32 j; if (!backTrackStack) { @@ -2162,7 +2219,7 @@ static REState *initMatch(REGlobalData *gData, REParseState *parseState, backTrackStack = (REBackTrackData *)malloc(sizeof(REBackTrackData) * maxBackTrack); if (!backTrackStack) { - reportRegExpError(&gData->error, OUT_OF_MEMORY); + reportRegExpError(&gData->error, RE_OUT_OF_MEMORY); return NULL; } } @@ -2171,29 +2228,32 @@ static REState *initMatch(REGlobalData *gData, REParseState *parseState, stateStack = (REProgState *)malloc(sizeof(REProgState) * maxStateStack); if (!stateStack) { - reportRegExpError(&gData->error, OUT_OF_MEMORY); + reportRegExpError(&gData->error, RE_OUT_OF_MEMORY); return NULL; } } - result = (REState *)malloc(sizeof(REState) - + (parseState->parenCount * sizeof(RECapture))); + result = (REMatchState *)malloc(sizeof(REMatchState) + + (pState->parenCount * sizeof(RECapture))); if (!result) { - reportRegExpError(&gData->error, OUT_OF_MEMORY); + reportRegExpError(&gData->error, RE_OUT_OF_MEMORY); return NULL; } - result->n = parseState->parenCount; - for (j = 0; j < result->n; j++) + result->parenCount = pState->parenCount; + for (j = 0; j < result->parenCount; j++) result->parens[j].index = -1; result->startIndex = 0; result->endIndex = 0; + + pState->error = RE_NO_ERROR; - gData->regexp = parseState; + gData->regexp = pState; gData->input = text; gData->length = length; - gData->error = NO_ERROR; + gData->error = RE_NO_ERROR; gData->lastParen = 0; + gData->globalMultiline = globalMultiline; backTrackStackTop = 0; stateStackTop = 0; @@ -2204,70 +2264,66 @@ static REState *initMatch(REGlobalData *gData, REParseState *parseState, * The [[Match]] implementation * */ -REState *REMatch(REParseState *parseState, const REchar *text, - REint32 length) +REMatchState *REMatch(REState *pState, const REchar *text, REint32 length) { REint32 j; REGlobalData gData; - REState *result; - REState *x = initMatch(&gData, parseState, text, length); + REMatchState *result; + REMatchState *x = initMatch(&gData, pState, text, length, 0); if (!x) return NULL; - result = executeREBytecode(parseState->pc, &gData, x); - for (j = 0; j < backTrackStackTop; j++) + result = executeREBytecode(pState->pc, &gData, x); + for (j = 0; j < backTrackStackTop; j++) { + if (backTrackStack[j].precedingState) + free(backTrackStack[j].precedingState); free(backTrackStack[j].state); - if (gData.error != NO_ERROR) return NULL; + } + if (gData.error != RE_NO_ERROR) return NULL; return result; } /* - * Execute the RegExp against the supplied text, filling in the REState. + * Execute the RegExp against the supplied text, filling in the REMatchState. * */ -REState *REExecute(REParseState *parseState, const REchar *text, - REint32 length) +REMatchState *REExecute(REState *pState, const REchar *text, REint32 offset, REint32 length, int globalMultiline) { - REState *result; + REMatchState *result; REGlobalData gData; REint32 i; - REState *x = initMatch(&gData, parseState, text, length); + REMatchState *x = initMatch(&gData, pState, text, length, globalMultiline); if (!x) return NULL; - if (parseState->flags & GLOBAL) { - x->startIndex = parseState->lastIndex; - if ((x->startIndex < 0) || (x->startIndex > length)) { - parseState->lastIndex = 0; - free(x); - return NULL; - } - x->endIndex = x->startIndex; - } - - while (true) { - result = executeREBytecode(parseState->pc, &gData, x); - for (i = 0; i < backTrackStackTop; i++) + x->startIndex = offset; + x->endIndex = offset; + while (RE_TRUE) { + result = executeREBytecode(pState->pc, &gData, x); + for (i = 0; i < backTrackStackTop; i++) { + if (backTrackStack[i].precedingState) + free(backTrackStack[i].precedingState); free(backTrackStack[i].state); + } + backTrackStackTop = 0; stateStackTop = 0; - if (gData.error != NO_ERROR) return NULL; + if (gData.error != RE_NO_ERROR) { + pState->error = gData.error; + return NULL; + } if (result == NULL) { x->startIndex++; if (x->startIndex > length) { - parseState->lastIndex = 0; free(x); return NULL; } x->endIndex = x->startIndex; } - else { - if (parseState->flags & GLOBAL) - parseState->lastIndex = result->endIndex; + else break; - } } return result; } @@ -2296,17 +2352,17 @@ int main(int argc, char* argv[]) char regexpInput[128]; char *regexpSrc; char str[128]; - REState *result; + REMatchState *result; int regexpLength; char *flagSrc; int flagSrcLength; REint32 i, j; printf("Delimit regexp by / / (with flags following) and strings by \" \"\n"); - while (true) { + while (RE_TRUE) { REchar *regexpWideSrc; REchar *flagWideSrc; - REParseState *pState; + REState *pState; printf("regexp : "); scanf("%s", regexpInput); @@ -2330,9 +2386,9 @@ int main(int argc, char* argv[]) regexpWideSrc = widen(regexpSrc, regexpLength); flagWideSrc = widen(flagSrc, flagSrcLength); pState = REParse(regexpWideSrc, regexpLength, - flagWideSrc, flagSrcLength, true); + flagWideSrc, flagSrcLength, RE_TRUE); if (pState) { - while (true) { + while (RE_TRUE) { printf("string : "); scanf("%s", str); if (*str != '"') diff --git a/js2/src/regexp/regexp.h b/js2/src/regexp/regexp.h index c61ce3b26246..26d279835b1b 100644 --- a/js2/src/regexp/regexp.h +++ b/js2/src/regexp/regexp.h @@ -45,96 +45,108 @@ typedef unsigned int REuint32; typedef int REint32; typedef unsigned char REuint8; -#ifdef STANDALONE -typedef unsigned char REbool; -enum { false, true }; -#else -typedef bool REbool; -#endif -typedef enum RE_FLAGS { - MULTILINE = 0x1, - IGNORECASE = 0x2, - GLOBAL = 0x4 -} RE_FLAGS; +typedef enum RE_Flags { + RE_IGNORECASE = 0x1, + RE_GLOBAL = 0x2, + RE_MULTILINE = 0x4 +} RE_Flags; -typedef enum REError { - NO_ERROR, - TRAILING_SLASH, /* an backslash just before the end of the RE */ - UNCLOSED_PAREN, /* mis-matched parens */ - UNCLOSED_BRACKET, /* mis-matched parens */ - UNCLOSED_CLASS, /* '[' missing ']' */ - BACKREF_IN_CLASS, /* used '\' in '[..]' */ - BAD_FLAG, /* unrecognized flag (not i, g or m) */ - WRONG_RANGE, /* range lo > range hi */ - OUT_OF_MEMORY -} REError; +typedef enum RE_Version { + RE_VERSION_1, /* octal literal support */ + RE_VERSION_2 +} RE_Version; + +typedef enum RE_Error { + RE_NO_ERROR, + RE_TRAILING_SLASH, /* a backslash just before the end of the RE */ + RE_UNCLOSED_PAREN, /* mis-matched parens */ + RE_UNCLOSED_BRACKET, /* mis-matched parens */ + RE_UNCLOSED_CLASS, /* '[' missing ']' */ + RE_BACKREF_IN_CLASS, /* used '\' in '[..]' */ + RE_BAD_FLAG, /* unrecognized flag (not i, g or m) */ + RE_WRONG_RANGE, /* range lo > range hi */ + RE_OUT_OF_MEMORY +} RE_Error; typedef struct RENode RENode; -typedef struct CharSet { +typedef struct RECharSet { REuint8 *bits; REuint32 length; - REbool sense; -} CharSet; + unsigned char sense; +} RECharSet; -typedef struct REParseState { +typedef struct REState { REchar *srcStart; /* copy of source text */ REchar *src; /* current parse position */ REchar *srcEnd; /* end of source text */ - RENode *result; /* head of result tree */ REuint32 parenCount; /* # capturing parens */ - REuint32 flags; /* flags from regexp */ - REint32 lastIndex; /* position from last match (for 'g' flag) */ - REbool oldSyntax; /* backward compatibility for octal chars */ - REError error; /* parse-time error */ + REuint32 flags; /* union of flags from regexp */ + RE_Version version; + RE_Error error; /* parse-time or runtime error */ REuint32 classCount; /* number of contained []'s */ - CharSet *classList; /* data for []'s */ + RECharSet *classList; /* data for []'s */ + RENode *result; /* head of result tree */ REint32 codeLength; /* length of bytecode */ REuint8 *pc; /* start of bytecode */ -} REParseState; +} REState; typedef struct RECapture { REint32 index; /* start of contents of this capture, -1 for empty */ REint32 length; /* length of capture */ } RECapture; -typedef struct REState { - REint32 startIndex; - REint32 endIndex; - REint32 n; /* set to (n - 1), i.e. for /((a)b)/, this field is 1 */ +typedef struct REMatchState { + REint32 startIndex; /* beginning of succesful match */ + REint32 endIndex; /* character beyond end of succesful match */ + REint32 parenCount; /* set to (n - 1), i.e. for /((a)b)/, this field is 1 */ RECapture parens[1]; /* first of 'n' captures, allocated at end of this struct */ -} REState; +} REMatchState; + +/* + * Compiles the flags source text into a union of flag values. Returns RE_NO_ERROR + * or RE_BAD_FLAG. + * + */ +RE_Error parseFlags(const REchar *flagsSource, REint32 flagsLength, REuint32 *flags); + /* - * Compiles the RegExp source into a tree of RENodes in the returned - * parse state result field. Errors are flagged via the error reporter - * function and signalled via a NULL return. - * The RegExp source does not have any delimiters and the flag string is - * to be supplied separately (can be NULL, with a 0 length) + * Compiles the RegExp source into a stream of REByteCodes and fills in the REState struct. + * Errors are recorded in the state 'error' field and signalled by a NULL return. + * The RegExp source does not have any delimiters. */ -REParseState *REParse(const REchar *source, REint32 sourceLength, const REchar *flags, REint32 flagsLength, REbool oldSyntax); +REState *REParse(const REchar *source, REint32 sourceLength, REuint32 flags, RE_Version version); + /* - * The [[Match]] implementation + * Execute the RegExp against the supplied text. + * The return value is NULL for no match, otherwise an REMatchState struct. * */ -REState *REMatch(REParseState *parseState, const REchar *text, REint32 length); +REMatchState *REExecute(REState *pState, const REchar *text, REint32 offset, REint32 length, int globalMulitline); + /* - * Execute the RegExp against the supplied text, filling in the REState. - * + * The [[Match]] implementation, applies the regexp at the start of the text + * only (i.e. it does not search repeatedly through the text for a match). + * NULL return for no match. * */ -REState *REExecute(REParseState *parseState, const REchar *text, REint32 length); +REMatchState *REMatch(REState *pState, const REchar *text, REint32 length); + /* * Throw away the RegExp and all data associated with it. */ -void freeRegExp(REParseState *parseState); +void REfreeRegExp(REState *pState); + + + /* * Needs to be provided by the host, following these specs: @@ -151,3 +163,11 @@ void freeRegExp(REParseState *parseState); * 6. Return cu. */ extern REchar canonicalize(REchar ch); + +/* + * The host should also provide a definition of whitespace to match the following: + * + */ +#ifndef RE_ISSPACE +#define RE_ISSPACE(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\v') || (c == '\f') ) +#endif \ No newline at end of file diff --git a/js2/src/winbuild/dikdik.dsp b/js2/src/winbuild/dikdik.dsp index 7f7daeeffa10..a48b4596ea4f 100644 --- a/js2/src/winbuild/dikdik.dsp +++ b/js2/src/winbuild/dikdik.dsp @@ -1,482 +1,486 @@ -# Microsoft Developer Studio Project File - Name="DikDik" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=DikDik - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "dikdik.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "dikdik.mak" CFG="DikDik - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "DikDik - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "DikDik - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /GR /GX /O2 /Ob2 /I "../../../js/src/fdlibm" /I "../regexp" /D "_LIB" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "XP_PC" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "DikDik___Win32_Debug" -# PROP BASE Intermediate_Dir "DikDik___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "DikDik___Win32_Debug" -# PROP Intermediate_Dir "DikDik___Win32_Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GR /GX /ZI /Od /I "../../../js/src/fdlibm" /I "../regexp" /D "_LIB" /D "DEBUG" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "XP_PC" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ENDIF - -# Begin Target - -# Name "DikDik - Win32 Release" -# Name "DikDik - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\bytecodegen.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\exception.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\fdlibm_ns.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\formatter.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\hash.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\js2execution.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\js2runtime.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jsarray.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jsdate.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jsmath.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jsstring.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\lexer.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\mem.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\numerics.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\parser.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\prmjtime.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\reader.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\regexpwrapper.cpp -# End Source File -# Begin Source File - -SOURCE=..\strings.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\token.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\tracer.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\utilities.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\world.cpp - -!IF "$(CFG)" == "DikDik - Win32 Release" - -# ADD CPP /W4 /GR /I "../../regexp" - -!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" - -!ENDIF - -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\algo.h -# End Source File -# Begin Source File - -SOURCE=..\bytecodegen.h -# End Source File -# Begin Source File - -SOURCE=..\ds.h -# End Source File -# Begin Source File - -SOURCE=..\exception.h -# End Source File -# Begin Source File - -SOURCE=..\fdlibm_ns.h -# End Source File -# Begin Source File - -SOURCE=..\formatter.h -# End Source File -# Begin Source File - -SOURCE=..\hash.h -# End Source File -# Begin Source File - -SOURCE=..\js2runtime.h -# End Source File -# Begin Source File - -SOURCE=..\jsarray.h -# End Source File -# Begin Source File - -SOURCE=..\jsdate.h -# End Source File -# Begin Source File - -SOURCE=..\jsmath.h -# End Source File -# Begin Source File - -SOURCE=..\jsstring.h -# End Source File -# Begin Source File - -SOURCE=..\lexer.h -# End Source File -# Begin Source File - -SOURCE=..\mem.h -# End Source File -# Begin Source File - -SOURCE=..\numerics.h -# End Source File -# Begin Source File - -SOURCE=..\parser.h -# End Source File -# Begin Source File - -SOURCE=..\prmjtime.h -# End Source File -# Begin Source File - -SOURCE=..\property.h -# End Source File -# Begin Source File - -SOURCE=..\reader.h -# End Source File -# Begin Source File - -SOURCE=..\stlcfg.h -# End Source File -# Begin Source File - -SOURCE=..\strings.h -# End Source File -# Begin Source File - -SOURCE=..\systemtypes.h -# End Source File -# Begin Source File - -SOURCE=..\token.h -# End Source File -# Begin Source File - -SOURCE=..\utilities.h -# End Source File -# Begin Source File - -SOURCE=..\world.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="DikDik" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=DikDik - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dikdik.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dikdik.mak" CFG="DikDik - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "DikDik - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "DikDik - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /GR /GX /O2 /Ob2 /I "../../../js/src/fdlibm" /I "../regexp" /D "_LIB" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "XP_PC" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "DikDik___Win32_Debug" +# PROP BASE Intermediate_Dir "DikDik___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DikDik___Win32_Debug" +# PROP Intermediate_Dir "DikDik___Win32_Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GR /GX /ZI /Od /I "../../../js/src/fdlibm" /I "../regexp" /D "_LIB" /D "DEBUG" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "XP_PC" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "DikDik - Win32 Release" +# Name "DikDik - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\bytecodegen.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\collector.cpp +# End Source File +# Begin Source File + +SOURCE=..\exception.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\fdlibm_ns.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\formatter.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\hash.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\js2execution.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\js2runtime.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\jsarray.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\jsdate.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\jsmath.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\jsstring.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\lexer.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\mem.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\numerics.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\parser.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\prmjtime.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\reader.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\regexpwrapper.cpp +# End Source File +# Begin Source File + +SOURCE=..\strings.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\token.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\tracer.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\utilities.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\world.cpp + +!IF "$(CFG)" == "DikDik - Win32 Release" + +# ADD CPP /W4 /GR /I "../../regexp" + +!ELSEIF "$(CFG)" == "DikDik - Win32 Debug" + +!ENDIF + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\algo.h +# End Source File +# Begin Source File + +SOURCE=..\bytecodegen.h +# End Source File +# Begin Source File + +SOURCE=..\ds.h +# End Source File +# Begin Source File + +SOURCE=..\exception.h +# End Source File +# Begin Source File + +SOURCE=..\fdlibm_ns.h +# End Source File +# Begin Source File + +SOURCE=..\formatter.h +# End Source File +# Begin Source File + +SOURCE=..\hash.h +# End Source File +# Begin Source File + +SOURCE=..\js2runtime.h +# End Source File +# Begin Source File + +SOURCE=..\jsarray.h +# End Source File +# Begin Source File + +SOURCE=..\jsdate.h +# End Source File +# Begin Source File + +SOURCE=..\jsmath.h +# End Source File +# Begin Source File + +SOURCE=..\jsstring.h +# End Source File +# Begin Source File + +SOURCE=..\lexer.h +# End Source File +# Begin Source File + +SOURCE=..\mem.h +# End Source File +# Begin Source File + +SOURCE=..\numerics.h +# End Source File +# Begin Source File + +SOURCE=..\parser.h +# End Source File +# Begin Source File + +SOURCE=..\prmjtime.h +# End Source File +# Begin Source File + +SOURCE=..\property.h +# End Source File +# Begin Source File + +SOURCE=..\reader.h +# End Source File +# Begin Source File + +SOURCE=..\stlcfg.h +# End Source File +# Begin Source File + +SOURCE=..\strings.h +# End Source File +# Begin Source File + +SOURCE=..\systemtypes.h +# End Source File +# Begin Source File + +SOURCE=..\token.h +# End Source File +# Begin Source File + +SOURCE=..\utilities.h +# End Source File +# Begin Source File + +SOURCE=..\world.h +# End Source File +# End Group +# End Target +# End Project