Correctly instantiate variables in the local scope
authorLars Knoll <lars.knoll@digia.com>
Mon, 26 Nov 2012 22:26:39 +0000 (23:26 +0100)
committerErik Verbruggen <erik.verbruggen@digia.com>
Tue, 27 Nov 2012 10:51:28 +0000 (11:51 +0100)
This fixes cases where eval() would create variables
in the wrong scope.

Change-Id: Ie93ec2d1fb125e588c1b6ffa2ca8ca4b6e3112c9
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
qmljs_environment.cpp
qmljs_environment.h
qmljs_objects.cpp
qmljs_runtime.cpp
qmljs_runtime.h
qv4codegen.cpp
qv4ir.cpp
qv4ir_p.h
qv4isel_masm.cpp

index 8093c12..0a8fada 100644 (file)
@@ -100,20 +100,22 @@ bool DeclarativeEnvironment::hasBinding(String *name) const
         if (__qmljs_string_equal(formals[i], name))
             return true;
     }
-    if (!deletableLocals)
-        return false;
-    return deletableLocals->contains(name->toQString());
+    return false;
 }
 
-void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable)
+void DeclarativeEnvironment::createMutableBinding(ExecutionContext *ctx, String *name, bool deletable)
 {
-    // all non deletable vars should get created at compile time
-    assert(deletable);
-    assert(!hasBinding(name));
-
-    if (!deletableLocals)
-        deletableLocals = new QHash<QString, Value>();
-    deletableLocals->insert(name->toQString(), Value::undefinedValue());
+    if (activation) {
+        if (activation->__hasProperty__(ctx, name))
+            return;
+        PropertyDescriptor desc;
+        desc.value = Value::undefinedValue();
+        desc.type = PropertyDescriptor::Data;
+        desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset;
+        desc.writable = PropertyDescriptor::Set;
+        desc.enumberable = PropertyDescriptor::Set;
+        activation->__defineOwnProperty__(ctx, name, &desc, true);
+    }
 }
 
 void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict)
@@ -133,12 +135,6 @@ void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool s
             return;
         }
     }
-    assert(deletableLocals);
-    QHash<QString, Value>::iterator it = deletableLocals->find(name->toQString());
-    if (it != deletableLocals->end()) {
-        *it = value;
-        return;
-    }
     assert(false);
 }
 
@@ -154,24 +150,16 @@ Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const
         if (__qmljs_string_equal(formals[i], name))
             return arguments[i];
     }
-    assert(deletableLocals);
-    QHash<QString, Value>::const_iterator it = deletableLocals->find(name->toQString());
-    if (it != deletableLocals->end())
-        return *it;
-
     assert(false);
 }
 
-bool DeclarativeEnvironment::deleteBinding(String *name)
+bool DeclarativeEnvironment::deleteBinding(ExecutionContext *ctx, String *name)
 {
-    if (deletableLocals) {
-        QHash<QString, Value>::iterator it = deletableLocals->find(name->toQString());
-        if (it != deletableLocals->end()) {
-            deletableLocals->erase(it);
-            return true;
-        }
-    }
-    return !hasBinding(name);
+    if (activation)
+        activation->__delete__(ctx, name, false);
+
+    // ### throw in strict mode?
+    return false;
 }
 
 void DeclarativeEnvironment::pushWithObject(Object *with)
index 4737664..e631ec7 100644 (file)
@@ -73,19 +73,15 @@ struct DeclarativeEnvironment
         With *next;
     } *withObject;
 
-    // these get used for createMutableBinding(..., true).
-    // the only place this is being used is eval(...)
-    QHash<QString, Value> *deletableLocals;
 
     DeclarativeEnvironment(ExecutionEngine *e);
     DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc);
-    ~DeclarativeEnvironment() { delete deletableLocals; }
 
     bool hasBinding(String *name) const;
-    void createMutableBinding(String *name, bool deletable);
+    void createMutableBinding(ExecutionContext *ctx, String *name, bool deletable);
     void setMutableBinding(String *name, Value value, bool strict);
     Value getBindingValue(String *name, bool strict) const;
-    bool deleteBinding(String *name);
+    bool deleteBinding(ExecutionContext *ctx, String *name);
 
     // ### needs a bit of work in exception handlers
     void pushWithObject(Object *with);
index 88a7c95..a7e2f80 100644 (file)
@@ -253,8 +253,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property
         if (!extensible)
             goto reject;
         // clause 4
-        *current = *desc;
-        current->fullyPopulated();
+        PropertyDescriptor *pd = members->insert(name);
+        *pd = *desc;
+        pd->fullyPopulated();
         return true;
     }
 
