aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-08-08 12:39:54 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-08 17:45:06 +0000
commitc1fed764a2495373a9e4563bc3ac0d578b2f9409 (patch)
treee263aa10cd111d4d8f0d4e1cfeb49460f2a0eb2b
parent5f17840a14e22da0cfa973b36b9f6ce3bc8d4067 (diff)
Fix reuse of regexp objects by regexp literals
Accoding to the standard the regexp objects created by literals should be separate objects as if calling new. We were violating that by caching the same object for every instance of a literal. This also fixes a problem with leaking values of lastIndex between separate instances of the same global regexp literal. Task-number: QTBUG-62175 Change-Id: Ib22e9ee68de1d1209fbd4212e72f576bc059d245 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/compiler/qv4compileddata.cpp13
-rw-r--r--src/qml/jsruntime/qv4engine.cpp16
-rw-r--r--src/qml/jsruntime/qv4engine_p.h2
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp9
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h13
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp14
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h6
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp7
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp7
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp23
11 files changed, 73 insertions, 39 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 9e9a419be7..34491a3a84 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -46,6 +46,7 @@
#include <private/qv4objectproto_p.h>
#include <private/qv4lookup_p.h>
#include <private/qv4regexpobject_p.h>
+#include <private/qv4regexp_p.h>
#include <private/qqmlpropertycache_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmlengine_p.h>
@@ -136,14 +137,16 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
- int flags = 0;
+ bool global = false;
+ bool multiline = false;
+ bool ignoreCase = false;
if (re->flags & CompiledData::RegExp::RegExp_Global)
- flags |= IR::RegExp::RegExp_Global;
+ global = true;
if (re->flags & CompiledData::RegExp::RegExp_IgnoreCase)
- flags |= IR::RegExp::RegExp_IgnoreCase;
+ ignoreCase = true;
if (re->flags & CompiledData::RegExp::RegExp_Multiline)
- flags |= IR::RegExp::RegExp_Multiline;
- runtimeRegularExpressions[i] = engine->newRegExpObject(data->stringAt(re->stringIndex), flags);
+ multiline = true;
+ runtimeRegularExpressions[i] = QV4::RegExp::create(engine, data->stringAt(re->stringIndex), ignoreCase, multiline, global);
}
if (data->lookupTableSize) {
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 5e776ea09b..a090ac7c47 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -660,21 +660,17 @@ Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t)
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
{
bool global = (flags & IR::RegExp::RegExp_Global);
- bool ignoreCase = false;
- bool multiline = false;
- if (flags & IR::RegExp::RegExp_IgnoreCase)
- ignoreCase = true;
- if (flags & IR::RegExp::RegExp_Multiline)
- multiline = true;
+ bool ignoreCase = (flags & IR::RegExp::RegExp_IgnoreCase);
+ bool multiline = (flags & IR::RegExp::RegExp_Multiline);
Scope scope(this);
- Scoped<RegExp> re(scope, RegExp::create(this, pattern, ignoreCase, multiline));
- return newRegExpObject(re, global);
+ Scoped<RegExp> re(scope, RegExp::create(this, pattern, ignoreCase, multiline, global));
+ return newRegExpObject(re);
}
-Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re, bool global)
+Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re)
{
- return memoryManager->allocObject<RegExpObject>(re, global);
+ return memoryManager->allocObject<RegExpObject>(re);
}
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index d331fd7bff..688a58e345 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -391,7 +391,7 @@ public:
Heap::DateObject *newDateObjectFromTime(const QTime &t);
Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags);
- Heap::RegExpObject *newRegExpObject(RegExp *re, bool global);
+ Heap::RegExpObject *newRegExpObject(RegExp *re);
Heap::RegExpObject *newRegExpObject(const QRegExp &re);
Heap::Object *newErrorObject(const Value &value);
diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp
index 9e94c58432..8cb15b9d7e 100644
--- a/src/qml/jsruntime/qv4regexp.cpp
+++ b/src/qml/jsruntime/qv4regexp.cpp
@@ -69,9 +69,9 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets)
return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets);
}
-Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline)
+Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global)
{
- RegExpCacheKey key(pattern, ignoreCase, multiline);
+ RegExpCacheKey key(pattern, ignoreCase, multiline, global);
RegExpCache *cache = engine->regExpCache;
if (!cache)
@@ -82,7 +82,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo
return result->d();
Scope scope(engine);
- Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline));
+ Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline, global));
result->d()->cache = cache;
cachedValue.set(engine, result);
@@ -90,12 +90,13 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo
return result->d();
}
-void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline)
+void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline, bool global)
{
Base::init();
this->pattern = new QString(pattern);
this->ignoreCase = ignoreCase;
this->multiLine = multiline;
+ this->global = global;
const char* error = 0;
JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error);
diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h
index 04cdb468bf..d9b536406c 100644
--- a/src/qml/jsruntime/qv4regexp_p.h
+++ b/src/qml/jsruntime/qv4regexp_p.h
@@ -76,7 +76,7 @@ struct RegExpCacheKey;
namespace Heap {
struct RegExp : Base {
- void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline);
+ void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global);
void destroy();
QString *pattern;
@@ -88,6 +88,7 @@ struct RegExp : Base {
int subPatternCount;
bool ignoreCase;
bool multiLine;
+ bool global;
int captureCount() const { return subPatternCount + 1; }
};
@@ -111,8 +112,9 @@ struct RegExp : public Managed
int subPatternCount() const { return d()->subPatternCount; }
bool ignoreCase() const { return d()->ignoreCase; }
bool multiLine() const { return d()->multiLine; }
+ bool global() const { return d()->global; }
- static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false);
+ static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false, bool global = false);
bool isValid() const { return d()->byteCode; }
@@ -127,27 +129,30 @@ struct RegExp : public Managed
struct RegExpCacheKey
{
- RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine)
+ RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine, bool global)
: pattern(pattern)
, ignoreCase(ignoreCase)
, multiLine(multiLine)
+ , global(global)
{ }
explicit inline RegExpCacheKey(const RegExp::Data *re);
bool operator==(const RegExpCacheKey &other) const
- { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine; }
+ { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine && global == other.global; }
bool operator!=(const RegExpCacheKey &other) const
{ return !operator==(other); }
QString pattern;
uint ignoreCase : 1;
uint multiLine : 1;
+ uint global : 1;
};
inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re)
: pattern(*re->pattern)
, ignoreCase(re->ignoreCase)
, multiLine(re->multiLine)
+ , global(re->global)
{}
inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 1f758e36f6..82b0f67075 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -74,15 +74,13 @@ void Heap::RegExpObject::init()
Object::init();
Scope scope(internalClass->engine);
Scoped<QV4::RegExpObject> o(scope, this);
- o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false);
- o->d()->global = false;
+ o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false, false);
o->initProperties();
}
-void Heap::RegExpObject::init(QV4::RegExp *value, bool global)
+void Heap::RegExpObject::init(QV4::RegExp *value)
{
Object::init();
- this->global = global;
this->value = value->d();
Scope scope(internalClass->engine);
Scoped<QV4::RegExpObject> o(scope, this);
@@ -95,7 +93,6 @@ void Heap::RegExpObject::init(QV4::RegExp *value, bool global)
void Heap::RegExpObject::init(const QRegExp &re)
{
Object::init();
- global = false;
// Convert the pattern to a ECMAScript pattern.
QString pattern = QT_PREPEND_NAMESPACE(qt_regexp_toCanonical)(re.pattern(), re.patternSyntax());
@@ -246,7 +243,7 @@ void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData)
}
Scoped<RegExp> regexp(scope, re->value());
- scope.result = Encode(scope.engine->newRegExpObject(regexp, re->global()));
+ scope.result = Encode(scope.engine->newRegExpObject(regexp));
return;
}
@@ -282,13 +279,13 @@ void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData)
}
}
- Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine));
+ Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine, global));
if (!regexp->isValid()) {
scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression"));
return;
}
- scope.result = Encode(scope.engine->newRegExpObject(regexp, global));
+ scope.result = Encode(scope.engine->newRegExpObject(regexp));
}
void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData)
@@ -433,7 +430,6 @@ void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, Call
Scoped<RegExpObject> re(scope, scope.result.asReturnedValue());
r->d()->value = re->value();
- r->d()->global = re->global();
RETURN_UNDEFINED();
}
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 54731cef14..af49a1bf5e 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -60,6 +60,7 @@
#include "qv4managed_p.h"
#include "qv4property_p.h"
#include "qv4objectiterator_p.h"
+#include "qv4regexp_p.h"
#include <QtCore/QString>
#include <QtCore/QHash>
@@ -75,11 +76,10 @@ namespace Heap {
struct RegExpObject : Object {
void init();
- void init(QV4::RegExp *value, bool global);
+ void init(QV4::RegExp *value);
void init(const QRegExp &re);
Pointer<RegExp> value;
- bool global;
};
struct RegExpCtor : FunctionObject {
@@ -117,7 +117,7 @@ struct RegExpObject: Object {
};
Heap::RegExp *value() const { return d()->value; }
- bool global() const { return d()->global; }
+ bool global() const { return d()->value->global; }
void initProperties();
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index ecf2fd8b11..28b344d154 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -51,6 +51,8 @@
#include "qv4lookup_p.h"
#include "qv4function_p.h"
#include "qv4numberobject_p.h"
+#include "qv4regexp_p.h"
+#include "qv4regexpobject_p.h"
#include "private/qlocale_tools_p.h"
#include "qv4scopedvalue_p.h"
#include <private/qv4qmlcontext_p.h>
@@ -1477,7 +1479,10 @@ ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine)
ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id)
{
- return static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeRegularExpressions[id].asReturnedValue();
+ Heap::RegExpObject *ro = engine->newRegExpObject(
+ static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)
+ ->runtimeRegularExpressions[id].as<RegExp>());
+ return ro->asReturnedValue();
}
ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 1596f4b0fa..7ccb7a762f 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -542,7 +542,7 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call
break;
}
nMatchOffsets += re->captureCount() * 2;
- if (!regExp->d()->global)
+ if (!regExp->global())
break;
offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
}
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 9d65f67f0f..3d95353fc0 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -50,6 +50,8 @@
#include <private/qv4math_p.h>
#include <private/qv4scopedvalue_p.h>
#include <private/qv4lookup_p.h>
+#include <private/qv4regexp_p.h>
+#include <private/qv4regexpobject_p.h>
#include <private/qv4string_p.h>
#include <iostream>
@@ -443,7 +445,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code)
MOTH_BEGIN_INSTR(LoadRegExp)
// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData());
- VALUE(instr.result) = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeRegularExpressions[instr.regExpId];
+ Heap::RegExpObject *ro = engine->newRegExpObject(
+ static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)
+ ->runtimeRegularExpressions[instr.regExpId].as<RegExp>());
+ VALUE(instr.result) = ro;
MOTH_END_INSTR(LoadRegExp)
MOTH_BEGIN_INSTR(LoadClosure)
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 2642d10545..8b815f7a06 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -145,6 +145,7 @@ private slots:
void array_join_QTBUG_53672();
void regexpLastMatch();
+ void regexpLastIndex();
void indexedAccesses();
void prototypeChainGc();
@@ -3296,6 +3297,28 @@ void tst_QJSEngine::regexpLastMatch()
}
+void tst_QJSEngine::regexpLastIndex()
+{
+ QJSEngine eng;
+ QJSValue result;
+ result = eng.evaluate("function test(text, rx) {"
+ " var res;"
+ " while (res = rx.exec(text)) { "
+ " return true;"
+ " }"
+ " return false;"
+ " }"
+ "function tester(text) {"
+ " return test(text, /,\\s*/g);"
+ "}");
+ QVERIFY(!result.isError());
+
+ result = eng.evaluate("tester(\", \\n\");");
+ QVERIFY(result.toBool());
+ result = eng.evaluate("tester(\", \\n\");");
+ QVERIFY(result.toBool());
+}
+
void tst_QJSEngine::indexedAccesses()
{
QJSEngine engine;