Fix typeof to work with undefined references
authorLars Knoll <lars.knoll@digia.com>
Sat, 8 Dec 2012 04:31:19 +0000 (20:31 -0800)
committerSimon Hausmann <simon.hausmann@digia.com>
Sat, 8 Dec 2012 04:33:51 +0000 (05:33 +0100)
Change-Id: I90cd2b34a25476dfee1ec01315275b6c179d11dc
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
llvm_runtime.cpp
qmljs_environment.cpp
qmljs_environment.h
qmljs_runtime.cpp
qmljs_runtime.h
qv4codegen.cpp
qv4isel_masm.cpp

index 76abd79..6a0c163 100644 (file)
@@ -451,7 +451,7 @@ void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *nam
 
 void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value)
 {
-    *result = __qmljs_typeof(*value, ctx);
+    *result = __qmljs_builtin_typeof(*value, ctx);
 }
 
 void __qmljs_llvm_throw(ExecutionContext *context, Value *value)
index 0d6b200..a64d9dc 100644 (file)
@@ -296,6 +296,36 @@ Value ExecutionContext::getProperty(String *name)
     return Value::undefinedValue();
 }
 
+Value ExecutionContext::getPropertyNoThrow(String *name)
+{
+    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
+        if (ctx->withObject) {
+            With *w = ctx->withObject;
+            while (w) {
+                if (w->object->__hasProperty__(ctx, name))
+                    return w->object->__get__(ctx, name);
+                w = w->next;
+            }
+        }
+
+        for (unsigned int i = 0; i < ctx->variableCount(); ++i)
+            if (__qmljs_string_equal(ctx->variables()[i], name))
+                return ctx->locals[i];
+        for (unsigned int i = 0; i < ctx->formalCount(); ++i)
+            if (__qmljs_string_equal(ctx->formals()[i], name))
+                return ctx->arguments[i];
+        if (ctx->activation && ctx->activation->__hasProperty__(ctx, name))
+            return ctx->activation->__get__(ctx, name);
+        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();
+}
+
 
 
 void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op)
index 4063f38..5ecc56e 100644 (file)
@@ -122,6 +122,7 @@ struct ExecutionContext
 
     void setProperty(String *name, Value value);
     Value getProperty(String *name);
+    Value getPropertyNoThrow(String *name);
     void inplaceBitOp(Value value, String *name, BinOp op);
     bool deleteProperty(String *name);
 
index d7bbf0e..aaa5be9 100644 (file)
@@ -798,9 +798,49 @@ Value __qmljs_get_exception(ExecutionContext *context)
     return context->engine->exception;
 }
 
-Value __qmljs_builtin_typeof(Value val, ExecutionContext *context)
+Value __qmljs_builtin_typeof(Value value, ExecutionContext *ctx)
+{
+    switch (value.type()) {
+    case Value::Undefined_Type:
+        return __qmljs_string_literal_undefined(ctx);
+        break;
+    case Value::Null_Type:
+        return __qmljs_string_literal_object(ctx);
+        break;
+    case Value::Boolean_Type:
+        return __qmljs_string_literal_boolean(ctx);
+        break;
+    case Value::String_Type:
+        return __qmljs_string_literal_string(ctx);
+        break;
+    case Value::Object_Type:
+        if (__qmljs_is_callable(value, ctx))
+            return __qmljs_string_literal_function(ctx);
+        else
+            return __qmljs_string_literal_object(ctx); // ### implementation-defined
+        break;
+    default:
+        return __qmljs_string_literal_number(ctx);
+        break;
+    }
+}
+
+Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context)
+{
+    return __qmljs_builtin_typeof(context->getPropertyNoThrow(name), context);
+}
+
+Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context)
+{
+    Value obj = base.toObject(context);
+    return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context);
+}
+
+Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context)
 {
-    return __qmljs_typeof(val, context);
+    String *name = index.toString(context);
+    Value obj = base.toObject(context);
+    return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context);
 }
 
 void __qmljs_builtin_throw(Value val, ExecutionContext *context)