@@ -270,7 +271,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property
     if (!current->isConfigurable()) {
         if (desc->isConfigurable())
             goto reject;
-        if (desc->enumberable != PropertyDescriptor::Unset && desc->enumberable != current->enumberable)
+        if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable)
             goto reject;
     }
 
@@ -313,6 +314,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property
     *current += *desc;
     return true;
   reject:
+    qDebug() << "___put__ rejected" << name->toQString();
     if (throwException)
         __qmljs_throw_type_error(ctx);
     return false;
index 86d1bbe..1491b01 100644 (file)
@@ -865,6 +865,10 @@ void __qmljs_builtin_pop_with(ExecutionContext *ctx)
     ctx->lexicalEnvironment->popWithObject();
 }
 
+void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name)
+{
+    ctx->lexicalEnvironment->createMutableBinding(ctx, name, deletable);
+}
 
 } // extern "C"
 
index 1831f68..a02713d 100644 (file)
@@ -103,6 +103,7 @@ Value __qmljs_builtin_typeof(Value val, ExecutionContext *context);
 void __qmljs_builtin_throw(Value val, ExecutionContext *context);
 void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx);
 void __qmljs_builtin_pop_with(ExecutionContext *ctx);
+void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name);
 
 // constructors
 Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx);
index cd4cbeb..e605e24 100644 (file)
@@ -1517,15 +1517,30 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
     function->maxNumberOfArguments = _env->maxNumberOfArguments;
 
     // variables in global code are properties of the global context object, not locals as with other functions.
-    for (int i = 0; i < _env->vars.size(); ++i) {
-        const QString &local = _env->vars.at(i);
-        if (!_env->parent) {
-            entryBlock->MOVE(entryBlock->NAME(local, 0, 0), entryBlock->CONST(IR::UndefinedType, 0));
-        } else {
+    if (_mode == FunctionCode) {
+        for (int i = 0; i < _env->vars.size(); ++i) {
+            const QString &local = _env->vars.at(i);
             function->LOCAL(local);
             unsigned t = entryBlock->newTemp();
             assert(t == unsigned(i));
         }
+    } else {
+        IR::ExprList *args = 0;
+        for (int i = 0; i < _env->vars.size(); ++i) {
+            const QString &local = _env->vars.at(i);
+            IR::ExprList *next = function->New<IR::ExprList>();
+            next->expr = entryBlock->NAME(local, 0, 0);
+            next->next = args;
+            args = next;
+        }
+        if (args) {
+            IR::ExprList *next = function->New<IR::ExprList>();
+            next->expr = entryBlock->CONST(IR::BoolType, mode == EvalCode);
+            next->next = args;
+            args = next;
+
+            entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args));
+        }
     }
 
     unsigned returnAddress = entryBlock->newTemp();
index 57f6de9..7646b37 100644 (file)
--- a/qv4ir.cpp
+++ b/qv4ir.cpp
@@ -238,6 +238,8 @@ static const char *builtin_to_string(Name::Builtin b)
         return "builtin_push_with";
     case IR::Name::builtin_pop_with:
         return "builtin_pop_with";
+    case IR::Name::builtin_declare_vars:
+        return "builtin_declare_vars";
     }
     return "builtin_(###FIXME)";
 };
index b0be0f9..740ca9b 100644 (file)
--- a/qv4ir_p.h
+++ b/qv4ir_p.h
@@ -278,7 +278,8 @@ struct Name: Expr {
         builtin_foreach_iterator_object,
         builtin_foreach_next_property_name,
         builtin_push_with,
-        builtin_pop_with
+        builtin_pop_with,
+        builtin_declare_vars
     };
 
     const QString *id;
index f39c3b7..a0262c8 100644 (file)
@@ -279,6 +279,18 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu
     case IR::Name::builtin_pop_with:
         generateFunctionCall(Void, __qmljs_builtin_pop_with, ContextRegister);
         break;
+    case IR::Name::builtin_declare_vars: {
+        if (!call->args)
+            return;
+        IR::Const *deletable = call->args->expr->asConst();
+        assert(deletable->type == IR::BoolType);
+        for (IR::ExprList *it = call->args->next; it; it = it->next) {
+            IR::Name *arg = it->expr->asName();
+            assert(arg != 0);
+            generateFunctionCall(Void, __qmljs_builtin_declare_var, ContextRegister,
+                                 TrustedImm32(deletable->value != 0), identifier(*arg->id));
+        }
+    }
     }
 }