Speed up invocation of builtin functions

Completely avoid creation of a CallContext for those methods,
as we don't need it.

Change-Id: Iff1a38fd3c7e846df6ec0374cb7b3fb8f1b4de3a
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Lars Knoll 2017-01-05 18:48:20 +01:00
parent 3e67a40860
commit c2a4277ae5
10 changed files with 695 additions and 618 deletions

File diff suppressed because it is too large Load Diff

View File

@ -78,30 +78,30 @@ struct ArrayPrototype: ArrayObject
{
void init(ExecutionEngine *engine, Object *ctor);
static ReturnedValue method_isArray(CallContext *ctx);
static ReturnedValue method_toString(CallContext *ctx);
static ReturnedValue method_toLocaleString(CallContext *ctx);
static ReturnedValue method_concat(CallContext *ctx);
static ReturnedValue method_find(CallContext *ctx);
static ReturnedValue method_findIndex(CallContext *ctx);
static ReturnedValue method_join(CallContext *ctx);
static ReturnedValue method_pop(CallContext *ctx);
static ReturnedValue method_push(CallContext *ctx);
static ReturnedValue method_reverse(CallContext *ctx);
static ReturnedValue method_shift(CallContext *ctx);
static ReturnedValue method_slice(CallContext *ctx);
static ReturnedValue method_sort(CallContext *ctx);
static ReturnedValue method_splice(CallContext *ctx);
static ReturnedValue method_unshift(CallContext *ctx);
static ReturnedValue method_indexOf(CallContext *ctx);
static ReturnedValue method_lastIndexOf(CallContext *ctx);
static ReturnedValue method_every(CallContext *ctx);
static ReturnedValue method_some(CallContext *ctx);
static ReturnedValue method_forEach(CallContext *ctx);
static ReturnedValue method_map(CallContext *ctx);
static ReturnedValue method_filter(CallContext *ctx);
static ReturnedValue method_reduce(CallContext *ctx);
static ReturnedValue method_reduceRight(CallContext *ctx);
static void method_isArray(const BuiltinFunction *, Scope &, CallData *callData);
static void method_toString(const BuiltinFunction *, Scope &, CallData *callData);
static void method_toLocaleString(const BuiltinFunction *builtin, Scope &, CallData *callData);
static void method_concat(const BuiltinFunction *, Scope &, CallData *callData);
static void method_find(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_join(const BuiltinFunction *, Scope &, CallData *callData);
static void method_pop(const BuiltinFunction *, Scope &, CallData *callData);
static void method_push(const BuiltinFunction *, Scope &, CallData *callData);
static void method_reverse(const BuiltinFunction *, Scope &, CallData *callData);
static void method_shift(const BuiltinFunction *, Scope &, CallData *callData);
static void method_slice(const BuiltinFunction *, Scope &, CallData *callData);
static void method_sort(const BuiltinFunction *, Scope &, CallData *callData);
static void method_splice(const BuiltinFunction *, Scope &, CallData *callData);
static void method_unshift(const BuiltinFunction *, Scope &, CallData *callData);
static void method_indexOf(const BuiltinFunction *, Scope &, CallData *callData);
static void method_lastIndexOf(const BuiltinFunction *, Scope &, CallData *callData);
static void method_every(const BuiltinFunction *, Scope &, CallData *callData);
static void method_some(const BuiltinFunction *, Scope &, CallData *callData);
static void method_forEach(const BuiltinFunction *, Scope &, CallData *callData);
static void method_map(const BuiltinFunction *, Scope &, CallData *callData);
static void method_filter(const BuiltinFunction *, Scope &, CallData *callData);
static void method_reduce(const BuiltinFunction *, Scope &, CallData *callData);
static void method_reduceRight(const BuiltinFunction *, Scope &, CallData *callData);
};

View File

@ -459,22 +459,22 @@ Heap::Object *ScriptFunction::protoForConstructor() const
DEFINE_OBJECT_VTABLE(BuiltinFunction);
DEFINE_OBJECT_VTABLE(OldBuiltinFunction);
void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *))
void Heap::OldBuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *))
{
Heap::FunctionObject::init(scope, name);
this->code = code;
}
void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *)
void OldBuiltinFunction::construct(const Managed *f, Scope &scope, CallData *)
{
scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError();
scope.result = static_cast<const OldBuiltinFunction *>(f)->internalClass()->engine->throwTypeError();
}
void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
void OldBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that);
const OldBuiltinFunction *f = static_cast<const OldBuiltinFunction *>(that);
ExecutionEngine *v4 = scope.engine;
if (v4->hasException) {
scope.result = Encode::undefined();
@ -494,6 +494,31 @@ void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData
v4->memoryManager->freeSimpleCallContext();
}
DEFINE_OBJECT_VTABLE(BuiltinFunction);
void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *))
{
Heap::FunctionObject::init(scope, name);
this->code = code;
}
void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *)
{
scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError();
}
void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that);
ExecutionEngine *v4 = scope.engine;
if (v4->hasException) {
scope.result = Encode::undefined();
return;
}
f->d()->code(f, scope, callData);
}
void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that);

