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>
__ 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();
__ 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;
}
// 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;
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()));
__ 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);
__ 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;
}
} 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).
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());
// 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());
__ 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();
__ 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;
}
// 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;
__ 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);
__ 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;
}
// 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;
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());
void externalteardown();
void globalcall();
void getcallingqmlglobal();
+ void typeOf();
+ void referenceerror();
};
void tst_v8::eval()
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);
RUN_TEST(userobjectcompare);
RUN_TEST(externalteardown);
RUN_TEST(globalcall);
+ RUN_TEST(getcallingqmlglobal);
+ RUN_TEST(typeof);
+ RUN_TEST(referenceerror);
return -1;
}
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
bool v8test_externalteardown();
bool v8test_globalcall();
bool v8test_getcallingqmlglobal();
+bool v8test_typeof();
+bool v8test_referenceerror();
#endif // V8TEST_H