Add Call method to the Object class in the API

Patch by Peter Varga.

BUG=v8:1336
TEST=cctest/test-api/CallAsFunction

Review URL: http://codereview.chromium.org/6883045

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7773 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
ager@chromium.org 2011-05-04 10:03:49 +00:00
parent 87f225936f
commit b8cdecb517
5 changed files with 163 additions and 37 deletions

View File

@ -1606,6 +1606,14 @@ class Object : public Value {
V8EXPORT ExternalArrayType GetIndexedPropertiesExternalArrayDataType();
V8EXPORT int GetIndexedPropertiesExternalArrayDataLength();
/**
* Call an Object as a function if a callback is set by the
* ObjectTemplate::SetCallAsFunctionHandler method.
*/
V8EXPORT Local<Value> CallAsFunction(Handle<Object> recv,
int argc,
Handle<Value> argv[]);
V8EXPORT static Local<Object> New();
static inline Object* Cast(Value* obj);
private:

View File

@ -3255,6 +3255,37 @@ int v8::Object::GetIndexedPropertiesExternalArrayDataLength() {
}
Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc,
v8::Handle<v8::Value> argv[]) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::CallAsFunction()",
return Local<v8::Value>());
LOG_API(isolate, "Object::CallAsFunction");
ENTER_V8(isolate);
HandleScope scope;
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv);
STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
i::Object*** args = reinterpret_cast<i::Object***>(argv);
i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>();
if (obj->IsJSFunction()) {
fun = i::Handle<i::JSFunction>::cast(obj);
} else {
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> delegate =
i::Execution::TryGetFunctionDelegate(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
fun = i::Handle<i::JSFunction>::cast(delegate);
recv_obj = obj;
}
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> returned =
i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
return scope.Close(Utils::ToLocal(i::Handle<i::JSObject>::cast(returned)));
}
Local<v8::Object> Function::NewInstance() const {
return NewInstance(0, NULL);
}

View File

@ -234,6 +234,30 @@ Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) {
}
Handle<Object> Execution::TryGetFunctionDelegate(Handle<Object> object,
bool* has_pending_exception) {
ASSERT(!object->IsJSFunction());
Isolate* isolate = Isolate::Current();
// 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>(
isolate->global_context()->call_as_function_delegate());
}
// If the Object doesn't have an instance-call handler we should
// throw a non-callable exception.
i::Handle<i::Object> error_obj = isolate->factory()->NewTypeError(
"called_non_callable", i::HandleVector<i::Object>(&object, 1));
isolate->Throw(*error_obj);
*has_pending_exception = true;
return isolate->factory()->undefined_value();
}
Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
ASSERT(!object->IsJSFunction());
Isolate* isolate = Isolate::Current();

View File

@ -138,6 +138,8 @@ 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);
static Handle<Object> TryGetFunctionDelegate(Handle<Object> object,
bool* has_pending_exception);
// Get a function delegate (or undefined) for the given non-function
// object. Used for support calling objects as constructors.

View File

@ -6962,50 +6962,111 @@ THREADED_TEST(CallAsFunction) {
v8::HandleScope scope;
LocalContext context;
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->SetCallAsFunctionHandler(call_as_function);
Local<v8::Object> instance = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("obj"), instance);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
{ Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->SetCallAsFunctionHandler(call_as_function);
Local<v8::Object> instance = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("obj"), instance);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
value = CompileRun("obj(42)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(42, value->Int32Value());
value = CompileRun("obj(42)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(42, value->Int32Value());
value = CompileRun("(function(o){return o(49)})(obj)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(49, value->Int32Value());
value = CompileRun("(function(o){return o(49)})(obj)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(49, value->Int32Value());
// test special case of call as function
value = CompileRun("[obj]['0'](45)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(45, value->Int32Value());
// test special case of call as function
value = CompileRun("[obj]['0'](45)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(45, value->Int32Value());
value = CompileRun("obj.call = Function.prototype.call;"
"obj.call(null, 87)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(87, value->Int32Value());
value = CompileRun("obj.call = Function.prototype.call;"
"obj.call(null, 87)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(87, value->Int32Value());
// Regression tests for bug #1116356: Calling call through call/apply
// must work for non-function receivers.
const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
value = CompileRun(apply_99);
CHECK(!try_catch.HasCaught());
CHECK_EQ(99, value->Int32Value());
// Regression tests for bug #1116356: Calling call through call/apply
// must work for non-function receivers.
const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
value = CompileRun(apply_99);
CHECK(!try_catch.HasCaught());
CHECK_EQ(99, value->Int32Value());
const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
value = CompileRun(call_17);
CHECK(!try_catch.HasCaught());
CHECK_EQ(17, value->Int32Value());
const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
value = CompileRun(call_17);
CHECK(!try_catch.HasCaught());
CHECK_EQ(17, value->Int32Value());
// Check that the call-as-function handler can be called through
// new.
value = CompileRun("new obj(43)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(-43, value->Int32Value());
// Check that the call-as-function handler can be called through
// new.
value = CompileRun("new obj(43)");
CHECK(!try_catch.HasCaught());
CHECK_EQ(-43, value->Int32Value());
// Check that the call-as-function handler can be called through
// the API.
v8::Handle<Value> args[] = { v8_num(28) };
value = instance->CallAsFunction(instance, 1, args);
CHECK(!try_catch.HasCaught());
CHECK_EQ(28, value->Int32Value());
}
{ Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
Local<v8::Object> instance = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("obj2"), instance);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
// Call an object without call-as-function handler through the JS
value = CompileRun("obj2(28)");
CHECK(value.IsEmpty());
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value1(try_catch.Exception());
CHECK_EQ(*exception_value1,
"TypeError: Property 'obj2' of object "
"#<Object> is not a function");
try_catch.Reset();
// Call an object without call-as-function handler through the API
value = CompileRun("obj2(28)");
v8::Handle<Value> args[] = { v8_num(28) };
value = instance->CallAsFunction(instance, 1, args);
CHECK(value.IsEmpty());
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value2(try_catch.Exception());
CHECK_EQ(*exception_value2, "TypeError: [object Object] is not a function");
try_catch.Reset();
}
{ Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->SetCallAsFunctionHandler(ThrowValue);
Local<v8::Object> instance = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("obj3"), instance);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
// Catch the exception which is thrown by call-as-function handler
value = CompileRun("obj3(22)");
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value1(try_catch.Exception());
CHECK_EQ(*exception_value1, "22");
try_catch.Reset();
v8::Handle<Value> args[] = { v8_num(23) };
value = instance->CallAsFunction(instance, 1, args);
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value2(try_catch.Exception());
CHECK_EQ(*exception_value2, "23");
try_catch.Reset();
}
}