Fixes for the arguments object
authorLars Knoll <lars.knoll@digia.com>
Fri, 14 Dec 2012 10:32:52 +0000 (11:32 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Fri, 14 Dec 2012 12:10:25 +0000 (13:10 +0100)
We now pass most of the test cases for it.

Change-Id: Idc43a9baa75c3c1e8fe760d78cf5e6092f051c6e
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
qmljs_environment.cpp
qmljs_objects.cpp
qmljs_objects.h
qv4codegen.cpp
qv4codegen_p.h
qv4ir_p.h
qv4isel_p.cpp

index a7cf2ce..e2bae58 100644 (file)
@@ -296,12 +296,6 @@ Value ExecutionContext::getProperty(String *name)
             if (hasProperty)
                 return v;
         }
-        if (name->isEqualTo(ctx->engine->id_arguments)) {
-            Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this));
-            createMutableBinding(ctx->engine->id_arguments, false);
-            setMutableBinding(this, ctx->engine->id_arguments, arguments);
-            return arguments;
-        }
     }
     throwReferenceError(Value::fromString(name));
     return Value::undefinedValue();
@@ -336,12 +330,6 @@ Value ExecutionContext::getPropertyNoThrow(String *name)
             if (hasProperty)
                 return v;
         }
-        if (name->isEqualTo(ctx->engine->id_arguments)) {
-            Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this));
-            createMutableBinding(ctx->engine->id_arguments, false);
-            setMutableBinding(this, ctx->engine->id_arguments, arguments);
-            return arguments;
-        }
     }
     return Value::undefinedValue();
 }
@@ -417,14 +405,21 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha
         if (argc < function->formalParameterCount)
             std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue());
     }
+
     locals = function->varCount ? new Value[function->varCount] : 0;
     if (function->varCount)
         std::fill(locals, locals + function->varCount, Value::undefinedValue());
 
     activation = 0;
-
     withObject = 0;
 
+    if (function->usesArgumentsObject) {
+        ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this);
+        args->prototype = engine->objectPrototype;
+        Value arguments = Value::fromObject(args);
+        createMutableBinding(engine->id_arguments, false);
+        setMutableBinding(this, engine->id_arguments, arguments);
+    }
 
     if (engine->debugger)
         engine->debugger->aboutToCall(f, this);
index 871985a..ceaa2d7 100644 (file)
@@ -106,7 +106,6 @@ Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const
 
 bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx)
 {
-    PropertyDescriptor to_fill;
     bool hasProperty = false;
     Value v = __get__(ctx, name, &hasProperty);
     if (!hasProperty)
@@ -547,6 +546,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function)
     if (!function->name.isEmpty())
         name = scope->engine->identifier(function->name);
     needsActivation = function->needsActivation();
+    usesArgumentsObject = function->usesArgumentsObject;
     strictMode = function->isStrict;
     formalParameterCount = function->formals.size();
     if (formalParameterCount) {
@@ -795,52 +795,21 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx)
     return ctx->thisObject;
 }
 
-Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty)
-{
-    if (name->isEqualTo(ctx->engine->id_length)) {
-        if (hasProperty)
-            *hasProperty = true;
-        return Value::fromInt32(context->argumentCount);
-    }
-    if (context) {
-        bool ok = false;
-        int idx = name->toQString().toInt(&ok);
-        if (ok && idx >= 0 && idx < context->argumentCount)
-            return context->argument(idx);
-    }
-
-    return Object::__get__(ctx, name, hasProperty);
-}
-
-
 ArgumentsObject::ArgumentsObject(ExecutionContext *context)
-    : context(context)
 {
     defineDefaultProperty(context->engine->id_length, Value::fromInt32(context->argumentCount));
-}
-
-void ArgumentsObject::__put__(ExecutionContext *ctx, String *name, Value value)
-{
-    if (context) {
-        bool ok = false;
-        int idx = name->toQString().toInt(&ok);
-        if (ok && idx >= 0 && idx < context->argumentCount)
-            context->arguments[idx] = value;
-    }
-
-    return Object::__put__(ctx, name, value);
-}
-
-bool ArgumentsObject::__canPut__(ExecutionContext *ctx, String *name)
-{
-    if (context) {
-        bool ok = false;
-        int idx = name->toQString().toInt(&ok);
-        if (ok && idx >= 0 && idx < context->argumentCount)
-            return true;
+    for (uint i = 0; i < context->argumentCount; ++i)
+        __put__(context, QString::number(i), context->arguments[i]);
+    if (context->strictMode) {
+        FunctionObject *thrower = context->engine->newNativeFunction(context, 0, __qmljs_throw_type_error);
+        PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower);
+        pd.configurable = PropertyDescriptor::Disabled;
+        pd.enumberable = PropertyDescriptor::Disabled;
+        __defineOwnProperty__(context, QStringLiteral("callee"), &pd);
+        __defineOwnProperty__(context, QStringLiteral("caller"), &pd);
+    } else {
+        defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function));
     }
-
-    return Object::__canPut__(ctx, name);
 }
 
 NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *))
