Protect write accesses to objects

Don't write to objects if we have a pending exception to
avoid any side effects.

Change-Id: I9f93a9195a652dbae7033cc6ebb355d5d86e9b5e
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Lars Knoll 2013-10-22 13:26:08 +02:00 committed by The Qt Project
parent af22149dd8
commit ffcdbfa03f
18 changed files with 132 additions and 23 deletions

View File

@ -213,6 +213,8 @@ ReturnedValue ArrayPrototype::method_join(SimpleCallContext *ctx)
R += r4;
e = a->getIndexed(i);
if (scope.hasException())
return Encode::undefined();
if (!e->isNullOrUndefined())
R += e->toString(ctx)->toQString();
}
@ -231,6 +233,8 @@ ReturnedValue ArrayPrototype::method_join(SimpleCallContext *ctx)
name = Primitive::fromDouble(k).toString(ctx);
r12 = self->get(name);
if (scope.hasException())
return Encode::undefined();
if (!r12->isNullOrUndefined())
R += r12->toString(ctx)->toQString();
@ -255,8 +259,12 @@ ReturnedValue ArrayPrototype::method_pop(SimpleCallContext *ctx)
}
ScopedValue result(scope, instance->getIndexed(len - 1));
if (scope.hasException())
return Encode::undefined();
instance->deleteIndexedProperty(len - 1);
if (scope.hasException())
return Encode::undefined();
if (instance->isArrayObject())
instance->setArrayLengthUnchecked(len - 1);
else
@ -334,10 +342,14 @@ ReturnedValue ArrayPrototype::method_reverse(SimpleCallContext *ctx)
bool loExists, hiExists;
lval = instance->getIndexed(lo, &loExists);
hval = instance->getIndexed(hi, &hiExists);
if (scope.hasException())
return Encode::undefined();
if (hiExists)
instance->putIndexed(lo, hval);
else
instance->deleteIndexedProperty(lo);
if (scope.hasException())
return Encode::undefined();
if (loExists)
instance->putIndexed(hi, lval);
else
@ -387,12 +399,16 @@ ReturnedValue ArrayPrototype::method_shift(SimpleCallContext *ctx)
for (uint k = 1; k < len; ++k) {
bool exists;
v = instance->getIndexed(k, &exists);
if (scope.hasException())
return Encode::undefined();
if (exists)
instance->putIndexed(k - 1, v);
else
instance->deleteIndexedProperty(k - 1);
}
instance->deleteIndexedProperty(len - 1);
if (scope.hasException())
return Encode::undefined();
}
if (instance->isArrayObject())
@ -435,9 +451,10 @@ ReturnedValue ArrayPrototype::method_slice(SimpleCallContext *ctx)
for (uint i = start; i < end; ++i) {
bool exists;
v = o->getIndexed(i, &exists);
if (exists) {
if (scope.hasException())
return Encode::undefined();
if (exists)
result->arraySet(n, v);
}
++n;
}
return result.asReturnedValue();
@ -479,6 +496,8 @@ ReturnedValue ArrayPrototype::method_splice(SimpleCallContext *ctx)
newArray->arrayReserve(deleteCount);
for (uint i = 0; i < deleteCount; ++i) {
newArray->arrayData[i].value = instance->getIndexed(start + i);
if (scope.hasException())
return Encode::undefined();
newArray->arrayDataLen = i + 1;
}
newArray->setArrayLengthUnchecked(deleteCount);
@ -490,28 +509,42 @@ ReturnedValue ArrayPrototype::method_splice(SimpleCallContext *ctx)
for (uint k = start; k < len - deleteCount; ++k) {
bool exists;
v = instance->getIndexed(k + deleteCount, &exists);
if (scope.hasException())
return Encode::undefined();
if (exists)
instance->putIndexed(k + itemCount, v);
else
instance->deleteIndexedProperty(k + itemCount);
if (scope.hasException())
return Encode::undefined();
}
for (uint k = len; k > len - deleteCount + itemCount; --k)
for (uint k = len; k > len - deleteCount + itemCount; --k) {
instance->deleteIndexedProperty(k - 1);
if (scope.hasException())
return Encode::undefined();
}
} else if (itemCount > deleteCount) {
uint k = len - deleteCount;
while (k > start) {
bool exists;
v = instance->getIndexed(k + deleteCount - 1, &exists);
if (scope.hasException())
return Encode::undefined();
if (exists)
instance->putIndexed(k + itemCount - 1, v);
else
instance->deleteIndexedProperty(k + itemCount - 1);
if (scope.hasException())
return Encode::undefined();
--k;
}
}
for (uint i = 0; i < itemCount; ++i)
for (uint i = 0; i < itemCount; ++i) {
instance->putIndexed(start + i, ctx->callData->args[i + 2]);
if (scope.hasException())
return Encode::undefined();
}
ctx->strictMode = true;
instance->put(ctx->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount)));
@ -582,16 +615,13 @@ ReturnedValue ArrayPrototype::method_indexOf(SimpleCallContext *ctx)
if (!len)
return Encode(-1);
ScopedValue searchValue(scope);
ScopedValue searchValue(scope, ctx->callData->argument(0));
uint fromIndex = 0;
if (ctx->callData->argc >= 1)
searchValue = ctx->callData->args[0];
else
searchValue = Primitive::undefinedValue();
if (ctx->callData->argc >= 2) {
double f = ctx->callData->args[1].toInteger();
if (scope.hasException())
return Encode::undefined();
if (f >= len)
return Encode(-1);
if (f < 0)
@ -634,6 +664,8 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(SimpleCallContext *ctx)
if (ctx->callData->argc >= 2) {
double f = ctx->callData->args[1].toInteger();
if (scope.hasException())
return Encode::undefined();
if (f > 0)
f = qMin(f, (double)(len - 1));
else if (f < 0) {
@ -649,6 +681,8 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(SimpleCallContext *ctx)
--k;
bool exists;
v = instance->getIndexed(k, &exists);
if (scope.hasException())
return Encode::undefined();
if (exists && __qmljs_strict_equal(v, searchValue))
return Encode(k);
}

View File

@ -808,7 +808,13 @@ QmlExtensions *ExecutionEngine::qmlExtensions()
ReturnedValue ExecutionEngine::throwException(const ValueRef value)
{
// Q_ASSERT(!hasException);
// we can get in here with an exception already set, as the runtime
// doesn't check after every operation that can throw.
// in this case preserve the first exception to give correct error
// information
if (hasException)
return Encode::undefined();
hasException = true;
exceptionValue = value;
QV4::Scope scope(this);

View File

@ -250,6 +250,8 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData)
}
body = callData->args[callData->argc - 1].toString(ctx)->toQString();
}
if (ctx->engine->hasException)
return Encode::undefined();
QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}");
@ -431,6 +433,9 @@ ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData)
{
ExecutionEngine *v4 = that->engine();
Scope scope(v4);
if (scope.hasException())
return Encode::undefined();
Scoped<ScriptFunction> f(scope, static_cast<ScriptFunction *>(that));
InternalClass *ic = v4->objectClass;
@ -456,6 +461,9 @@ ReturnedValue ScriptFunction::call(Managed *that, CallData *callData)
void *stackSpace;
ExecutionContext *context = f->engine()->current;
Scope scope(context);
if (scope.hasException())
return Encode::undefined();
CallContext *ctx = context->newCallContext(f, callData);
if (!f->strictMode && !callData->thisObject.isObject()) {
@ -511,6 +519,9 @@ ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData)
{
ExecutionEngine *v4 = that->engine();
Scope scope(v4);
if (scope.hasException())
return Encode::undefined();
Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that));
InternalClass *ic = v4->objectClass;
@ -536,6 +547,9 @@ ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData)
{
ExecutionEngine *v4 = that->engine();
Scope scope(v4);
if (scope.hasException())
return Encode::undefined();
Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that));
void *stackSpace = alloca(requiredMemoryForExecutionContectSimple(f));
@ -576,6 +590,9 @@ ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData)
BuiltinFunction *f = static_cast<BuiltinFunction *>(that);
ExecutionEngine *v4 = f->engine();
Scope scope(v4);
if (scope.hasException())
return Encode::undefined();
ExecutionContext *context = v4->current;
SimpleCallContext ctx;
@ -594,6 +611,8 @@ ReturnedValue IndexedBuiltinFunction::call(Managed *that, CallData *callData)
ExecutionEngine *v4 = f->engine();
ExecutionContext *context = v4->current;
Scope scope(v4);
if (scope.hasException())
return Encode::undefined();
SimpleCallContext ctx;
ctx.initSimpleCallContext(f->scope->engine);
@ -643,6 +662,8 @@ ReturnedValue BoundFunction::call(Managed *that, CallData *dd)
{
BoundFunction *f = static_cast<BoundFunction *>(that);
Scope scope(f->scope->engine);
if (scope.hasException())
return Encode::undefined();
ScopedCallData callData(scope, f->boundArgs.size() + dd->argc);
callData->thisObject = f->boundThis;
@ -655,6 +676,9 @@ ReturnedValue BoundFunction::construct(Managed *that, CallData *dd)
{
BoundFunction *f = static_cast<BoundFunction *>(that);
Scope scope(f->scope->engine);
if (scope.hasException())
return Encode::undefined();
ScopedCallData callData(scope, f->boundArgs.size() + dd->argc);
memcpy(callData->args, f->boundArgs.constData(), f->boundArgs.size()*sizeof(SafeValue));
memcpy(callData->args + f->boundArgs.size(), dd->args, dd->argc*sizeof(SafeValue));

View File

@ -469,6 +469,10 @@ ReturnedValue GlobalFunctions::method_parseInt(SimpleCallContext *ctx)
// [15.1.2.2] step by step:
String *inputString = string->toString(ctx); // 1
QString trimmed = inputString->toQString().trimmed(); // 2
if (ctx->engine->hasException)
return Encode::undefined();
const QChar *pos = trimmed.constData();
const QChar *end = pos + trimmed.length();

View File

@ -801,7 +801,7 @@ QString Stringify::JO(ObjectRef o)
name = it.nextPropertyNameAsString(val);
if (name->isNull())
break;
QString key = name->toQStringNoThrow();
QString key = name->toQString();
QString member = makeMember(key, val);
if (!member.isEmpty())
partial += member;
@ -951,7 +951,7 @@ ReturnedValue JsonObject::method_stringify(SimpleCallContext *ctx)
ScopedValue arg0(scope, ctx->argument(0));
QString result = stringify.Str(QString(), arg0);
if (result.isEmpty())
if (result.isEmpty() || scope.engine->hasException)
return Encode::undefined();
return ctx->engine->newString(result)->asReturnedValue();
}

View File

@ -177,10 +177,9 @@ ReturnedValue NumberPrototype::method_toLocaleString(SimpleCallContext *ctx)
{
Scope scope(ctx);
ScopedValue v(scope, thisNumberValue(ctx));
ScopedString str(scope, v->toString(ctx));
if (ctx->engine->hasException)
return Encode::undefined();
ScopedString str(scope, v->toString(ctx));
return str.asReturnedValue();
}

View File

@ -147,6 +147,9 @@ ReturnedValue Object::getValue(const ValueRef thisObject, const Property *p, Pro
void Object::putValue(Property *pd, PropertyAttributes attrs, const ValueRef value)
{
if (internalClass->engine->hasException)
return;
if (attrs.isAccessor()) {
if (pd->set) {
Scope scope(pd->set->engine());
@ -689,6 +692,9 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty)
// Section 8.12.5
void Object::internalPut(const StringRef name, const ValueRef value)
{
if (internalClass->engine->hasException)
return;
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
return putIndexed(idx, value);
@ -773,6 +779,9 @@ void Object::internalPut(const StringRef name, const ValueRef value)
void Object::internalPutIndexed(uint index, const ValueRef value)
{
if (internalClass->engine->hasException)
return;
Property *pd = 0;
PropertyAttributes attrs;
@ -842,6 +851,9 @@ void Object::internalPutIndexed(uint index, const ValueRef value)
// Section 8.12.7
bool Object::internalDeleteProperty(const StringRef name)
{
if (internalClass->engine->hasException)
return false;
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
return deleteIndexedProperty(idx);
@ -865,6 +877,9 @@ bool Object::internalDeleteProperty(const StringRef name)
bool Object::internalDeleteIndexedProperty(uint index)
{
if (internalClass->engine->hasException)
return false;
uint pidx = propertyIndexFromArrayIndex(index);
if (pidx == UINT_MAX)
return true;
@ -1121,12 +1136,16 @@ ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endInd
for (uint i = fromIndex; i < endIndex; ++i) {
bool exists;
value = o->getIndexed(i, &exists);
if (scope.hasException())
return Encode::undefined();
if (exists && __qmljs_strict_equal(value, v))
return Encode(i);
}
} else if (sparseArray) {
for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) {
value = o->getValue(arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data);
if (scope.hasException())
return Encode::undefined();
if (__qmljs_strict_equal(value, v))
return Encode(n->key());
}
@ -1139,6 +1158,8 @@ ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endInd
while (pd < end) {
if (!pd->value.isEmpty()) {
value = o->getValue(pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data);
if (scope.hasException())
return Encode::undefined();
if (__qmljs_strict_equal(value, v))
return Encode((uint)(pd - arrayData));
}
@ -1437,7 +1458,7 @@ QStringList ArrayObject::toQStringList() const
uint32_t length = arrayLength();
for (uint32_t i = 0; i < length; ++i) {
v = const_cast<ArrayObject *>(this)->getIndexed(i);
result.append(v->toString(engine->current)->toQString());
result.append(v->toQStringNoThrow());
}
return result;
}

View File

@ -158,6 +158,8 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext
ScopedValue v(scope, ctx->argument(1));
Scoped<String> name(scope, v->toString(ctx));
if (scope.hasException())
return Encode::undefined();
PropertyAttributes attrs;
Property *desc = O->__getOwnProperty__(name, &attrs);
return fromPropertyDescriptor(ctx, desc, attrs);

View File

@ -627,7 +627,7 @@ void QObjectWrapper::put(Managed *m, const StringRef name, const ValueRef value)
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
ExecutionEngine *v4 = m->engine();
if (QQmlData::wasDeleted(that->m_object))
if (v4->hasException || QQmlData::wasDeleted(that->m_object))
return;
QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4);

View File

@ -258,6 +258,8 @@ ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData)
QString pattern;
if (!r->isUndefined())
pattern = r->toString(ctx)->toQString();
if (scope.hasException())
return Encode::undefined();
bool global = false;
bool ignoreCase = false;
@ -265,6 +267,8 @@ ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData)
if (!f->isUndefined()) {
f = __qmljs_to_string(f, ctx);
QString str = f->stringValue()->toQString();
if (scope.hasException())
return Encode::undefined();
for (int i = 0; i < str.length(); ++i) {
if (str.at(i) == QChar('g') && !global) {
global = true;

View File

@ -478,8 +478,6 @@ void __qmljs_set_property(ExecutionContext *ctx, const ValueRef object, const St
{
Scope scope(ctx);
ScopedObject o(scope, object->toObject(ctx));
if (scope.engine->hasException)
return;
o->put(name, value);
}
@ -571,8 +569,6 @@ void __qmljs_set_element(ExecutionContext *ctx, const ValueRef object, const Val
}
ScopedString name(scope, index->toString(ctx));
if (scope.hasException())
return;
o->put(name, value);
}

View File

@ -231,6 +231,9 @@ public:
void containerPutIndexed(uint index, const QV4::ValueRef value)
{
if (internalClass->engine->hasException)
return;
/* Qt containers have int (rather than uint) allowable indexes. */
if (index > INT_MAX) {
generateWarning(engine()->current, QLatin1String("Index out of range during indexed set"));

View File

@ -178,6 +178,8 @@ ReturnedValue String::getIndexed(Managed *m, uint index, bool *hasProperty)
void String::put(Managed *m, const StringRef name, const ValueRef value)
{
Scope scope(m->engine());
if (scope.hasException())
return;
ScopedString that(scope, static_cast<String *>(m));
Scoped<Object> o(scope, that->engine()->newStringObject(that));
o->put(name, value);
@ -186,6 +188,9 @@ void String::put(Managed *m, const StringRef name, const ValueRef value)
void String::putIndexed(Managed *m, uint index, const ValueRef value)
{
Scope scope(m->engine());
if (scope.hasException())
return;
ScopedString that(scope, static_cast<String *>(m));
Scoped<Object> o(scope, that->engine()->newStringObject(that));
o->putIndexed(index, value);

View File

@ -597,15 +597,16 @@ ReturnedValue StringPrototype::method_search(SimpleCallContext *ctx)
{
Scope scope(ctx);
QString string = getThisString(ctx);
ScopedValue regExpValue(scope, ctx->argument(0));
if (scope.engine->hasException)
return Encode::undefined();
ScopedValue regExpValue(scope, ctx->argument(0));
Scoped<RegExpObject> regExp(scope, regExpValue->as<RegExpObject>());
if (!regExp) {
ScopedCallData callData(scope, 1);
callData->args[0] = regExpValue;
regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(callData);
if (scope.engine->hasException)
return Encode::undefined();
regExp = regExpValue->as<RegExpObject>();
Q_ASSERT(regExp);
}

View File

@ -281,6 +281,8 @@ void QmlContextWrapper::put(Managed *m, const StringRef name, const ValueRef val
{
ExecutionEngine *v4 = m->engine();
QV4::Scope scope(v4);
if (scope.hasException())
return;
QV4::Scoped<QmlContextWrapper> wrapper(scope, m->as<QmlContextWrapper>());
if (!wrapper) {
v4->current->throwTypeError();

View File

@ -229,6 +229,8 @@ void QmlTypeWrapper::put(Managed *m, const StringRef name, const ValueRef value)
{
QmlTypeWrapper *w = m->as<QmlTypeWrapper>();
QV4::ExecutionEngine *v4 = m->engine();
if (v4->hasException)
return;
if (!w) {
v4->current->throwTypeError();
return;

View File

@ -334,6 +334,9 @@ void QmlValueTypeWrapper::put(Managed *m, const StringRef name, const ValueRef v
{
ExecutionEngine *v4 = m->engine();
Scope scope(v4);
if (scope.hasException())
return;
Scoped<QmlValueTypeWrapper> r(scope, m->as<QmlValueTypeWrapper>());
if (!r) {
v4->current->throwTypeError();

View File

@ -3194,6 +3194,9 @@ void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q
{
QV4::ExecutionEngine *v4 = m->engine();
QV4::Scope scope(v4);
if (scope.hasException())
return;
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, m->as<QQuickJSContext2DPixelData>());
if (!r) {
v4->current->throwTypeError();