Ensure correct boxing of values when calling functions on them
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 15 Jan 2010 13:42:32 +0000 (13:42 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 15 Jan 2010 13:42:32 +0000 (13:42 +0000)
When a function is called with a value type as the receiver this is now boxed as an object.

This is a low-impact solution where the receiver is only boxed when required. For IC calls to the V8 builtins values are not boxed and as most of the functions on String.prototype, Number.prototype and Boolean.prototype are sitting there most IC calls on values will not need any boxing of the receiver.

For calls which are not IC calls but calls through the CallFunctionStub a flag is used to determine whether the receiver might be a value and only when that is the case will the receiver be boxed.

No changtes to Function.call and Function.apply - they already boxed values. According to the ES5 spec the receiver should not be boxed for these functions, but current browsers have not adopted that change yet.

BUG=223
TEST=test/mjsunit/value-wrapper.js
TEST=test/mjsunit/regress/regress-crbug-3184.js
Review URL: http://codereview.chromium.org/542087

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3617 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

20 files changed:
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/arm/fast-codegen-arm.cc
src/arm/stub-cache-arm.cc
src/codegen.h
src/debug.cc
src/globals.h
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/ia32/fast-codegen-ia32.cc
src/ia32/stub-cache-ia32.cc
src/ic.cc
src/ic.h
src/x64/codegen-x64.cc
src/x64/codegen-x64.h
src/x64/fast-codegen-x64.cc
src/x64/stub-cache-x64.cc
test/mjsunit/bugs/bug-223.js [deleted file]
test/mjsunit/regress/regress-crbug-3184.js [new file with mode: 0644]
test/mjsunit/value-wrapper.js [new file with mode: 0644]

index 70d8ab49550f57150c8e5bd9b0979e574c9d94b5..9841405d45d52769836ac8cf77417a82646c58bd 100644 (file)
@@ -1091,7 +1091,8 @@ void CodeGenerator::Comparison(Condition cc,
 
 // Call the function on the stack with the given arguments.
 void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
-                                         int position) {
+                                      CallFunctionFlags flags,
+                                      int position) {
   VirtualFrame::SpilledScope spilled_scope;
   // Push the arguments ("left-to-right") on the stack.
   int arg_count = args->length();
@@ -1104,7 +1105,7 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
 
   // Use the shared code stub to call the function.
   InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
-  CallFunctionStub call_function(arg_count, in_loop);
+  CallFunctionStub call_function(arg_count, in_loop, flags);
   frame_->CallStub(&call_function, arg_count + 1);
 
   // Restore context and pop function from the stack.
@@ -2999,7 +3000,7 @@ void CodeGenerator::VisitCall(Call* node) {
     CodeForSourcePosition(node->position());
 
     InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
-    CallFunctionStub call_function(arg_count, in_loop);
+    CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
     frame_->CallStub(&call_function, arg_count + 1);
 
     __ ldr(cp, frame_->Context());
@@ -3056,7 +3057,7 @@ void CodeGenerator::VisitCall(Call* node) {
     frame_->EmitPush(r1);  // receiver
 
     // Call the function.
-    CallWithArguments(args, node->position());
+    CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
     frame_->EmitPush(r0);
 
   } else if (property != NULL) {
@@ -3109,7 +3110,7 @@ void CodeGenerator::VisitCall(Call* node) {
       }
 
       // Call the function.
-      CallWithArguments(args, node->position());
+      CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
       frame_->EmitPush(r0);
     }
 
@@ -3125,7 +3126,7 @@ void CodeGenerator::VisitCall(Call* node) {
     LoadGlobalReceiver(r0);
 
     // Call the function.
-    CallWithArguments(args, node->position());
+    CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
     frame_->EmitPush(r0);
   }
   ASSERT(frame_->height() == original_height + 1);
@@ -6601,6 +6602,33 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
 
 void CallFunctionStub::Generate(MacroAssembler* masm) {
   Label slow;
+
+  // If the receiver might be a value (string, number or boolean) check for this
+  // and box it if it is.
+  if (ReceiverMightBeValue()) {
+    // Get the receiver from the stack.
+    // function, receiver [, arguments]
+    Label receiver_is_value, receiver_is_js_object;
+    __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
+
+    // Check if receiver is a smi (which is a number value).
+    __ BranchOnSmi(r1, &receiver_is_value);
+
+    // Check if the receiver is a valid JS object.
+    __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
+    __ b(ge, &receiver_is_js_object);
+
+    // Call the runtime to box the value.
+    __ bind(&receiver_is_value);
+    __ EnterInternalFrame();
+    __ push(r1);
+    __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
+    __ LeaveInternalFrame();
+    __ str(r0, MemOperand(sp, argc_ * kPointerSize));
+
+    __ bind(&receiver_is_js_object);
+  }
+
   // Get the function to call from the stack.
   // function, receiver [, arguments]
   __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
index b62bc36d70574323219ad957a22dbe0306016414..f5de0ebcfb6d8266b34457bc7a4d80624237169f 100644 (file)
@@ -304,7 +304,9 @@ class CodeGenerator: public AstVisitor {
                     bool reversed,
                     OverwriteMode mode);
 
-  void CallWithArguments(ZoneList<Expression*>* arguments, int position);
+  void CallWithArguments(ZoneList<Expression*>* arguments,
+                         CallFunctionFlags flags,
+                         int position);
 
   // Control flow
   void Branch(bool if_true, JumpTarget* target);
@@ -432,27 +434,6 @@ class CodeGenerator: public AstVisitor {
 };
 
 
-class CallFunctionStub: public CodeStub {
- public:
-  CallFunctionStub(int argc, InLoopFlag in_loop)
-      : argc_(argc), in_loop_(in_loop) {}
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  int argc_;
-  InLoopFlag in_loop_;
-
-#if defined(DEBUG)
-  void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); }
-#endif  // defined(DEBUG)
-
-  Major MajorKey() { return CallFunction; }
-  int MinorKey() { return argc_; }
-  InLoopFlag InLoop() { return in_loop_; }
-};
-
-
 class GenericBinaryOpStub : public CodeStub {
  public:
   GenericBinaryOpStub(Token::Value op,
index dbfbc05e0e08d87948d00d642ff8b419acc41d51..1b6ec045795a9405d93dce6baf75fd205cdedb0a 100644 (file)
@@ -1073,7 +1073,7 @@ void FastCodeGenerator::EmitCallWithStub(Call* expr) {
   }
   // Record source position for debugger.
   SetSourcePosition(expr->position());
-  CallFunctionStub stub(arg_count, NOT_IN_LOOP);
+  CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
   __ CallStub(&stub);
   // Restore context register.
   __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
index 958842d2c8645213cff47fb0bf340722d0981f56..687fb1e73dc31b3e855f3c643984b55b6a950807 100644 (file)
@@ -637,50 +637,65 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
       break;
 
     case STRING_CHECK:
-      // Check that the object is a two-byte string or a symbol.
-      __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
-      __ b(hs, &miss);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::STRING_FUNCTION_INDEX,
-                                          r2);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
-                      r1, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        // Check that the object is a two-byte string or a symbol.
+        __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
+        __ b(hs, &miss);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::STRING_FUNCTION_INDEX,
+                                            r2);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
+                        r1, name, &miss);
+      }
       break;
 
     case NUMBER_CHECK: {
-      Label fast;
-      // Check that the object is a smi or a heap number.
-      __ tst(r1, Operand(kSmiTagMask));
-      __ b(eq, &fast);
-      __ CompareObjectType(r1, r2, r2, HEAP_NUMBER_TYPE);
-      __ b(ne, &miss);
-      __ bind(&fast);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::NUMBER_FUNCTION_INDEX,
-                                          r2);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
-                      r1, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        Label fast;
+        // Check that the object is a smi or a heap number.
+        __ tst(r1, Operand(kSmiTagMask));
+        __ b(eq, &fast);
+        __ CompareObjectType(r1, r2, r2, HEAP_NUMBER_TYPE);
+        __ b(ne, &miss);
+        __ bind(&fast);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::NUMBER_FUNCTION_INDEX,
+                                            r2);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
+                        r1, name, &miss);
+      }
       break;
     }
 
     case BOOLEAN_CHECK: {
-      Label fast;
-      // Check that the object is a boolean.
-      __ LoadRoot(ip, Heap::kTrueValueRootIndex);
-      __ cmp(r1, ip);
-      __ b(eq, &fast);
-      __ LoadRoot(ip, Heap::kFalseValueRootIndex);
-      __ cmp(r1, ip);
-      __ b(ne, &miss);
-      __ bind(&fast);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::BOOLEAN_FUNCTION_INDEX,
-                                          r2);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
-                      r1, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        Label fast;
+        // Check that the object is a boolean.
+        __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+        __ cmp(r1, ip);
+        __ b(eq, &fast);
+        __ LoadRoot(ip, Heap::kFalseValueRootIndex);
+        __ cmp(r1, ip);
+        __ b(ne, &miss);
+        __ bind(&fast);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::BOOLEAN_FUNCTION_INDEX,
+                                            r2);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
+                        r1, name, &miss);
+      }
       break;
     }
 
