From: sgjesse@chromium.org Date: Tue, 5 May 2009 09:38:45 +0000 (+0000) Subject: Add the ability to set embedder data on created contexts from the API. X-Git-Tag: upstream/4.7.83~24193 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3cdb194c56403882231037151e15c48971e8ca37;p=platform%2Fupstream%2Fv8.git Add the ability to set embedder data on created contexts from the API. Expose the active context where a break event occoured through the debug message handler. Review URL: http://codereview.chromium.org/109013 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1857 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8.h b/include/v8.h index 068a1cd..6d18261 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2260,6 +2260,14 @@ class V8EXPORT Context { static bool InContext(); /** + * Associate an additional data object with the context. This is mainly used + * with the debugger to provide additional information on the context through + * the debugger API. + */ + void SetData(Handle data); + Local GetData(); + + /** * Stack-allocated class which sets the execution context for all * operations executed within a local scope. */ diff --git a/src/api.cc b/src/api.cc index 058b246..59eed30 100644 --- a/src/api.cc +++ b/src/api.cc @@ -445,6 +445,40 @@ void Context::Exit() { } +void Context::SetData(v8::Handle data) { + if (IsDeadCheck("v8::Context::SetData()")) return; + ENTER_V8; + { + HandleScope scope; + i::Handle env = Utils::OpenHandle(this); + i::Handle raw_data = Utils::OpenHandle(*data); + ASSERT(env->IsGlobalContext()); + if (env->IsGlobalContext()) { + env->set_data(*raw_data); + } + } +} + + +v8::Local Context::GetData() { + if (IsDeadCheck("v8::Context::GetData()")) return v8::Local(); + ENTER_V8; + i::Object* raw_result = NULL; + { + HandleScope scope; + i::Handle env = Utils::OpenHandle(this); + ASSERT(env->IsGlobalContext()); + if (env->IsGlobalContext()) { + raw_result = env->data(); + } else { + return Local(); + } + } + i::Handle result(raw_result); + return Utils::ToLocal(result); +} + + void** v8::HandleScope::RawClose(void** value) { if (!ApiCheck(!is_closed_, "v8::HandleScope::Close()", diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index e4962ef..48e322e 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -833,6 +833,9 @@ void Genesis::CreateRoots(v8::Handle global_template, // Initialize the out of memory slot. global_context()->set_out_of_memory(Heap::false_value()); + + // Initialize the data slot. + global_context()->set_data(Heap::undefined_value()); } diff --git a/src/contexts.h b/src/contexts.h index ed4b1cf..f561431 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -94,7 +94,8 @@ enum ContextLookupFlags { V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \ V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \ V(OUT_OF_MEMORY_INDEX, Object, out_of_memory) \ - V(MAP_CACHE_INDEX, Object, map_cache) + V(MAP_CACHE_INDEX, Object, map_cache) \ + V(CONTEXT_DATA_INDEX, Object, data) // JSFunctions are pairs (context, function code), sometimes also called // closures. A Context object is used to represent function contexts and @@ -213,6 +214,7 @@ class Context: public FixedArray { CONTEXT_EXTENSION_FUNCTION_INDEX, OUT_OF_MEMORY_INDEX, MAP_CACHE_INDEX, + CONTEXT_DATA_INDEX, GLOBAL_CONTEXT_SLOTS }; diff --git a/src/debug.cc b/src/debug.cc index 1688443..8422a67 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -2172,7 +2172,7 @@ v8::Handle MessageImpl::GetJSON() const { v8::Handle MessageImpl::GetEventContext() const { - return v8::Handle(); + return v8::Utils::ToLocal(Debug::debugger_entry()->GetContext()); } diff --git a/src/debug.h b/src/debug.h index cc76567..35336cb 100644 --- a/src/debug.h +++ b/src/debug.h @@ -687,6 +687,9 @@ class EnterDebugger BASE_EMBEDDED { // Check whether there are any JavaScript frames on the stack. inline bool HasJavaScriptFrames() { return has_js_frames_; } + // Get the active context from before entering the debugger. + inline Handle GetContext() { return save_.context(); } + private: EnterDebugger* prev_; // Previous debugger entry if entered recursively. JavaScriptFrameIterator it_; diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index 93286eb..0b498ed 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -167,11 +167,22 @@ static v8::Local CompileFunction(DebugLocalContext* env, (*env)->Global()->Get(v8::String::New(function_name))); } + +// Compile and run the supplied source and return the requested function. +static v8::Local CompileFunction(const char* source, + const char* function_name) { + v8::Script::Compile(v8::String::New(source))->Run(); + return v8::Local::Cast( + v8::Context::GetCurrent()->Global()->Get(v8::String::New(function_name))); +} + + // Helper function that compiles and runs the source. static v8::Local CompileRun(const char* source) { return v8::Script::Compile(v8::String::New(source))->Run(); } + // Is there any debug info for the function? static bool HasDebugInfo(v8::Handle fun) { Handle f = v8::Utils::OpenHandle(*fun); @@ -4588,3 +4599,84 @@ TEST(ScriptNameAndData) { CHECK_EQ("new name", last_script_name_hit); CHECK_EQ("abc 123", last_script_data_hit); } + + +static v8::Persistent expected_context; +static v8::Handle expected_context_data; + + +// Check that the expected context is the one generating the debug event. +static void ContextCheckMessageHandler(const v8::Debug::Message& message) { + CHECK(message.GetEventContext() == expected_context); + CHECK(message.GetEventContext()->GetData()->StrictEquals( + expected_context_data)); + message_handler_hit_count++; + + const int kBufferSize = 1000; + uint16_t buffer[kBufferSize]; + const char* command_continue = + "{\"seq\":0," + "\"type\":\"request\"," + "\"command\":\"continue\"}"; + + // Send a continue command for break events. + if (message.GetEvent() == v8::Break) { + v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer)); + } +} + + +// Test which creates two contexts and sets different embedder data on each. +// Checks that this data is set correctly and that when the debug message +// handler is called the expected context is the one active. +TEST(ContextData) { + v8::HandleScope scope; + + v8::Debug::SetMessageHandler2(ContextCheckMessageHandler); + + // Create two contexts. + v8::Persistent context_1; + v8::Persistent context_2; + v8::Handle global_template = + v8::Handle(); + v8::Handle global_object = v8::Handle(); + context_1 = v8::Context::New(NULL, global_template, global_object); + context_2 = v8::Context::New(NULL, global_template, global_object); + + // Default data value is undefined. + CHECK(context_1->GetData()->IsUndefined()); + CHECK(context_2->GetData()->IsUndefined()); + + // Set and check different data values. + v8::Handle data_1 = v8::Number::New(1); + v8::Handle data_2 = v8::String::New("2"); + context_1->SetData(data_1); + context_2->SetData(data_2); + CHECK(context_1->GetData()->StrictEquals(data_1)); + CHECK(context_2->GetData()->StrictEquals(data_2)); + + // Simple test function which causes a break. + char* source = "function f() { debugger; }"; + + // Enter and run function in the first context. + { + v8::Context::Scope context_scope(context_1); + expected_context = context_1; + expected_context_data = data_1; + v8::Local f = CompileFunction(source, "f"); + f->Call(context_1->Global(), 0, NULL); + } + + + // Enter and run function in the second context. + { + v8::Context::Scope context_scope(context_2); + expected_context = context_2; + expected_context_data = data_2; + v8::Local f = CompileFunction(source, "f"); + f->Call(context_2->Global(), 0, NULL); + } + + // Two times compile event and two times break event. + CHECK_GT(message_handler_hit_count, 4); +}