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:
parent
4ffa7d3f65
commit
26db9863f1
|
@ -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),
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue