Let V8 throw the exception when QML property lookup fails
authorKent Hansen <kent.hansen@nokia.com>
Wed, 14 Mar 2012 12:06:26 +0000 (13:06 +0100)
committerQt by Nokia <qt-info@nokia.com>
Wed, 14 Mar 2012 18:13:42 +0000 (19:13 +0100)
This should be squashed into the "Introduce a QML compilation mode"
commit (8a4d849a0152c76bd9107a1b38a641cf8c0ff226) the next time V8
is rebased.

In normal (non-QML) mode, when a global property lookup fails
(implicit receiver ("this") object), V8 decides to throw an exception
iff the expression evaluation is not part of a "typeof" evaluation.
If it is evaluated as part of "typeof", the result silently becomes
"undefined". This is the expected behavior as per the ECMA-262
standard; "typeof" shouldn't throw a ReferenceError even if the
expression involves a global variable that doesn't exist.

This commit brings the QML mode behavior in line with normal mode.
When the receiver object is a QML global object, V8 up until now
hasn't detected whether it should throw an error in that case. The
QML implementation has been working around that by explicitly
throwing the ReferenceError in the QML context wrapper, but that
breaks the "typeof" operator in QML mode. The QML context wrapper
should rather return an empty handle, and leave it up to V8 to
throw the exception as appropriate.

This also reverts the parts of the original QML mode patch that
changed the RelocInfo mode for QML variables, since V8 relies
precisely on this mode to know whether it's evaluating a "typeof".
(It's no longer clear why the RelocInfo mode was modified in the
first place.)

Task-number: QTBUG-24448
Change-Id: Ic33610d5e91bdf373b22d97a3181e6e5f2fc1843
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
12 files changed:
src/3rdparty/v8/src/arm/full-codegen-arm.cc
src/3rdparty/v8/src/arm/lithium-codegen-arm.cc
src/3rdparty/v8/src/ia32/full-codegen-ia32.cc
src/3rdparty/v8/src/ia32/lithium-codegen-ia32.cc
src/3rdparty/v8/src/ic.h
src/3rdparty/v8/src/mips/full-codegen-mips.cc
src/3rdparty/v8/src/x64/full-codegen-x64.cc
src/3rdparty/v8/src/x64/lithium-codegen-x64.cc
tests/auto/v8/tst_v8.cpp
tests/auto/v8/v8main.cpp
tests/auto/v8/v8test.cpp
tests/auto/v8/v8test.h

index a2fa27a..bf3c15f 100644 (file)
@@ -1185,7 +1185,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
 
   __ ldr(r0, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
   __ mov(r2, Operand(var->name()));
-  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF || var->is_qml_global())
+  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
       ? RelocInfo::CODE_TARGET
       : RelocInfo::CODE_TARGET_CONTEXT;
   Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
@@ -1273,7 +1273,7 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
       __ ldr(r0, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
       __ mov(r2, Operand(var->name()));
       Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
-      __ Call(ic, var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT);
+      __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT);
       context()->Plug(r0);
       break;
     }
@@ -2271,7 +2271,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     // Push global object as receiver for the call IC.
     __ ldr(r0, proxy->var()->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
     __ push(r0);
-    EmitCallWithIC(expr, proxy->name(), proxy->var()->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT);
+    EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
   } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
     // Call to a lookup slot (dynamically introduced variable).
     Label slow, done;
index 2e1e6fa..2a80ff9 100644 (file)
@@ -3281,7 +3281,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
   ASSERT(ToRegister(instr->result()).is(r0));
 
   int arity = instr->arity();
-  RelocInfo::Mode mode = instr->qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT;
+  RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
   Handle<Code> ic =
       isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
   __ mov(r2, Operand(instr->name()));
index 18ecb2b..84149bd 100644 (file)
@@ -1140,7 +1140,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
   __ mov(eax, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
   __ mov(ecx, var->name());
   Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
-  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF || var->is_qml_global())
+  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
       ? RelocInfo::CODE_TARGET
       : RelocInfo::CODE_TARGET_CONTEXT;
   __ call(ic, mode);
