From 806b5fb4c14b9da712d57525c35b6333665cf26d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 7 Dec 2012 20:31:19 -0800 Subject: [PATCH] Fix typeof to work with undefined references Change-Id: I90cd2b34a25476dfee1ec01315275b6c179d11dc Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- qmljs_environment.cpp | 30 ++++++++++++++++++++++++++++++ qmljs_environment.h | 1 + qmljs_runtime.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++-- qmljs_runtime.h | 35 +++++------------------------------ qv4codegen.cpp | 2 +- qv4isel_masm.cpp | 20 ++++++++++++++++---- 7 files changed, 96 insertions(+), 38 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 76abd79..6a0c163 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -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) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 0d6b200..a64d9dc 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -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) diff --git a/qmljs_environment.h b/qmljs_environment.h index 4063f38..5ecc56e 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -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); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index d7bbf0e..aaa5be9 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -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) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index adcdae6..6675920 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -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); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 8b3361b..8159ede 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1384,7 +1384,7 @@ bool Codegen::visit(TypeOfExpression *ast) { Result expr = expression(ast->expression); IR::ExprList *args = _function->New(); - 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; } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2fa506d..a754bee 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -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 -- 2.7.4