View File

@ -61,6 +61,8 @@ struct QQmlSourceLocation;
namespace QV4 {
struct BuiltinFunction;
namespace Heap {
struct Q_QML_PRIVATE_EXPORT FunctionObject : Object {
@ -93,11 +95,16 @@ struct FunctionPrototype : FunctionObject {
void init();
};
struct Q_QML_EXPORT BuiltinFunction : FunctionObject {
struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject {
void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *));
ReturnedValue (*code)(QV4::CallContext *);
};
struct Q_QML_EXPORT BuiltinFunction : FunctionObject {
void init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *));
void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *);
};
struct IndexedBuiltinFunction : FunctionObject {
inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(QV4::CallContext *ctx, uint index));
ReturnedValue (*code)(QV4::CallContext *, uint index);
@ -183,10 +190,21 @@ struct FunctionPrototype: FunctionObject
static ReturnedValue method_bind(CallContext *ctx);
};
struct Q_QML_EXPORT BuiltinFunction: FunctionObject {
struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject {
V4_OBJECT2(OldBuiltinFunction, FunctionObject)
static void construct(const Managed *, Scope &scope, CallData *);
static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct Q_QML_EXPORT BuiltinFunction : FunctionObject {
V4_OBJECT2(BuiltinFunction, FunctionObject)
static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *))
static Heap::OldBuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *))
{
return scope->engine()->memoryManager->allocObject<OldBuiltinFunction>(scope, name, code);
}
static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *))
{
return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code);
}

View File

@ -166,6 +166,17 @@ void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(Ca
defineDefaultProperty(s, function);
}
void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount)
{
ExecutionEngine *e = engine();
Scope scope(e);
ScopedString s(scope, e->newIdentifier(name));
ExecutionContext *global = e->rootContext();
ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code));
function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount));
defineDefaultProperty(s, function);
}
void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount)
{
ExecutionEngine *e = engine();
@ -176,6 +187,16 @@ void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallConte
defineDefaultProperty(name, function);
}
void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount)
{
ExecutionEngine *e = engine();
Scope scope(e);
ExecutionContext *global = e->rootContext();
ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code));
function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount));
defineDefaultProperty(name, function);
}
void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *))
{
ExecutionEngine *e = engine();

View File

@ -63,6 +63,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct BuiltinFunction;
namespace Heap {
struct Object : Base {
@ -236,7 +238,9 @@ struct Q_QML_EXPORT Object: Managed {
}
void defineDefaultProperty(const QString &name, const Value &value);
void defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount = 0);
void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0);
void defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount = 0);
void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0);
void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *));
void defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *));
/* Fixed: Writable: false, Enumerable: false, Configurable: false */

