Whenever a script object is created it is assigned a unique id. Currently this id...
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 10 Mar 2009 08:10:50 +0000 (08:10 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 10 Mar 2009 08:10:50 +0000 (08:10 +0000)
Changed the script break points to be able to handle both break points based on script names and script ids. When break points are set through a script id the position is relative to the script itself. This is different from the script break points set through script names where the line/coulmn offset is taken into account.

This has the side effect that function break points are not converted into script break points for named scripts.

Show the script id in the D8 shell debugger when listing all scripts using the 'scripts' command.
Review URL: http://codereview.chromium.org/40317

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

15 files changed:
include/v8.h
src/accessors.cc
src/accessors.h
src/api.cc
src/bootstrapper.cc
src/d8.js
src/debug-delay.js
src/factory.cc
src/factory.h
src/mirror-delay.js
src/objects-inl.h
src/objects.h
test/cctest/test-debug.cc
test/mjsunit/debug-script-breakpoints.js
test/mjsunit/debug-setbreakpoint.js

index a048acd..44d0307 100644 (file)
@@ -524,6 +524,11 @@ class EXPORT Script {
    * Runs the script returning the resulting value.
    */
   Local<Value> Run();
+
+  /**
+   * Returns the script id value.
+   */
+  Local<Value> Id();
 };
 
 
index 901dc07..cc89808 100644 (file)
@@ -198,6 +198,24 @@ const AccessorDescriptor Accessors::ScriptName = {
 
 
 //
+// Accessors::ScriptId
+//
+
+
+Object* Accessors::ScriptGetId(Object* object, void*) {
+  Object* script = JSValue::cast(object)->value();
+  return Script::cast(script)->id();
+}
+
+
+const AccessorDescriptor Accessors::ScriptId = {
+  ScriptGetId,
+  IllegalSetter,
+  0
+};
+
+
+//
 // Accessors::ScriptLineOffset
 //
 
index 0175c9a..938b014 100644 (file)
@@ -42,6 +42,7 @@ namespace v8 { namespace internal {
   V(StringLength)        \
   V(ScriptSource)        \
   V(ScriptName)          \
+  V(ScriptId)            \
   V(ScriptLineOffset)    \
   V(ScriptColumnOffset)  \
   V(ScriptType)          \
@@ -79,6 +80,7 @@ class Accessors : public AllStatic {
   static Object* ArrayGetLength(Object* object, void*);
   static Object* StringGetLength(Object* object, void*);
   static Object* ScriptGetName(Object* object, void*);
+  static Object* ScriptGetId(Object* object, void*);
   static Object* ScriptGetSource(Object* object, void*);
   static Object* ScriptGetLineOffset(Object* object, void*);
   static Object* ScriptGetColumnOffset(Object* object, void*);
index dfdab9f..804557f 100644 (file)
@@ -1054,6 +1054,22 @@ Local<Value> Script::Run() {
 }
 
 
+Local<Value> Script::Id() {
+  ON_BAILOUT("v8::Script::Id()", return Local<Value>());
+  LOG_API("Script::Id");
+  i::Object* raw_id = NULL;
+  {
+    HandleScope scope;
+    i::Handle<i::JSFunction> fun = Utils::OpenHandle(this);
+    i::Handle<i::Script> script(i::Script::cast(fun->shared()->script()));
+    i::Handle<i::Object> id(script->id());
+    raw_id = *id;
+  }
+  i::Handle<i::Object> id(raw_id);
+  return Utils::ToLocal(id);
+}
+
+
 // --- E x c e p t i o n s ---
 
 
index f59f081..0a0ed83 100644 (file)
@@ -985,12 +985,19 @@ bool Genesis::InstallNatives() {
             Factory::LookupAsciiSymbol("source"),
             proxy_source,
             common_attributes);
-    Handle<Proxy> proxy_data = Factory::NewProxy(&Accessors::ScriptName);
+    Handle<Proxy> proxy_name = Factory::NewProxy(&Accessors::ScriptName);
     script_descriptors =
         Factory::CopyAppendProxyDescriptor(
             script_descriptors,
             Factory::LookupAsciiSymbol("name"),
-            proxy_data,
+            proxy_name,
+            common_attributes);
+    Handle<Proxy> proxy_id = Factory::NewProxy(&Accessors::ScriptId);
+    script_descriptors =
+        Factory::CopyAppendProxyDescriptor(
+            script_descriptors,
+            Factory::LookupAsciiSymbol("id"),
+            proxy_id,
             common_attributes);
     Handle<Proxy> proxy_line_offset =
         Factory::NewProxy(&Accessors::ScriptLineOffset);
index 27c2492..04b90fe 100644 (file)
--- a/src/d8.js
+++ b/src/d8.js
@@ -888,18 +888,19 @@ function DebugResponseDetails(response) {
         var result = '';
         for (i = 0; i < body.length; i++) {
           if (i != 0) result += '\n';
+          if (body[i].id) {
+            result += body[i].id;
+          } else {
+            result += '[no id]';
+          }
+          result += ', ';
           if (body[i].name) {
             result += body[i].name;
           } else {
             result += '[unnamed] ';
-            var sourceStart = body[i].sourceStart;
-            if (sourceStart.length > 40) {
-              sourceStart = sourceStart.substring(0, 37) + '...';
-            }
-            result += sourceStart;
           }
           result += ' (lines: ';
-          result += body[i].sourceLines;
+          result += body[i].lineCount;
           result += ', length: ';
           result += body[i].sourceLength;
           if (body[i].type == Debug.ScriptType.Native) {
@@ -907,7 +908,13 @@ function DebugResponseDetails(response) {
           } else if (body[i].type == Debug.ScriptType.Extension) {
             result += ', extension';
           }
-          result += ')';
+          result += '), [';
+          var sourceStart = body[i].sourceStart;
+          if (sourceStart.length > 40) {
+            sourceStart = sourceStart.substring(0, 37) + '...';
+          }
+          result += sourceStart;
+          result += ']';
         }
         details.text = result;
         break;
index b84fe2b..11796ad 100644 (file)
@@ -61,6 +61,10 @@ Debug.ScriptType = { Native: 0,
                      Extension: 1,
                      Normal: 2 };
 
+// The different script break point types.
+Debug.ScriptBreakPointType = { ScriptId: 0,
+                               ScriptName: 1 };
+
 function ScriptTypeFlag(type) {
   return (1 << type);
 }
@@ -210,9 +214,15 @@ function IsBreakPointTriggered(break_id, break_point) {
 
 
 // Object representing a script break point. The script is referenced by its
-// script name and the break point is represented as line and column.
-function ScriptBreakPoint(script_name, opt_line, opt_column) {
-  this.script_name_ = script_name;
+// script name or script id and the break point is represented as line and
+// column.
+function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column) {
+  this.type_ = type;
+  if (type == Debug.ScriptBreakPointType.ScriptId) {
+    this.script_id_ = script_id_or_name;
+  } else {  // type == Debug.ScriptBreakPointType.ScriptName
+    this.script_name_ = script_id_or_name;
+  }
   this.line_ = opt_line || 0;
   this.column_ = opt_column;
   this.hit_count_ = 0;
@@ -227,6 +237,16 @@ ScriptBreakPoint.prototype.number = function() {
 };
 
 
+ScriptBreakPoint.prototype.type = function() {
+  return this.type_;
+};
+
+
+ScriptBreakPoint.prototype.script_id = function() {
+  return this.script_id_;
+};
+
+
 ScriptBreakPoint.prototype.script_name = function() {
   return this.script_name_;
 };
@@ -292,9 +312,13 @@ ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
 // Check whether a script matches this script break point. Currently this is
 // only based on script name.
 ScriptBreakPoint.prototype.matchesScript = function(script) {
-  return this.script_name_ == script.name &&
-         script.line_offset <= this.line_  &&
-         this.line_ < script.line_offset + script.lineCount();
+  if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
+    return this.script_id_ == script.id;
+  } else {  // this.type_ == Debug.ScriptBreakPointType.ScriptName
+    return this.script_name_ == script.name &&
+           script.line_offset <= this.line_  &&
+           this.line_ < script.line_offset + script.lineCount();
+  }
 };
 
 
@@ -356,7 +380,8 @@ ScriptBreakPoint.prototype.clear = function () {
 // break points set in this script.
 function UpdateScriptBreakPoints(script) {
   for (var i = 0; i < script_break_points.length; i++) {
-    if (script_break_points[i].script_name() == script.name) {
+    if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName &&
+        script_break_points[i].script_name() == script.name) {
       script_break_points[i].set(script);
     }
   }
@@ -491,15 +516,15 @@ Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
   }
   // If the script for the function has a name convert this to a script break
   // point.
-  if (script && script.name) {
+  if (script && script.id) {
     // Adjust the source position to be script relative.
     source_position += %FunctionGetScriptSourcePosition(func);
     // Find line and column for the position in the script and set a script
     // break point from that.
     var location = script.locationFromPosition(source_position);
-    return this.setScriptBreakPoint(script.name,
-                                    location.line, location.column,
-                                    opt_condition);
+    return this.setScriptBreakPointById(script.id,
+                                        location.line, location.column,
+                                        opt_condition);
   } else {
     // Set a break point directly on the function.
     var break_point = MakeBreakPoint(source_position, opt_line, opt_column);
@@ -576,18 +601,20 @@ Debug.findScriptBreakPoint = function(break_point_number, remove) {
 }
 
 
-// Sets a breakpoint in a script identified through script name at the
+// Sets a breakpoint in a script identified through id or name at the
 // specified source line and column within that line.
-Debug.setScriptBreakPoint = function(script_name, opt_line, opt_column, opt_condition) {
+Debug.setScriptBreakPoint = function(type, script_id_or_name,
+                                     opt_line, opt_column, opt_condition) {
   // Create script break point object.
-  var script_break_point = new ScriptBreakPoint(script_name, opt_line, opt_column);
+  var script_break_point =
+      new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column);
 
   // Assign number to the new script break point and add it.
   script_break_point.number_ = next_break_point_number++;
   script_break_point.setCondition(opt_condition);
   script_break_points.push(script_break_point);
 
-  // Run through all scripts to see it this script break point matches any
+  // Run through all scripts to see if this script break point matches any
   // loaded scripts.
   var scripts = this.scripts();
   for (var i = 0; i < scripts.length; i++) {
@@ -600,6 +627,24 @@ Debug.setScriptBreakPoint = function(script_name, opt_line, opt_column, opt_cond
 }
 
 
+Debug.setScriptBreakPointById = function(script_id,
+                                         opt_line, opt_column,
+                                         opt_condition) {
+  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
+                                  script_id, opt_line, opt_column,
+                                  opt_condition)
+}
+
+
+Debug.setScriptBreakPointByName = function(script_name,
+                                           opt_line, opt_column,
+                                           opt_condition) {
+  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
+                                  script_name, opt_line, opt_column,
+                                  opt_condition)
+}
+
+
 Debug.enableScriptBreakPoint = function(break_point_number) {
   var script_break_point = this.findScriptBreakPoint(break_point_number, false);
   script_break_point.enable();
@@ -792,11 +837,7 @@ BreakEvent.prototype.toJSONProtocol = function() {
     o.body.sourceLine = this.sourceLine(),
     o.body.sourceColumn = this.sourceColumn(),
     o.body.sourceLineText = this.sourceLineText(),
-    o.body.script = { name: script.name(),
-                      lineOffset: script.lineOffset(),
-                      columnOffset: script.columnOffset(),
-                      lineCount: script.lineCount()
-                    };
+    o.body.script = MakeScriptObject_(script, false);
   }
 
   // Add an Array of break points hit if any.
@@ -889,11 +930,7 @@ ExceptionEvent.prototype.toJSONProtocol = function() {
     // Add script information to the event if available.
     var script = this.func().script();
     if (script) {
-      o.body.script = { name: script.name(),
-                        lineOffset: script.lineOffset(),
-                        columnOffset: script.columnOffset(),
-                        lineCount: script.lineCount()
-                      };
+      o.body.script = MakeScriptObject_(script, false);
     }
   } else {
     o.body.sourceLine = -1;
@@ -942,12 +979,7 @@ CompileEvent.prototype.toJSONProtocol = function() {
     o.event = "afterCompile";
   }
   o.body = {};
-  o.body.script = { name: this.script_.name(),
-                    lineOffset: this.script_.lineOffset(),
-                    columnOffset: this.script_.columnOffset(),
-                    lineCount: this.script_.lineCount(),
-                    source: this.script_.source()
-                   };
+  o.body.script = MakeScriptObject_(this.script_, true);
 
   return o.toJSONProtocol();
 }
@@ -978,6 +1010,20 @@ NewFunctionEvent.prototype.setBreakPoint = function(p) {
 };
 
 
+function MakeScriptObject_(script, include_source) {
+  var o = { id: script.id(),
+            name: script.name(),
+            lineOffset: script.lineOffset(),
+            columnOffset: script.columnOffset(),
+            lineCount: script.lineCount(),
+          };
+  if (include_source) {
+    o.source = script.source();
+  }
+  return o;
+};
+
+
 function DebugCommandProcessor(exec_state) {
   this.exec_state_ = exec_state;
   this.running_ = false;
@@ -1222,7 +1268,7 @@ DebugCommandProcessor.prototype.setBreakPointRequest_ =
     response.failed('Missing argument "type" or "target"');
     return;
   }
-  if (type != 'function' && type != 'script') {
+  if (type != 'function' && type != 'script' && type != 'scriptId') {
     response.failed('Illegal type "' + type + '"');
     return;
   }
@@ -1251,11 +1297,13 @@ DebugCommandProcessor.prototype.setBreakPointRequest_ =
 
     // Set function break point.
     break_point_number = Debug.setBreakPoint(f, line, column, condition);
-  } else {
+  } else if (type == 'script') {
     // set script break point.
-    break_point_number = Debug.setScriptBreakPoint(target,
-                                                   line, column,
-                                                   condition);
+    break_point_number =
+        Debug.setScriptBreakPointByName(target, line, column, condition);
+  } else {  // type == 'scriptId.
+    break_point_number =
+        Debug.setScriptBreakPointById(target, line, column, condition);
   }
 
   // Set additional break point properties.
@@ -1273,8 +1321,13 @@ DebugCommandProcessor.prototype.setBreakPointRequest_ =
 
   // Add break point information to the response.
   if (break_point instanceof ScriptBreakPoint) {
-    response.body.type = 'script';
-    response.body.script_name = break_point.script_name();
+    if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
+      response.body.type = 'scriptId';
+      response.body.script_id = break_point.script_id();
+    } else {
+      response.body.type = 'scriptName';
+      response.body.script_name = break_point.script_name();
+    }
     response.body.line = break_point.line();
     response.body.column = break_point.column();
   } else {
@@ -1607,6 +1660,7 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
       if (scripts[i].name) {
         script.name = scripts[i].name;
       }
+      script.id = scripts[i].id;
       script.lineOffset = scripts[i].line_offset;
       script.columnOffset = scripts[i].column_offset;
       script.lineCount = scripts[i].lineCount();
index 98b7619..a9cdd43 100644 (file)
@@ -153,9 +153,12 @@ Handle<AccessorInfo> Factory::NewAccessorInfo() {
 
 
 Handle<Script> Factory::NewScript(Handle<String> source) {
+  static uint32_t next_id = 1;
+
   Handle<Script> script = Handle<Script>::cast(NewStruct(SCRIPT_TYPE));
   script->set_source(*source);
   script->set_name(Heap::undefined_value());
+  script->set_id(*Factory::NewNumberFromUint(next_id++));
   script->set_line_offset(Smi::FromInt(0));
   script->set_column_offset(Smi::FromInt(0));
   script->set_type(Smi::FromInt(SCRIPT_TYPE_NORMAL));
@@ -277,6 +280,11 @@ Handle<Object> Factory::NewNumberFromInt(int value) {
 }
 
 
+Handle<Object> Factory::NewNumberFromUint(uint32_t value) {
+  CALL_HEAP_FUNCTION(Heap::NumberFromUint32(value), Object);
+}
+
+
 Handle<JSObject> Factory::NewNeanderObject() {
   CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(Heap::neander_map()),
                      JSObject);
index f282896..555982e 100644 (file)
@@ -170,6 +170,7 @@ class Factory : public AllStatic {
                                   PretenureFlag pretenure = NOT_TENURED);
 
   static Handle<Object> NewNumberFromInt(int value);
+  static Handle<Object> NewNumberFromUint(uint32_t value);
 
   // These objects are used by the api to create env-independent data
   // structures in the heap.
index 2f413fa..d5da445 100644 (file)
@@ -1359,7 +1359,8 @@ FrameMirror.prototype.sourcePosition = function() {
 
 FrameMirror.prototype.sourceLocation = function() {
   if (this.func().resolved() && this.func().script()) {
-    return this.func().script().locationFromPosition(this.sourcePosition());
+    return this.func().script().locationFromPosition(this.sourcePosition(),
+                                                     true);
   }
 };
 
@@ -1561,6 +1562,11 @@ ScriptMirror.prototype.name = function() {
 };
 
 
+ScriptMirror.prototype.id = function() {
+  return this.script_.id;
+};
+
+
 ScriptMirror.prototype.source = function() {
   return this.script_.source;
 };
index 9705b75..fe3f62f 100644 (file)
@@ -2041,6 +2041,7 @@ ACCESSORS(TypeSwitchInfo, types, Object, kTypesOffset)
 
 ACCESSORS(Script, source, Object, kSourceOffset)
 ACCESSORS(Script, name, Object, kNameOffset)
+ACCESSORS(Script, id, Object, kIdOffset)
 ACCESSORS(Script, line_offset, Smi, kLineOffsetOffset)
 ACCESSORS(Script, column_offset, Smi, kColumnOffsetOffset)
 ACCESSORS(Script, wrapper, Proxy, kWrapperOffset)
index 80b46f6..f9f0e84 100644 (file)
@@ -2550,6 +2550,9 @@ class Script: public Struct {
   // [name]: the script name.
   DECL_ACCESSORS(name, Object)
 
+  // [id]: the script id.
+  DECL_ACCESSORS(id, Object)
+
   // [line_offset]: script line offset in resource from where it was extracted.
   DECL_ACCESSORS(line_offset, Smi)
 
@@ -2583,7 +2586,8 @@ class Script: public Struct {
   static const int kWrapperOffset = kColumnOffsetOffset + kPointerSize;
   static const int kTypeOffset = kWrapperOffset + kPointerSize;
   static const int kLineEndsOffset = kTypeOffset + kPointerSize;
-  static const int kSize = kLineEndsOffset + kPointerSize;
+  static const int kIdOffset = kLineEndsOffset + kPointerSize;
+  static const int kSize = kIdOffset + kPointerSize;
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(Script);
index c745011..280d507 100644 (file)
@@ -210,20 +210,46 @@ static int SetBreakPointFromJS(const char* function_name,
 }
 
 
-// Set a break point in a script using the global Debug object.
-static int SetScriptBreakPointFromJS(const char* script_data,
-                                     int line, int column) {
+// Set a break point in a script identified by id using the global Debug object.
+static int SetScriptBreakPointByIdFromJS(int script_id, int line, int column) {
   EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
   if (column >= 0) {
     // Column specified set script break point on precise location.
     OS::SNPrintF(buffer,
-                 "debug.Debug.setScriptBreakPoint(\"%s\",%d,%d)",
-                 script_data, line, column);
+                 "debug.Debug.setScriptBreakPointById(%d,%d,%d)",
+                 script_id, line, column);
   } else {
     // Column not specified set script break point on line.
     OS::SNPrintF(buffer,
-                 "debug.Debug.setScriptBreakPoint(\"%s\",%d)",
-                 script_data, line);
+                 "debug.Debug.setScriptBreakPointById(%d,%d)",
+                 script_id, line);
+  }
+  buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+  {
+    v8::TryCatch try_catch;
+    v8::Handle<v8::String> str = v8::String::New(buffer.start());
+    v8::Handle<v8::Value> value = v8::Script::Compile(str)->Run();
+    ASSERT(!try_catch.HasCaught());
+    return value->Int32Value();
+  }
+}
+
+
+// Set a break point in a script identified by name using the global Debug
+// object.
+static int SetScriptBreakPointByNameFromJS(const char* script_name,
+                                           int line, int column) {
+  EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+  if (column >= 0) {
+    // Column specified set script break point on precise location.
+    OS::SNPrintF(buffer,
+                 "debug.Debug.setScriptBreakPointByName(\"%s\",%d,%d)",
+                 script_name, line, column);
+  } else {
+    // Column not specified set script break point on line.
+    OS::SNPrintF(buffer,
+                 "debug.Debug.setScriptBreakPointByName(\"%s\",%d)",
+                 script_name, line);
   }
   buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
   {
@@ -1166,8 +1192,9 @@ TEST(BreakPointThroughJavaScript) {
 }
 
 
-// Test that break points can be set using the global Debug object.
-TEST(ScriptBreakPointThroughJavaScript) {
+// Test that break points on scripts identified by name can be set using the
+// global Debug object.
+TEST(ScriptBreakPointByNameThroughJavaScript) {
   break_point_hit_count = 0;
   v8::HandleScope scope;
   DebugLocalContext env;
@@ -1175,7 +1202,6 @@ TEST(ScriptBreakPointThroughJavaScript) {
 
   v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
                                    v8::Undefined());
-  v8::Script::Compile(v8::String::New("function foo(){bar();bar();}"))->Run();
 
   v8::Local<v8::String> script = v8::String::New(
     "function f() {\n"
@@ -1213,7 +1239,7 @@ TEST(ScriptBreakPointThroughJavaScript) {
   CHECK_EQ(0, break_point_hit_count);
 
   // Call f and g with break point on line 12.
-  int sbp1 = SetScriptBreakPointFromJS("test", 12, 0);
+  int sbp1 = SetScriptBreakPointByNameFromJS("test", 12, 0);
   break_point_hit_count = 0;
   f->Call(env->Global(), 0, NULL);
   CHECK_EQ(0, break_point_hit_count);
@@ -1229,7 +1255,7 @@ TEST(ScriptBreakPointThroughJavaScript) {
   CHECK_EQ(0, break_point_hit_count);
 
   // Call f and g with break point on line 2.
-  int sbp2 = SetScriptBreakPointFromJS("test", 2, 0);
+  int sbp2 = SetScriptBreakPointByNameFromJS("test", 2, 0);
   break_point_hit_count = 0;
   f->Call(env->Global(), 0, NULL);
   CHECK_EQ(1, break_point_hit_count);
@@ -1237,17 +1263,17 @@ TEST(ScriptBreakPointThroughJavaScript) {
   CHECK_EQ(2, break_point_hit_count);
 
   // Call f and g with break point on line 2, 4, 12, 14 and 15.
-  int sbp3 = SetScriptBreakPointFromJS("test", 4, 0);
-  int sbp4 = SetScriptBreakPointFromJS("test", 12, 0);
-  int sbp5 = SetScriptBreakPointFromJS("test", 14, 0);
-  int sbp6 = SetScriptBreakPointFromJS("test", 15, 0);
+  int sbp3 = SetScriptBreakPointByNameFromJS("test", 4, 0);
+  int sbp4 = SetScriptBreakPointByNameFromJS("test", 12, 0);
+  int sbp5 = SetScriptBreakPointByNameFromJS("test", 14, 0);
+  int sbp6 = SetScriptBreakPointByNameFromJS("test", 15, 0);
   break_point_hit_count = 0;
   f->Call(env->Global(), 0, NULL);
   CHECK_EQ(2, break_point_hit_count);
   g->Call(env->Global(), 0, NULL);
   CHECK_EQ(7, break_point_hit_count);
 
-  // Remove the all the break points again.
+  // Remove all the break points again.
   break_point_hit_count = 0;
   ClearBreakPointFromJS(sbp2);
   ClearBreakPointFromJS(sbp3);
@@ -1259,19 +1285,114 @@ TEST(ScriptBreakPointThroughJavaScript) {
   g->Call(env->Global(), 0, NULL);
   CHECK_EQ(0, break_point_hit_count);
 
-  // Now set a function break point
-  int bp7 = SetBreakPointFromJS("g", 0, 0);
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+
+  // Make sure that the break point numbers are consecutive.
+  CHECK_EQ(1, sbp1);
+  CHECK_EQ(2, sbp2);
+  CHECK_EQ(3, sbp3);
+  CHECK_EQ(4, sbp4);
+  CHECK_EQ(5, sbp5);
+  CHECK_EQ(6, sbp6);
+}
+
+
+TEST(ScriptBreakPointByIdThroughJavaScript) {
+  break_point_hit_count = 0;
+  v8::HandleScope scope;
+  DebugLocalContext env;
+  env.ExposeDebug();
+
+  v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+                                   v8::Undefined());
+
+  v8::Local<v8::String> source = v8::String::New(
+    "function f() {\n"
+    "  function h() {\n"
+    "    a = 0;  // line 2\n"
+    "  }\n"
+    "  b = 1;  // line 4\n"
+    "  return h();\n"
+    "}\n"
+    "\n"
+    "function g() {\n"
+    "  function h() {\n"
+    "    a = 0;\n"
+    "  }\n"
+    "  b = 2;  // line 12\n"
+    "  h();\n"
+    "  b = 3;  // line 14\n"
+    "  f();    // line 15\n"
+    "}");
+
+  // Compile the script and get the two functions.
+  v8::ScriptOrigin origin =
+      v8::ScriptOrigin(v8::String::New("test"));
+  v8::Local<v8::Script> script = v8::Script::Compile(source, &origin);
+  script->Run();
+  v8::Local<v8::Function> f =
+      v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+  v8::Local<v8::Function> g =
+      v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("g")));
+
+  // Get the script id knowing that internally it is a 32 integer.
+  uint32_t script_id = script->Id()->Uint32Value();
+
+  // Call f and g without break points.
+  break_point_hit_count = 0;
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(0, break_point_hit_count);
+  g->Call(env->Global(), 0, NULL);
+  CHECK_EQ(0, break_point_hit_count);
+
+  // Call f and g with break point on line 12.
+  int sbp1 = SetScriptBreakPointByIdFromJS(script_id, 12, 0);
+  break_point_hit_count = 0;
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(0, break_point_hit_count);
   g->Call(env->Global(), 0, NULL);
   CHECK_EQ(1, break_point_hit_count);
 
-  // Reload the script and get g again checking that the break point survives.
-  // This tests that the function break point was converted to a script break
-  // point.
-  v8::Script::Compile(script, &origin)->Run();
-  g = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("g")));
+  // Remove the break point again.
+  break_point_hit_count = 0;
+  ClearBreakPointFromJS(sbp1);
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(0, break_point_hit_count);
+  g->Call(env->Global(), 0, NULL);
+  CHECK_EQ(0, break_point_hit_count);
+
+  // Call f and g with break point on line 2.
+  int sbp2 = SetScriptBreakPointByIdFromJS(script_id, 2, 0);
+  break_point_hit_count = 0;
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(1, break_point_hit_count);
   g->Call(env->Global(), 0, NULL);
   CHECK_EQ(2, break_point_hit_count);
 
+  // Call f and g with break point on line 2, 4, 12, 14 and 15.
+  int sbp3 = SetScriptBreakPointByIdFromJS(script_id, 4, 0);
+  int sbp4 = SetScriptBreakPointByIdFromJS(script_id, 12, 0);
+  int sbp5 = SetScriptBreakPointByIdFromJS(script_id, 14, 0);
+  int sbp6 = SetScriptBreakPointByIdFromJS(script_id, 15, 0);
+  break_point_hit_count = 0;
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(2, break_point_hit_count);
+  g->Call(env->Global(), 0, NULL);
+  CHECK_EQ(7, break_point_hit_count);
+
+  // Remove all the break points again.
+  break_point_hit_count = 0;
+  ClearBreakPointFromJS(sbp2);
+  ClearBreakPointFromJS(sbp3);
+  ClearBreakPointFromJS(sbp4);
+  ClearBreakPointFromJS(sbp5);
+  ClearBreakPointFromJS(sbp6);
+  f->Call(env->Global(), 0, NULL);
+  CHECK_EQ(0, break_point_hit_count);
+  g->Call(env->Global(), 0, NULL);
+  CHECK_EQ(0, break_point_hit_count);
+
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
 
@@ -1282,7 +1403,6 @@ TEST(ScriptBreakPointThroughJavaScript) {
   CHECK_EQ(4, sbp4);
   CHECK_EQ(5, sbp5);
   CHECK_EQ(6, sbp6);
-  CHECK_EQ(7, bp7);
 }
 
 
@@ -1309,7 +1429,7 @@ TEST(EnableDisableScriptBreakPoint) {
       v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
 
   // Set script break point on line 1 (in function f).
-  int sbp = SetScriptBreakPointFromJS("test", 1, 0);
+  int sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
 
   // Call f while enabeling and disabling the script break point.
   break_point_hit_count = 0;
@@ -1370,7 +1490,7 @@ TEST(ConditionalScriptBreakPoint) {
       v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
 
   // Set script break point on line 5 (in function g).
-  int sbp1 = SetScriptBreakPointFromJS("test", 5, 0);
+  int sbp1 = SetScriptBreakPointByNameFromJS("test", 5, 0);
 
   // Call f with different conditions on the script break point.
   break_point_hit_count = 0;
@@ -1428,7 +1548,7 @@ TEST(ScriptBreakPointIgnoreCount) {
       v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
 
   // Set script break point on line 1 (in function f).
-  int sbp = SetScriptBreakPointFromJS("test", 1, 0);
+  int sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
 
   // Call f with different ignores on the script break point.
   break_point_hit_count = 0;
@@ -1484,7 +1604,7 @@ TEST(ScriptBreakPointReload) {
   v8::ScriptOrigin origin_2 = v8::ScriptOrigin(v8::String::New("2"));
 
   // Set a script break point before the script is loaded.
-  SetScriptBreakPointFromJS("1", 2, 0);
+  SetScriptBreakPointByNameFromJS("1", 2, 0);
 
   // Compile the script and get the function.
   v8::Script::Compile(script, &origin_1)->Run();
@@ -1545,7 +1665,7 @@ TEST(ScriptBreakPointMultiple) {
       v8::ScriptOrigin(v8::String::New("test"));
 
   // Set a script break point before the scripts are loaded.
-  int sbp = SetScriptBreakPointFromJS("test", 1, 0);
+  int sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
 
   // Compile the scripts with same script data and get the functions.
   v8::Script::Compile(script_f, &origin)->Run();
@@ -1571,7 +1691,7 @@ TEST(ScriptBreakPointMultiple) {
   CHECK_EQ(0, break_point_hit_count);
 
   // Set script break point with the scripts loaded.
-  sbp = SetScriptBreakPointFromJS("test", 1, 0);
+  sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
 
   // Call f and g and check that the script break point is active.
   break_point_hit_count = 0;
@@ -1607,8 +1727,8 @@ TEST(ScriptBreakPointLineOffset) {
                           v8::Integer::New(7));
 
   // Set two script break points before the script is loaded.
-  int sbp1 = SetScriptBreakPointFromJS("test.html", 8, 0);
-  int sbp2 = SetScriptBreakPointFromJS("test.html", 9, 0);
+  int sbp1 = SetScriptBreakPointByNameFromJS("test.html", 8, 0);
+  int sbp2 = SetScriptBreakPointByNameFromJS("test.html", 9, 0);
 
   // Compile the script and get the function.
   v8::Script::Compile(script, &origin)->Run();
@@ -1629,7 +1749,7 @@ TEST(ScriptBreakPointLineOffset) {
   CHECK_EQ(0, break_point_hit_count);
 
   // Set a script break point with the script loaded.
-  sbp1 = SetScriptBreakPointFromJS("test.html", 9, 0);
+  sbp1 = SetScriptBreakPointByNameFromJS("test.html", 9, 0);
 
   // Call f and check that the script break point is active.
   break_point_hit_count = 0;
@@ -1673,9 +1793,9 @@ TEST(ScriptBreakPointLine) {
     " a=5;                      // line 12");
 
   // Set a couple script break point before the script is loaded.
-  int sbp1 = SetScriptBreakPointFromJS("test.html", 0, -1);
-  int sbp2 = SetScriptBreakPointFromJS("test.html", 1, -1);
-  int sbp3 = SetScriptBreakPointFromJS("test.html", 5, -1);
+  int sbp1 = SetScriptBreakPointByNameFromJS("test.html", 0, -1);
+  int sbp2 = SetScriptBreakPointByNameFromJS("test.html", 1, -1);
+  int sbp3 = SetScriptBreakPointByNameFromJS("test.html", 5, -1);
 
   // Compile the script and get the function.
   break_point_hit_count = 0;
@@ -1700,7 +1820,7 @@ TEST(ScriptBreakPointLine) {
 
   // Clear the script break point on g and set one on h.
   ClearBreakPointFromJS(sbp3);
-  int sbp4 = SetScriptBreakPointFromJS("test.html", 6, -1);
+  int sbp4 = SetScriptBreakPointByNameFromJS("test.html", 6, -1);
 
   // Call g and check that the script break point in h is hit.
   g->Call(env->Global(), 0, NULL);
@@ -1712,7 +1832,7 @@ TEST(ScriptBreakPointLine) {
   // more.
   ClearBreakPointFromJS(sbp2);
   ClearBreakPointFromJS(sbp4);
-  int sbp5 = SetScriptBreakPointFromJS("test.html", 4, -1);
+  int sbp5 = SetScriptBreakPointByNameFromJS("test.html", 4, -1);
   break_point_hit_count = 0;
   f->Call(env->Global(), 0, NULL);
   g->Call(env->Global(), 0, NULL);
@@ -1725,7 +1845,7 @@ TEST(ScriptBreakPointLine) {
   CHECK_EQ(0, strlen(last_function_hit));
 
   // Set a break point in the code after the last function decleration.
-  int sbp6 = SetScriptBreakPointFromJS("test.html", 12, -1);
+  int sbp6 = SetScriptBreakPointByNameFromJS("test.html", 12, -1);
 
   // Reload the script which should hit three break points.
   break_point_hit_count = 0;
index 28c8018..ec9656c 100644 (file)
@@ -29,8 +29,8 @@
 // Get the Debug object exposed from the debug context global object.
 Debug = debug.Debug
 
-// Set and remove a script break point.
-var sbp = Debug.setScriptBreakPoint("1", 2, 3);
+// Set and remove a script break point for a named script.
+var sbp = Debug.setScriptBreakPointByName("1", 2, 3);
 assertEquals(1, Debug.scriptBreakPoints().length);
 assertEquals("1", Debug.scriptBreakPoints()[0].script_name());
 assertEquals(2, Debug.scriptBreakPoints()[0].line());
@@ -38,10 +38,10 @@ assertEquals(3, Debug.scriptBreakPoints()[0].column());
 Debug.clearBreakPoint(sbp);
 assertEquals(0, Debug.scriptBreakPoints().length);
 
-// Set three script break points.
-var sbp1 = Debug.setScriptBreakPoint("1", 2, 3);
-var sbp2 = Debug.setScriptBreakPoint("2", 3, 4);
-var sbp3 = Debug.setScriptBreakPoint("3", 4, 5);
+// Set three script break points for named scripts.
+var sbp1 = Debug.setScriptBreakPointByName("1", 2, 3);
+var sbp2 = Debug.setScriptBreakPointByName("2", 3, 4);
+var sbp3 = Debug.setScriptBreakPointByName("3", 4, 5);
 
 // Check the content of the script break points.
 assertEquals(3, Debug.scriptBreakPoints().length);
@@ -57,7 +57,48 @@ for (var i = 0; i < Debug.scriptBreakPoints().length; i++) {
     assertEquals(4, x.line());
     assertEquals(5, x.column());
   } else {
-    assertUnreachable("unecpected script_data " + x.script_data());
+    assertUnreachable("unecpected script_name " + x.script_name());
+  }
+}
+
+// Remove script break points (in another order than they where added).
+assertEquals(3, Debug.scriptBreakPoints().length);
+Debug.clearBreakPoint(sbp1);
+assertEquals(2, Debug.scriptBreakPoints().length);
+Debug.clearBreakPoint(sbp3);
+assertEquals(1, Debug.scriptBreakPoints().length);
+Debug.clearBreakPoint(sbp2);
+assertEquals(0, Debug.scriptBreakPoints().length);
+
+// Set and remove a script break point for a script id.
+var sbp = Debug.setScriptBreakPointById(1, 2, 3);
+assertEquals(1, Debug.scriptBreakPoints().length);
+assertEquals(1, Debug.scriptBreakPoints()[0].script_id());
+assertEquals(2, Debug.scriptBreakPoints()[0].line());
+assertEquals(3, Debug.scriptBreakPoints()[0].column());
+Debug.clearBreakPoint(sbp);
+assertEquals(0, Debug.scriptBreakPoints().length);
+
+// Set three script break points for script ids.
+var sbp1 = Debug.setScriptBreakPointById(1, 2, 3);
+var sbp2 = Debug.setScriptBreakPointById(2, 3, 4);
+var sbp3 = Debug.setScriptBreakPointById(3, 4, 5);
+
+// Check the content of the script break points.
+assertEquals(3, Debug.scriptBreakPoints().length);
+for (var i = 0; i < Debug.scriptBreakPoints().length; i++) {
+  var x = Debug.scriptBreakPoints()[i];
+  if (1 == x.script_id()) {
+    assertEquals(2, x.line());
+    assertEquals(3, x.column());
+  } else if (2 == x.script_id()) {
+    assertEquals(3, x.line());
+    assertEquals(4, x.column());
+  } else if (3 == x.script_id()) {
+    assertEquals(4, x.line());
+    assertEquals(5, x.column());
+  } else {
+    assertUnreachable("unecpected script_id " + x.script_id());
   }
 }
 
index 2a8cd6c..329362b 100644 (file)
 Debug = debug.Debug
 
 // Simple function which stores the last debug event.
-listenerComplete = false;
-exception = false;
+var listenerComplete = false;
+var exception = false;
+var f_script_id = 0;
+var g_script_id = 0;
+var h_script_id = 0;
+var f_line = 0;
+var g_line = 0;
 
 var base_request = '"seq":0,"type":"request","command":"setbreakpoint"'
 
@@ -44,13 +49,18 @@ function safeEval(code) {
   }
 }
 
-function testArguments(dcp, arguments, success, type) {
+function testArguments(dcp, arguments, success, is_script) {
   var request = '{' + base_request + ',"arguments":' + arguments + '}'
   var json_response = dcp.processDebugJSONRequest(request);
   var response = safeEval(json_response);
   if (success) {
     assertTrue(response.success, json_response);
-    assertEquals(type ? type : 'script', response.body.type, json_response);
+    if (is_script) {
+      assertEquals('scriptName', response.body.type, json_response);
+    } else {
+      assertEquals('scriptId', response.body.type, json_response);
+      print(response.body.script_id);
+    }
   } else {
     assertFalse(response.success, json_response);
   }
@@ -79,18 +89,23 @@ function listener(event, exec_state, event_data, data) {
     testArguments(dcp, '{"type":"function","target":"f","ignoreCount":-1}', false);
 
     // Test some legal setbreakpoint requests.
-    testArguments(dcp, '{"type":"function","target":"f"}', true);
-    testArguments(dcp, '{"type":"function","target":"h"}', true, 'function');
-    testArguments(dcp, '{"type":"function","target":"f","line":1}', true);
-    testArguments(dcp, '{"type":"function","target":"f","position":1}', true);
-    testArguments(dcp, '{"type":"function","target":"f","condition":"i == 1"}', true);
-    testArguments(dcp, '{"type":"function","target":"f","enabled":true}', true);
-    testArguments(dcp, '{"type":"function","target":"f","enabled":false}', true);
-    testArguments(dcp, '{"type":"function","target":"f","ignoreCount":7}', true);
-    testArguments(dcp, '{"type":"script","target":"test"}', true);
-    testArguments(dcp, '{"type":"script","target":"test"}', true);
-    testArguments(dcp, '{"type":"script","target":"test","line":1}', true);
-    testArguments(dcp, '{"type":"script","target":"test","column":1}', true);
+    testArguments(dcp, '{"type":"function","target":"f"}', true, false);
+    testArguments(dcp, '{"type":"function","target":"h"}', true, false);
+    testArguments(dcp, '{"type":"function","target":"f","line":1}', true, false);
+    testArguments(dcp, '{"type":"function","target":"f","position":1}', true, false);
+    testArguments(dcp, '{"type":"function","target":"f","condition":"i == 1"}', true, false);
+    testArguments(dcp, '{"type":"function","target":"f","enabled":true}', true, false);
+    testArguments(dcp, '{"type":"function","target":"f","enabled":false}', true, false);
+    testArguments(dcp, '{"type":"function","target":"f","ignoreCount":7}', true, false);
+
+    testArguments(dcp, '{"type":"script","target":"test"}', true, true);
+    testArguments(dcp, '{"type":"script","target":"test"}', true, true);
+    testArguments(dcp, '{"type":"script","target":"test","line":1}', true, true);
+    testArguments(dcp, '{"type":"script","target":"test","column":1}', true, true);
+
+    testArguments(dcp, '{"type":"scriptId","target":' + f_script_id + ',"line":' + f_line + '}', true, false);
+    testArguments(dcp, '{"type":"scriptId","target":' + g_script_id + ',"line":' + g_line + '}', true, false);
+    testArguments(dcp, '{"type":"scriptId","target":' + h_script_id + ',"line":' + h_line + '}', true, false);
 
     // Indicate that all was processed.
     listenerComplete = true;
@@ -113,10 +128,27 @@ function g() {
 
 eval('function h(){}');
 
+// Check the script ids for the test functions.
+f_script_id = Debug.findScript(f).id;
+g_script_id = Debug.findScript(g).id;
+h_script_id = Debug.findScript(h).id;
+assertTrue(f_script_id > 0, "invalid script id for f");
+assertTrue(g_script_id > 0, "invalid script id for g");
+assertTrue(h_script_id > 0, "invalid script id for h");
+assertEquals(f_script_id, g_script_id);
+
+// Get the source line for the test functions.
+f_line = Debug.findFunctionSourceLocation(f).line;
+g_line = Debug.findFunctionSourceLocation(g).line;
+h_line = Debug.findFunctionSourceLocation(h).line;
+assertTrue(f_line > 0, "invalid line for f");
+assertTrue(g_line > 0, "invalid line for g");
+assertTrue(f_line < g_line);
+assertEquals(h_line, 0, "invalid line for h");
+
 // Set a break point and call to invoke the debug event listener.
 Debug.setBreakPoint(g, 0, 0);
 g();
 
 // Make sure that the debug event listener vas invoked.
-assertTrue(listenerComplete, "listener did not run to completion");
-assertFalse(exception, "exception in listener")
+assertTrue(listenerComplete, "listener did not run to completion: " + exception);