Commit d498361c authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Add more debugging information to scripts compiled through eval.

Scripts now have a compilation type which can be host, eval or JSON. Host scripts are compiled through the API, eval scripts are compiled through call to evan and JSON scripts are compiled as a result of calling JSON.parse.

For scripts scripts compiled through eval the JavaScript function in top of the stack and the pc offset into the code is stored in the script object. This makes it possible to calculate the source position of the eval call later when requested. This information can be obtained through the script mirror object and is part of the script mirror JSON serialization for the debugger protocol.

Moved the enumeration ScripType into class Script and remamed to Type. The new compilation type enumeration is also inside the class Script.

This information is now shown when using the scripts command in he developer shell debugger.
Review URL: http://codereview.chromium.org/119108

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2119 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 24d22b56
......@@ -288,6 +288,24 @@ const AccessorDescriptor Accessors::ScriptType = {
};
//
// Accessors::ScriptCompilationType
//
Object* Accessors::ScriptGetCompilationType(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->compilation_type();
}
const AccessorDescriptor Accessors::ScriptCompilationType = {
ScriptGetCompilationType,
IllegalSetter,
0
};
//
// Accessors::ScriptGetLineEnds
//
......@@ -314,9 +332,8 @@ const AccessorDescriptor Accessors::ScriptLineEnds = {
Object* Accessors::ScriptGetContextData(Object* object, void*) {
HandleScope scope;
Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
return script->context_data();
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->context_data();
}
......@@ -327,6 +344,54 @@ const AccessorDescriptor Accessors::ScriptContextData = {
};
//
// Accessors::ScriptGetEvalFromFunction
//
Object* Accessors::ScriptGetEvalFromFunction(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->eval_from_function();
}
const AccessorDescriptor Accessors::ScriptEvalFromFunction = {
ScriptGetEvalFromFunction,
IllegalSetter,
0
};
//
// Accessors::ScriptGetEvalFromPosition
//
Object* Accessors::ScriptGetEvalFromPosition(Object* object, void*) {
HandleScope scope;
Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
// If this is not a script compiled through eval there is no eval position.
int compilation_type = Smi::cast(script->compilation_type())->value();
if (compilation_type != Script::COMPILATION_TYPE_EVAL) {
return Heap::undefined_value();
}
// Get the function from where eval was called and find the source position
// from the instruction offset.
Handle<Code> code(JSFunction::cast(script->eval_from_function())->code());
return Smi::FromInt(code->SourcePosition(code->instruction_start() +
script->eval_from_instructions_offset()->value()));
}
const AccessorDescriptor Accessors::ScriptEvalFromPosition = {
ScriptGetEvalFromPosition,
IllegalSetter,
0
};
//
// Accessors::FunctionPrototype
//
......
......@@ -34,22 +34,25 @@ namespace internal {
// The list of accessor descriptors. This is a second-order macro
// taking a macro to be applied to all accessor descriptor names.
#define ACCESSOR_DESCRIPTOR_LIST(V) \
V(FunctionPrototype) \
V(FunctionLength) \
V(FunctionName) \
V(FunctionArguments) \
V(FunctionCaller) \
V(ArrayLength) \
V(StringLength) \
V(ScriptSource) \
V(ScriptName) \
V(ScriptId) \
V(ScriptLineOffset) \
V(ScriptColumnOffset) \
V(ScriptData) \
V(ScriptType) \
V(ScriptLineEnds) \
V(ScriptContextData) \
V(FunctionPrototype) \
V(FunctionLength) \
V(FunctionName) \
V(FunctionArguments) \
V(FunctionCaller) \
V(ArrayLength) \
V(StringLength) \
V(ScriptSource) \
V(ScriptName) \
V(ScriptId) \
V(ScriptLineOffset) \
V(ScriptColumnOffset) \
V(ScriptData) \
V(ScriptType) \
V(ScriptCompilationType) \
V(ScriptLineEnds) \
V(ScriptContextData) \
V(ScriptEvalFromFunction) \
V(ScriptEvalFromPosition) \
V(ObjectPrototype)
// Accessors contains all predefined proxy accessors.
......@@ -89,8 +92,11 @@ class Accessors : public AllStatic {
static Object* ScriptGetColumnOffset(Object* object, void*);
static Object* ScriptGetData(Object* object, void*);
static Object* ScriptGetType(Object* object, void*);
static Object* ScriptGetCompilationType(Object* object, void*);
static Object* ScriptGetLineEnds(Object* object, void*);
static Object* ScriptGetContextData(Object* object, void*);
static Object* ScriptGetEvalFromFunction(Object* object, void*);
static Object* ScriptGetEvalFromPosition(Object* object, void*);
static Object* ObjectGetPrototype(Object* receiver, void*);
static Object* ObjectSetPrototype(JSObject* receiver, Object* value, void*);
......
......@@ -47,7 +47,7 @@ namespace internal {
// generate an index for each native JS file.
class SourceCodeCache BASE_EMBEDDED {
public:
explicit SourceCodeCache(ScriptType type): type_(type) { }
explicit SourceCodeCache(Script::Type type): type_(type) { }
void Initialize(bool create_heap_objects) {
if (create_heap_objects) {
......@@ -89,13 +89,13 @@ class SourceCodeCache BASE_EMBEDDED {
}
private:
ScriptType type_;
Script::Type type_;
FixedArray* cache_;
DISALLOW_COPY_AND_ASSIGN(SourceCodeCache);
};
static SourceCodeCache natives_cache(SCRIPT_TYPE_NATIVE);
static SourceCodeCache extensions_cache(SCRIPT_TYPE_EXTENSION);
static SourceCodeCache natives_cache(Script::TYPE_NATIVE);
static SourceCodeCache extensions_cache(Script::TYPE_EXTENSION);
Handle<String> Bootstrapper::NativesSourceLookup(int index) {
......@@ -522,7 +522,7 @@ void Genesis::CreateRoots(v8::Handle<v8::ObjectTemplate> global_template,
empty_function->set_code(*code);
Handle<String> source = Factory::NewStringFromAscii(CStrVector("() {}"));
Handle<Script> script = Factory::NewScript(source);
script->set_type(Smi::FromInt(SCRIPT_TYPE_NATIVE));
script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
empty_function->shared()->set_script(*script);
empty_function->shared()->set_start_position(0);
empty_function->shared()->set_end_position(source->length());
......@@ -1062,6 +1062,14 @@ bool Genesis::InstallNatives() {
Factory::LookupAsciiSymbol("type"),
proxy_type,
common_attributes);
Handle<Proxy> proxy_compilation_type =
Factory::NewProxy(&Accessors::ScriptCompilationType);
script_descriptors =
Factory::CopyAppendProxyDescriptor(
script_descriptors,
Factory::LookupAsciiSymbol("compilation_type"),
proxy_compilation_type,
common_attributes);
Handle<Proxy> proxy_line_ends =
Factory::NewProxy(&Accessors::ScriptLineEnds);
script_descriptors =
......@@ -1078,13 +1086,29 @@ bool Genesis::InstallNatives() {
Factory::LookupAsciiSymbol("context_data"),
proxy_context_data,
common_attributes);
Handle<Proxy> proxy_eval_from_function =
Factory::NewProxy(&Accessors::ScriptEvalFromFunction);
script_descriptors =
Factory::CopyAppendProxyDescriptor(
script_descriptors,
Factory::LookupAsciiSymbol("eval_from_function"),
proxy_eval_from_function,
common_attributes);
Handle<Proxy> proxy_eval_from_position =
Factory::NewProxy(&Accessors::ScriptEvalFromPosition);
script_descriptors =
Factory::CopyAppendProxyDescriptor(
script_descriptors,
Factory::LookupAsciiSymbol("eval_from_position"),
proxy_eval_from_position,
common_attributes);
Handle<Map> script_map = Handle<Map>(script_fun->initial_map());
script_map->set_instance_descriptors(*script_descriptors);
// Allocate the empty script.
Handle<Script> script = Factory::NewScript(Factory::empty_string());
script->set_type(Smi::FromInt(SCRIPT_TYPE_NATIVE));
script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
global_context()->set_empty_script(*script);
}
......
......@@ -110,7 +110,22 @@ static Handle<JSFunction> MakeFunction(bool is_global,
ASSERT(!i::Top::global_context().is_null());
script->set_context_data((*i::Top::global_context())->data());
#ifdef ENABLE_DEBUGGER_SUPPORT
if (is_eval || is_json) {
script->set_compilation_type(
is_json ? Smi::FromInt(Script::COMPILATION_TYPE_JSON) :
Smi::FromInt(Script::COMPILATION_TYPE_EVAL));
// For eval scripts add information on the function from which eval was
// called.
if (is_eval) {
JavaScriptFrameIterator it;
script->set_eval_from_function(it.frame()->function());
int offset = it.frame()->pc() - it.frame()->code()->instruction_start();
script->set_eval_from_instructions_offset(Smi::FromInt(offset));
}
}
// Notify debugger
Debugger::OnBeforeCompile(script);
#endif
......
......@@ -451,7 +451,7 @@ void Shell::Initialize() {
i::Handle<i::JSFunction> script_fun = Utils::OpenHandle(*script);
i::Handle<i::Script> script_object =
i::Handle<i::Script>(i::Script::cast(script_fun->shared()->script()));
script_object->set_type(i::Smi::FromInt(i::SCRIPT_TYPE_NATIVE));
script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
// Create the evaluation context
evaluation_context_ = Context::New(NULL, global_template);
......
......@@ -93,6 +93,13 @@ Debug.ScriptType = { Native: 0,
Normal: 2 };
// The different types of script compilations matching enum
// Script::CompilationType in objects.h.
Debug.ScriptCompilationType = { Host: 0,
Eval: 1,
JSON: 2 };
// Current debug state.
const kNoFrame = -1;
Debug.State = {
......@@ -963,7 +970,18 @@ function DebugResponseDetails(response) {
if (body[i].name) {
result += body[i].name;
} else {
result += '[unnamed] ';
if (body[i].compilationType == Debug.ScriptCompilationType.Eval) {
result += 'eval from ';
var script_value = response.lookup(body[i].evalFromScript.ref);
result += ' ' + script_value.field('name');
result += ':' + (body[i].evalFromLocation.line + 1);
result += ':' + body[i].evalFromLocation.column;
} else if (body[i].compilationType ==
Debug.ScriptCompilationType.JSON) {
result += 'JSON ';
} else { // body[i].compilation == Debug.ScriptCompilationType.Host
result += '[unnamed] ';
}
}
result += ' (lines: ';
result += body[i].lineCount;
......@@ -1125,6 +1143,15 @@ ProtocolValue.prototype.type = function() {
}
/**
* Get a metadata field from a protocol value.
* @return {Object} the metadata field value
*/
ProtocolValue.prototype.field = function(name) {
return this.value_[name];
}
/**
* Check is the value is a primitive value.
* @return {boolean} true if the value is primitive
......
......@@ -62,6 +62,12 @@ Debug.ScriptType = { Native: 0,
Extension: 1,
Normal: 2 };
// The different types of script compilations matching enum
// Script::CompilationType in objects.h.
Debug.ScriptCompilationType = { Host: 0,
Eval: 1,
JSON: 2 };
// The different script break point types.
Debug.ScriptBreakPointType = { ScriptId: 0,
ScriptName: 1 };
......
......@@ -652,7 +652,7 @@ bool Debug::CompileDebuggerScript(int index) {
// Mark this script as native and return successfully.
Handle<Script> script(Script::cast(function->shared()->script()));
script->set_type(Smi::FromInt(SCRIPT_TYPE_NATIVE));
script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
return true;
}
......
......@@ -177,9 +177,12 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
script->set_column_offset(Smi::FromInt(0));
script->set_data(Heap::undefined_value());
script->set_context_data(Heap::undefined_value());
script->set_type(Smi::FromInt(SCRIPT_TYPE_NORMAL));
script->set_type(Smi::FromInt(Script::TYPE_NORMAL));
script->set_compilation_type(Smi::FromInt(Script::COMPILATION_TYPE_HOST));
script->set_wrapper(*wrapper);
script->set_line_ends(Heap::undefined_value());
script->set_eval_from_function(Heap::undefined_value());
script->set_eval_from_instructions_offset(Smi::FromInt(0));
return script;
}
......
......@@ -1617,6 +1617,11 @@ ScriptMirror.prototype.scriptType = function() {
};
ScriptMirror.prototype.compilationType = function() {
return this.script_.compilation_type;
};
ScriptMirror.prototype.lineCount = function() {
return this.script_.lineCount();
};
......@@ -1638,6 +1643,20 @@ ScriptMirror.prototype.context = function() {
};
ScriptMirror.prototype.evalFromFunction = function() {
return MakeMirror(this.script_.eval_from_function);
};
ScriptMirror.prototype.evalFromLocation = function() {
var eval_from_function = this.evalFromFunction();
if (!eval_from_function.isUndefined()) {
var position = this.script_.eval_from_position;
return eval_from_function.script().locationFromPosition(position, true);
}
};
ScriptMirror.prototype.toText = function() {
var result = '';
result += this.name();
......@@ -1901,6 +1920,14 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
}
content.sourceLength = mirror.source().length;
content.scriptType = mirror.scriptType();
content.compilationType = mirror.compilationType();
if (mirror.compilationType() == 1) { // Compilation type eval.
content.evalFromScript =
this.serializeReference(mirror.evalFromFunction().script());
var evalFromLocation = mirror.evalFromLocation()
content.evalFromLocation = { line: evalFromLocation.line,
column: evalFromLocation.column}
}
if (mirror.context()) {
content.context = this.serializeReference(mirror.context());
}
......
......@@ -2112,7 +2112,11 @@ ACCESSORS(Script, data, Object, kDataOffset)
ACCESSORS(Script, context_data, Object, kContextOffset)
ACCESSORS(Script, wrapper, Proxy, kWrapperOffset)
ACCESSORS(Script, type, Smi, kTypeOffset)
ACCESSORS(Script, compilation_type, Smi, kCompilationTypeOffset)
ACCESSORS(Script, line_ends, Object, kLineEndsOffset)
ACCESSORS(Script, eval_from_function, Object, kEvalFromFunctionOffset)
ACCESSORS(Script, eval_from_instructions_offset, Smi,
kEvalFrominstructionsOffsetOffset)
#ifdef ENABLE_DEBUGGER_SUPPORT
ACCESSORS(DebugInfo, shared, SharedFunctionInfo, kSharedFunctionInfoIndex)
......
......@@ -2649,17 +2649,23 @@ class Struct: public HeapObject {
};
// Script types.
enum ScriptType {
SCRIPT_TYPE_NATIVE,
SCRIPT_TYPE_EXTENSION,
SCRIPT_TYPE_NORMAL
};
// Script describes a script which has been added to the VM.
class Script: public Struct {
public:
// Script types.
enum Type {
TYPE_NATIVE,
TYPE_EXTENSION,
TYPE_NORMAL
};
// Script compilation types.
enum CompilationType {
COMPILATION_TYPE_HOST,
COMPILATION_TYPE_EVAL,
COMPILATION_TYPE_JSON
};
// [source]: the script source.
DECL_ACCESSORS(source, Object)
......@@ -2688,9 +2694,20 @@ class Script: public Struct {
// [type]: the script type.
DECL_ACCESSORS(type, Smi)
// [line_ends]: array of line ends positions
// [compilation]: how the the script was compiled.
DECL_ACCESSORS(compilation_type, Smi)
// [line_ends]: array of line ends positions.
DECL_ACCESSORS(line_ends, Object)
// [eval_from_function]: for eval scripts the funcion from which eval was
// called.
DECL_ACCESSORS(eval_from_function, Object)
// [eval_from_instructions_offset]: the instruction offset in the code for the
// function from which eval was called where eval was called.
DECL_ACCESSORS(eval_from_instructions_offset, Smi)
static inline Script* cast(Object* obj);
#ifdef DEBUG
......@@ -2706,9 +2723,13 @@ class Script: public Struct {
static const int kContextOffset = kDataOffset + kPointerSize;
static const int kWrapperOffset = kContextOffset + kPointerSize;
static const int kTypeOffset = kWrapperOffset + kPointerSize;
static const int kLineEndsOffset = kTypeOffset + kPointerSize;
static const int kCompilationTypeOffset = kTypeOffset + kPointerSize;
static const int kLineEndsOffset = kCompilationTypeOffset + kPointerSize;
static const int kIdOffset = kLineEndsOffset + kPointerSize;
static const int kSize = kIdOffset + kPointerSize;
static const int kEvalFromFunctionOffset = kIdOffset + kPointerSize;
static const int kEvalFrominstructionsOffsetOffset =
kEvalFromFunctionOffset + kPointerSize;
static const int kSize = kEvalFrominstructionsOffsetOffset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Script);
......
......@@ -32,8 +32,11 @@ Debug = debug.Debug
var exception = false; // Exception in debug event listener.
var before_compile_count = 0;
var after_compile_count = 0;
var current_source = ''; // Current source compiled.
var source_count = 0; // Total number of scource sompiled.
var current_source = ''; // Current source being compiled.
var source_count = 0; // Total number of scources compiled.
var host_compilations = 0; // Number of scources compiled through the API.
var eval_compilations = 0; // Number of scources compiled through eval.
var json_compilations = 0; // Number of scources compiled through JSON.parse.
function compileSource(source) {
......@@ -52,14 +55,32 @@ function listener(event, exec_state, event_data, data) {
before_compile_count++;
} else {
after_compile_count++;
switch (event_data.script().compilationType()) {
case Debug.ScriptCompilationType.Host:
host_compilations++;
break;
case Debug.ScriptCompilationType.Eval:
eval_compilations++;
break;
case Debug.ScriptCompilationType.JSON:
json_compilations++;
break;
}
}
// If the compiled source contains 'eval' there will be additional compile
// events for the source inside eval.
if (current_source.indexOf('eval') == 0) {
// For source with 'eval' there will be compile events with substrings
// as well as with with the exact source.
assertTrue(current_source.indexOf(event_data.script().source()) >= 0);
} else if (current_source.indexOf('JSON.parse') == 0) {
// For JSON the JSON source will be in parentheses.
var s = event_data.script().source();
if (s[0] == '(') {
s = s.substring(1, s.length - 2);
}
assertTrue(current_source.indexOf(s) >= 0);
} else {
// For source without 'eval' there will be a compile events with the
// exact source.
......@@ -86,6 +107,8 @@ compileSource('eval("a=2")');
source_count++; // Using eval causes additional compilation event.
compileSource('eval("eval(\'function(){return a;}\')")');
source_count += 2; // Using eval causes additional compilation event.
compileSource('JSON.parse("{a:1,b:2}")');
source_count++; // Using JSON.parse causes additional compilation event.
// Make sure that the debug event listener was invoked.
assertFalse(exception, "exception in listener")
......@@ -93,7 +116,11 @@ assertFalse(exception, "exception in listener")
// Number of before and after compile events should be the same.
assertEquals(before_compile_count, after_compile_count);
// Check the actual number of events.
// Check the actual number of events (no compilation through the API as all
// source compiled through eval except for one JSON.parse call).
assertEquals(source_count, after_compile_count);
assertEquals(0, host_compilations);
assertEquals(source_count - 1, eval_compilations);
assertEquals(1, json_compilations);
Debug.setListener(null);
......@@ -25,10 +25,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Flags: --expose-debug-as debug --allow-natives-syntax
// Test the mirror object for scripts.
function testScriptMirror(f, file_name, file_lines, script_type, script_source) {
function testScriptMirror(f, file_name, file_lines, type, compilation_type,
source, eval_from_line) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(f).script();
var serializer = debug.MakeMirrorSerializer();
......@@ -53,13 +54,17 @@ function testScriptMirror(f, file_name, file_lines, script_type, script_source)
if (file_lines > 0) {
assertEquals(file_lines, mirror.lineCount());
}
assertEquals(script_type, mirror.scriptType());
if (script_source) {
assertEquals(script_source, mirror.source());
assertEquals(type, mirror.scriptType());
assertEquals(compilation_type, mirror.compilationType(), "compilation type");
if (source) {
assertEquals(source, mirror.source());
}
if (eval_from_line) {
assertEquals(eval_from_line, mirror.evalFromLocation().line);
}
// Parse JSON representation and check.
var fromJSON = eval('(' + json + ')');
var fromJSON = JSON.parse(json);
assertEquals('script', fromJSON.type);
name = fromJSON.name;
if (name) {
......@@ -72,15 +77,18 @@ function testScriptMirror(f, file_name, file_lines, script_type, script_source)
if (file_lines > 0) {
assertEquals(file_lines, fromJSON.lineCount);
}
assertEquals(script_type, fromJSON.scriptType);
assertEquals(type, fromJSON.scriptType);
assertEquals(compilation_type, fromJSON.compilationType);
}
// Test the script mirror for different functions.
testScriptMirror(function(){}, 'mirror-script.js', 92, 2);
testScriptMirror(Math.sin, 'native math.js', -1, 0);
testScriptMirror(eval('function(){}'), null, 1, 2, 'function(){}');
testScriptMirror(eval('function(){\n }'), null, 2, 2, 'function(){\n }');
testScriptMirror(function(){}, 'mirror-script.js', 100, 2, 0);
testScriptMirror(Math.sin, 'native math.js', -1, 0, 0);
testScriptMirror(eval('function(){}'), null, 1, 2, 1, 'function(){}', 87);
testScriptMirror(eval('function(){\n }'), null, 2, 2, 1, 'function(){\n }', 88);
testScriptMirror(%CompileString("({a:1,b:2})", true), null, 1, 2, 2, '({a:1,b:2})');
testScriptMirror(%CompileString("({a:1,\n b:2})", true), null, 2, 2, 2, '({a:1,\n b:2})');
// Test taking slices of source.
var mirror = debug.MakeMirror(eval('function(){\n 1;\n}')).script();
......
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