index adcdae6..6675920 100644 (file)
@@ -98,7 +98,11 @@ Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Va
 Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc);
 Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc);
 
-Value __qmljs_builtin_typeof(Value val, ExecutionContext *context);
+Value __qmljs_builtin_typeof(Value val, ExecutionContext *ctx);
+Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context);
+Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context);
+Value __qmljs_builtin_typeof_element(Value base, Value index, 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);
@@ -179,7 +183,6 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index);
 Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name);
 Value __qmljs_delete_name(ExecutionContext *ctx, String *name);
 
-Value __qmljs_typeof(Value value, ExecutionContext *ctx);
 void __qmljs_throw(Value value, ExecutionContext *context);
 // actually returns a jmp_buf *
 void *__qmljs_create_exception_handler(ExecutionContext *context);
@@ -443,34 +446,6 @@ inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeH
 }
 
 
-// unary operators
-inline Value __qmljs_typeof(Value value, ExecutionContext *ctx)
-{
-    switch (value.type()) {
-    case Value::Undefined_Type:
-        return __qmljs_string_literal_undefined(ctx);
-        break;
-    case Value::Null_Type:
-        return __qmljs_string_literal_object(ctx);
-        break;
-    case Value::Boolean_Type:
-        return __qmljs_string_literal_boolean(ctx);
-        break;
-    case Value::String_Type:
-        return __qmljs_string_literal_string(ctx);
-        break;
-    case Value::Object_Type:
-        if (__qmljs_is_callable(value, ctx))
-            return __qmljs_string_literal_function(ctx);
-        else
-            return __qmljs_string_literal_object(ctx); // ### implementation-defined
-        break;
-    default:
-        return __qmljs_string_literal_number(ctx);
-        break;
-    }
-}
-
 inline Value __qmljs_uplus(Value value, ExecutionContext *ctx)
 {
     TRACE1(value);
index 8b3361b..8159ede 100644 (file)
@@ -1384,7 +1384,7 @@ bool Codegen::visit(TypeOfExpression *ast)
 {
     Result expr = expression(ast->expression);
     IR::ExprList *args = _function->New<IR::ExprList>();
-    args->init(argument(*expr));
+    args->init(*expr);
     _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
     return false;
 }
index 2fa506d..a754bee 100644 (file)
@@ -259,9 +259,21 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu
         callRuntimeMethod(result, __qmljs_call_activation_property, call->base, call->args);
         break;
     case IR::Name::builtin_typeof: {
-        IR::Temp *arg = call->args->expr->asTemp();
-        assert(arg != 0);
-        generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister);
+        if (IR::Member *m = call->args->expr->asMember()) {
+            generateFunctionCall(result, __qmljs_builtin_typeof_member, m->base->asTemp(), identifier(*m->name), ContextRegister);
+            return;
+        } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
+            generateFunctionCall(result, __qmljs_builtin_typeof_element, ss->base->asTemp(), ss->index->asTemp(), ContextRegister);
+            return;
+        } else if (IR::Name *n = call->args->expr->asName()) {
+            generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(*n->id), ContextRegister);
+            return;
+        } else if (IR::Temp *arg = call->args->expr->asTemp()){
+            assert(arg != 0);
+            generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister);
+        } else {
+            assert(false);
+        }
     }
         break;
     case IR::Name::builtin_delete: {
@@ -272,7 +284,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu
             generateFunctionCall(result, __qmljs_delete_subscript, ContextRegister, ss->base->asTemp(), ss->index->asTemp());
             return;
         } else if (IR::Name *n = call->args->expr->asName()) {
-            generateFunctionCall(result, __qmljs_delete_name, ContextRegister, n);
+            generateFunctionCall(result, __qmljs_delete_name, ContextRegister, identifier(*n->id));
             return;
         } else if (call->args->expr->asTemp()){
             // ### should throw in strict mode