Optimise string additions

Small optimisation for string additions, also add one more check
for exceptions in the code where required.

Change-Id: I6c14bc88ea5d03f7eeed0e0168c5195f9f823693
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Lars Knoll 2013-11-02 17:11:06 +01:00 committed by The Qt Project
parent 4ffa7d3f65
commit 26db9863f1
7 changed files with 107 additions and 35 deletions

View File

@ -1446,7 +1446,7 @@ Assembler::Jump InstructionSelection::genInlineBinop(V4IR::AluOp oper, V4IR::Exp
void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
{
if (oper != V4IR:: OpMod
if (oper != V4IR::OpMod
&& leftSource->type == V4IR::DoubleType && rightSource->type == V4IR::DoubleType
&& isPregOrConst(leftSource) && isPregOrConst(rightSource)) {
doubleBinop(oper, leftSource, rightSource, target);
@ -1462,7 +1462,14 @@ void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR:
done = genInlineBinop(oper, leftSource, rightSource, target);
// TODO: inline var===null and var!==null
const Assembler::BinaryOperationInfo& info = Assembler::binaryOperation(oper);
Assembler::BinaryOperationInfo info = Assembler::binaryOperation(oper);
if (oper == V4IR::OpAdd &&
(leftSource->type == V4IR::StringType || rightSource->type == V4IR::StringType)) {
const Assembler::BinaryOperationInfo stringAdd = OPCONTEXT(__qmljs_add_string);
info = stringAdd;
}
if (info.fallbackImplementation) {
_as->generateFunctionCallImp(target, info.name, info.fallbackImplementation,
Assembler::PointerToValue(leftSource),

View File

@ -926,7 +926,7 @@ ReturnedValue JsonObject::method_stringify(SimpleCallContext *ctx)
for (uint i = 0; i < arrayLen; ++i) {
v = o->getIndexed(i);
if (v->asNumberObject() || v->asStringObject() || v->isNumber())
v = __qmljs_to_string(v, ctx);
v = __qmljs_to_string(ctx, v);
if (v->isString()) {
String *s = v->stringValue();
if (!stringify.propertyList.contains(s))

View File

@ -250,7 +250,7 @@ ReturnedValue NumberPrototype::method_toPrecision(SimpleCallContext *ctx)
return Encode::undefined();
if (!ctx->callData->argc || ctx->callData->args[0].isUndefined())
return __qmljs_to_string(v, ctx);
return __qmljs_to_string(ctx, v);
double precision = ctx->callData->args[0].toInt32();
if (precision < 1 || precision > 21) {

View File

@ -266,10 +266,10 @@ ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData)
bool ignoreCase = false;
bool multiLine = false;
if (!f->isUndefined()) {
f = __qmljs_to_string(f, ctx);
QString str = f->stringValue()->toQString();
f = __qmljs_to_string(ctx, f);
if (scope.hasException())
return Encode::undefined();
QString str = f->stringValue()->toQString();
for (int i = 0; i < str.length(); ++i) {
if (str.at(i) == QLatin1Char('g') && !global) {
global = true;
@ -322,7 +322,9 @@ ReturnedValue RegExpPrototype::method_exec(SimpleCallContext *ctx)
return ctx->throwTypeError();
ScopedValue arg(scope, ctx->argument(0));
arg = __qmljs_to_string(arg, ctx);
arg = __qmljs_to_string(ctx, arg);
if (scope.hasException())
return Encode::undefined();
QString s = arg->stringValue()->toQString();
int offset = r->global ? r->lastIndexProperty(ctx)->value.toInt32() : 0;

View File

@ -297,30 +297,6 @@ ReturnedValue __qmljs_delete_name(ExecutionContext *ctx, const StringRef name)
return Encode(ctx->deleteProperty(name));
}
QV4::ReturnedValue __qmljs_add_helper(ExecutionContext *ctx, const ValueRef left, const ValueRef right)
{
Scope scope(ctx);
ScopedValue pleft(scope, __qmljs_to_primitive(left, PREFERREDTYPE_HINT));
ScopedValue pright(scope, __qmljs_to_primitive(right, PREFERREDTYPE_HINT));
if (pleft->isString() || pright->isString()) {
if (!pleft->isString())
pleft = __qmljs_to_string(pleft, ctx);
if (!pright->isString())
pright = __qmljs_to_string(pright, ctx);
if (scope.engine->hasException)
return Encode::undefined();
if (!pleft->stringValue()->length())
return right->asReturnedValue();
if (!pright->stringValue()->length())
return pleft->asReturnedValue();
return (new (ctx->engine->memoryManager) String(ctx->engine, pleft->stringValue(), pright->stringValue()))->asReturnedValue();
}
double x = __qmljs_to_number(pleft);
double y = __qmljs_to_number(pright);
return Encode(x + y);
}
QV4::ReturnedValue __qmljs_instanceof(ExecutionContext *ctx, const ValueRef left, const ValueRef right)
{
Object *o = right->asObject();
@ -469,6 +445,90 @@ Returned<String> *__qmljs_convert_to_string(ExecutionContext *ctx, const ValueRe
} // switch
}
// This is slightly different from the method above, as
// the + operator requires a slightly different conversion
static Returned<String> *convert_to_string_add(ExecutionContext *ctx, const ValueRef value)
{
switch (value->type()) {
case Value::Empty_Type:
Q_ASSERT(!"empty Value encountered");
case Value::Undefined_Type:
return ctx->engine->id_undefined.ret();
case Value::Null_Type:
return ctx->engine->id_null.ret();
case Value::Boolean_Type:
if (value->booleanValue())
return ctx->engine->id_true.ret();
else
return ctx->engine->id_false.ret();
case Value::Managed_Type:
if (value->isString())
return value->stringValue()->asReturned<String>();
{
Scope scope(ctx);
ScopedValue prim(scope, __qmljs_to_primitive(value, PREFERREDTYPE_HINT));
return __qmljs_convert_to_string(ctx, prim);
}
case Value::Integer_Type:
return __qmljs_string_from_number(ctx, value->int_32);
default: // double
return __qmljs_string_from_number(ctx, value->doubleValue());
} // switch
}
QV4::ReturnedValue __qmljs_add_helper(ExecutionContext *ctx, const ValueRef left, const ValueRef right)
{
Scope scope(ctx);
ScopedValue pleft(scope, __qmljs_to_primitive(left, PREFERREDTYPE_HINT));
ScopedValue pright(scope, __qmljs_to_primitive(right, PREFERREDTYPE_HINT));
if (pleft->isString() || pright->isString()) {
if (!pleft->isString())
pleft = convert_to_string_add(ctx, pleft);
if (!pright->isString())
pright = convert_to_string_add(ctx, pright);
if (scope.engine->hasException)
return Encode::undefined();
if (!pleft->stringValue()->length())
return pright->asReturnedValue();
if (!pright->stringValue()->length())
return pleft->asReturnedValue();
return (new (ctx->engine->memoryManager) String(ctx->engine, pleft->stringValue(), pright->stringValue()))->asReturnedValue();
}
double x = __qmljs_to_number(pleft);
double y = __qmljs_to_number(pright);
return Encode(x + y);
}
QV4::ReturnedValue __qmljs_add_string(QV4::ExecutionContext *ctx, const QV4::ValueRef left, const QV4::ValueRef right)
{
Q_ASSERT(left->isString() || right->isString());
if (left->isString() && right->isString()) {
if (!left->stringValue()->length())
return right->asReturnedValue();
if (!right->stringValue()->length())
return left->asReturnedValue();
return (new (ctx->engine->memoryManager) String(ctx->engine, left->stringValue(), right->stringValue()))->asReturnedValue();
}
Scope scope(ctx);
ScopedValue pleft(scope, *left);
ScopedValue pright(scope, *right);
if (!pleft->isString())
pleft = convert_to_string_add(ctx, left);
if (!pright->isString())
pright = convert_to_string_add(ctx, right);
if (scope.engine->hasException)
return Encode::undefined();
if (!pleft->stringValue()->length())
return pright->asReturnedValue();
if (!pright->stringValue()->length())
return pleft->asReturnedValue();
return (new (ctx->engine->memoryManager) String(ctx->engine, pleft->stringValue(), pright->stringValue()))->asReturnedValue();
}
void __qmljs_set_property(ExecutionContext *ctx, const ValueRef object, const StringRef name, const ValueRef value)
{
Scope scope(ctx);
@ -1110,7 +1170,7 @@ QV4::ReturnedValue __qmljs_decrement(const QV4::ValueRef value)
}
}
QV4::ReturnedValue __qmljs_to_string(const QV4::ValueRef value, QV4::ExecutionContext *ctx)
QV4::ReturnedValue __qmljs_to_string(QV4::ExecutionContext *ctx, const QV4::ValueRef value)
{
if (value->isString())
return value.asReturnedValue();

View File

@ -184,7 +184,7 @@ QV4::ReturnedValue __qmljs_foreach_next_property_name(const ValueRef foreach_ite
QV4::ReturnedValue __qmljs_to_primitive(const ValueRef value, int typeHint);
Q_QML_EXPORT QV4::Bool __qmljs_to_boolean(const QV4::ValueRef value);
double __qmljs_to_number(const QV4::ValueRef value);
QV4::ReturnedValue __qmljs_to_string(const ValueRef value, QV4::ExecutionContext *ctx);
QV4::ReturnedValue __qmljs_to_string(QV4::ExecutionContext *ctx, const ValueRef value);
Q_QML_EXPORT Returned<String> *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const ValueRef value);
void __qmljs_numberToString(QString *result, double num, int radix = 10);
ReturnedValue __qmljs_to_object(QV4::ExecutionContext *ctx, const ValueRef value);
@ -221,6 +221,7 @@ typedef QV4::ReturnedValue (*BinOpContext)(QV4::ExecutionContext *ctx, const QV4
QV4::ReturnedValue __qmljs_instanceof(QV4::ExecutionContext *ctx, const QV4::ValueRef left, const QV4::ValueRef right);
QV4::ReturnedValue __qmljs_in(QV4::ExecutionContext *ctx, const QV4::ValueRef left, const QV4::ValueRef right);
QV4::ReturnedValue __qmljs_add(ExecutionContext *ctx, const QV4::ValueRef left, const QV4::ValueRef right);
QV4::ReturnedValue __qmljs_add_string(QV4::ExecutionContext *ctx, const QV4::ValueRef left, const QV4::ValueRef right);
QV4::ReturnedValue __qmljs_bit_or(const QV4::ValueRef left, const QV4::ValueRef right);
QV4::ReturnedValue __qmljs_bit_xor(const QV4::ValueRef left, const QV4::ValueRef right);
QV4::ReturnedValue __qmljs_bit_and(const QV4::ValueRef left, const QV4::ValueRef right);

View File

@ -301,8 +301,10 @@ ReturnedValue StringPrototype::method_concat(SimpleCallContext *context)
ScopedValue v(scope);
for (int i = 0; i < context->callData->argc; ++i) {
v = __qmljs_to_string(ValueRef(&context->callData->args[i]), context);
assert(v->isString());
v = __qmljs_to_string(context, ValueRef(&context->callData->args[i]));
if (scope.hasException())
return Encode::undefined();
Q_ASSERT(v->isString());
value += v->stringValue()->toQString();
}