Commit cf191792 authored by ager@chromium.org's avatar ager@chromium.org

Implement CallAsConstructor method for Object in the API

Patch by Peter Varga.

BUG=v8:1348
TEST=cctest/test-api/ConstructorForObject

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7803 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 76d5eda6
...@@ -1614,6 +1614,14 @@ class Object : public Value { ...@@ -1614,6 +1614,14 @@ class Object : public Value {
int argc, int argc,
Handle<Value> argv[]); Handle<Value> argv[]);
/**
* Call an Object as a consturctor if a callback is set by the
* ObjectTemplate::SetCallAsFunctionHandler method.
* Note: This method behaves like the Function::NewInstance method.
*/
V8EXPORT Local<Value> CallAsConstructor(int argc,
Handle<Value> argv[]);
V8EXPORT static Local<Object> New(); V8EXPORT static Local<Object> New();
static inline Object* Cast(Value* obj); static inline Object* Cast(Value* obj);
private: private:
......
...@@ -3262,7 +3262,7 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, ...@@ -3262,7 +3262,7 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc,
return Local<v8::Value>()); return Local<v8::Value>());
LOG_API(isolate, "Object::CallAsFunction"); LOG_API(isolate, "Object::CallAsFunction");
ENTER_V8(isolate); ENTER_V8(isolate);
HandleScope scope; i::HandleScope scope(isolate);
i::Handle<i::JSObject> obj = Utils::OpenHandle(this); i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv); i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv);
STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
...@@ -3282,7 +3282,44 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, ...@@ -3282,7 +3282,44 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc,
i::Handle<i::Object> returned = i::Handle<i::Object> returned =
i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception); i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
return scope.Close(Utils::ToLocal(returned)); return Utils::ToLocal(scope.CloseAndEscape(returned));
}
Local<v8::Value> Object::CallAsConstructor(int argc,
v8::Handle<v8::Value> argv[]) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::CallAsConstructor()",
return Local<v8::Object>());
LOG_API(isolate, "Object::CallAsConstructor");
ENTER_V8(isolate);
i::HandleScope scope(isolate);
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
i::Object*** args = reinterpret_cast<i::Object***>(argv);
if (obj->IsJSFunction()) {
i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(obj);
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> returned =
i::Execution::New(fun, argc, args, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
return Utils::ToLocal(scope.CloseAndEscape(
i::Handle<i::JSObject>::cast(returned)));
}
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> delegate =
i::Execution::TryGetConstructorDelegate(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
if (!delegate->IsUndefined()) {
i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(delegate);
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> returned =
i::Execution::Call(fun, obj, argc, args, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
ASSERT(!delegate->IsUndefined());
return Utils::ToLocal(scope.CloseAndEscape(returned));
}
return Local<v8::Object>();
} }
......
...@@ -277,6 +277,34 @@ Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) { ...@@ -277,6 +277,34 @@ Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
} }
Handle<Object> Execution::TryGetConstructorDelegate(
Handle<Object> object,
bool* has_pending_exception) {
ASSERT(!object->IsJSFunction());
Isolate* isolate = Isolate::Current();
// 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>(
isolate->global_context()->call_as_constructor_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();
}
bool StackGuard::IsStackOverflow() { bool StackGuard::IsStackOverflow() {
ExecutionAccess access(isolate_); ExecutionAccess access(isolate_);
return (thread_local_.jslimit_ != kInterruptLimit && return (thread_local_.jslimit_ != kInterruptLimit &&
......
...@@ -146,6 +146,8 @@ class Execution : public AllStatic { ...@@ -146,6 +146,8 @@ class Execution : public AllStatic {
// Get a function delegate (or undefined) for the given non-function // Get a function delegate (or undefined) for the given non-function
// object. Used for support calling objects as constructors. // object. Used for support calling objects as constructors.
static Handle<Object> GetConstructorDelegate(Handle<Object> object); static Handle<Object> GetConstructorDelegate(Handle<Object> object);
static Handle<Object> TryGetConstructorDelegate(Handle<Object> object,
bool* has_pending_exception);
}; };
......
...@@ -6747,6 +6747,200 @@ THREADED_TEST(Constructor) { ...@@ -6747,6 +6747,200 @@ THREADED_TEST(Constructor) {
CHECK(value->BooleanValue()); CHECK(value->BooleanValue());
} }
static Handle<Value> ConstructorCallback(const Arguments& args) {
ApiTestFuzzer::Fuzz();
Local<Object> This;
if (args.IsConstructCall()) {
Local<Object> Holder = args.Holder();
This = Object::New();
Local<Value> proto = Holder->GetPrototype();
if (proto->IsObject()) {
This->SetPrototype(proto);
}
} else {
This = args.This();
}
This->Set(v8_str("a"), args[0]);
return This;
}
static Handle<Value> FakeConstructorCallback(const Arguments& args) {
ApiTestFuzzer::Fuzz();
return args[0];
}
THREADED_TEST(ConstructorForObject) {
v8::HandleScope handle_scope;
LocalContext context;
{ Local<ObjectTemplate> instance_template = ObjectTemplate::New();
instance_template->SetCallAsFunctionHandler(ConstructorCallback);
Local<Object> instance = instance_template->NewInstance();
context->Global()->Set(v8_str("obj"), instance);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
// Call the Object's constructor with a 32-bit signed integer.
value = CompileRun("(function() { var o = new obj(28); return o.a; })()");
CHECK(!try_catch.HasCaught());
CHECK(value->IsInt32());
CHECK_EQ(28, value->Int32Value());
Local<Value> args1[] = { v8_num(28) };
Local<Value> value_obj1 = instance->CallAsConstructor(1, args1);
CHECK(value_obj1->IsObject());
Local<Object> object1 = Local<Object>::Cast(value_obj1);
value = object1->Get(v8_str("a"));
CHECK(value->IsInt32());
CHECK(!try_catch.HasCaught());
CHECK_EQ(28, value->Int32Value());
// Call the Object's constructor with a String.
value = CompileRun(
"(function() { var o = new obj('tipli'); return o.a; })()");
CHECK(!try_catch.HasCaught());
CHECK(value->IsString());
String::AsciiValue string_value1(value->ToString());
CHECK_EQ("tipli", *string_value1);
Local<Value> args2[] = { v8_str("tipli") };
Local<Value> value_obj2 = instance->CallAsConstructor(1, args2);
CHECK(value_obj2->IsObject());
Local<Object> object2 = Local<Object>::Cast(value_obj2);
value = object2->Get(v8_str("a"));
CHECK(!try_catch.HasCaught());
CHECK(value->IsString());
String::AsciiValue string_value2(value->ToString());
CHECK_EQ("tipli", *string_value2);
// Call the Object's constructor with a Boolean.
value = CompileRun("(function() { var o = new obj(true); return o.a; })()");
CHECK(!try_catch.HasCaught());
CHECK(value->IsBoolean());
CHECK_EQ(true, value->BooleanValue());
Handle<Value> args3[] = { v8::Boolean::New(true) };
Local<Value> value_obj3 = instance->CallAsConstructor(1, args3);
CHECK(value_obj3->IsObject());
Local<Object> object3 = Local<Object>::Cast(value_obj3);
value = object3->Get(v8_str("a"));
CHECK(!try_catch.HasCaught());
CHECK(value->IsBoolean());
CHECK_EQ(true, value->BooleanValue());
// Call the Object's constructor with undefined.
Handle<Value> args4[] = { v8::Undefined() };
Local<Value> value_obj4 = instance->CallAsConstructor(1, args4);
CHECK(value_obj4->IsObject());
Local<Object> object4 = Local<Object>::Cast(value_obj4);
value = object4->Get(v8_str("a"));
CHECK(!try_catch.HasCaught());
CHECK(value->IsUndefined());
// Call the Object's constructor with null.
Handle<Value> args5[] = { v8::Null() };
Local<Value> value_obj5 = instance->CallAsConstructor(1, args5);
CHECK(value_obj5->IsObject());
Local<Object> object5 = Local<Object>::Cast(value_obj5);
value = object5->Get(v8_str("a"));
CHECK(!try_catch.HasCaught());
CHECK(value->IsNull());
}
// Check exception handling when there is no constructor set for the Object.
{ Local<ObjectTemplate> instance_template = ObjectTemplate::New();
Local<Object> instance = instance_template->NewInstance();
context->Global()->Set(v8_str("obj2"), instance);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
value = CompileRun("new obj2(28)");
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value1(try_catch.Exception());
CHECK_EQ("TypeError: object is not a function", *exception_value1);
try_catch.Reset();
Local<Value> args[] = { v8_num(29) };
value = instance->CallAsConstructor(1, args);
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value2(try_catch.Exception());
CHECK_EQ("TypeError: #<Object> is not a function", *exception_value2);
try_catch.Reset();
}
// Check the case when constructor throws exception.
{ Local<ObjectTemplate> instance_template = ObjectTemplate::New();
instance_template->SetCallAsFunctionHandler(ThrowValue);
Local<Object> instance = instance_template->NewInstance();
context->Global()->Set(v8_str("obj3"), instance);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
value = CompileRun("new obj3(22)");
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value1(try_catch.Exception());
CHECK_EQ("22", *exception_value1);
try_catch.Reset();
Local<Value> args[] = { v8_num(23) };
value = instance->CallAsConstructor(1, args);
CHECK(try_catch.HasCaught());
String::AsciiValue exception_value2(try_catch.Exception());
CHECK_EQ("23", *exception_value2);
try_catch.Reset();
}
// Check whether constructor returns with an object or non-object.
{ Local<FunctionTemplate> function_template =
FunctionTemplate::New(FakeConstructorCallback);
Local<Function> function = function_template->GetFunction();
Local<Object> instance1 = function;
context->Global()->Set(v8_str("obj4"), instance1);
v8::TryCatch try_catch;
Local<Value> value;
CHECK(!try_catch.HasCaught());
CHECK(instance1->IsObject());
CHECK(instance1->IsFunction());
value = CompileRun("new obj4(28)");
CHECK(!try_catch.HasCaught());
CHECK(value->IsObject());
Local<Value> args1[] = { v8_num(28) };
value = instance1->CallAsConstructor(1, args1);
CHECK(!try_catch.HasCaught());
CHECK(value->IsObject());
Local<ObjectTemplate> instance_template = ObjectTemplate::New();
instance_template->SetCallAsFunctionHandler(FakeConstructorCallback);
Local<Object> instance2 = instance_template->NewInstance();
context->Global()->Set(v8_str("obj5"), instance2);
CHECK(!try_catch.HasCaught());
CHECK(instance2->IsObject());
CHECK(!instance2->IsFunction());
value = CompileRun("new obj5(28)");
CHECK(!try_catch.HasCaught());
CHECK(!value->IsObject());
Local<Value> args2[] = { v8_num(28) };
value = instance2->CallAsConstructor(1, args2);
CHECK(!try_catch.HasCaught());
CHECK(!value->IsObject());
}
}
THREADED_TEST(FunctionDescriptorException) { THREADED_TEST(FunctionDescriptorException) {
v8::HandleScope handle_scope; v8::HandleScope handle_scope;
LocalContext context; LocalContext context;
...@@ -7029,9 +7223,8 @@ THREADED_TEST(CallAsFunction) { ...@@ -7029,9 +7223,8 @@ THREADED_TEST(CallAsFunction) {
CHECK(value.IsEmpty()); CHECK(value.IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
String::AsciiValue exception_value1(try_catch.Exception()); String::AsciiValue exception_value1(try_catch.Exception());
CHECK_EQ(*exception_value1, CHECK_EQ("TypeError: Property 'obj2' of object #<Object> is not a function",
"TypeError: Property 'obj2' of object " *exception_value1);
"#<Object> is not a function");
try_catch.Reset(); try_catch.Reset();
// Call an object without call-as-function handler through the API // Call an object without call-as-function handler through the API
...@@ -7041,7 +7234,7 @@ THREADED_TEST(CallAsFunction) { ...@@ -7041,7 +7234,7 @@ THREADED_TEST(CallAsFunction) {
CHECK(value.IsEmpty()); CHECK(value.IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
String::AsciiValue exception_value2(try_catch.Exception()); String::AsciiValue exception_value2(try_catch.Exception());
CHECK_EQ(*exception_value2, "TypeError: [object Object] is not a function"); CHECK_EQ("TypeError: [object Object] is not a function", *exception_value2);
try_catch.Reset(); try_catch.Reset();
} }
...@@ -7058,14 +7251,14 @@ THREADED_TEST(CallAsFunction) { ...@@ -7058,14 +7251,14 @@ THREADED_TEST(CallAsFunction) {
value = CompileRun("obj3(22)"); value = CompileRun("obj3(22)");
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
String::AsciiValue exception_value1(try_catch.Exception()); String::AsciiValue exception_value1(try_catch.Exception());
CHECK_EQ(*exception_value1, "22"); CHECK_EQ("22", *exception_value1);
try_catch.Reset(); try_catch.Reset();
v8::Handle<Value> args[] = { v8_num(23) }; v8::Handle<Value> args[] = { v8_num(23) };
value = instance->CallAsFunction(instance, 1, args); value = instance->CallAsFunction(instance, 1, args);
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
String::AsciiValue exception_value2(try_catch.Exception()); String::AsciiValue exception_value2(try_catch.Exception());
CHECK_EQ(*exception_value2, "23"); CHECK_EQ("23", *exception_value2);
try_catch.Reset(); try_catch.Reset();
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment