aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4engine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4engine.cpp')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp227
1 files changed, 146 insertions, 81 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 2c68c4f400..f5a515a0ae 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -60,7 +60,6 @@
#include <qv4jsonobject_p.h>
#include <qv4stringobject_p.h>
#include <qv4identifiertable_p.h>
-#include <qv4unwindhelper_p.h>
#include "qv4debugging_p.h"
#include "qv4executableallocator_p.h"
#include "qv4sequenceobject_p.h"
@@ -73,18 +72,64 @@
#include "qv4isel_moth_p.h"
+#if USE(PTHREADS)
+# include <pthread.h>
+#endif
+
QT_BEGIN_NAMESPACE
using namespace QV4;
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
-static ReturnedValue throwTypeError(SimpleCallContext *ctx)
+static ReturnedValue throwTypeError(CallContext *ctx)
{
- ctx->throwTypeError();
- return Encode::undefined();
+ return ctx->throwTypeError();
+}
+
+quintptr getStackLimit()
+{
+ quintptr stackLimit;
+#if USE(PTHREADS) && !OS(QNX)
+# if OS(DARWIN)
+ pthread_t thread_self = pthread_self();
+ void *stackTop = pthread_get_stackaddr_np(thread_self);
+ stackLimit = reinterpret_cast<quintptr>(stackTop);
+ quintptr size = 0;
+ if (pthread_main_np()) {
+ rlimit limit;
+ getrlimit(RLIMIT_STACK, &limit);
+ size = limit.rlim_cur;
+ } else
+ size = pthread_get_stacksize_np(thread_self);
+ stackLimit -= size;
+# 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)
@@ -120,11 +165,17 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory)
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);
@@ -338,7 +389,8 @@ void ExecutionEngine::enableDebugger()
void ExecutionEngine::initRootContext()
{
- rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext) + sizeof(CallData)));
+ rootContext = static_cast<GlobalContext *>(memoryManager->allocManaged(sizeof(GlobalContext) + sizeof(CallData)));
+ rootContext->init();
current = rootContext;
current->parent = 0;
rootContext->initGlobalContext(this);
@@ -351,9 +403,9 @@ InternalClass *ExecutionEngine::newClass(const InternalClass &other)
ExecutionContext *ExecutionEngine::pushGlobalContext()
{
- GlobalContext *g = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext)));
+ GlobalContext *g = new (memoryManager) GlobalContext;
ExecutionContext *oldNext = g->next;
- *g = *rootContext;
+ memcpy(g, rootContext, sizeof(GlobalContext));
g->next = oldNext;
g->parent = current;
current = g;
@@ -361,7 +413,7 @@ ExecutionContext *ExecutionEngine::pushGlobalContext()
return current;
}
-Returned<FunctionObject> *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, const StringRef name, ReturnedValue (*code)(SimpleCallContext *))
+Returned<FunctionObject> *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, const StringRef name, ReturnedValue (*code)(CallContext *))
{
BuiltinFunction *f = new (memoryManager) BuiltinFunction(scope, name, code);
return f->asReturned<FunctionObject>();
@@ -548,7 +600,7 @@ Returned<Object> *ExecutionEngine::qmlContextObject() const
{
ExecutionContext *ctx = current;
- if (ctx->type == QV4::ExecutionContext::Type_SimpleCallContext)
+ if (ctx->type == QV4::ExecutionContext::Type_SimpleCallContext && !ctx->outer)
ctx = ctx->parent;
if (!ctx->outer)
@@ -557,7 +609,7 @@ Returned<Object> *ExecutionEngine::qmlContextObject() const
while (ctx->outer && ctx->outer->type != ExecutionContext::Type_GlobalContext)
ctx = ctx->outer;
- assert(ctx);
+ Q_ASSERT(ctx);
if (ctx->type != ExecutionContext::Type_QmlContext)
return 0;
@@ -576,11 +628,12 @@ namespace {
void resolve(StackFrame *frame, ExecutionContext *context, Function *function)
{
qptrdiff offset;
- if (context->interpreterInstructionPointer)
+ if (context->interpreterInstructionPointer) {
offset = *context->interpreterInstructionPointer - 1 - function->codeData;
- else
- offset = context->jitInstructionPointer - (char*)function->codePtr;
- frame->line = function->lineNumberForProgramCounter(offset);
+ frame->line = function->lineNumberForProgramCounter(offset);
+ } else {
+ frame->line = context->lineNumber;
+ }
}
};
}
@@ -593,7 +646,8 @@ QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const
QV4::ExecutionContext *c = current;
while (c && frameLimit) {
- if (CallContext *callCtx = c->asCallContext()) {
+ CallContext *callCtx = c->asCallContext();
+ if (callCtx && callCtx->function) {
StackFrame frame;
if (callCtx->function->function)
frame.source = callCtx->function->function->sourceFile();
@@ -646,7 +700,8 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file)
QUrl base;
QV4::ExecutionContext *c = current;
while (c) {
- if (CallContext *callCtx = c->asCallContext()) {
+ CallContext *callCtx = c->asCallContext();
+ if (callCtx && callCtx->function) {
if (callCtx->function->function)
base.setUrl(callCtx->function->function->sourceFile());
break;
@@ -684,76 +739,76 @@ void ExecutionEngine::requireArgumentsAccessors(int n)
void ExecutionEngine::markObjects()
{
- identifierTable->mark();
+ identifierTable->mark(this);
- globalObject->mark();
+ globalObject->mark(this);
if (globalCode)
- globalCode->mark();
+ globalCode->mark(this);
for (int i = 0; i < argumentsAccessors.size(); ++i) {
const Property &pd = argumentsAccessors.at(i);
if (FunctionObject *getter = pd.getter())
- getter->mark();
+ getter->mark(this);
if (FunctionObject *setter = pd.setter())
- setter->mark();
+ setter->mark(this);
}
ExecutionContext *c = current;
while (c) {
- c->mark();
+ c->mark(this);
c = c->parent;
}
- id_length->mark();
- id_prototype->mark();
- id_constructor->mark();
- id_arguments->mark();
- id_caller->mark();
- id_this->mark();
- id___proto__->mark();
- id_enumerable->mark();
- id_configurable->mark();
- id_writable->mark();
- id_value->mark();
- id_get->mark();
- id_set->mark();
- id_eval->mark();
- id_uintMax->mark();
- id_name->mark();
- id_index->mark();
- id_input->mark();
- id_toString->mark();
- id_valueOf->mark();
-
- objectCtor.mark();
- stringCtor.mark();
- numberCtor.mark();
- booleanCtor.mark();
- arrayCtor.mark();
- functionCtor.mark();
- dateCtor.mark();
- regExpCtor.mark();
- errorCtor.mark();
- evalErrorCtor.mark();
- rangeErrorCtor.mark();
- referenceErrorCtor.mark();
- syntaxErrorCtor.mark();
- typeErrorCtor.mark();
- uRIErrorCtor.mark();
-
- exceptionValue.mark();
-
- thrower->mark();
+ id_length->mark(this);
+ id_prototype->mark(this);
+ id_constructor->mark(this);
+ id_arguments->mark(this);
+ id_caller->mark(this);
+ id_this->mark(this);
+ id___proto__->mark(this);
+ id_enumerable->mark(this);
+ id_configurable->mark(this);
+ id_writable->mark(this);
+ id_value->mark(this);
+ id_get->mark(this);
+ id_set->mark(this);
+ id_eval->mark(this);
+ id_uintMax->mark(this);
+ id_name->mark(this);
+ id_index->mark(this);
+ id_input->mark(this);
+ id_toString->mark(this);
+ id_valueOf->mark(this);
+
+ objectCtor.mark(this);
+ stringCtor.mark(this);
+ numberCtor.mark(this);
+ booleanCtor.mark(this);
+ arrayCtor.mark(this);
+ functionCtor.mark(this);
+ dateCtor.mark(this);
+ regExpCtor.mark(this);
+ errorCtor.mark(this);
+ evalErrorCtor.mark(this);
+ rangeErrorCtor.mark(this);
+ referenceErrorCtor.mark(this);
+ syntaxErrorCtor.mark(this);
+ typeErrorCtor.mark(this);
+ uRIErrorCtor.mark(this);
+
+ exceptionValue.mark(this);
+
+ thrower->mark(this);
if (m_qmlExtensions)
- m_qmlExtensions->markObjects();
+ m_qmlExtensions->markObjects(this);
emptyClass->markObjects();
for (QSet<CompiledData::CompilationUnit*>::ConstIterator it = compilationUnits.constBegin(), end = compilationUnits.constEnd();
it != end; ++it)
- (*it)->markObjects();
+ (*it)->markObjects(this);
}
namespace {
@@ -807,9 +862,15 @@ QmlExtensions *ExecutionEngine::qmlExtensions()
return m_qmlExtensions;
}
-void ExecutionEngine::throwException(const ValueRef value)
+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);
@@ -820,10 +881,9 @@ void ExecutionEngine::throwException(const ValueRef value)
exceptionStackTrace = stackTrace();
if (debugger)
- debugger->aboutToThrow(value);
+ debugger->aboutToThrow();
- UnwindHelper::prepareForUnwind(current);
- throwInternal();
+ return Encode::undefined();
}
ReturnedValue ExecutionEngine::catchException(ExecutionContext *catchingContext, StackTrace *trace)
@@ -836,11 +896,11 @@ ReturnedValue ExecutionEngine::catchException(ExecutionContext *catchingContext,
exceptionStackTrace.clear();
hasException = false;
ReturnedValue res = exceptionValue.asReturnedValue();
- exceptionValue = Encode::undefined();
+ exceptionValue = Primitive::emptyValue();
return res;
}
-QQmlError ExecutionEngine::convertJavaScriptException(ExecutionContext *context)
+QQmlError ExecutionEngine::catchExceptionAsQmlError(ExecutionContext *context)
{
QV4::StackTrace trace;
QV4::Scope scope(context);
@@ -854,7 +914,7 @@ QQmlError ExecutionEngine::convertJavaScriptException(ExecutionContext *context)
}
QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
if (!!errorObj && errorObj->asSyntaxError()) {
- QV4::ScopedString m(scope, errorObj->engine()->newString("message"));
+ QV4::ScopedString m(scope, errorObj->engine()->newString(QStringLiteral("message")));
QV4::ScopedValue v(scope, errorObj->get(m));
error.setDescription(v->toQStringNoThrow());
} else
@@ -862,14 +922,19 @@ QQmlError ExecutionEngine::convertJavaScriptException(ExecutionContext *context)
return error;
}
-#if !defined(V4_CXX_ABI_EXCEPTION)
-struct DummyException
-{};
-
-void ExecutionEngine::throwInternal()
+bool ExecutionEngine::recheckCStackLimits()
{
- throw DummyException();
-}
+ 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