Propagate information on whether a non function was called as constructor or not...
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 20 May 2009 19:33:44 +0000 (19:33 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 20 May 2009 19:33:44 +0000 (19:33 +0000)
Review URL: http://codereview.chromium.org/113634

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

12 files changed:
src/arm/builtins-arm.cc
src/bootstrapper.cc
src/builtins.cc
src/builtins.h
src/contexts.h
src/execution.cc
src/execution.h
src/ia32/builtins-ia32.cc
src/runtime.cc
src/runtime.h
src/runtime.js
test/cctest/test-api.cc

index 9c7a42a..a65bc35 100644 (file)
@@ -187,7 +187,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
 
   // Set expected number of arguments to zero (not changing r0).
   __ mov(r2, Operand(0));
-  __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
+  __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
   __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
           RelocInfo::CODE_TARGET);
 }
index 09cf68d..76bcc05 100644 (file)
@@ -820,14 +820,28 @@ void Genesis::CreateRoots(v8::Handle<v8::ObjectTemplate> global_template,
     global_context()->set_context_extension_function(*context_extension_fun);
   }
 
-  // Setup the call-as-function delegate.
-  Handle<Code> code =
-      Handle<Code>(Builtins::builtin(Builtins::HandleApiCallAsFunction));
-  Handle<JSFunction> delegate =
-      Factory::NewFunction(Factory::empty_symbol(), JS_OBJECT_TYPE,
-                           JSObject::kHeaderSize, code, true);
-  global_context()->set_call_as_function_delegate(*delegate);
-  delegate->shared()->DontAdaptArguments();
+
+  {
+    // Setup the call-as-function delegate.
+    Handle<Code> code =
+        Handle<Code>(Builtins::builtin(Builtins::HandleApiCallAsFunction));
+    Handle<JSFunction> delegate =
+        Factory::NewFunction(Factory::empty_symbol(), JS_OBJECT_TYPE,
+                             JSObject::kHeaderSize, code, true);
+    global_context()->set_call_as_function_delegate(*delegate);
+    delegate->shared()->DontAdaptArguments();
+  }
+
+  {
+    // Setup the call-as-constructor delegate.
+    Handle<Code> code =
+        Handle<Code>(Builtins::builtin(Builtins::HandleApiCallAsConstructor));
+    Handle<JSFunction> delegate =
+        Factory::NewFunction(Factory::empty_symbol(), JS_OBJECT_TYPE,
+                             JSObject::kHeaderSize, code, true);
+    global_context()->set_call_as_constructor_delegate(*delegate);
+    delegate->shared()->DontAdaptArguments();
+  }
 
   global_context()->set_special_function_table(Heap::empty_fixed_array());
 