index 2247c5c80a4f19b3a1f2f6f7f5c39862f90a4f37..76cc4914e7bd028323463611c4a98d8fa02ac010 100644 (file)
@@ -516,6 +516,64 @@ class RegExpExecStub: public CodeStub {
 };
 
 
+class CallFunctionStub: public CodeStub {
+ public:
+  CallFunctionStub(int argc, InLoopFlag in_loop, CallFunctionFlags flags)
+      : argc_(argc), in_loop_(in_loop), flags_(flags) { }
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  int argc_;
+  InLoopFlag in_loop_;
+  CallFunctionFlags flags_;
+
+#ifdef DEBUG
+  void Print() {
+    PrintF("CallFunctionStub (args %d, in_loop %d, flags %d)\n",
+           argc_,
+           static_cast<int>(in_loop_),
+           static_cast<int>(flags_));
+  }
+#endif
+
+  // Minor key encoding in 31 bits AAAAAAAAAAAAAAAAAAAAAFI A(rgs)F(lag)I(nloop).
+  class InLoopBits: public BitField<InLoopFlag, 0, 1> {};
+  class FlagBits: public BitField<CallFunctionFlags, 1, 1> {};
+  class ArgcBits: public BitField<int, 2, 29> {};
+
+  Major MajorKey() { return CallFunction; }
+  int MinorKey() {
+    // Encode the parameters in a unique 31 bit value.
+    return InLoopBits::encode(in_loop_)
+           | FlagBits::encode(flags_)
+           | ArgcBits::encode(argc_);
+  }
+
+  InLoopFlag InLoop() { return in_loop_; }
+  bool ReceiverMightBeValue() {
+    return (flags_ & RECEIVER_MIGHT_BE_VALUE) != 0;
+  }
+
+ public:
+  static int ExtractArgcFromMinorKey(int minor_key) {
+    return ArgcBits::decode(minor_key);
+  }
+};
+
+
+class ToBooleanStub: public CodeStub {
+ public:
+  ToBooleanStub() { }
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  Major MajorKey() { return ToBoolean; }
+  int MinorKey() { return 0; }
+};
+
+
 }  // namespace internal
 }  // namespace v8
 