View File

@ -121,100 +121,100 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable);
}
ReturnedValue ObjectPrototype::method_getPrototypeOf(CallContext *ctx)
void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
ScopedObject p(scope, o->prototype());
return !!p ? p->asReturnedValue() : Encode::null();
scope.result = !!p ? p->asReturnedValue() : Encode::null();
}
ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx)
void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject O(scope, ctx->argument(0));
if (!O)
return ctx->engine()->throwTypeError();
ScopedObject O(scope, callData->argument(0));
if (!O) {
scope.result = scope.engine->throwTypeError();
return;
}
if (ArgumentsObject::isNonStrictArgumentsObject(O))
static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate();
ScopedValue v(scope, ctx->argument(1));
ScopedValue v(scope, callData->argument(1));
ScopedString name(scope, v->toString(scope.engine));
if (scope.hasException())
return Encode::undefined();
CHECK_EXCEPTION();
PropertyAttributes attrs;
ScopedProperty desc(scope);
O->getOwnProperty(name, &attrs, desc);
return fromPropertyDescriptor(scope.engine, desc, attrs);
scope.result = fromPropertyDescriptor(scope.engine, desc, attrs);
}
ReturnedValue ObjectPrototype::method_getOwnPropertyNames(CallContext *context)
void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(context);
ScopedObject O(scope, context->argument(0));
if (!O)
return context->engine()->throwTypeError();
ScopedArrayObject array(scope, getOwnPropertyNames(context->d()->engine, context->args()[0]));
return array.asReturnedValue();
}
ReturnedValue ObjectPrototype::method_create(CallContext *ctx)
{
Scope scope(ctx);
ScopedValue O(scope, ctx->argument(0));
if (!O->isObject() && !O->isNull())
return ctx->engine()->throwTypeError();
ScopedObject newObject(scope, ctx->d()->engine->newObject());
newObject->setPrototype(O->as<Object>());
if (ctx->argc() > 1 && !ctx->args()[1].isUndefined()) {
ctx->d()->callData->args[0] = newObject.asReturnedValue();
return method_defineProperties(ctx);
ScopedObject O(scope, callData->argument(0));
if (!O) {
scope.result = scope.engine->throwTypeError();
return;
}
return newObject.asReturnedValue();
scope.result = getOwnPropertyNames(scope.engine, callData->args[0]);
}
ReturnedValue ObjectPrototype::method_defineProperty(CallContext *ctx)
void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject O(scope, ctx->argument(0));
if (!O)
return ctx->engine()->throwTypeError();
ScopedValue O(scope, callData->argument(0));
if (!O->isObject() && !O->isNull()) {
scope.result = scope.engine->throwTypeError();
return;
}
ScopedString name(scope, ctx->argument(1), ScopedString::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedObject newObject(scope, scope.engine->newObject());
newObject->setPrototype(O->as<Object>());
ScopedValue attributes(scope, ctx->argument(2));
if (callData->argc > 1 && !callData->args[1].isUndefined()) {
callData->args[0] = newObject;
method_defineProperties(builtin, scope, callData);
return;
}
scope.result = newObject;
}
void ObjectPrototype::method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData)
{
ScopedObject O(scope, callData->argument(0));
if (!O) {
scope.result = scope.engine->throwTypeError();
return;
}
ScopedString name(scope, callData->argument(1), ScopedString::Convert);
CHECK_EXCEPTION();
ScopedValue attributes(scope, callData->argument(2));
ScopedProperty pd(scope);
PropertyAttributes attrs;
toPropertyDescriptor(scope.engine, attributes, pd, &attrs);
if (scope.engine->hasException)
return Encode::undefined();
CHECK_EXCEPTION();
if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs))
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
return O.asReturnedValue();
scope.result = O;
}
ReturnedValue ObjectPrototype::method_defineProperties(CallContext *ctx)
void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject O(scope, ctx->argument(0));
ScopedObject O(scope, callData->argument(0));
if (!O)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
ScopedObject o(scope, callData->argument(1), ScopedObject::Convert);
CHECK_EXCEPTION();
ScopedObject o(scope, ctx->argument(1), ScopedObject::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedValue val(scope);
ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
@ -230,26 +230,24 @@ ReturnedValue ObjectPrototype::method_defineProperties(CallContext *ctx)
PropertyAttributes nattrs;
val = o->getValue(pd->value, attrs);
toPropertyDescriptor(scope.engine, val, n, &nattrs);
if (scope.engine->hasException)
return Encode::undefined();
CHECK_EXCEPTION();
bool ok;
if (name)
ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs);
else
ok = O->__defineOwnProperty__(scope.engine, index, n, nattrs);
if (!ok)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
}
return O.asReturnedValue();
scope.result = O;
}
ReturnedValue ObjectPrototype::method_seal(CallContext *ctx)
void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
o->setInternalClass(o->internalClass()->sealed());
@ -261,15 +259,14 @@ ReturnedValue ObjectPrototype::method_seal(CallContext *ctx)
}
}
return o.asReturnedValue();
scope.result = o;
}
ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx)
void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
if (ArgumentsObject::isNonStrictArgumentsObject(o))
static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate();
@ -285,96 +282,111 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx)
o->arrayData()->attrs[i].setWritable(false);
}
}
return o.asReturnedValue();
scope.result = o;
}
ReturnedValue ObjectPrototype::method_preventExtensions(CallContext *ctx)
void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
o->setInternalClass(o->internalClass()->nonExtensible());
return o.asReturnedValue();
scope.result = o;
}
ReturnedValue ObjectPrototype::method_isSealed(CallContext *ctx)
void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
if (o->isExtensible())
return Encode(false);
if (o->isExtensible()) {
scope.result = Encode(false);
return;
}
if (o->internalClass() != o->internalClass()->sealed())
return Encode(false);
if (o->internalClass() != o->internalClass()->sealed()) {
scope.result = Encode(false);
return;
}
if (!o->arrayData() || !o->arrayData()->length())
return Encode(true);
if (!o->arrayData() || !o->arrayData()->length()) {
scope.result = Encode(true);
return;
}
Q_ASSERT(o->arrayData() && o->arrayData()->length());
if (!o->arrayData()->attrs)
return Encode(false);
if (!o->arrayData()->attrs) {
scope.result = Encode(false);
return;
}
for (uint i = 0; i < o->arrayData()->alloc; ++i) {
if (!o->arrayData()->isEmpty(i))
if (o->arrayData()->attributes(i).isConfigurable())
return Encode(false);
if (o->arrayData()->attributes(i).isConfigurable()) {
scope.result = Encode(false);
return;
}
}
return Encode(true);
scope.result = Encode(true);
}
ReturnedValue ObjectPrototype::method_isFrozen(CallContext *ctx)
void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
if (o->isExtensible())
return Encode(false);
if (o->isExtensible()) {
scope.result = Encode(false);
return;
}
if (o->internalClass() != o->internalClass()->frozen())
return Encode(false);
if (o->internalClass() != o->internalClass()->frozen()) {
scope.result = Encode(false);
return;
}
if (!o->arrayData() || !o->arrayData()->length())
return Encode(true);
if (!o->arrayData() || !o->arrayData()->length()) {
scope.result = Encode(true);
return;
}
Q_ASSERT(o->arrayData() && o->arrayData()->length());
if (!o->arrayData()->attrs)
return Encode(false);
if (!o->arrayData()->attrs) {
scope.result = Encode(false);
return;
}
for (uint i = 0; i < o->arrayData()->alloc; ++i) {
if (!o->arrayData()->isEmpty(i))
if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable())
return Encode(false);
if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) {
scope.result = Encode(false);
return;
}
}
return Encode(true);
scope.result = Encode(true);
}
ReturnedValue ObjectPrototype::method_isExtensible(CallContext *ctx)
void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
return Encode((bool)o->isExtensible());
scope.result = Encode((bool)o->isExtensible());
}
ReturnedValue ObjectPrototype::method_keys(CallContext *ctx)
void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->argument(0));
ScopedObject o(scope, callData->argument(0));
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
ScopedArrayObject a(scope, ctx->d()->engine->newArrayObject());
ScopedArrayObject a(scope, scope.engine->newArrayObject());
ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
ScopedValue name(scope);
@ -385,175 +397,159 @@ ReturnedValue ObjectPrototype::method_keys(CallContext *ctx)
a->push_back(name);
}
return a.asReturnedValue();
scope.result = a;
}
ReturnedValue ObjectPrototype::method_toString(CallContext *ctx)
void ObjectPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
if (ctx->thisObject().isUndefined()) {
return ctx->d()->engine->newString(QStringLiteral("[object Undefined]"))->asReturnedValue();
} else if (ctx->thisObject().isNull()) {
return ctx->d()->engine->newString(QStringLiteral("[object Null]"))->asReturnedValue();
if (callData->thisObject.isUndefined()) {
scope.result = scope.engine->newString(QStringLiteral("[object Undefined]"));
} else if (callData->thisObject.isNull()) {
scope.result = scope.engine->newString(QStringLiteral("[object Null]"));
} else {
ScopedObject obj(scope, ctx->thisObject().toObject(scope.engine));
ScopedObject obj(scope, callData->thisObject.toObject(scope.engine));
QString className = obj->className();
return ctx->d()->engine->newString(QStringLiteral("[object %1]").arg(className))->asReturnedValue();
scope.result = scope.engine->newString(QStringLiteral("[object %1]").arg(className));
}
}
ReturnedValue ObjectPrototype::method_toLocaleString(CallContext *ctx)
void ObjectPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->thisObject().toObject(scope.engine));
ScopedObject o(scope, callData->thisObject.toObject(scope.engine));
if (!o)
return Encode::undefined();
ScopedFunctionObject f(scope, o->get(ctx->d()->engine->id_toString()));
RETURN_UNDEFINED();
ScopedFunctionObject f(scope, o->get(scope.engine->id_toString()));
if (!f)
return ctx->engine()->throwTypeError();
ScopedCallData callData(scope);
callData->thisObject = o;
THROW_TYPE_ERROR();
ScopedCallData cData(scope);
cData->thisObject = o;
f->call(scope, callData);
return scope.result.asReturnedValue();
}
ReturnedValue ObjectPrototype::method_valueOf(CallContext *ctx)
void ObjectPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedValue v(scope, ctx->thisObject().toObject(scope.engine));
if (ctx->d()->engine->hasException)
return Encode::undefined();
return v->asReturnedValue();
scope.result = callData->thisObject.toObject(scope.engine);
}
ReturnedValue ObjectPrototype::method_hasOwnProperty(CallContext *ctx)
void ObjectPrototype::method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedString P(scope, ctx->argument(0), ScopedString::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedObject O(scope, ctx->thisObject(), ScopedObject::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedString P(scope, callData->argument(0), ScopedString::Convert);
CHECK_EXCEPTION();
ScopedObject O(scope, callData->thisObject, ScopedObject::Convert);
CHECK_EXCEPTION();
bool r = O->hasOwnProperty(P);
if (!r)
r = !O->query(P).isEmpty();
return Encode(r);
scope.result = Encode(r);
}
ReturnedValue ObjectPrototype::method_isPrototypeOf(CallContext *ctx)
void ObjectPrototype::method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject V(scope, ctx->argument(0));
if (!V)
return Encode(false);
ScopedObject V(scope, callData->argument(0));
if (!V) {
scope.result = Encode(false);
return;
}
ScopedObject O(scope, ctx->thisObject(), ScopedObject::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedObject O(scope, callData->thisObject, ScopedObject::Convert);
CHECK_EXCEPTION();
ScopedObject proto(scope, V->prototype());
while (proto) {
if (O->d() == proto->d())
return Encode(true);
if (O->d() == proto->d()) {
scope.result = Encode(true);
return;
}
proto = proto->prototype();
}
return Encode(false);
scope.result = Encode(false);
}
ReturnedValue ObjectPrototype::method_propertyIsEnumerable(CallContext *ctx)
void ObjectPrototype::method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedString p(scope, ctx->argument(0), ScopedString::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedString p(scope, callData->argument(0), ScopedString::Convert);
CHECK_EXCEPTION();
ScopedObject o(scope, ctx->thisObject(), ScopedObject::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedObject o(scope, callData->thisObject, ScopedObject::Convert);
CHECK_EXCEPTION();
PropertyAttributes attrs;
o->getOwnProperty(p, &attrs);
return Encode(attrs.isEnumerable());
scope.result = Encode(attrs.isEnumerable());
}
ReturnedValue ObjectPrototype::method_defineGetter(CallContext *ctx)
void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData)
{
if (ctx->argc() < 2)
return ctx->engine()->throwTypeError();
if (callData->argc < 2)
THROW_TYPE_ERROR();
Scope scope(ctx);
ScopedFunctionObject f(scope, ctx->argument(1));
ScopedFunctionObject f(scope, callData->argument(1));
if (!f)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
ScopedString prop(scope, ctx->argument(0), ScopedString::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedString prop(scope, callData->argument(0), ScopedString::Convert);
CHECK_EXCEPTION();
ScopedObject o(scope, ctx->thisObject());
ScopedObject o(scope, callData->thisObject);
if (!o) {
if (!ctx->thisObject().isUndefined())
return Encode::undefined();
o = ctx->d()->engine->globalObject;
if (!callData->thisObject.isUndefined())
RETURN_UNDEFINED();
o = scope.engine->globalObject;
}
ScopedProperty pd(scope);
pd->value = f;
pd->set = Primitive::emptyValue();
o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
return Encode::undefined();
RETURN_UNDEFINED();
}
ReturnedValue ObjectPrototype::method_defineSetter(CallContext *ctx)
void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData)
{
if (ctx->argc() < 2)
return ctx->engine()->throwTypeError();
if (callData->argc < 2)
THROW_TYPE_ERROR();
Scope scope(ctx);
ScopedFunctionObject f(scope, ctx->argument(1));
ScopedFunctionObject f(scope, callData->argument(1));
if (!f)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
ScopedString prop(scope, ctx->argument(0), ScopedString::Convert);
if (scope.engine->hasException)
return Encode::undefined();
ScopedString prop(scope, callData->argument(0), ScopedString::Convert);
CHECK_EXCEPTION();
ScopedObject o(scope, ctx->thisObject());
ScopedObject o(scope, callData->thisObject);
if (!o) {
if (!ctx->thisObject().isUndefined())
return Encode::undefined();
o = ctx->d()->engine->globalObject;
if (!callData->thisObject.isUndefined())
RETURN_UNDEFINED();
o = scope.engine->globalObject;
}
ScopedProperty pd(scope);
pd->value = Primitive::emptyValue();
pd->set = f;
o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
return Encode::undefined();
RETURN_UNDEFINED();
}
ReturnedValue ObjectPrototype::method_get_proto(CallContext *ctx)
void ObjectPrototype::method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->thisObject().as<Object>());
ScopedObject o(scope, callData->thisObject.as<Object>());
if (!o)
return ctx->engine()->throwTypeError();
THROW_TYPE_ERROR();
return o->prototype()->asReturnedValue();
scope.result = o->prototype();
}
ReturnedValue ObjectPrototype::method_set_proto(CallContext *ctx)
void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData)
{
Scope scope(ctx);
ScopedObject o(scope, ctx->thisObject());
if (!o || !ctx->argc())
return ctx->engine()->throwTypeError();
ScopedObject o(scope, callData->thisObject);
if (!o || !callData->argc)
THROW_TYPE_ERROR();
if (ctx->args()[0].isNull()) {
if (callData->args[0].isNull()) {
o->setPrototype(0);
return Encode::undefined();
RETURN_UNDEFINED();
}
ScopedObject p(scope, ctx->args()[0]);
ScopedObject p(scope, callData->args[0]);
bool ok = false;
if (!!p) {
if (o->prototype() == p->d()) {
@ -562,9 +558,11 @@ ReturnedValue ObjectPrototype::method_set_proto(CallContext *ctx)
ok = o->setPrototype(p);
}
}
if (!ok)
return ctx->engine()->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
return Encode::undefined();
if (!ok) {
scope.result = scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
return;
}
RETURN_UNDEFINED();
}
void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs)