@@ -1224,7 +1224,7 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
       __ mov(eax, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
       __ mov(ecx, var->name());
       Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
-      __ call(ic, var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT);
+      __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
       context()->Plug(eax);
       break;
     }
@@ -2166,7 +2166,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
   } else if (proxy != NULL && proxy->var()->IsUnallocated()) {
     // Push global object as receiver for the call IC.
     __ push(proxy->var()->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
-    EmitCallWithIC(expr, proxy->name(), proxy->var()->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT);
+    EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
 
   } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
     // Call to a lookup slot (dynamically introduced variable).
index b381227..2a23bf1 100644 (file)
@@ -3132,7 +3132,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
   ASSERT(ToRegister(instr->result()).is(eax));
 
   int arity = instr->arity();
-  RelocInfo::Mode mode = instr->qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT;
+  RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
   Handle<Code> ic =
       isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
   __ mov(ecx, instr->name());
index 81aa6b7..ee53f0f 100644 (file)
@@ -107,10 +107,16 @@ class IC {
   // object that contains this IC site.
   RelocInfo::Mode ComputeMode();
 
+  bool IsQmlGlobal(Handle<Object> receiver) {
+    JSObject* qml_global = isolate_->context()->qml_global();
+    return !qml_global->IsUndefined() && qml_global == *receiver;
+  }
+
   // Returns if this IC is for contextual (no explicit receiver)
   // access to properties.
   bool IsContextual(Handle<Object> receiver) {
-    if (receiver->IsGlobalObject()) {
+    if (receiver->IsGlobalObject() ||
+        IsQmlGlobal(receiver)) {
       return SlowIsContextual();
     } else {
       ASSERT(!SlowIsContextual());
index a4cfefd..5d0bc9a 100644 (file)
@@ -1191,7 +1191,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
 
   __ lw(a0, var->is_qml_global() ? QmlGlobalObjectOperand():GlobalObjectOperand());
   __ li(a2, Operand(var->name()));
-  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF || var->is_qml_global())
+  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
       ? RelocInfo::CODE_TARGET
       : RelocInfo::CODE_TARGET_CONTEXT;
   Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
@@ -1279,7 +1279,7 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
       __ lw(a0, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
       __ li(a2, Operand(var->name()));
       Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
-      __ Call(ic, var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT);
+      __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT);
       context()->Plug(v0);
       break;
     }
@@ -2296,7 +2296,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     // Push global object as receiver for the call IC.
     __ lw(a0, proxy->var()->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand());
     __ push(a0);
-    EmitCallWithIC(expr, proxy->name(), proxy->var()->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT);
+    EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
   } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
     // Call to a lookup slot (dynamically introduced variable).
     Label slow, done;
index 0b1181b..815e3b4 100644 (file)
@@ -1148,7 +1148,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
   __ movq(rax, var->is_qml_global() ? QmlGlobalObjectOperand() : GlobalObjectOperand());
   __ Move(rcx, var->name());
   Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
-  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF || var->is_qml_global())
+  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
       ? RelocInfo::CODE_TARGET
       : RelocInfo::CODE_TARGET_CONTEXT;
   __ call(ic, mode);
@@ -1232,7 +1232,7 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
       __ Move(rcx, var->name());
       __ movq(rax, var->is_qml_global() ? QmlGlobalObjectOperand() : GlobalObjectOperand());
       Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
-      __ call(ic, var->is_qml_global() ? RelocInfo::CODE_TARGET : RelocInfo::CODE_TARGET_CONTEXT);
+      __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
       context()->Plug(rax);
       break;
     }
@@ -2161,7 +2161,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     // Call to a global variable.  Push global object as receiver for the
     // call IC lookup.
     __ push(proxy->var()->is_qml_global() ? QmlGlobalObjectOperand() : GlobalObjectOperand());
-    EmitCallWithIC(expr, proxy->name(), proxy->var()->is_qml_global() ? RelocInfo::CODE_TARGET : RelocInfo::CODE_TARGET_CONTEXT);
+    EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
   } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
     // Call to a lookup slot (dynamically introduced variable).
     Label slow, done;