index fbe09391e27fabe4c9934adb79463a57288565ab..c869d16f88c607bf922cf935f1e93f00932ea5be 100644 (file)
@@ -1241,12 +1241,14 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
       uint32_t key = Smi::cast(*obj)->value();
       // Argc in the stub is the number of arguments passed - not the
       // expected arguments of the called function.
-      int call_function_arg_count = CodeStub::MinorKeyFromKey(key);
+      int call_function_arg_count =
+          CallFunctionStub::ExtractArgcFromMinorKey(
+              CodeStub::MinorKeyFromKey(key));
       ASSERT(call_function_stub->major_key() ==
              CodeStub::MajorKeyFromKey(key));
 
       // Find target function on the expression stack.
-      // Expression stack lools like this (top to bottom):
+      // Expression stack looks like this (top to bottom):
       // argN
       // ...
       // arg0
index a2cd2e454fa7363aace5da2208d711d5191c22f0..f5cb1c093c1b2013c53311909f80c08f742ff15d 100644 (file)
@@ -379,6 +379,12 @@ enum InLoopFlag {
 };
 
 
+enum CallFunctionFlags {
+  NO_CALL_FUNCTION_FLAGS = 0,
+  RECEIVER_MIGHT_BE_VALUE = 1 << 0  // Receiver might not be a JSObject.
+};
+
+
 // Type of properties.
 // Order of properties is significant.
 // Must fit in the BitField PropertyDetails::TypeField.
