return throwError(error);
}
+ReturnedValue ExecutionContext::throwRangeError(const QString &message)
+{
+ Scope scope(this);
+ ScopedObject error(scope, engine->newRangeErrorObject(message));
+ return throwError(error);
+}
+
ReturnedValue ExecutionContext::throwURIError(const ValueRef msg)
{
Scope scope(this);
ReturnedValue throwReferenceError(const ValueRef value);
ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int line, int column);
ReturnedValue throwRangeError(const ValueRef value);
+ ReturnedValue throwRangeError(const QString &message);
ReturnedValue throwURIError(const ValueRef msg);
ReturnedValue throwUnimplemented(const QString &message);
#include "qv4isel_moth_p.h"
+#if USE(PTHREADS)
+# include <pthread.h>
+#endif
+
QT_BEGIN_NAMESPACE
using namespace QV4;
return ctx->throwTypeError();
}
+quintptr getStackLimit()
+{
+ quintptr stackLimit;
+#if USE(PTHREADS)
+# if OS(DARWIN)
+ pthread_t thread_self = pthread_self();
+ void *stackTop = pthread_get_stackaddr_np(thread_self);
+ stackLimit = reinterpret_cast<quintptr>(stackTop);
+ stackLimit -= pthread_get_stacksize_np(thread_self);
+# else
+ void* stackBottom = 0;
+ pthread_attr_t attr;
+ pthread_getattr_np(pthread_self(), &attr);
+ size_t stackSize = 0;
+ pthread_attr_getstack(&attr, &stackBottom, &stackSize);
+ pthread_attr_destroy(&attr);
+
+ stackLimit = reinterpret_cast<quintptr>(stackBottom);
+# endif
+// This is wrong. StackLimit is the currently committed stack size, not the real end.
+// only way to get that limit is apparently by using VirtualQuery (Yuck)
+//#elif OS(WINDOWS)
+// PNT_TIB tib = (PNT_TIB)NtCurrentTeb();
+// stackLimit = static_cast<quintptr>(tib->StackLimit);
+#else
+ int dummy;
+ // this is inexact, as part of the stack is used when being called here,
+ // but let's simply default to 1MB from where the stack is right now
+ stackLimit = reinterpret_cast<qintptr>(&dummy) - 1024*1024;
+#endif
+
+ // 256k slack
+ return stackLimit + 256*1024;
+}
+
+
ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory)
: memoryManager(new QV4::MemoryManager)
, executableAllocator(new QV4::ExecutableAllocator)
memoryManager->setExecutionEngine(this);
- // reserve 8MB for the JS stack
- *jsStack = WTF::PageAllocation::allocate(8*1024*1024, WTF::OSAllocator::JSVMStackPages, true);
+ // reserve space for the JS stack
+ // we allow it to grow to 2 times JSStackLimit, as we can overshoot due to garbage collection
+ // and ScopedValues allocated outside of JIT'ed methods.
+ *jsStack = WTF::PageAllocation::allocate(2*JSStackLimit, WTF::OSAllocator::JSVMStackPages, true);
jsStackBase = (SafeValue *)jsStack->base();
jsStackTop = jsStackBase;
+ // set up stack limits
+ jsStackLimit = jsStackBase + JSStackLimit/sizeof(SafeValue);
+ cStackLimit = getStackLimit();
+
Scope scope(this);
identifierTable = new IdentifierTable(this);
return error;
}
+bool ExecutionEngine::recheckCStackLimits()
+{
+ int dummy;
+#ifdef Q_OS_WIN
+ // ### this is only required on windows, where we currently use heuristics to get the stack limit
+ if (cStackLimit - reinterpret_cast<quintptr>(&dummy) > 128*1024)
+ // we're more then 128k away from our stack limit, assume the thread has changed, and
+ // call getStackLimit
+#endif
+ // this can happen after a thread change
+ cStackLimit = getStackLimit();
+
+ return (reinterpret_cast<quintptr>(&dummy) >= cStackLimit);
+}
+
QT_END_NAMESPACE
struct QmlExtensions;
struct Exception;
+#define CHECK_STACK_LIMITS(v4) \
+ if ((v4->jsStackTop <= v4->jsStackLimit) && (reinterpret_cast<quintptr>(&v4) >= v4->cStackLimit || v4->recheckCStackLimits())) {} \
+ else \
+ return v4->current->throwRangeError(QStringLiteral("Maximum call stack size exceeded."))
+
+
struct Q_QML_EXPORT ExecutionEngine
{
MemoryManager *memoryManager;
ExecutionContext *current;
GlobalContext *rootContext;
+ SafeValue *jsStackTop;
+ SafeValue *jsStackLimit;
+ quintptr cStackLimit;
+
WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine.
+ enum { JSStackLimit = 4*1024*1024 };
WTF::PageAllocation *jsStack;
SafeValue *jsStackBase;
- SafeValue *jsStackTop;
SafeValue *stackPush(uint nValues) {
SafeValue *ptr = jsStackTop;
QmlExtensions *qmlExtensions();
+ bool recheckCStackLimits();
+
// Exception handling
SafeValue exceptionValue;
quint32 hasException;
ExecutionEngine *v4 = that->internalClass->engine;
if (v4->hasException)
return Encode::undefined();
+ CHECK_STACK_LIMITS(v4);
Scope scope(v4);
Scoped<ScriptFunction> f(scope, static_cast<ScriptFunction *>(that));
ExecutionEngine *v4 = f->internalClass->engine;
if (v4->hasException)
return Encode::undefined();
+ CHECK_STACK_LIMITS(v4);
ExecutionContext *context = v4->current;
Scope scope(context);
ExecutionEngine *v4 = that->internalClass->engine;
if (v4->hasException)
return Encode::undefined();
+ CHECK_STACK_LIMITS(v4);
Scope scope(v4);
Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that));
ExecutionEngine *v4 = that->internalClass->engine;
if (v4->hasException)
return Encode::undefined();
+ CHECK_STACK_LIMITS(v4);
SimpleScriptFunction *f = static_cast<SimpleScriptFunction *>(that);
ExecutionEngine *v4 = f->internalClass->engine;
if (v4->hasException)
return Encode::undefined();
+ CHECK_STACK_LIMITS(v4);
ExecutionContext *context = v4->current;
ExecutionEngine *v4 = f->internalClass->engine;
if (v4->hasException)
return Encode::undefined();
+ CHECK_STACK_LIMITS(v4);
+
ExecutionContext *context = v4->current;
Scope scope(v4);
ReturnedValue QmlBindingWrapper::call(Managed *that, CallData *)
{
ExecutionEngine *engine = that->engine();
+ CHECK_STACK_LIMITS(engine);
+
Scope scope(engine);
QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that);
Q_ASSERT(This->function);
void singletonFromQMLToCpp();
void setPropertyOnInvalid();
void miscTypeTest();
+ void stackLimits();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
QVERIFY(q.toBool() == true);
delete object;
+}
+void tst_qqmlecmascript::stackLimits()
+{
+ QJSEngine engine;
+ engine.evaluate(QStringLiteral("function foo() {foo();} try {foo()} catch(e) { }"));
}
QTEST_MAIN(tst_qqmlecmascript)