index b27974f..3ddb681 100644 (file)
@@ -394,12 +394,18 @@ BUILTIN(HandleApiCall) {
 BUILTIN_END
 
 
-// Handle calls to non-function objects created through the API that
-// support calls.
-BUILTIN(HandleApiCallAsFunction) {
-  // Non-functions are never called as constructors.
+// Helper function to handle calls to non-function objects created through the
+// API. The object can be called as either a constructor (using new) or just as
+// a function (without new).
+static Object* HandleApiCallAsFunctionOrConstructor(bool is_construct_call,
+                                                    int __argc__,
+                                                    Object** __argv__) {
+  // Non-functions are never called as constructors. Even if this is an object
+  // called as a constructor the delegate call is not a construct call.
   ASSERT(!CalledAsConstructor());
 
+  Handle<Object> receiver(&__argv__[0]);
+
   // Get the object called.
   JSObject* obj = JSObject::cast(*receiver);
 
@@ -431,7 +437,7 @@ BUILTIN(HandleApiCallAsFunction) {
         data,
         self,
         callee,
-        false,
+        is_construct_call,
         reinterpret_cast<void**>(__argv__ - 1),
         __argc__ - 1);
     v8::Handle<v8::Value> value;
@@ -450,6 +456,21 @@ BUILTIN(HandleApiCallAsFunction) {
   RETURN_IF_SCHEDULED_EXCEPTION();
   return result;
 }
+
+
+// Handle calls to non-function objects created through the API. This delegate
+// function is used when the call is a normal function call.
+BUILTIN(HandleApiCallAsFunction) {
+  return HandleApiCallAsFunctionOrConstructor(false, __argc__, __argv__);
+}
+BUILTIN_END
+
+
+// Handle calls to non-function objects created through the API. This delegate
+// function is used when the call is a construct call.
+BUILTIN(HandleApiCallAsConstructor) {
+  return HandleApiCallAsFunctionOrConstructor(true, __argc__, __argv__);
+}
 BUILTIN_END
 
 
index 4e74a3c..c011f22 100644 (file)
@@ -42,7 +42,8 @@ namespace v8 { namespace internal {
   V(ArrayPop)                                      \
                                                    \
   V(HandleApiCall)                                 \
-  V(HandleApiCallAsFunction)
+  V(HandleApiCallAsFunction)                       \
+  V(HandleApiCallAsConstructor)
 
 
 // Define list of builtins implemented in assembly.
@@ -99,35 +100,36 @@ namespace v8 { namespace internal {
 #endif
 
 // Define list of builtins implemented in JavaScript.
-#define BUILTINS_LIST_JS(V)    \
-  V(EQUALS, 1)                 \
-  V(STRICT_EQUALS, 1)          \
-  V(COMPARE, 2)                \
-  V(ADD, 1)                    \
-  V(SUB, 1)                    \
-  V(MUL, 1)                    \
-  V(DIV, 1)                    \
-  V(MOD, 1)                    \
-  V(BIT_OR, 1)                 \
-  V(BIT_AND, 1)                \
-  V(BIT_XOR, 1)                \
-  V(UNARY_MINUS, 0)            \
-  V(BIT_NOT, 0)                \
-  V(SHL, 1)                    \
-  V(SAR, 1)                    \
-  V(SHR, 1)                    \
-  V(DELETE, 1)                 \
-  V(IN, 1)                     \
-  V(INSTANCE_OF, 1)            \
-  V(GET_KEYS, 0)               \
-  V(FILTER_KEY, 1)             \
-  V(CALL_NON_FUNCTION, 0)      \
-  V(TO_OBJECT, 0)              \
-  V(TO_NUMBER, 0)              \
-  V(TO_STRING, 0)              \
-  V(STRING_ADD_LEFT, 1)        \
-  V(STRING_ADD_RIGHT, 1)       \
-  V(APPLY_PREPARE, 1)          \
+#define BUILTINS_LIST_JS(V)              \
+  V(EQUALS, 1)                           \
+  V(STRICT_EQUALS, 1)                    \
+  V(COMPARE, 2)                          \
+  V(ADD, 1)                              \
+  V(SUB, 1)                              \
+  V(MUL, 1)                              \
+  V(DIV, 1)                              \
+  V(MOD, 1)                              \
+  V(BIT_OR, 1)                           \
+  V(BIT_AND, 1)                          \
+  V(BIT_XOR, 1)                          \
+  V(UNARY_MINUS, 0)                      \
+  V(BIT_NOT, 0)                          \
+  V(SHL, 1)                              \
+  V(SAR, 1)                              \
+  V(SHR, 1)                              \
+  V(DELETE, 1)                           \
+  V(IN, 1)                               \
+  V(INSTANCE_OF, 1)                      \
+  V(GET_KEYS, 0)                         \
+  V(FILTER_KEY, 1)                       \
+  V(CALL_NON_FUNCTION, 0)                \
+  V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
+  V(TO_OBJECT, 0)                        \
+  V(TO_NUMBER, 0)                        \
+  V(TO_STRING, 0)                        \
+  V(STRING_ADD_LEFT, 1)                  \
+  V(STRING_ADD_RIGHT, 1)                 \
+  V(APPLY_PREPARE, 1)                    \
   V(APPLY_OVERFLOW, 1)
 
 
index f561431..eb0b962 100644 (file)
@@ -90,6 +90,8 @@ enum ContextLookupFlags {
   V(FUNCTION_CACHE_INDEX, JSObject, function_cache) \
   V(RUNTIME_CONTEXT_INDEX, Context, runtime_context) \
   V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \
+  V(CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, JSFunction, \
+    call_as_constructor_delegate) \
   V(EMPTY_SCRIPT_INDEX, Script, empty_script) \
   V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
   V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
@@ -209,6 +211,7 @@ class Context: public FixedArray {
     FUNCTION_CACHE_INDEX,
     RUNTIME_CONTEXT_INDEX,
     CALL_AS_FUNCTION_DELEGATE_INDEX,
+    CALL_AS_CONSTRUCTOR_DELEGATE_INDEX,
     EMPTY_SCRIPT_INDEX,
     SCRIPT_FUNCTION_INDEX,
     CONTEXT_EXTENSION_FUNCTION_INDEX,
index 32dde9e..352131b 100644 (file)
@@ -188,6 +188,24 @@ Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) {
 }
 
 
+Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
+  ASSERT(!object->IsJSFunction());
+
+  // If you return a function from here, it will be called when an
+  // attempt is made to call the given object as a constructor.
+
+  // Objects created through the API can have an instance-call handler
+  // that should be used when calling the object as a function.
+  if (object->IsHeapObject() &&
+      HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+    return Handle<JSFunction>(
+        Top::global_context()->call_as_constructor_delegate());
+  }
+
+  return Factory::undefined_value();
+}
+
+
 // Static state for stack guards.
 StackGuard::ThreadLocal StackGuard::thread_local_;
 
index 6f2f689..533043b 100644 (file)
@@ -129,6 +129,10 @@ class Execution : public AllStatic {
   // Get a function delegate (or undefined) for the given non-function
   // object. Used for support calling objects as functions.
   static Handle<Object> GetFunctionDelegate(Handle<Object> object);
+
+  // Get a function delegate (or undefined) for the given non-function
+  // object. Used for support calling objects as constructors.
+  static Handle<Object> GetConstructorDelegate(Handle<Object> object);
 };
 
 
index 5c7ba8e..8fbe634 100644 (file)
@@ -311,7 +311,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
 
   // Set expected number of arguments to zero (not changing eax).
   __ Set(ebx, Immediate(0));
-  __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
+  __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
   __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
          RelocInfo::CODE_TARGET);
 }
index 231d172..eeb33c3 100644 (file)
@@ -4358,6 +4358,14 @@ static Object* Runtime_GetFunctionDelegate(Arguments args) {
 }
 
 
+static Object* Runtime_GetConstructorDelegate(Arguments args) {
+  HandleScope scope;
+  ASSERT(args.length() == 1);
+  RUNTIME_ASSERT(!args[0]->IsJSFunction());
+  return *Execution::GetConstructorDelegate(args.at<Object>(0));
+}
+
+
 static Object* Runtime_NewContext(Arguments args) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 1);
index 4597dfa..9061904 100644 (file)
@@ -63,6 +63,7 @@ namespace v8 { namespace internal {
   /* Utilities */ \
   F(GetCalledFunction, 0) \
   F(GetFunctionDelegate, 1) \
+  F(GetConstructorDelegate, 1) \
   F(NewArguments, 1) \
   F(NewArgumentsFast, 3) \
   F(LazyCompile, 1) \
index 63e9292..c8ccf9f 100644 (file)
@@ -327,6 +327,18 @@ function CALL_NON_FUNCTION() {
 }
 
 
+function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
+  var callee = %GetCalledFunction();
+  var delegate = %GetConstructorDelegate(callee);
+  if (!IS_FUNCTION(delegate)) {
+    throw %MakeTypeError('called_non_callable', [typeof callee]);
+  }
+
+  var parameters = %NewArguments(delegate);
+  return delegate.apply(callee, parameters);
+}
+
+
 function APPLY_PREPARE(args) {
   var length;
   // First check whether length is a positive Smi and args is an array.  This is the
index 926823b..dbb8172 100644 (file)
@@ -4644,6 +4644,12 @@ THREADED_TEST(CrossLazyLoad) {
 
 static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
   ApiTestFuzzer::Fuzz();
+  if (args.IsConstructCall()) {
+    if (args[0]->IsInt32()) {
+       return v8_num(-args[0]->Int32Value());
+    }
+  }
+
   return args[0];
 }
 
@@ -4697,9 +4703,9 @@ THREADED_TEST(CallAsFunction) {
   // Check that the call-as-function handler can be called through
   // new.  Currently, there is no way to check in the call-as-function
   // handler if it has been called through new or not.
-  value = CompileRun("new obj(42)");
+  value = CompileRun("new obj(43)");
   CHECK(!try_catch.HasCaught());
-  CHECK_EQ(42, value->Int32Value());
+  CHECK_EQ(-43, value->Int32Value());
 }