index 993675ba652ae20dbd0537672c69afe96262f4f6..240f4da3bd2da3e0f28046e0772fbd9539e7c0a3 100644 (file)
@@ -2289,6 +2289,7 @@ void CodeGenerator::Comparison(AstNode* node,
 // Call the function just below TOS on the stack with the given
 // arguments. The receiver is the TOS.
 void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
+                                      CallFunctionFlags flags,
                                       int position) {
   // Push the arguments ("left-to-right") on the stack.
   int arg_count = args->length();
@@ -2301,7 +2302,7 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
 
   // Use the shared code stub to call the function.
   InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
-  CallFunctionStub call_function(arg_count, in_loop);
+  CallFunctionStub call_function(arg_count, in_loop, flags);
   Result answer = frame_->CallStub(&call_function, arg_count + 1);
   // Restore context and replace function on the stack with the
   // result of the stub invocation.
@@ -2471,7 +2472,7 @@ void CodeGenerator::CallApplyLazy(Property* apply,
   frame_->Push(&fn);
   frame_->Push(&a1);
   frame_->Push(&a2);
-  CallFunctionStub call_function(2, NOT_IN_LOOP);
+  CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
   Result res = frame_->CallStub(&call_function, 3);
   frame_->Push(&res);
 
@@ -4746,7 +4747,7 @@ void CodeGenerator::VisitCall(Call* node) {
     // Call the function.
     CodeForSourcePosition(node->position());
     InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
-    CallFunctionStub call_function(arg_count, in_loop);
+    CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
     result = frame_->CallStub(&call_function, arg_count + 1);
 
     // Restore the context and overwrite the function on the stack with
@@ -4806,7 +4807,7 @@ void CodeGenerator::VisitCall(Call* node) {
     frame_->EmitPush(edx);
 
     // Call the function.
-    CallWithArguments(args, node->position());
+    CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
 
   } else if (property != NULL) {
     // Check if the key is a literal string.
@@ -4872,7 +4873,7 @@ void CodeGenerator::VisitCall(Call* node) {
       }
 
       // Call the function.
-      CallWithArguments(args, node->position());
+      CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
     }
 
   } else {
@@ -4887,7 +4888,7 @@ void CodeGenerator::VisitCall(Call* node) {
     LoadGlobalReceiver();
 
     // Call the function.
-    CallWithArguments(args, node->position());
+    CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
   }
 }
 
@@ -8681,6 +8682,33 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
 void CallFunctionStub::Generate(MacroAssembler* masm) {
   Label slow;
 
+  // If the receiver might be a value (string, number or boolean) check for this
+  // and box it if it is.
+  if (ReceiverMightBeValue()) {
+    // Get the receiver from the stack.
+    // +1 ~ return address
+    Label receiver_is_value, receiver_is_js_object;
+    __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
+
+    // Check if receiver is a smi (which is a number value).
+    __ test(eax, Immediate(kSmiTagMask));
+    __ j(zero, &receiver_is_value, not_taken);
+
+    // Check if the receiver is a valid JS object.
+    __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, edi);
+    __ j(above_equal, &receiver_is_js_object);
+
+    // Call the runtime to box the value.
+    __ bind(&receiver_is_value);
+    __ EnterInternalFrame();
+    __ push(eax);
+    __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+    __ LeaveInternalFrame();
+    __ mov(Operand(esp, (argc_ + 1) * kPointerSize), eax);
+
+    __ bind(&receiver_is_js_object);
+  }
+
   // Get the function to call from the stack.
   // +2 ~ receiver, return address
   __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
index 000222ff1cd24db78da0abab6f832bfa0da6e667..56cf978d3d6d8f4e7dc7c0636cb4f05ac84c0355 100644 (file)
@@ -266,7 +266,7 @@ class CodeGenState BASE_EMBEDDED {
 
 
 // -------------------------------------------------------------------------
-// Arguments allocation mode
+// Arguments allocation mode.
 
 enum ArgumentsAllocationMode {
   NO_ARGUMENTS_ALLOCATION,
@@ -475,7 +475,9 @@ class CodeGenerator: public AstVisitor {
   void StoreUnsafeSmiToLocal(int offset, Handle<Object> value);
   void PushUnsafeSmi(Handle<Object> value);
 
-  void CallWithArguments(ZoneList<Expression*>* arguments, int position);
+  void CallWithArguments(ZoneList<Expression*>* arguments,
+                         CallFunctionFlags flags,
+                         int position);
 
   // Use an optimized version of Function.prototype.apply that avoid
   // allocating the arguments object and just copies the arguments
@@ -620,39 +622,6 @@ class CodeGenerator: public AstVisitor {
 };
 
 
-class CallFunctionStub: public CodeStub {
- public:
-  CallFunctionStub(int argc, InLoopFlag in_loop)
-      : argc_(argc), in_loop_(in_loop) { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  int argc_;
-  InLoopFlag in_loop_;
-
-#ifdef DEBUG
-  void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
-#endif
-
-  Major MajorKey() { return CallFunction; }
-  int MinorKey() { return argc_; }
-  InLoopFlag InLoop() { return in_loop_; }
-};
-
-
-class ToBooleanStub: public CodeStub {
- public:
-  ToBooleanStub() { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  Major MajorKey() { return ToBoolean; }
-  int MinorKey() { return 0; }
-};
-
-
 // Flag that indicates how to generate code for the stub GenericBinaryOpStub.
 enum GenericBinaryFlags {
   NO_GENERIC_BINARY_FLAGS = 0,
index 9effca34b1966b70720cfb01453071613520269e..c8d0b0023e926b827eeb67e2c9852bb5adccce79 100644 (file)
@@ -1049,7 +1049,7 @@ void FastCodeGenerator::EmitCallWithStub(Call* expr) {
   }
   // Record source position for debugger.
   SetSourcePosition(expr->position());
-  CallFunctionStub stub(arg_count, NOT_IN_LOOP);
+  CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
   __ CallStub(&stub);
   // Restore context register.
   __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
index 21842303194e381b474cafe0e1090343f9c04a62..5961294afa38817e32abc341fdfba59628778dac 100644 (file)
@@ -997,50 +997,65 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
       break;
 
     case STRING_CHECK:
-      // Check that the object is a two-byte string or a symbol.
-      __ mov(eax, FieldOperand(edx, HeapObject::kMapOffset));
-      __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
-      __ cmp(eax, FIRST_NONSTRING_TYPE);
-      __ j(above_equal, &miss, not_taken);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::STRING_FUNCTION_INDEX,
-                                          eax);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
-                      ebx, edx, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        // Check that the object is a string or a symbol.
+        __ mov(eax, FieldOperand(edx, HeapObject::kMapOffset));
+        __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
+        __ cmp(eax, FIRST_NONSTRING_TYPE);
+        __ j(above_equal, &miss, not_taken);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::STRING_FUNCTION_INDEX,
+                                            eax);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
+                        ebx, edx, name, &miss);
+      }
       break;
 
     case NUMBER_CHECK: {
-      Label fast;
-      // Check that the object is a smi or a heap number.
-      __ test(edx, Immediate(kSmiTagMask));
-      __ j(zero, &fast, taken);
-      __ CmpObjectType(edx, HEAP_NUMBER_TYPE, eax);
-      __ j(not_equal, &miss, not_taken);
-      __ bind(&fast);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::NUMBER_FUNCTION_INDEX,
-                                          eax);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
-                      ebx, edx, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        Label fast;
+        // Check that the object is a smi or a heap number.
+        __ test(edx, Immediate(kSmiTagMask));
+        __ j(zero, &fast, taken);
+        __ CmpObjectType(edx, HEAP_NUMBER_TYPE, eax);
+        __ j(not_equal, &miss, not_taken);
+        __ bind(&fast);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::NUMBER_FUNCTION_INDEX,
+                                            eax);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
+                        ebx, edx, name, &miss);
+      }
       break;
     }
 
     case BOOLEAN_CHECK: {
-      Label fast;
-      // Check that the object is a boolean.
-      __ cmp(edx, Factory::true_value());
-      __ j(equal, &fast, taken);
-      __ cmp(edx, Factory::false_value());
-      __ j(not_equal, &miss, not_taken);
-      __ bind(&fast);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::BOOLEAN_FUNCTION_INDEX,
-                                          eax);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
-                      ebx, edx, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        Label fast;
+        // Check that the object is a boolean.
+        __ cmp(edx, Factory::true_value());
+        __ j(equal, &fast, taken);
+        __ cmp(edx, Factory::false_value());
+        __ j(not_equal, &miss, not_taken);
+        __ bind(&fast);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::BOOLEAN_FUNCTION_INDEX,
+                                            eax);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
+                        ebx, edx, name, &miss);
+      }
       break;
     }
 