View File

@ -78,32 +78,32 @@ struct ObjectPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
static ReturnedValue method_getPrototypeOf(CallContext *ctx);
static ReturnedValue method_getOwnPropertyDescriptor(CallContext *ctx);
static ReturnedValue method_getOwnPropertyNames(CallContext *context);
static ReturnedValue method_create(CallContext *ctx);
static ReturnedValue method_defineProperty(CallContext *ctx);
static ReturnedValue method_defineProperties(CallContext *ctx);
static ReturnedValue method_seal(CallContext *ctx);
static ReturnedValue method_freeze(CallContext *ctx);
static ReturnedValue method_preventExtensions(CallContext *ctx);
static ReturnedValue method_isSealed(CallContext *ctx);
static ReturnedValue method_isFrozen(CallContext *ctx);
static ReturnedValue method_isExtensible(CallContext *ctx);
static ReturnedValue method_keys(CallContext *ctx);
static void method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_create(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_seal(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_keys(const BuiltinFunction *, Scope &scope, CallData *callData);
static ReturnedValue method_toString(CallContext *ctx);
static ReturnedValue method_toLocaleString(CallContext *ctx);
static ReturnedValue method_valueOf(CallContext *ctx);
static ReturnedValue method_hasOwnProperty(CallContext *ctx);
static ReturnedValue method_isPrototypeOf(CallContext *ctx);
static ReturnedValue method_propertyIsEnumerable(CallContext *ctx);
static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData);
static ReturnedValue method_defineGetter(CallContext *ctx);
static ReturnedValue method_defineSetter(CallContext *ctx);
static void method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData);
static ReturnedValue method_get_proto(CallContext *ctx);
static ReturnedValue method_set_proto(CallContext *ctx);
static void method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData);
static void method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData);
static void toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs);
static ReturnedValue fromPropertyDescriptor(ExecutionEngine *engine, const Property *desc, PropertyAttributes attrs);

View File

@ -68,6 +68,32 @@ namespace QV4 {
struct ScopedValue;
#define CHECK_EXCEPTION() \
do { \
if (scope.hasException()) { \
scope.result = Encode::undefined(); \
return; \
} \
} while (false)
#define RETURN_UNDEFINED() \
do { \
scope.result = Encode::undefined(); \
return; \
} while (false)
#define RETURN_RESULT(r) \
do { \
scope.result = r; \
return; \
} while (false)
#define THROW_TYPE_ERROR() \
do { \
scope.result = scope.engine->throwTypeError(); \
return; \
} while (false)
struct Scope {
inline Scope(ExecutionContext *ctx)
: engine(ctx->d()->engine)

View File

@ -347,6 +347,10 @@ Param traceParam(const Param &param)
goto catchException; \
VALUE(param) = tmp; \
}
// qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro
#ifdef CHECK_EXCEPTION
#undef CHECK_EXCEPTION
#endif
#define CHECK_EXCEPTION \
if (engine->hasException) \
goto catchException