index ab30971..21dc82a 100644 (file)
@@ -539,6 +539,7 @@ struct Function {
     QVector<Function *> nestedFunctions;
 
     bool hasDirectEval: 1;
+    bool usesArgumentsObject : 1;
     bool isStrict: 1;
 
     Function(const QString &name)
@@ -546,6 +547,7 @@ struct Function {
         , code(0)
         , codeData(0)
         , hasDirectEval(false)
+        , usesArgumentsObject(false)
         , isStrict(false)
     {}
     ~Function();
@@ -563,6 +565,7 @@ struct FunctionObject: Object {
     String **varList;
     unsigned int varCount;
     bool needsActivation;
+    bool usesArgumentsObject;
     bool strictMode;
 
     FunctionObject(ExecutionContext *scope)
@@ -573,6 +576,7 @@ struct FunctionObject: Object {
         , varList(0)
         , varCount(0)
         , needsActivation(false)
+        , usesArgumentsObject(false)
         , strictMode(false) {}
 
     virtual QString className() { return QStringLiteral("Function"); }
@@ -706,13 +710,9 @@ struct URIErrorObject: ErrorObject {
 };
 
 struct ArgumentsObject: Object {
-    ExecutionContext *context;
     ArgumentsObject(ExecutionContext *context);
     virtual QString className() { return QStringLiteral("Arguments"); }
     virtual ArgumentsObject *asArgumentsObject() { return this; }
-    virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty);
-    virtual void __put__(ExecutionContext *ctx, String *name, Value value);
-    virtual bool __canPut__(ExecutionContext *ctx, String *name);
 };
 
 } // namespace VM
index 17b96fe..6cf1d62 100644 (file)
@@ -255,6 +255,14 @@ protected:
             }
         }
     }
+    void checkForArguments(AST::FormalParameterList *parameters)
+    {
+        while (parameters) {
+            if (parameters->name == QStringLiteral("arguments"))
+                _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
+            parameters = parameters->next;
+        }
+    }
 
     virtual bool visit(Program *ast)
     {
@@ -273,6 +281,8 @@ protected:
         if (! _env->hasDirectEval) {
             if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
                 if (id->name == QStringLiteral("eval")) {
+                    if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown)
+                        _env->usesArgumentsObject = Environment::ArgumentsObjectUsed;
                     _env->hasDirectEval = true;
                 }
             }
@@ -296,6 +306,8 @@ protected:
     virtual bool visit(VariableDeclaration *ast)
     {
         checkName(ast->name, ast->identifierToken);
+        if (ast->name == QLatin1String("arguments"))
+            _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
         _env->enter(ast->name.toString());
         return true;
     }
@@ -303,6 +315,8 @@ protected:
     virtual bool visit(IdentifierExpression *ast)
     {
         checkName(ast->name, ast->identifierToken);
+        if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments"))
+            _env->usesArgumentsObject = Environment::ArgumentsObjectUsed;
         return true;
     }
 
@@ -313,6 +327,7 @@ protected:
             _env->enter(ast->name.toString());
         }
         enterEnvironment(ast);
+        checkForArguments(ast->formals);
         if (ast->body)
             checkDirectivePrologue(ast->body->elements);
         return true;
@@ -328,7 +343,10 @@ protected:
         _env->functions.append(ast);
         _env->hasNestedFunctions = true;
         _env->enter(ast->name.toString());
+        if (ast->name == QLatin1String("arguments"))
+            _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
         enterEnvironment(ast);
+        checkForArguments(ast->formals);
         if (ast->body)
             checkDirectivePrologue(ast->body->elements);
         return true;
@@ -1661,6 +1679,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
     IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock);
     IR::BasicBlock *throwBlock = function->newBasicBlock();
     function->hasDirectEval = _env->hasDirectEval;
+    function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed);
     function->maxNumberOfArguments = _env->maxNumberOfArguments;
     function->isStrict = _env->isStrict;
 
index ba3b477..3776db6 100644 (file)
@@ -117,6 +117,13 @@ protected:
         bool hasDirectEval;
         bool hasNestedFunctions;
         bool isStrict;
+        enum UsesArgumentsObject {
+            ArgumentsObjectUnknown,
+            ArgumentsObjectNotUsed,
+            ArgumentsObjectUsed
+        };
+
+        UsesArgumentsObject usesArgumentsObject;
 
         Environment(Environment *parent)
             : parent(parent)
@@ -124,6 +131,7 @@ protected:
             , hasDirectEval(false)
             , hasNestedFunctions(false)
             , isStrict(false)
+            , usesArgumentsObject(ArgumentsObjectUnknown)
         {
             if (parent && parent->isStrict)
                 isStrict = true;
index 5f474f2..8c570d1 100644 (file)
--- a/qv4ir_p.h
+++ b/qv4ir_p.h
@@ -610,6 +610,7 @@ struct Function {
     int insideWith;
 
     bool hasDirectEval: 1;
+    bool usesArgumentsObject : 1;
     bool isStrict: 1;
 
     template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
@@ -621,6 +622,7 @@ struct Function {
         , maxNumberOfArguments(0)
         , insideWith(0)
         , hasDirectEval(false)
+        , usesArgumentsObject(false)
         , isStrict(false)
     { this->name = newString(name); }
 
index b49d4d3..6d5d229 100644 (file)
@@ -34,6 +34,7 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin
     _irToVM.insert(irFunction, vmFunction);
 
     vmFunction->hasDirectEval = irFunction->hasDirectEval;
+    vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject;
     vmFunction->isStrict = irFunction->isStrict;
 
     foreach (const QString *formal, irFunction->formals)