From 017935408d82520c9c6e48962e1dee4b07084adb Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Mon, 30 May 2011 13:49:22 +0000 Subject: [PATCH] Reapply change to Pass undefined to JS builtins when called with implicit receiver. A couple of corner cases have to be treated specially to not break everything: eval and getter/setter definitions. R=fschneider@chromium.org BUG=v8:1365 TEST=mjsunit/regress/regress-1365.js Review URL: http://codereview.chromium.org/7085034 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8110 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/builtins-arm.cc | 6 ++-- src/arm/full-codegen-arm.cc | 10 +++--- src/arm/lithium-codegen-arm.cc | 10 +++--- src/compiler.cc | 6 +++- src/compiler.h | 14 ++++++-- src/handles.cc | 2 +- src/heap.cc | 2 +- src/hydrogen.cc | 9 ++--- src/ia32/builtins-ia32.cc | 8 ++--- src/ia32/full-codegen-ia32.cc | 10 +++--- src/ia32/lithium-codegen-ia32.cc | 10 +++--- src/mips/builtins-mips.cc | 6 ++-- src/objects-inl.h | 8 ++--- src/objects.h | 22 ++++++------ src/runtime.cc | 6 ++-- src/runtime.h | 4 +-- src/v8natives.js | 43 +++++++++++++++--------- src/x64/builtins-x64.cc | 8 ++--- src/x64/full-codegen-x64.cc | 10 +++--- src/x64/lithium-codegen-x64.cc | 2 +- test/cctest/test-api.cc | 2 +- test/mjsunit/regress/regress-1170.js | 11 +++--- test/mjsunit/regress/regress-124.js | 8 ++--- test/mjsunit/regress/regress-1365.js | 65 ++++++++++++++++++++++++++++++++++++ test/mjsunit/regress/regress-485.js | 5 --- test/mozilla/mozilla.status | 4 +++ test/sputnik/sputnik.status | 17 ++++++++++ 27 files changed, 206 insertions(+), 102 deletions(-) create mode 100644 test/mjsunit/regress/regress-1365.js diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 794b370..871b4d8 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -1257,8 +1257,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ b(ne, &shift_arguments); // Do not transform the receiver for native (Compilerhints already in r3). - __ tst(r3, Operand(1 << (SharedFunctionInfo::kES5Native + - kSmiTagSize))); + __ tst(r3, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); __ b(ne, &shift_arguments); // Compute the receiver in non-strict mode. @@ -1443,8 +1442,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ b(ne, &push_receiver); // Do not transform the receiver for strict mode functions. - __ tst(r2, Operand(1 << (SharedFunctionInfo::kES5Native + - kSmiTagSize))); + __ tst(r2, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); __ b(ne, &push_receiver); // Compute the receiver in non-strict mode. diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 6116513..98e2268 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -139,11 +139,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } #endif - // Strict mode functions need to replace the receiver with undefined - // when called as functions (without an explicit receiver - // object). r5 is zero for method calls and non-zero for function - // calls. - if (info->is_strict_mode()) { + // Strict mode functions and builtins need to replace the receiver + // with undefined when called as functions (without an explicit + // receiver object). r5 is zero for method calls and non-zero for + // function calls. + if (info->is_strict_mode() || info->is_native()) { Label ok; __ cmp(r5, Operand(0)); __ b(eq, &ok); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index a60d9b5..01f95a7 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -146,11 +146,11 @@ bool LCodeGen::GeneratePrologue() { // fp: Caller's frame pointer. // lr: Caller's pc. - // Strict mode functions need to replace the receiver with undefined - // when called as functions (without an explicit receiver - // object). r5 is zero for method calls and non-zero for function - // calls. - if (info_->is_strict_mode()) { + // Strict mode functions and builtins need to replace the receiver + // with undefined when called as functions (without an explicit + // receiver object). r5 is zero for method calls and non-zero for + // function calls. + if (info_->is_strict_mode() || info_->is_native()) { Label ok; __ cmp(r5, Operand(0)); __ b(eq, &ok); diff --git a/src/compiler.cc b/src/compiler.cc index a59de17..893f788 100755 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -508,7 +508,10 @@ Handle Compiler::Compile(Handle source, info.MarkAsGlobal(); info.SetExtension(extension); info.SetPreParseData(pre_data); - if (natives == NATIVES_CODE) info.MarkAsAllowingNativesSyntax(); + if (natives == NATIVES_CODE) { + info.MarkAsAllowingNativesSyntax(); + info.MarkAsNative(); + } result = MakeFunctionInfo(&info); if (extension == NULL && !result.is_null()) { compilation_cache->PutScript(source, result); @@ -677,6 +680,7 @@ Handle Compiler::BuildFunctionInfo(FunctionLiteral* literal, info.SetFunction(literal); info.SetScope(literal->scope()); if (literal->scope()->is_strict_mode()) info.MarkAsStrictMode(); + if (script->type()->value() == Script::TYPE_NATIVE) info.MarkAsNative(); LiveEditFunctionTracker live_edit_tracker(info.isolate(), literal); // Determine if the function can be lazily compiled. This is necessary to diff --git a/src/compiler.h b/src/compiler.h index 4eb33e2..4fc9ffe 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -89,6 +89,12 @@ class CompilationInfo BASE_EMBEDDED { bool allows_natives_syntax() const { return IsNativesSyntaxAllowed::decode(flags_); } + void MarkAsNative() { + flags_ |= IsNative::encode(true); + } + bool is_native() const { + return IsNative::decode(flags_); + } void SetFunction(FunctionLiteral* literal) { ASSERT(function_ == NULL); function_ = literal; @@ -167,8 +173,9 @@ class CompilationInfo BASE_EMBEDDED { void Initialize(Mode mode) { mode_ = V8::UseCrankshaft() ? mode : NONOPT; - if (!shared_info_.is_null() && shared_info_->strict_mode()) { - MarkAsStrictMode(); + if (!shared_info_.is_null()) { + if (shared_info_->strict_mode()) MarkAsStrictMode(); + if (shared_info_->native()) MarkAsNative(); } } @@ -191,6 +198,9 @@ class CompilationInfo BASE_EMBEDDED { class IsStrictMode: public BitField {}; // Native syntax (%-stuff) allowed? class IsNativesSyntaxAllowed: public BitField {}; + // Is this a function from our natives. + class IsNative: public BitField {}; + unsigned flags_; diff --git a/src/handles.cc b/src/handles.cc index b03b642..ef7133a 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: diff --git a/src/heap.cc b/src/heap.cc index 0a3b3d3..54359d6 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2388,7 +2388,7 @@ MaybeObject* Heap::AllocateSharedFunctionInfo(Object* name) { share->set_num_literals(0); share->set_end_position(0); share->set_function_token_position(0); - share->set_es5_native(false); + share->set_native(false); return result; } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index c76f7c4..a03ceb4 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -5959,10 +5959,11 @@ HEnvironment* HEnvironment::CopyForInlining( inner->SetValueAt(i, push); } } - // If the function we are inlining is a strict mode function, pass - // undefined as the receiver for function calls (instead of the - // global receiver). - if (function->strict_mode() && call_kind == CALL_AS_FUNCTION) { + // If the function we are inlining is a strict mode function or a + // builtin function, pass undefined as the receiver for function + // calls (instead of the global receiver). + if ((target->shared()->native() || function->strict_mode()) && + call_kind == CALL_AS_FUNCTION) { inner->SetValueAt(0, undefined); } inner->SetValueAt(arity + 1, outer->LookupContext()); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 1212566..d0b563e 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -615,8 +615,8 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ j(not_equal, &shift_arguments); // Do not transform the receiver for natives (shared already in ebx). - __ test_b(FieldOperand(ebx, SharedFunctionInfo::kES5NativeByteOffset), - 1 << SharedFunctionInfo::kES5NativeBitWithinByte); + __ test_b(FieldOperand(ebx, SharedFunctionInfo::kNativeByteOffset), + 1 << SharedFunctionInfo::kNativeBitWithinByte); __ j(not_equal, &shift_arguments); // Compute the receiver in non-strict mode. @@ -780,8 +780,8 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { Factory* factory = masm->isolate()->factory(); // Do not transform the receiver for natives (shared already in ecx). - __ test_b(FieldOperand(ecx, SharedFunctionInfo::kES5NativeByteOffset), - 1 << SharedFunctionInfo::kES5NativeBitWithinByte); + __ test_b(FieldOperand(ecx, SharedFunctionInfo::kNativeByteOffset), + 1 << SharedFunctionInfo::kNativeBitWithinByte); __ j(not_equal, &push_receiver); // Compute the receiver in non-strict mode. diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 5f0a0b6..2e0b722 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -131,11 +131,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } #endif - // Strict mode functions need to replace the receiver with undefined - // when called as functions (without an explicit receiver - // object). ecx is zero for method calls and non-zero for function - // calls. - if (info->is_strict_mode()) { + // Strict mode functions and builtins need to replace the receiver + // with undefined when called as functions (without an explicit + // receiver object). ecx is zero for method calls and non-zero for + // function calls. + if (info->is_strict_mode() || info->is_native()) { Label ok; __ test(ecx, Operand(ecx)); __ j(zero, &ok, Label::kNear); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 7cc2774..19df65c 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -128,11 +128,11 @@ bool LCodeGen::GeneratePrologue() { } #endif - // Strict mode functions need to replace the receiver with undefined - // when called as functions (without an explicit receiver - // object). ecx is zero for method calls and non-zero for function - // calls. - if (info_->is_strict_mode()) { + // Strict mode functions and builtins need to replace the receiver + // with undefined when called as functions (without an explicit + // receiver object). ecx is zero for method calls and non-zero for + // function calls. + if (info_->is_strict_mode() || info_->is_native()) { Label ok; __ test(ecx, Operand(ecx)); __ j(zero, &ok, Label::kNear); diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index e22259d..857fc12 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -1214,8 +1214,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ Branch(&shift_arguments, ne, t0, Operand(zero_reg)); // Do not transform the receiver for native (Compilerhints already in a3). - __ And(t0, a3, Operand(1 << (SharedFunctionInfo::kES5Native + - kSmiTagSize))); + __ And(t0, a3, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); __ Branch(&shift_arguments, ne, t0, Operand(zero_reg)); // Compute the receiver in non-strict mode. @@ -1401,8 +1400,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ Branch(&push_receiver, ne, t0, Operand(zero_reg)); // Do not transform the receiver for native (Compilerhints already in a2). - __ And(t0, a2, Operand(1 << (SharedFunctionInfo::kES5Native + - kSmiTagSize))); + __ And(t0, a2, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); __ Branch(&push_receiver, ne, t0, Operand(zero_reg)); // Compute the receiver in non-strict mode. diff --git a/src/objects-inl.h b/src/objects-inl.h index c8fcd02..22af01e 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3309,14 +3309,14 @@ void SharedFunctionInfo::set_strict_mode(bool value) { } -bool SharedFunctionInfo::es5_native() { - return BooleanBit::get(compiler_hints(), kES5Native); +bool SharedFunctionInfo::native() { + return BooleanBit::get(compiler_hints(), kNative); } -void SharedFunctionInfo::set_es5_native(bool value) { +void SharedFunctionInfo::set_native(bool value) { set_compiler_hints(BooleanBit::set(compiler_hints(), - kES5Native, + kNative, value)); } diff --git a/src/objects.h b/src/objects.h index 4c0e6ed..3cbbee3 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4406,12 +4406,12 @@ class SharedFunctionInfo: public HeapObject { inline bool strict_mode(); inline void set_strict_mode(bool value); - // Indicates whether the function is a native ES5 function. + // Indicates whether the function is a native function. // These needs special threatment in .call and .apply since // null passed as the receiver should not be translated to the // global object. - inline bool es5_native(); - inline void set_es5_native(bool value); + inline bool native(); + inline void set_native(bool value); // Indicates whether or not the code in the shared function support // deoptimization. @@ -4598,7 +4598,7 @@ class SharedFunctionInfo: public HeapObject { static const int kCodeAgeMask = 0x7; static const int kOptimizationDisabled = 6; static const int kStrictModeFunction = 7; - static const int kES5Native = 8; + static const int kNative = 8; private: #if V8_HOST_ARCH_32_BIT @@ -4613,26 +4613,26 @@ class SharedFunctionInfo: public HeapObject { public: // Constants for optimizing codegen for strict mode function and - // es5 native tests. + // native tests. // Allows to use byte-widgh instructions. static const int kStrictModeBitWithinByte = (kStrictModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte; - static const int kES5NativeBitWithinByte = - (kES5Native + kCompilerHintsSmiTagSize) % kBitsPerByte; + static const int kNativeBitWithinByte = + (kNative + kCompilerHintsSmiTagSize) % kBitsPerByte; #if __BYTE_ORDER == __LITTLE_ENDIAN static const int kStrictModeByteOffset = kCompilerHintsOffset + (kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte; - static const int kES5NativeByteOffset = kCompilerHintsOffset + - (kES5Native + kCompilerHintsSmiTagSize) / kBitsPerByte; + static const int kNativeByteOffset = kCompilerHintsOffset + + (kNative + kCompilerHintsSmiTagSize) / kBitsPerByte; #elif __BYTE_ORDER == __BIG_ENDIAN static const int kStrictModeByteOffset = kCompilerHintsOffset + (kCompilerHintsSize - 1) - ((kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte); - static const int kES5NativeByteOffset = kCompilerHintsOffset + + static const int kNativeByteOffset = kCompilerHintsOffset + (kCompilerHintsSize - 1) - - ((kES5Native + kCompilerHintsSmiTagSize) / kBitsPerByte); + ((kNative + kCompilerHintsSmiTagSize) / kBitsPerByte); #else #error Unknown byte ordering #endif diff --git a/src/runtime.cc b/src/runtime.cc index 705555f..1b8abf0 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -4117,10 +4117,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) { } -// Set the ES5 native flag on the function. +// Set the native flag on the function. // This is used to decide if we should transform null and undefined // into the global object when doing call and apply. -RUNTIME_FUNCTION(MaybeObject*, Runtime_SetES5Flag) { +RUNTIME_FUNCTION(MaybeObject*, Runtime_SetNativeFlag) { NoHandleAllocation ha; RUNTIME_ASSERT(args.length() == 1); @@ -4128,7 +4128,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetES5Flag) { if (object->IsJSFunction()) { JSFunction* func = JSFunction::cast(*object); - func->shared()->set_es5_native(true); + func->shared()->set_native(true); } return isolate->heap()->undefined_value(); } diff --git a/src/runtime.h b/src/runtime.h index 0d776b6..0a0afc0 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -92,7 +92,7 @@ namespace internal { F(CompileForOnStackReplacement, 1, 1) \ F(SetNewFunctionAttributes, 1, 1) \ F(AllocateInNewSpace, 1, 1) \ - F(SetES5Flag, 1, 1) \ + F(SetNativeFlag, 1, 1) \ \ /* Array join support */ \ F(PushIfAbsent, 2, 1) \ diff --git a/src/v8natives.js b/src/v8natives.js index d22e94d..469059b 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -56,7 +56,7 @@ function InstallFunctions(object, attributes, functions) { %FunctionSetName(f, key); %FunctionRemovePrototype(f); %SetProperty(object, key, f, attributes); - %SetES5Flag(f); + %SetNativeFlag(f); } %ToFastProperties(object); } @@ -132,10 +132,19 @@ function GlobalParseFloat(string) { function GlobalEval(x) { if (!IS_STRING(x)) return x; + var receiver = this; var global_receiver = %GlobalReceiver(global); - var this_is_global_receiver = (this === global_receiver); + + if (receiver == null && !IS_UNDETECTABLE(receiver)) { + receiver = global_receiver; + } + + var this_is_global_receiver = (receiver === global_receiver); var global_is_detached = (global === global_receiver); + // For consistency with JSC we require the global object passed to + // eval to be the global object from which 'eval' originated. This + // is not mandated by the spec. if (!this_is_global_receiver || global_is_detached) { throw new $EvalError('The "this" object passed to eval must ' + 'be the global object from which eval originated'); @@ -144,7 +153,7 @@ function GlobalEval(x) { var f = %CompileString(x); if (!IS_FUNCTION(f)) return f; - return %_CallFunction(this, f); + return %_CallFunction(receiver, f); } @@ -246,8 +255,9 @@ function ObjectPropertyIsEnumerable(V) { // Extensions for providing property getters and setters. function ObjectDefineGetter(name, fun) { - if (this == null && !IS_UNDETECTABLE(this)) { - throw new $TypeError('Object.prototype.__defineGetter__: this is Null'); + var receiver = this; + if (receiver == null && !IS_UNDETECTABLE(receiver)) { + receiver = %GlobalReceiver(global); } if (!IS_FUNCTION(fun)) { throw new $TypeError('Object.prototype.__defineGetter__: Expecting function'); @@ -256,21 +266,23 @@ function ObjectDefineGetter(name, fun) { desc.setGet(fun); desc.setEnumerable(true); desc.setConfigurable(true); - DefineOwnProperty(ToObject(this), ToString(name), desc, false); + DefineOwnProperty(ToObject(receiver), ToString(name), desc, false); } function ObjectLookupGetter(name) { - if (this == null && !IS_UNDETECTABLE(this)) { - throw new $TypeError('Object.prototype.__lookupGetter__: this is Null'); + var receiver = this; + if (receiver == null && !IS_UNDETECTABLE(receiver)) { + receiver = %GlobalReceiver(global); } - return %LookupAccessor(ToObject(this), ToString(name), GETTER); + return %LookupAccessor(ToObject(receiver), ToString(name), GETTER); } function ObjectDefineSetter(name, fun) { - if (this == null && !IS_UNDETECTABLE(this)) { - throw new $TypeError('Object.prototype.__defineSetter__: this is Null'); + var receiver = this; + if (receiver == null && !IS_UNDETECTABLE(receiver)) { + receiver = %GlobalReceiver(global); } if (!IS_FUNCTION(fun)) { throw new $TypeError( @@ -280,15 +292,16 @@ function ObjectDefineSetter(name, fun) { desc.setSet(fun); desc.setEnumerable(true); desc.setConfigurable(true); - DefineOwnProperty(ToObject(this), ToString(name), desc, false); + DefineOwnProperty(ToObject(receiver), ToString(name), desc, false); } function ObjectLookupSetter(name) { - if (this == null && !IS_UNDETECTABLE(this)) { - throw new $TypeError('Object.prototype.__lookupSetter__: this is Null'); + var receiver = this; + if (receiver == null && !IS_UNDETECTABLE(receiver)) { + receiver = %GlobalReceiver(global); } - return %LookupAccessor(ToObject(this), ToString(name), SETTER); + return %LookupAccessor(ToObject(receiver), ToString(name), SETTER); } diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index fc4581c..62f2b57 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -675,8 +675,8 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // Do not transform the receiver for natives. // SharedFunctionInfo is already loaded into rbx. - __ testb(FieldOperand(rbx, SharedFunctionInfo::kES5NativeByteOffset), - Immediate(1 << SharedFunctionInfo::kES5NativeBitWithinByte)); + __ testb(FieldOperand(rbx, SharedFunctionInfo::kNativeByteOffset), + Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte)); __ j(not_zero, &shift_arguments); // Compute the receiver in non-strict mode. @@ -847,8 +847,8 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ j(not_equal, &push_receiver); // Do not transform the receiver for natives. - __ testb(FieldOperand(rdx, SharedFunctionInfo::kES5NativeByteOffset), - Immediate(1 << SharedFunctionInfo::kES5NativeBitWithinByte)); + __ testb(FieldOperand(rdx, SharedFunctionInfo::kNativeByteOffset), + Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte)); __ j(not_zero, &push_receiver); // Compute the receiver in non-strict mode. diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 57baf77..e1bb4c0 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -131,11 +131,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } #endif - // Strict mode functions need to replace the receiver with undefined - // when called as functions (without an explicit receiver - // object). rcx is zero for method calls and non-zero for function - // calls. - if (info->is_strict_mode()) { + // Strict mode functions and builtins need to replace the receiver + // with undefined when called as functions (without an explicit + // receiver object). rcx is zero for method calls and non-zero for + // function calls. + if (info->is_strict_mode() || info->is_native()) { Label ok; __ testq(rcx, rcx); __ j(zero, &ok, Label::kNear); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 983c75a..fe71ff5 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -144,7 +144,7 @@ bool LCodeGen::GeneratePrologue() { // when called as functions (without an explicit receiver // object). rcx is zero for method calls and non-zero for function // calls. - if (info_->is_strict_mode()) { + if (info_->is_strict_mode() || info_->is_native()) { Label ok; __ testq(rcx, rcx); __ j(zero, &ok, Label::kNear); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 1121210..34d70b6 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -6664,7 +6664,7 @@ THREADED_TEST(ShadowObject) { context->Global()->Set(v8_str("__proto__"), o); Local value = - Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); + Script::Compile(v8_str("this.propertyIsEnumerable(0)"))->Run(); CHECK(value->IsBoolean()); CHECK(!value->BooleanValue()); diff --git a/test/mjsunit/regress/regress-1170.js b/test/mjsunit/regress/regress-1170.js index 8a5a9cf..2ed7458 100644 --- a/test/mjsunit/regress/regress-1170.js +++ b/test/mjsunit/regress/regress-1170.js @@ -30,18 +30,18 @@ var setter_value = 0; __proto__.__defineSetter__("a", function(v) { setter_value = v; }); eval("var a = 1"); assertEquals(1, setter_value); -assertFalse(hasOwnProperty("a")); +assertFalse(this.hasOwnProperty("a")); eval("with({}) { eval('var a = 2') }"); assertEquals(2, setter_value); -assertFalse(hasOwnProperty("a")); +assertFalse(this.hasOwnProperty("a")); // Function declarations are treated specially to match Safari. We do // not call setters for them. eval("function a() {}"); -assertTrue(hasOwnProperty("a")); +assertTrue(this.hasOwnProperty("a")); -__proto__.__defineSetter__("b", function(v) { assertUnreachable(); }); +__proto__.__defineSetter__("b", function(v) { assertUnreachable(); }); try { eval("const b = 23"); assertUnreachable(); @@ -61,6 +61,5 @@ try { assertUnreachable(); } catch(e) { assertEquals(42, e); - assertFalse(hasOwnProperty("c")); + assertFalse(this.hasOwnProperty("c")); } - diff --git a/test/mjsunit/regress/regress-124.js b/test/mjsunit/regress/regress-124.js index 0b3aae5..e0df6f5 100644 --- a/test/mjsunit/regress/regress-124.js +++ b/test/mjsunit/regress/regress-124.js @@ -26,13 +26,13 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. assertEquals("[object global]", this.toString()); -assertEquals("[object global]", toString()); +assertEquals("[object Undefined]", toString()); assertEquals("[object global]", eval("this.toString()")); -assertEquals("[object global]", eval("toString()")); +assertEquals("[object Undefined]", eval("toString()")); assertEquals("[object global]", eval("var f; this.toString()")); -assertEquals("[object global]", eval("var f; toString()")); +assertEquals("[object Undefined]", eval("var f; toString()")); function F(f) { @@ -45,7 +45,7 @@ function F(f) { assertEquals("[object global]", eval("var f; this.toString()")); assertEquals("[object global]", eval("var f; toString()")); - assertEquals("[object global]", eval("f()")); + assertEquals("[object Undefined]", eval("f()")); // Receiver should be the arguments object here. assertEquals("[object Arguments]", eval("arguments[0]()")); diff --git a/test/mjsunit/regress/regress-1365.js b/test/mjsunit/regress/regress-1365.js new file mode 100644 index 0000000..f19bdd0 --- /dev/null +++ b/test/mjsunit/regress/regress-1365.js @@ -0,0 +1,65 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See: http://code.google.com/p/v8/issues/detail?id=1365 + +// Check that builtin methods are passed undefined as the receiver +// when called as functions through variables. + +// Flags: --allow-natives-syntax + +// Global variable. +var valueOf = Object.prototype.valueOf; +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function callGlobalValueOf() { valueOf(); } +function callGlobalHasOwnProperty() { valueOf(); } + +assertEquals(Object.prototype, Object.prototype.valueOf()); +assertThrows(callGlobalValueOf); +assertThrows(callGlobalHasOwnProperty); + +%OptimizeFunctionOnNextCall(Object.prototype.valueOf); +Object.prototype.valueOf(); + +assertEquals(Object.prototype, Object.prototype.valueOf()); +assertThrows(callGlobalValueOf); +assertThrows(callGlobalHasOwnProperty); + +function CheckExceptionCallLocal() { + var valueOf = Object.prototype.valueOf; + var hasOwnProperty = Object.prototype.hasOwnProperty; + try { valueOf(); assertUnreachable(); } catch(e) { } + try { hasOwnProperty(); assertUnreachable(); } catch(e) { } +} +CheckExceptionCallLocal(); + +function CheckExceptionCallParameter(f) { + try { f(); assertUnreachable(); } catch(e) { } +} +CheckExceptionCallParameter(Object.prototype.valueOf); +CheckExceptionCallParameter(Object.prototype.hasOwnProperty); diff --git a/test/mjsunit/regress/regress-485.js b/test/mjsunit/regress/regress-485.js index e5182b3..f26e0eb 100755 --- a/test/mjsunit/regress/regress-485.js +++ b/test/mjsunit/regress/regress-485.js @@ -34,11 +34,6 @@ var global = this; var global2 = (function(){return this;})(); assertEquals(global, global2, "direct call to local function returns global"); -var builtin = Object.prototype.valueOf; // Builtin function that returns this. - -assertEquals(global, builtin(), "Direct call to builtin"); - - // Builtin that depends on value of this to compute result. var builtin2 = Object.prototype.toString; diff --git a/test/mozilla/mozilla.status b/test/mozilla/mozilla.status index 44b5577..09937f7 100644 --- a/test/mozilla/mozilla.status +++ b/test/mozilla/mozilla.status @@ -206,6 +206,10 @@ ecma_3/RegExp/regress-85721: PASS || FAIL if $mode == debug # This section is for tests that fail in both V8 and JSC. Thus they # have been determined to be incompatible between Mozilla and V8/JSC. +# Fail because it calls builtins as functions and do not expect the +# builtin to have undefined as the receiver. +ecma/String/15.5.4.6-2: FAIL_OK + # Fail because of toLowerCase and toUpperCase conversion. ecma/String/15.5.4.11-2: FAIL_OK ecma/String/15.5.4.11-5: FAIL_OK diff --git a/test/sputnik/sputnik.status b/test/sputnik/sputnik.status index 472db6e..aeb85b0 100644 --- a/test/sputnik/sputnik.status +++ b/test/sputnik/sputnik.status @@ -175,6 +175,23 @@ S11.1.5_A4.2: FAIL_OK S9.9_A1: FAIL_OK S9.9_A2: FAIL_OK +# Calls builtins without an explicit receiver which means that +# undefined is passed to the builtin. The tests expect the global +# object to be passed which was true in ES3 but not in ES5. +S11.1.1_A2: FAIL_OK +S15.5.4.4_A1_T3: FAIL_OK +S15.5.4.5_A1_T3: FAIL_OK +S15.5.4.6_A1_T3: FAIL_OK +S15.5.4.7_A1_T3: FAIL_OK +S15.5.4.8_A1_T3: FAIL_OK +S15.5.4.9_A1_T3: FAIL_OK +S15.5.4.10_A1_T3: FAIL_OK +S15.5.4.11_A1_T3: FAIL_OK +S15.5.4.12_A1_T3: FAIL_OK +S15.5.4.13_A1_T3: FAIL_OK +S15.5.4.14_A1_T3: FAIL_OK +S15.5.4.15_A1_T3: FAIL_OK + ##################### SKIPPED TESTS ##################### # These tests take a looong time to run in debug mode. -- 2.7.4