index 4edf6f18b54ee378ebe8e413cb01a4eebf052ee3..762765430de2f504bef7c98c02cc75f0368fc6b9 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -378,6 +378,18 @@ Object* CallIC::TryCallAsFunction(Object* object) {
   return *delegate;
 }
 
+void CallIC::ReceiverToObject(Object* object) {
+  HandleScope scope;
+  Handle<Object> receiver(object);
+
+  // Change the receiver to the result of calling ToObject on it.
+  const int argc = this->target()->arguments_count();
+  StackFrameLocator locator;
+  JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
+  int index = frame->ComputeExpressionsCount() - (argc + 1);
+  frame->SetExpression(index, object->ToObject());
+}
+
 
 Object* CallIC::LoadFunction(State state,
                              Handle<Object> object,
@@ -388,6 +400,10 @@ Object* CallIC::LoadFunction(State state,
     return TypeError("non_object_property_call", object, name);
   }
 
+  if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
+    ReceiverToObject(*object);
+  }
+
   // Check if the name is trivially convertible to an index and get
   // the element if so.
   uint32_t index;
index 1dd7edf6172d77b4d4b75488f76bb36988b801c2..5bbc584eb886a943e7fd757bb6e69b2f16c5a2c6 100644 (file)
--- a/src/ic.h
+++ b/src/ic.h
@@ -209,6 +209,8 @@ class CallIC: public IC {
   // Otherwise, it returns the undefined value.
   Object* TryCallAsFunction(Object* object);
 
+  void ReceiverToObject(Object* object);
+
   static void Clear(Address address, Code* target);
   friend class IC;
 };
