Make it possible to add a user data object to each script compiled.
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 21 Apr 2009 07:22:06 +0000 (07:22 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 21 Apr 2009 07:22:06 +0000 (07:22 +0000)
Review URL: http://codereview.chromium.org/90003

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

13 files changed:
include/v8.h
src/accessors.cc
src/accessors.h
src/api.cc
src/bootstrapper.cc
src/debug-delay.js
src/factory.cc
src/mirror-delay.js
src/objects-debug.cc
src/objects-inl.h
src/objects.h
test/cctest/test-api.cc
test/cctest/test-debug.cc

index 1ddaee02c9a12ddb630571275aa75155e8bd5817..892507b4f69f73b9fbcddd4ab8fae2dd527c7439 100644 (file)
@@ -529,6 +529,13 @@ class V8EXPORT Script {
    * Returns the script id value.
    */
   Local<Value> Id();
+
+  /**
+   * Associate an additional data object with the script. This is mainly used
+   * with the debugger as this data object is only available through the
+   * debugger API.
+   */
+  void SetData(Handle<Value> data);
 };
 
 
@@ -540,8 +547,18 @@ class V8EXPORT Message {
   Local<String> Get() const;
   Local<String> GetSourceLine() const;
 
+  /**
+   * Returns the resource name for the script from where the function causing
+   * the error originates.
+   */
   Handle<Value> GetScriptResourceName() const;
 
+  /**
+   * Returns the resource data for the script from where the function causing
+   * the error originates.
+   */
+  Handle<Value> GetScriptData() const;
+
   /**
    * Returns the number, 1-based, of the line where the error occurred.
    */
index d779eb26aea81797542e244ee3c361b2b1f4bc07..2d6a3a11b2d48fc4fc681a8826a2447c4f812be0 100644 (file)
@@ -251,6 +251,24 @@ const AccessorDescriptor Accessors::ScriptColumnOffset = {
 };
 
 
+//
+// Accessors::ScriptData
+//
+
+
+Object* Accessors::ScriptGetData(Object* object, void*) {
+  Object* script = JSValue::cast(object)->value();
+  return Script::cast(script)->data();
+}
+
+
+const AccessorDescriptor Accessors::ScriptData = {
+  ScriptGetData,
+  IllegalSetter,
+  0
+};
+
+
 //
 // Accessors::ScriptType
 //
index 938b0142aa48a9edc185972403a78bc6394b6d62..d174c9088b943c3b7abedd2e879f1c07cf656b17 100644 (file)
@@ -45,6 +45,7 @@ namespace v8 { namespace internal {
   V(ScriptId)            \
   V(ScriptLineOffset)    \
   V(ScriptColumnOffset)  \
+  V(ScriptData)          \
   V(ScriptType)          \
   V(ScriptLineEnds)      \
   V(ObjectPrototype)
@@ -84,6 +85,7 @@ class Accessors : public AllStatic {
   static Object* ScriptGetSource(Object* object, void*);
   static Object* ScriptGetLineOffset(Object* object, void*);
   static Object* ScriptGetColumnOffset(Object* object, void*);
+  static Object* ScriptGetData(Object* object, void*);
   static Object* ScriptGetType(Object* object, void*);
   static Object* ScriptGetLineEnds(Object* object, void*);
   static Object* ObjectGetPrototype(Object* receiver, void*);
index ca76c5ecd44199c803a3a8031884feb2eabc1a70..1a55830ed33fa2363157055231f3ac3848c72ead 100644 (file)
@@ -1108,6 +1108,19 @@ Local<Value> Script::Id() {
 }
 
 
+void Script::SetData(v8::Handle<Value> data) {
+  ON_BAILOUT("v8::Script::SetData()", return);
+  LOG_API("Script::SetData");
+  {
+    HandleScope scope;
+    i::Handle<i::JSFunction> fun = Utils::OpenHandle(this);
+    i::Handle<i::Object> raw_data = Utils::OpenHandle(*data);
+    i::Handle<i::Script> script(i::Script::cast(fun->shared()->script()));
+    script->set_data(*raw_data);
+  }
+}
+
+
 // --- E x c e p t i o n s ---
 
 
@@ -1199,6 +1212,22 @@ v8::Handle<Value> Message::GetScriptResourceName() const {
 }
 
 
+v8::Handle<Value> Message::GetScriptData() const {
+  if (IsDeadCheck("v8::Message::GetScriptResourceData()")) {
+    return Local<Value>();
+  }
+  ENTER_V8;
+  HandleScope scope;
+  i::Handle<i::JSObject> obj =
+      i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
+  // Return this.script.data.
+  i::Handle<i::JSValue> script =
+      i::Handle<i::JSValue>::cast(GetProperty(obj, "script"));
+  i::Handle<i::Object> data(i::Script::cast(script->value())->data());
+  return scope.Close(Utils::ToLocal(data));
+}
+
+
 static i::Handle<i::Object> CallV8HeapFunction(const char* name,
                                                i::Handle<i::Object> recv,
                                                int argc,
index d1b638cc2da9a04a8ca994a0c71a16483380d6fa..eebec748af8ab117a91b0a2cd48442df753d88bc 100644 (file)
@@ -1019,6 +1019,13 @@ bool Genesis::InstallNatives() {
             Factory::LookupAsciiSymbol("column_offset"),
             proxy_column_offset,
             common_attributes);
+    Handle<Proxy> proxy_data = Factory::NewProxy(&Accessors::ScriptData);
+    script_descriptors =
+        Factory::CopyAppendProxyDescriptor(
+            script_descriptors,
+            Factory::LookupAsciiSymbol("data"),
+            proxy_data,
+            common_attributes);
     Handle<Proxy> proxy_type = Factory::NewProxy(&Accessors::ScriptType);
     script_descriptors =
         Factory::CopyAppendProxyDescriptor(
index 10d0614cdc362809ed90187ae1bbcc0e64aeb2fd..da36cc473e3262e94c0421dda90982df12aa8433 100644 (file)
@@ -1021,6 +1021,9 @@ function MakeScriptObject_(script, include_source) {
             columnOffset: script.columnOffset(),
             lineCount: script.lineCount(),
           };
+  if (!IS_UNDEFINED(script.data())) {
+    o.data = script.data();
+  }
   if (include_source) {
     o.source = script.source();
   }
@@ -1679,6 +1682,9 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
       script.lineOffset = scripts[i].line_offset;
       script.columnOffset = scripts[i].column_offset;
       script.lineCount = scripts[i].lineCount();
+      if (scripts[i].data) {
+        script.data = scripts[i].data;
+      }
       if (includeSource) {
         script.source = scripts[i].source;
       } else {
index 2d126a75eaadfbb1d405438d3da478d4007389d1..c849ab71e94bc1c6e36a1c4b82f739e91a7bca55 100644 (file)
@@ -173,6 +173,7 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
   script->set_id(Heap::last_script_id());
   script->set_line_offset(Smi::FromInt(0));
   script->set_column_offset(Smi::FromInt(0));
+  script->set_data(Heap::undefined_value());
   script->set_type(Smi::FromInt(SCRIPT_TYPE_NORMAL));
   script->set_wrapper(*Factory::NewProxy(0, TENURED));
   script->set_line_ends(Heap::undefined_value());
index 9c9d713da8f197d4232832bf510d4b246776d75b..dc4d7eb27c71931dd74e300243e7fb0ff99df413 100644 (file)
@@ -1582,6 +1582,11 @@ ScriptMirror.prototype.columnOffset = function() {
 };
 
 
+ScriptMirror.prototype.data = function() {
+  return this.script_.data;
+};
+
+
 ScriptMirror.prototype.scriptType = function() {
   return this.script_.type;
 };
index f40fd3e6be717df28e5df7ddc16ad57582467995..635ef0f2b48f3d6c3b5a542c2efe848eb89224c2 100644 (file)
@@ -924,7 +924,11 @@ void Script::ScriptVerify() {
   VerifyPointer(name());
   line_offset()->SmiVerify();
   column_offset()->SmiVerify();
+  VerifyPointer(data());
+  VerifyPointer(wrapper());
   type()->SmiVerify();
+  VerifyPointer(line_ends());
+  VerifyPointer(id());
 }
 
 
index 50608f75716739c99b59f6d9f275ef2a06465443..73b9c84943eda2bf788e057530c96b867cf9e346 100644 (file)
@@ -2053,6 +2053,7 @@ ACCESSORS(Script, name, Object, kNameOffset)
 ACCESSORS(Script, id, Object, kIdOffset)
 ACCESSORS(Script, line_offset, Smi, kLineOffsetOffset)
 ACCESSORS(Script, column_offset, Smi, kColumnOffsetOffset)
+ACCESSORS(Script, data, Object, kDataOffset)
 ACCESSORS(Script, wrapper, Proxy, kWrapperOffset)
 ACCESSORS(Script, type, Smi, kTypeOffset)
 ACCESSORS(Script, line_ends, Object, kLineEndsOffset)
index 89e5182c18f8bed908eab9f6a711d81782213cc5..db3c4498a3b92072250b5243476ac7d0b4698d0a 100644 (file)
@@ -2603,6 +2603,9 @@ class Script: public Struct {
   // extracted.
   DECL_ACCESSORS(column_offset, Smi)
 
+  // [data]: additional data associated with this script.
+  DECL_ACCESSORS(data, Object)
+
   // [wrapper]: the wrapper cache.
   DECL_ACCESSORS(wrapper, Proxy)
 
@@ -2623,7 +2626,8 @@ class Script: public Struct {
   static const int kNameOffset = kSourceOffset + kPointerSize;
   static const int kLineOffsetOffset = kNameOffset + kPointerSize;
   static const int kColumnOffsetOffset = kLineOffsetOffset + kPointerSize;
-  static const int kWrapperOffset = kColumnOffsetOffset + kPointerSize;
+  static const int kDataOffset = kColumnOffsetOffset + kPointerSize;
+  static const int kWrapperOffset = kDataOffset + kPointerSize;
   static const int kTypeOffset = kWrapperOffset + kPointerSize;
   static const int kLineEndsOffset = kTypeOffset + kPointerSize;
   static const int kIdOffset = kLineEndsOffset + kPointerSize;
index 13185abf5665bc3a16beb2a472469db7b92875dd..ccbb262a1c1c8037d9d2195b45bf25cab30aab7c 100644 (file)
@@ -1394,6 +1394,7 @@ static void check_message(v8::Handle<v8::Message> message,
                           v8::Handle<Value> data) {
   CHECK_EQ(5.76, data->NumberValue());
   CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
+  CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
   message_received = true;
 }
 
@@ -1406,7 +1407,10 @@ THREADED_TEST(MessageHandlerData) {
   LocalContext context;
   v8::ScriptOrigin origin =
       v8::ScriptOrigin(v8_str("6.75"));
-  Script::Compile(v8_str("throw 'error'"), &origin)->Run();
+  v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
+                                                  &origin);
+  script->SetData(v8_str("7.56"));
+  script->Run();
   CHECK(message_received);
   // clear out the message listener
   v8::V8::RemoveMessageListeners(check_message);
index e482c7f1c3e070a4eb103e0298e2a40089ed46cd..c153593607dcbf5d34ec9c5a4fe963e64ba0a888 100644 (file)
@@ -525,6 +525,24 @@ const char* frame_source_column_source =
 v8::Local<v8::Function> frame_source_column;
 
 
+// Source for The JavaScript function which picks out the script name for the
+// top frame.
+const char* frame_script_name_source =
+    "function frame_script_name(exec_state) {"
+    "  return exec_state.frame(0).func().script().name();"
+    "}";
+v8::Local<v8::Function> frame_script_name;
+
+
+// Source for The JavaScript function which picks out the script data for the
+// top frame.
+const char* frame_script_data_source =
+    "function frame_script_data(exec_state) {"
+    "  return exec_state.frame(0).func().script().data();"
+    "}";
+v8::Local<v8::Function> frame_script_data;
+
+
 // Source for The JavaScript function which returns the number of frames.
 static const char* frame_count_source =
     "function frame_count(exec_state) {"
@@ -536,6 +554,11 @@ v8::Handle<v8::Function> frame_count;
 // Global variable to store the last function hit - used by some tests.
 char last_function_hit[80];
 
+// Global variable to store the name and data for last script hit - used by some
+// tests.
+char last_script_name_hit[80];
+char last_script_data_hit[80];
+
 // Global variables to store the last source position - used by some tests.
 int last_source_line = -1;
 int last_source_column = -1;
@@ -586,6 +609,37 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event,
       CHECK(result->IsNumber());
       last_source_column = result->Int32Value();
     }
+
+    if (!frame_script_name.IsEmpty()) {
+      // Get the script name of the function script.
+      const int argc = 1;
+      v8::Handle<v8::Value> argv[argc] = { exec_state };
+      v8::Handle<v8::Value> result = frame_script_name->Call(exec_state,
+                                                             argc, argv);
+      if (result->IsUndefined()) {
+        last_script_name_hit[0] = '\0';
+      } else {
+        CHECK(result->IsString());
+        v8::Handle<v8::String> script_name(result->ToString());
+        script_name->WriteAscii(last_script_name_hit);
+      }
+    }
+
+    if (!frame_script_data.IsEmpty()) {
+      // Get the script data of the function script.
+      const int argc = 1;
+      v8::Handle<v8::Value> argv[argc] = { exec_state };
+      v8::Handle<v8::Value> result = frame_script_data->Call(exec_state,
+                                                             argc, argv);
+      if (result->IsUndefined()) {
+        last_script_data_hit[0] = '\0';
+      } else {
+        result = result->ToString();
+        CHECK(result->IsString());
+        v8::Handle<v8::String> script_data(result->ToString());
+        script_data->WriteAscii(last_script_data_hit);
+      }
+    }
   }
 }
 
@@ -4249,3 +4303,58 @@ TEST(DebugGetLoadedScripts) {
   // Must not crash while accessing line_ends.
   i::FLAG_allow_natives_syntax = allow_natives_syntax;
 }
+
+
+// Test script break points set on lines.
+TEST(ScriptNameAndData) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+  env.ExposeDebug();
+
+  // Create functions for retrieving script name and data for the function on
+  // the top frame when hitting a break point.
+  frame_script_name = CompileFunction(&env,
+                                      frame_script_name_source,
+                                      "frame_script_name");
+  frame_script_data = CompileFunction(&env,
+                                      frame_script_data_source,
+                                      "frame_script_data");
+
+  v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+                                   v8::Undefined());
+
+  // Test function source.
+  v8::Local<v8::String> script = v8::String::New(
+    "function f() {\n"
+    "  debugger;\n"
+    "}\n");
+
+  v8::ScriptOrigin origin1 = v8::ScriptOrigin(v8::String::New("name"));
+  v8::Handle<v8::Script> script1 = v8::Script::Compile(script, &origin1);
+  script1->SetData(v8::String::New("data"));
+  script1->Run();
+  v8::Script::Compile(script, &origin1)->Run();
+  v8::Local<v8::Function> f;
+  f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(1, break_point_hit_count);
+  CHECK_EQ("name", last_script_name_hit);
+  CHECK_EQ("data", last_script_data_hit);
+
+  v8::Local<v8::String> data_obj_source = v8::String::New(
+    "({ a: 'abc',\n"
+    "  b: 123,\n"
+    "  toString: function() { return this.a + ' ' + this.b; }\n"
+    "})\n");
+  v8::Local<v8::Value> data_obj = v8::Script::Compile(data_obj_source)->Run();
+  v8::ScriptOrigin origin2 = v8::ScriptOrigin(v8::String::New("new name"));
+  v8::Handle<v8::Script> script2 = v8::Script::Compile(script, &origin2);
+  script2->Run();
+  script2->SetData(data_obj);
+  f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(2, break_point_hit_count);
+  CHECK_EQ("new name", last_script_name_hit);
+  CHECK_EQ("abc 123", last_script_data_hit);
+}