summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/script/api/qscriptcontext.cpp9
-rw-r--r--src/script/api/qscriptcontextinfo.cpp4
-rw-r--r--src/script/api/qscriptengine.cpp3
-rw-r--r--src/script/api/qscriptengine_p.h18
-rw-r--r--tests/auto/qscriptcontext/tst_qscriptcontext.cpp31
-rw-r--r--tests/auto/qscriptengine/tst_qscriptengine.cpp16
6 files changed, 76 insertions, 5 deletions
diff --git a/src/script/api/qscriptcontext.cpp b/src/script/api/qscriptcontext.cpp
index 59ea52dfb0..2468a46cae 100644
--- a/src/script/api/qscriptcontext.cpp
+++ b/src/script/api/qscriptcontext.cpp
@@ -299,6 +299,12 @@ QScriptValue QScriptContext::argumentsObject() const
//for a js function
if (frame->codeBlock() && frame->callee()) {
+ if (!QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) {
+ // We have a built-in JS host call.
+ // codeBlock is needed by retrieveArguments(), but since it
+ // contains junk, we would crash. Return an invalid value for now.
+ return QScriptValue();
+ }
JSC::JSValue result = frame->interpreter()->retrieveArguments(frame, JSC::asFunction(frame->callee()));
return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result);
}
@@ -309,7 +315,8 @@ QScriptValue QScriptContext::argumentsObject() const
}
//for a native function
- if (!frame->optionalCalleeArguments()) {
+ if (!frame->optionalCalleeArguments()
+ && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { // Make sure we don't go here for host JSFunctions
Q_ASSERT(frame->argumentCount() > 0); //we need at least 'this' otherwise we'll crash later
JSC::Arguments* arguments = new (&frame->globalData())JSC::Arguments(frame, JSC::Arguments::NoParameters);
frame->setCalleeArguments(arguments);
diff --git a/src/script/api/qscriptcontextinfo.cpp b/src/script/api/qscriptcontextinfo.cpp
index d39abe6a94..0f9de1d7f0 100644
--- a/src/script/api/qscriptcontextinfo.cpp
+++ b/src/script/api/qscriptcontextinfo.cpp
@@ -157,7 +157,7 @@ QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *conte
JSC::Instruction *returnPC = rewindContext->returnPC();
JSC::CodeBlock *codeBlock = frame->codeBlock();
- if (returnPC && codeBlock) {
+ if (returnPC && codeBlock && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) {
#if ENABLE(JIT)
unsigned bytecodeOffset = codeBlock->getBytecodeIndex(frame, JSC::ReturnAddressPtr(returnPC));
#else
@@ -171,7 +171,7 @@ QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *conte
// Get the filename and the scriptId:
JSC::CodeBlock *codeBlock = frame->codeBlock();
- if (codeBlock) {
+ if (codeBlock && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) {
JSC::SourceProvider *source = codeBlock->source();
scriptId = source->asID();
fileName = source->url();
diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp
index 54039c0759..478fdaa3bf 100644
--- a/src/script/api/qscriptengine.cpp
+++ b/src/script/api/qscriptengine.cpp
@@ -858,7 +858,8 @@ JSC::JSValue JSC_HOST_CALL functionQsTr(JSC::ExecState *exec, JSC::JSObject*, JS
{
JSC::ExecState *frame = exec->callerFrame()->removeHostCallFrameFlag();
while (frame) {
- if (frame->codeBlock() && frame->codeBlock()->source()
+ if (frame->codeBlock() && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)
+ && frame->codeBlock()->source()
&& !frame->codeBlock()->source()->url().isEmpty()) {
context = engine->translationContextFromUrl(frame->codeBlock()->source()->url());
break;
diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h
index f8144e9155..94d195e883 100644
--- a/src/script/api/qscriptengine_p.h
+++ b/src/script/api/qscriptengine_p.h
@@ -56,6 +56,7 @@
#include "Debugger.h"
#include "ErrorInstance.h"
#include "JSArray.h"
+#include "Executable.h"
#include "Lexer.h"
#include "RefPtr.h"
#include "RegExpConstructor.h"
@@ -231,6 +232,8 @@ public:
static inline JSC::ExecState *frameForContext(QScriptContext *context);
static inline const JSC::ExecState *frameForContext(const QScriptContext *context);
+ static inline bool hasValidCodeBlockRegister(JSC::ExecState *frame);
+
JSC::JSGlobalObject *originalGlobalObject() const;
JSC::JSObject *getOriginalGlobalObjectProxy();
JSC::JSObject *customGlobalObject() const;
@@ -862,6 +865,21 @@ inline const JSC::ExecState *QScriptEnginePrivate::frameForContext(const QScript
return reinterpret_cast<const JSC::ExecState*>(context);
}
+inline bool QScriptEnginePrivate::hasValidCodeBlockRegister(JSC::ExecState *frame)
+{
+#if ENABLE(JIT)
+ // Frames created by the VM don't have their CodeBlock register
+ // initialized. We can detect such frames by checking if the
+ // callee is a host JSFunction.
+ JSC::JSObject *callee = frame->callee();
+ return !(callee && callee->inherits(&JSC::JSFunction::info)
+ && JSC::asFunction(callee)->isHostFunction());
+#else
+ Q_UNUSED(frame);
+ return true;
+#endif
+}
+
inline JSC::ExecState *QScriptEnginePrivate::globalExec() const
{
return originalGlobalObject()->globalExec();
diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp
index dd21555ff1..7915eb02a6 100644
--- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp
+++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp
@@ -871,7 +871,36 @@ void tst_QScriptContext::backtrace_data()
QStringList expected;
expected << "<native>() at -1"
<< "<anonymous>(0, 0, 0) at testfile:3"
- << "forEach(0) at -1"
+ << QString::fromLatin1("forEach(%0) at -1")
+ // Because the JIT doesn't store the arguments in the frame
+ // for built-in functions, arguments are not available.
+ // Will work when the copy of JavaScriptCore is updated
+ // (QTBUG-16568).
+ .arg(qt_script_isJITEnabled()
+ ? ""
+ : "function () {\n result = bt();\n}")
+ << "<global>() at testfile:4";
+ QTest::newRow("js callback from built-in") << source << expected;
+ }
+
+ {
+ QString source = QString::fromLatin1(
+ "[10,20].forEach(\n"
+ " function() {\n"
+ " result = bt();\n"
+ "}); result");
+
+ QStringList expected;
+ expected << "<native>() at -1"
+ << "<anonymous>(20, 1, 10,20) at testfile:3"
+ << QString::fromLatin1("forEach(%0) at -1")
+ // Because the JIT doesn't store the arguments in the frame
+ // for built-in functions, arguments are not available.
+ // Will work when the copy of JavaScriptCore is updated
+ // (QTBUG-16568).
+ .arg(qt_script_isJITEnabled()
+ ? ""
+ : "function () {\n result = bt();\n}")
<< "<global>() at testfile:4";
QTest::newRow("js callback from built-in") << source << expected;
}
diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp
index c3a0ba1bfe..8de6fbc3af 100644
--- a/tests/auto/qscriptengine/tst_qscriptengine.cpp
+++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp
@@ -164,6 +164,7 @@ private slots:
void translationContext_data();
void translationContext();
void translateScriptIdBased();
+ void translateFromBuiltinCallback();
void functionScopes();
void nativeFunctionScopes();
void evaluateProgram();
@@ -4725,6 +4726,21 @@ void tst_QScriptEngine::translateScriptIdBased()
QString::fromLatin1("qtn_foo_bar")); // Doesn't have plural
}
+void tst_QScriptEngine::translateFromBuiltinCallback()
+{
+ QScriptEngine eng;
+ eng.installTranslatorFunctions();
+
+ // Callback has no translation context.
+ eng.evaluate("function foo() { qsTr('foo'); }");
+
+ // Stack at translation time will be:
+ // qsTr, foo, forEach, global
+ // qsTr() needs to walk to the outer-most (global) frame before it finds
+ // a translation context, and this should not crash.
+ eng.evaluate("[10,20].forEach(foo)", "script.js");
+}
+
void tst_QScriptEngine::functionScopes()
{
QScriptEngine eng;