index e912bbcff9a80fc34cf9435f0337b23386797ff4..59b003a8dbf4deee87592ddd8dbfd93656525b34 100644 (file)
@@ -815,7 +815,7 @@ void CodeGenerator::CallApplyLazy(Property* apply,
   frame_->Push(&fn);
   frame_->Push(&a1);
   frame_->Push(&a2);
-  CallFunctionStub call_function(2, NOT_IN_LOOP);
+  CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
   Result res = frame_->CallStub(&call_function, 3);
   frame_->Push(&res);
 
@@ -2706,7 +2706,7 @@ void CodeGenerator::VisitCall(Call* node) {
     // Call the function.
     CodeForSourcePosition(node->position());
     InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
-    CallFunctionStub call_function(arg_count, in_loop);
+    CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
     result = frame_->CallStub(&call_function, arg_count + 1);
 
     // Restore the context and overwrite the function on the stack with
@@ -2767,7 +2767,7 @@ void CodeGenerator::VisitCall(Call* node) {
     frame_->EmitPush(rdx);
 
     // Call the function.
-    CallWithArguments(args, node->position());
+    CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
 
   } else if (property != NULL) {
     // Check if the key is a literal string.
@@ -2832,7 +2832,7 @@ void CodeGenerator::VisitCall(Call* node) {
       }
 
       // Call the function.
-      CallWithArguments(args, node->position());
+      CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
     }
 
   } else {
@@ -2847,7 +2847,7 @@ void CodeGenerator::VisitCall(Call* node) {
     LoadGlobalReceiver();
 
     // Call the function.
-    CallWithArguments(args, node->position());
+    CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
   }
 }
 
@@ -6558,6 +6558,7 @@ void CompareStub::BranchIfNonSymbol(MacroAssembler* masm,
 // Call the function just below TOS on the stack with the given
 // arguments. The receiver is the TOS.
 void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
+                                      CallFunctionFlags flags,
                                       int position) {
   // Push the arguments ("left-to-right") on the stack.
   int arg_count = args->length();
@@ -6570,7 +6571,7 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
 
   // Use the shared code stub to call the function.
   InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
-  CallFunctionStub call_function(arg_count, in_loop);
+  CallFunctionStub call_function(arg_count, in_loop, flags);
   Result answer = frame_->CallStub(&call_function, arg_count + 1);
   // Restore context and replace function on the stack with the
   // result of the stub invocation.
@@ -6968,6 +6969,32 @@ void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
 void CallFunctionStub::Generate(MacroAssembler* masm) {
   Label slow;
 
+  // If the receiver might be a value (string, number or boolean) check for this
+  // and box it if it is.
+  if (ReceiverMightBeValue()) {
+    // Get the receiver from the stack.
+    // +1 ~ return address
+    Label receiver_is_value, receiver_is_js_object;
+    __ movq(rax, Operand(rsp, (argc_ + 1) * kPointerSize));
+
+    // Check if receiver is a smi (which is a number value).
+    __ JumpIfSmi(rax, &receiver_is_value);
+
+    // Check if the receiver is a valid JS object.
+    __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rdi);
+    __ j(above_equal, &receiver_is_js_object);
+
+    // Call the runtime to box the value.
+    __ bind(&receiver_is_value);
+    __ EnterInternalFrame();
+    __ push(rax);
+    __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+    __ LeaveInternalFrame();
+    __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rax);
+
+    __ bind(&receiver_is_js_object);
+  }
+
   // Get the function to call from the stack.
   // +2 ~ receiver, return address
   __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
index fa90f0248f62844c80a96d99c90fc8ece8474548..ade9a1fe9888042c74fb4d720e22824e33a32483 100644 (file)
@@ -474,7 +474,9 @@ class CodeGenerator: public AstVisitor {
   // at most 16 bits of user-controlled data per assembly operation.
   void LoadUnsafeSmi(Register target, Handle<Object> value);
 
-  void CallWithArguments(ZoneList<Expression*>* arguments, int position);
+  void CallWithArguments(ZoneList<Expression*>* arguments,
+                         CallFunctionFlags flags,
+                         int position);
 
   // Use an optimized version of Function.prototype.apply that avoid
   // allocating the arguments object and just copies the arguments
@@ -617,46 +619,6 @@ class CodeGenerator: public AstVisitor {
 };
 
 
-// -------------------------------------------------------------------------
-// Code stubs
-//
-// These independent code objects are created once, and used multiple
-// times by generated code to perform common tasks, often the slow
-// case of a JavaScript operation.  They are all subclasses of CodeStub,
-// which is declared in code-stubs.h.
-class CallFunctionStub: public CodeStub {
- public:
-  CallFunctionStub(int argc, InLoopFlag in_loop)
-      : argc_(argc), in_loop_(in_loop) { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  int argc_;
-  InLoopFlag in_loop_;
-
-#ifdef DEBUG
-  void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
-#endif
-
-  Major MajorKey() { return CallFunction; }
-  int MinorKey() { return argc_; }
-  InLoopFlag InLoop() { return in_loop_; }
-};
-
-
-class ToBooleanStub: public CodeStub {
- public:
-  ToBooleanStub() { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  Major MajorKey() { return ToBoolean; }
-  int MinorKey() { return 0; }
-};
-
-
 // Flag that indicates how to generate code for the stub GenericBinaryOpStub.
 enum GenericBinaryFlags {
   NO_GENERIC_BINARY_FLAGS = 0,
index eefae37468921801eed49ab3161070cb64700e92..dea8df4d81b5b353dd9fa161069f23f38ea0831a 100644 (file)
@@ -1063,7 +1063,7 @@ void FastCodeGenerator::EmitCallWithStub(Call* expr) {
   }
   // Record source position for debugger.
   SetSourcePosition(expr->position());
-  CallFunctionStub stub(arg_count, NOT_IN_LOOP);
+  CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
   __ CallStub(&stub);
   // Restore context register.
   __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
index cbddb61ee5037d561a38bc3c7a035285582b8c99..784a1985c21da6d918244d12052846e9a14bbf25 100644 (file)
@@ -721,47 +721,62 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
       break;
 
     case STRING_CHECK:
-      // Check that the object is a two-byte string or a symbol.
-      __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rcx);
-      __ j(above_equal, &miss);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::STRING_FUNCTION_INDEX,
-                                          rcx);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
-                      rbx, rdx, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        // Check that the object is a two-byte string or a symbol.
+        __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rcx);
+        __ j(above_equal, &miss);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::STRING_FUNCTION_INDEX,
+                                            rcx);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+                        rbx, rdx, name, &miss);
+      }
       break;
 
     case NUMBER_CHECK: {
-      Label fast;
-      // Check that the object is a smi or a heap number.
-      __ JumpIfSmi(rdx, &fast);
-      __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
-      __ j(not_equal, &miss);
-      __ bind(&fast);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::NUMBER_FUNCTION_INDEX,
-                                          rcx);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
-                      rbx, rdx, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        Label fast;
+        // Check that the object is a smi or a heap number.
+        __ JumpIfSmi(rdx, &fast);
+        __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
+        __ j(not_equal, &miss);
+        __ bind(&fast);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::NUMBER_FUNCTION_INDEX,
+                                            rcx);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+                        rbx, rdx, name, &miss);
+      }
       break;
     }
 
     case BOOLEAN_CHECK: {
-      Label fast;
-      // Check that the object is a boolean.
-      __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
-      __ j(equal, &fast);
-      __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
-      __ j(not_equal, &miss);
-      __ bind(&fast);
-      // Check that the maps starting from the prototype haven't changed.
-      GenerateLoadGlobalFunctionPrototype(masm(),
-                                          Context::BOOLEAN_FUNCTION_INDEX,
-                                          rcx);
-      CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
-                      rbx, rdx, name, &miss);
+      if (!function->IsBuiltin()) {
+        // Calling non-builtins with a value as receiver requires boxing.
+        __ jmp(&miss);
+      } else {
+        Label fast;
+        // Check that the object is a boolean.
+        __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
+        __ j(equal, &fast);
+        __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
+        __ j(not_equal, &miss);
+        __ bind(&fast);
+        // Check that the maps starting from the prototype haven't changed.
+        GenerateLoadGlobalFunctionPrototype(masm(),
+                                            Context::BOOLEAN_FUNCTION_INDEX,
+                                            rcx);
+        CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+                        rbx, rdx, name, &miss);
+      }
       break;
     }
 