index 2a95fee..90e897b 100644 (file)
@@ -3050,7 +3050,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
 void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
   ASSERT(ToRegister(instr->result()).is(rax));
   int arity = instr->arity();
-  RelocInfo::Mode mode = instr->qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT;
+  RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
   Handle<Code> ic =
       isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
   __ Move(rcx, instr->name());
index 1a309f7..a131441 100644 (file)
@@ -60,6 +60,8 @@ private slots:
     void externalteardown();
     void globalcall();
     void getcallingqmlglobal();
+    void typeOf();
+    void referenceerror();
 };
 
 void tst_v8::eval()
@@ -92,6 +94,16 @@ void tst_v8::getcallingqmlglobal()
     QVERIFY(v8test_getcallingqmlglobal());
 }
 
+void tst_v8::typeOf()
+{
+    QVERIFY(v8test_typeof());
+}
+
+void tst_v8::referenceerror()
+{
+    QVERIFY(v8test_referenceerror());
+}
+
 int main(int argc, char *argv[])
 {
     V8::SetFlagsFromCommandLine(&argc, argv, true);
index d55e84e..95f8d73 100644 (file)
@@ -61,6 +61,9 @@ int main(int argc, char *argv[])
     RUN_TEST(userobjectcompare);
     RUN_TEST(externalteardown);
     RUN_TEST(globalcall);
+    RUN_TEST(getcallingqmlglobal);
+    RUN_TEST(typeof);
+    RUN_TEST(referenceerror);
 
     return -1;
 }
index db03883..f0d3aa8 100644 (file)
@@ -432,5 +432,66 @@ cleanup:
     ENDTEST();
 }
 
+bool v8test_typeof()
+{
+    BEGINTEST();
+
+    HandleScope handle_scope;
+    Persistent<Context> context = Context::New();
+    Context::Scope context_scope(context);
+
+    Local<Object> qmlglobal = Object::New();
+    qmlglobal->Set(String::New("a"), Integer::New(123));
+
+    Local<Script> script = Script::Compile(String::New("["
+                                                       "typeof a === 'number', "
+                                                       "typeof b === 'undefined', "
+                                                       "(function() { return typeof c === 'undefined'; })()"
+                                                       "]"), NULL, NULL,
+                                           Handle<String>(), Script::QmlMode);
+
+    TryCatch tc;
+    Local<Value> result = script->Run(qmlglobal);
+
+    VERIFY(!tc.HasCaught());
+    VERIFY(result->IsArray());
+    VERIFY(v8::Array::Cast(*result)->Length() == 3);
+    VERIFY(v8::Array::Cast(*result)->Get(0)->IsTrue());
+    VERIFY(v8::Array::Cast(*result)->Get(1)->IsTrue());
+    VERIFY(v8::Array::Cast(*result)->Get(2)->IsTrue());
+
+cleanup:
+    context.Dispose();
+
+    ENDTEST();
+}
+
+bool v8test_referenceerror()
+{
+    BEGINTEST();
+
+    HandleScope handle_scope;
+    Persistent<Context> context = Context::New();
+    Context::Scope context_scope(context);
+
+    Local<Object> qmlglobal = Object::New();
+
+    Local<Script> script = Script::Compile(String::New("a"), NULL, NULL,
+                                           Handle<String>(), Script::QmlMode);
+
+    TryCatch tc;
+    Local<Value> result = script->Run(qmlglobal);
+
+    VERIFY(tc.HasCaught());
+    VERIFY(result.IsEmpty());
+    VERIFY(tc.Exception()->IsError());
+    VERIFY(tc.Exception()->ToString()->Equals(v8::String::New("ReferenceError: a is not defined")));
+
+cleanup:
+    context.Dispose();
+
+    ENDTEST();
+}
+
 #undef VARNAME
 #undef VARVALUE
index b1989f9..38dfa62 100644 (file)
@@ -54,6 +54,8 @@ bool v8test_userobjectcompare();
 bool v8test_externalteardown();
 bool v8test_globalcall();
 bool v8test_getcallingqmlglobal();
+bool v8test_typeof();
+bool v8test_referenceerror();
 
 #endif // V8TEST_H