diff --git a/test/mjsunit/bugs/bug-223.js b/test/mjsunit/bugs/bug-223.js
deleted file mode 100644 (file)
index 04b296b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2008 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.
-
-// When calling user-defined functions on strings, booleans or
-// numbers, we should create a wrapper object.
-
-function TypeOfThis() { return typeof this; }
-
-String.prototype.TypeOfThis = TypeOfThis;
-Boolean.prototype.TypeOfThis = TypeOfThis;
-Number.prototype.TypeOfThis = TypeOfThis;
-
-assertEquals('object', 'xxx'.TypeOfThis());
-assertEquals('object', true.TypeOfThis());
-assertEquals('object', (42).TypeOfThis());
diff --git a/test/mjsunit/regress/regress-crbug-3184.js b/test/mjsunit/regress/regress-crbug-3184.js
new file mode 100644 (file)
index 0000000..fe6cfed
--- /dev/null
@@ -0,0 +1,56 @@
+Object.extend = function (dest, source) {
+       for (property in source) dest[property] = source[property];
+       return dest;
+};
+
+Object.extend ( Function.prototype,
+{
+       wrap : function (wrapper) {
+               var method = this;
+               var bmethod = (function(_method) {
+                       return function () {
+                               this.$$$parentMethodStore$$$ = this.$proceed;
+                               this.$proceed = function() { return _method.apply(this, arguments); };
+                       };
+               })(method);
+               var amethod = function () {
+                       this.$proceed = this.$$$parentMethodStore$$$;
+                       if (this.$proceed == undefined) delete this.$proceed;
+                       delete this.$$$parentMethodStore$$$;
+               };
+               var value = function() { bmethod.call(this); retval = wrapper.apply(this, arguments); amethod.call(this); return retval; };
+               return value;
+       }
+});
+
+String.prototype.cap = function() {
+       return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+};
+
+String.prototype.cap = String.prototype.cap.wrap(
+       function(each) {
+               if (each && this.indexOf(" ") != -1) {
+                       return this.split(" ").map(
+                               function (value) {
+                                       return value.cap();
+                               }
+                       ).join(" ");
+               } else {
+                       return this.$proceed();
+       }
+});
+
+Object.extend( Array.prototype,
+{
+       map : function(fun) {
+               if (typeof fun != "function") throw new TypeError();
+               var len = this.length;
+               var res = new Array(len);
+               var thisp = arguments[1];
+               for (var i = 0; i < len; i++) { if (i in this) res[i] = fun.call(thisp, this[i], i, this); }
+               return res;
+       }
+});
+assertEquals("Test1 test1", "test1 test1".cap());
+assertEquals("Test2 Test2", "test2 test2".cap(true));
+
diff --git a/test/mjsunit/value-wrapper.js b/test/mjsunit/value-wrapper.js
new file mode 100644 (file)
index 0000000..33ef013
--- /dev/null
@@ -0,0 +1,138 @@
+// Copyright 2008 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.
+
+// When calling user-defined functions on strings, booleans or
+// numbers, we should create a wrapper object.
+
+function RunTests() {
+  for (var i = 0; i < 10; i++) {
+    assertEquals('object', 'xxx'.TypeOfThis());
+    assertEquals('object', true.TypeOfThis(2,3));
+    assertEquals('object', false.TypeOfThis());
+    assertEquals('object', (42).TypeOfThis());
+    assertEquals('object', (3.14).TypeOfThis());
+  }
+  
+  for (var i = 0; i < 10; i++) {
+    assertEquals('object', 'xxx'['TypeOfThis']());
+    assertEquals('object', true['TypeOfThis']());
+    assertEquals('object', false['TypeOfThis']());
+    assertEquals('object', (42)['TypeOfThis']());
+    assertEquals('object', (3.14)['TypeOfThis']());
+  }
+  
+  function CallTypeOfThis(obj) {
+    assertEquals('object', obj.TypeOfThis());
+  }
+  
+  for (var i = 0; i < 10; i++) {
+    CallTypeOfThis('xxx');
+    CallTypeOfThis(true);
+    CallTypeOfThis(false);
+    CallTypeOfThis(42);
+    CallTypeOfThis(3.14);
+  }
+  
+  function TestWithWith(obj) {
+    with (obj) {
+      for (var i = 0; i < 10; i++) {
+        assertEquals('object', TypeOfThis());
+      }
+    }
+  }
+  
+  TestWithWith('xxx');
+  TestWithWith(true);
+  TestWithWith(false);
+  TestWithWith(42);
+  TestWithWith(3.14);
+  
+  for (var i = 0; i < 10; i++) {
+    assertEquals('object', true[7]());
+    assertEquals('object', false[7]());
+    assertEquals('object', (42)[7]());
+    assertEquals('object', (3.14)[7]());
+  }
+}
+
+function TypeOfThis() { return typeof this; }
+
+// Test with normal setup of prototype. 
+String.prototype.TypeOfThis = TypeOfThis;
+Boolean.prototype.TypeOfThis = TypeOfThis;
+Number.prototype.TypeOfThis = TypeOfThis;
+Boolean.prototype[7] = TypeOfThis;
+Number.prototype[7] = TypeOfThis;
+  
+
+RunTests();
+
+// Run test after properties have been set to a different value.
+String.prototype.TypeOfThis = 'x';
+Boolean.prototype.TypeOfThis = 'x';
+Number.prototype.TypeOfThis = 'x';
+Boolean.prototype[7] = 'x';
+Number.prototype[7] = 'x';
+
+String.prototype.TypeOfThis = TypeOfThis;
+Boolean.prototype.TypeOfThis = TypeOfThis;
+Number.prototype.TypeOfThis = TypeOfThis;
+Boolean.prototype[7] = TypeOfThis;
+Number.prototype[7] = TypeOfThis;
+
+RunTests();
+
+// Force the prototype into slow case and run the test again.
+delete String.prototype.TypeOfThis;
+delete Boolean.prototype.TypeOfThis;
+delete Number.prototype.TypeOfThis;
+Boolean.prototype[7];
+Number.prototype[7];
+
+String.prototype.TypeOfThis = TypeOfThis;
+Boolean.prototype.TypeOfThis = TypeOfThis;
+Number.prototype.TypeOfThis = TypeOfThis;
+Boolean.prototype[7] = TypeOfThis;
+Number.prototype[7] = TypeOfThis;
+
+RunTests();
+
+// According to ES3 15.3.4.3 the this value passed to Function.prototyle.apply
+// should wrapped. According to ES5 it should not.
+assertEquals('object', TypeOfThis.apply('xxx', []));
+assertEquals('object', TypeOfThis.apply(true, []));
+assertEquals('object', TypeOfThis.apply(false, []));
+assertEquals('object', TypeOfThis.apply(42, []));
+assertEquals('object', TypeOfThis.apply(3.14, []));
+
+// According to ES3 15.3.4.3 the this value passed to Function.prototyle.call
+// should wrapped. According to ES5 it should not.
+assertEquals('object', TypeOfThis.call('xxx'));
+assertEquals('object', TypeOfThis.call(true));
+assertEquals('object', TypeOfThis.call(false));
+assertEquals('object', TypeOfThis.call(42));
+assertEquals('object', TypeOfThis.call(3.14));