debug.js
/*******************************************************************************
*
* ==============================================================================
*
* Copyright (c) 2008-2011 ayound@gmail.com This program and the accompanying
* materials are made available under the terms of the Apache License 2.0 which
* accompanies this distribution, and is available at
* http://www.apache.org/licenses/LICENSE-2.0 All rights reserved.
*
* Created on 2008-10-26
******************************************************************************/
/**
* handle window.onerror method
*/
window.onerror = function(e, resource, line) {
if (e == "exit") {
return true;
} else {
jsDebug.error(e, resource, line);
}
}
var arguments = [];
/**
* create xmlhttp to cross browser
*/
function createXMLHttp() {
if (typeof XMLHttpRequest != "undefined") {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
var aVersions = ["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0",
"MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", "Microsoft.XMLHttp"];
for (var i = 0; i < aVersions.length; i++) {
try {
var oXmlHttp = new ActiveXObject(aVersions[i]);
return oXmlHttp;
} catch (oError) {
}
}
}
}
function jsDebug() {
}
jsDebug.xmlHttp = createXMLHttp();
jsDebug.debugCommand = null;
jsDebug.currResource = null;
jsDebug.breakpoints = null;
jsDebug.functionStack = [];
jsDebug.currResource = null;
jsDebug.currLine = null;
jsDebug.isExpression = false;
/**
* javascript debug error resolver
*/
jsDebug.getErrorStack = function(func) {
var stack = [];
while (func) {
var funcStr = func.toString();
var funcOffset = funcStr.indexOf(")");
var funcHead = funcStr.substring(0, funcOffset + 1);
var args = func.arguments;
var argArr = [];
for (var i = 0; i < args.length; i++) {
argArr.push(args[i]);
}
stack.push(funcHead + json2string(argArr, 12));
func = func.caller;
}
return stack.join("\n");
}
/**
* get function by arguments.callee and parse the function string find all the
* arguments and vars
*/
jsDebug.getFuncData = function(args, evalFunc) {
alert(args + " dddd "+ evalFunc)
if (!evalFunc) {
return {};
}
if (args == null) {
return {
"window" : window
};
} else {
var vars = [];
var func = args.callee;
if (func) {
var funcStr = func.toString().replace(/\n/g, "");
alert("funcStr : "+funcStr);
var argStart = funcStr.indexOf("(");
var argEnd = funcStr.indexOf(")");
if (argStart > 0 && argEnd > 0) {
var argStr = funcStr.substring(argStart + 1, argEnd);
vars = argStr.split(",");
}
var nameArr = funcStr.split("var ");
alert("nameArr: " + nameArr);
for (var i = 1; i < nameArr.length; i++) {
var line = nameArr[i];
alert("line: " + line);
vars = vars.concat(jsDebug.parseVars(line));
}
}
alert("vars: " + vars);
var data = {};
for (var i = 0; i < vars.length; i++) {
var key = vars[i];
if (key && key.length > 0) {
key = key.replace(/\n|\r|\t| /g, "");
if (/^[A-Za-z0-9_\$]*$/.test(key)) {
try {
var result = evalFunc(key);
if (result == undefined) {
data[key] = "undefined";
} else if (result == null) {
data[key] = "null";
} else {
data[key] = evalFunc(key);
}
} catch (e) {
}
}
}
}
alert("data: " + json2string(data));
return data;
}
}
/**
* parse all the varibles
*所有变量解析
*/
jsDebug.parseVars = function(line) {
var varNames = [];
if (line && line.length > 0) {
var endOffset = line.indexOf(";");
if (endOffset > 0) {
line = line.substring(0, endOffset);
}
var lineArr = line.split(",");
for (var i = 0; i < lineArr.length; i++) {
var varStr = lineArr[i];
var varEndOffset = varStr.indexOf("=");
if (varEndOffset > 0) {
varNames.push(varStr.substring(0, varEndOffset));
} else {
varNames.push(varStr);
}
}
}
alert("varNames: " + varNames );
return varNames;
}
/**
* find if arguments is stepreturn context
*/
jsDebug.isStepReturn = function(args) {
if (args) {
var func = args.callee;
for (var i = jsDebug.functionStack.length - 2; i > -1; i--) {
if (func == jsDebug.functionStack[i]) {
return true;
}
}
return false;
} else {
return true;
}
}
/**
* find if arguments is stepover context
*/
jsDebug.isStepOver = function(args) {
if (args) {
var func = args.callee;
for (var i = jsDebug.functionStack.length - 1; i > -1; i--) {
if (func == jsDebug.functionStack[i]) {
return true;
}
}
return false;
} else {
return true;
}
}
/**
* get breakpoints from server
*/
jsDebug.getBreakPoint = function() {
try {
var postData = {
"COMMAND" : "RESUME"
}
var xmlHttp = createXMLHttp();
xmlHttp.open("POST", "/jsdebug.debug?" + new Date(), false);
alert("AAAAAA="+json2string(postData));
xmlHttp.send(json2string(postData));
eval("var retObj = " + xmlHttp.responseText);
jsDebug.breakpoints = retObj["BREAKPOINTS"];
//返回所有的断点信息 {COMMAND:'BREAKPOINT',BREAKPOINTS:{'/temp/transform.js2':true,'/temp/transform.js7':true,'/temp/transform.js10':true,'/temp/transform.js3':true,'end':false}}
alert(",,,,,,,,,,,,, " + json2string(jsDebug.breakpoints));
setTimeout(jsDebug.getBreakPoint, 5000);
} catch (e) {
}
}
/**
* update function stack
*/
jsDebug.updateStack = function(args, resource, scope, line, evalFunc) {
if (args) {
var func = args.callee;
func.__resource = resource;
func.__line = line;
func.__evalFunc = evalFunc;
func.__scope = scope;
for (var i = jsDebug.functionStack.length - 1; i > -1; i--) {
if (func == jsDebug.functionStack[i]) {
jsDebug.functionStack = jsDebug.functionStack.slice(0, i + 1);//.slice(start,end) 返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素
return;
}
}
jsDebug.functionStack.push(func);//把指定的值添加到数组后的新长度
} else {
jsDebug.functionStack = [];
}
}
jsDebug.error = function(e, resource, line) {
try {
if (resource && jsDebug.currResource
&& resource.indexOf(jsDebug.currResource) < 0) {
line = line - 1;
}
resource = jsDebug.currResource;
jsDebug.xmlHttp.open("POST", "/jsdebug.debug?" + new Date(), false);
var postData = {
"ERROR" : encodeURI(e),
"COMMAND" : "ERROR",
"RESOURCE" : resource,
"LINE" : line
}
alert("ERROR="+json2string(postData));
jsDebug.xmlHttp.send(json2string(postData));
} catch (e) {
alert(e);
}
}
jsDebug.stepReturn = function(func) {
try {
var data = jsDebug.getFuncData(func.arguments, func.__evalFunc);
jsDebug.updateStack(func.arguments, func.__resource, func.__scope,
func.__line, func.__evalFunc);
jsDebug.xmlHttp.open("POST", "/jsdebug.debug?" + new Date(), false);
if (func.__scope != window) {
data["this"] = func.__scope;
}
var postData = {
"STACK" : data,
"COMMAND" : jsDebug.debugCommand,
"RESOURCE" : func.__resource,
"LINE" : func.__line
}
alert("STEPRETURN="+json2string(postData));
jsDebug.xmlHttp.send(json2string(postData));
jsDebug.parseResult(jsDebug.xmlHttp.responseText);
} catch (e) {
}
}
jsDebug.evalExpression = function(expression, evalFunc) {
try {
jsDebug.xmlHttp.open("POST", "/jsdebug.debug?" + new Date(), false);
var postData = {
"STACK" : {},
"COMMAND" : "EXPRESSION",
"EXPRESSION" : expression
}
try {
jsDebug.isExpression = true;
var ret = evalFunc(expression);
if(typeof(ret)=="undefined"){
ret = "undefined";
}else if(ret==null){
ret = "null";
}else{
ret = ret.toString().replace(/\n|\r/gm, "");
}
postData["RESULT"] = ret;
} catch (e) {
postData["ERROR"] = e;
}
jsDebug.isExpression = false;
alert("evalExpression="+json2string(postData));
jsDebug.xmlHttp.send(json2string(postData));
jsDebug.parseResult(jsDebug.xmlHttp.responseText, evalFunc);
} catch (e) {
}
}
jsDebug.debug = function(resource, line, scope, args, evalFunc) {
alert("jsDebug.debug resource, line, scope, args, evalFunc="+resource+","+line+","+ scope+","+ args+","+ evalFunc);
if (jsDebug.isExpression) {
return;
}
try {
jsDebug.currResource = resource;
jsDebug.currLine = line;
alert("jsDebug.debug1 : " + jsDebug.debugCommand+",jsDebug.currResource="+jsDebug.currResource+",jsDebug.currLine="+jsDebug.currLine);
if (jsDebug.debugCommand == null) {
jsDebug.debugCommand = "START";
jsDebug.getBreakPoint();
}
if (jsDebug.debugCommand == "TERMINATE") {
throw "exit";
}
if (!(jsDebug.breakpoints && jsDebug.breakpoints[resource + line])) {
if (jsDebug.debugCommand == "STEPRETURN"
|| jsDebug.debugCommand == "STEPOVER") {
var parentFunc = jsDebug.functionStack[jsDebug.functionStack.length
- 2];
if (args.callee.caller == parentFunc && parentFunc) {
jsDebug.stepReturn(parentFunc);
jsDebug.debug(resource, line, scope, args, evalFunc);
return;
}
}
if (jsDebug.debugCommand == "STEPRETURN") {
if (!jsDebug.isStepReturn(args)) {
return;
}
} else if (jsDebug.debugCommand == "STEPOVER") {
if (!jsDebug.isStepOver(args)) {
return;
}
} else if (jsDebug.debugCommand == "RESUME") {
return;
} else if (jsDebug.debugCommand == "STEPINTO") {
} else {
return;
}
} else {
jsDebug.debugCommand = "BREAKPOINT";
}
alert("args: " + json2string(args));
var data = jsDebug.getFuncData(args, evalFunc);
alert("end getFuncData: " + json2string(data));//data 数据值 如:{"name":"undefined"}
jsDebug.updateStack(args, resource, scope, line, evalFunc);
jsDebug.xmlHttp.open("POST", "/jsdebug.debug?" + new Date(), false);
if (scope != window) {
data["this"] = scope;
}
var postData = {
"STACK" : data,
"COMMAND" : jsDebug.debugCommand,
"RESOURCE" : resource,
"LINE" : line
}
alert("ssssss="+json2string(postData));
jsDebug.xmlHttp.send(json2string(postData));
jsDebug.parseResult(jsDebug.xmlHttp.responseText, evalFunc);
alert("6666666666666666666666" + jsDebug.xmlHttp.responseText);
} catch (e) {
}
}
jsDebug.parseResult = function(result, evalFunc) {
if (result) {
if (result.indexOf("{") == 0) {
try {
eval("var retObj = " + result);
if( retObj["COMMAND"]){
if(retObj["COMMAND"]!="EXPRESSION"){
jsDebug.isExpression = false;
jsDebug.debugCommand = retObj["COMMAND"];
if (jsDebug.debugCommand == "BREAKPOINT") {
jsDebug.breakpoints = retObj["BREAKPOINTS"];
}
}else{
jsDebug.evalExpression(retObj["EXPRESSION"], evalFunc);
}
}
} catch (e) {
}
} else {
// alert("Debug error:" + result);
}
} else {
}
}
var $jsd = jsDebug.debug;
function json2string(obj, depth) {
depth = depth || 0;
if (typeof obj == "function") {
return "\"function\"";
} else if (typeof obj == "object") {
return obj2string(obj, depth + 1);
} else if (typeof obj == "array") {
return array2string(obj, depth + 1);
} else {
if (typeof obj == "string") {
return "\"" + obj.replace(/"/gm, "\\\"").replace(/\n|\r/gm, "")
+ "\"";
} else if (typeof obj == "number") {
if (isFinite(obj)) {
return obj;
} else {
return "\"out of number\"";
}
} else {
return obj.toString().replace(/"/gm, "\\\"").replace(/\n|\r/gm, "");
}
}
}
function obj2string(obj, depth) {
depth = depth || 0;
if (obj == window) {
return "window";
} else if (obj == document) {
return "document";
} else if (obj == document.body) {
return "document.body";
} else {
var arr = [];
for (var prop in obj) {
try {
if (obj.hasOwnProperty(prop) && typeof(obj[prop]) != "function") {
if (depth < 9) {
arr.push("\"" + prop + "\":"
+ json2string(obj[prop], depth + 1));
} else {
arr.push("\"" + prop + "\":\"...\"")
}
}
} catch (e) {
}
}
return "{" + arr.join(",") + "}";
}
}
function array2string(array, depth) {
depth = depth || 0;
var arr = [];
for (var i = 0; i < array.length; i++) {
arr.push(json2string(array[i]), depth + 1);
}
return "[" + arr.join(",") + "]";
}
test.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<script type='text/javascript' src='transform.js'></script>
<body>
<div align="center">
hello:
<input name="startdate" type="button" style="width:200px" οnclick="test()" value="add"/>
</div>
</body>
</html>
transform.js
function test(){
var name = "javascript debug toolkit";
alert(add("hello,",name));
}
function add(a,b){
return a+b;
}
function cc(a){
return a+b;
}
function bb(){
return "dddd@@@@@@@@@@@@@@";
}
发送消息结构
AA: GET /temp/test.html HTTP/1.1
BB: Accept: */*
BB: Accept-Language: zh-cn
BB: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
BB: Accept-Encoding: gzip, deflate
BB: Host: localhost:8040
BB: Connection: Keep-Alive
BB:
CC:
AA: GET /temp/transform.js HTTP/1.1
BB: Accept: */*
BB: Referer: http://localhost:8040/temp/test.html
BB: Accept-Language: zh-cn
BB: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
BB: Accept-Encoding: gzip, deflate
BB: Host: localhost:8040
BB: Connection: Keep-Alive
BB:
CC:
***************start******************
AA: POST /jsdebug.debug?Wed%20Oct%2030%2014:26:34%20UTC+0800%202013 HTTP/1.1
BB: Accept: */*
BB: Accept-Language: zh-cn
BB: Referer: http://localhost:8040/temp/test.html
BB: Accept-Encoding: gzip, deflate
BB: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
BB: Host: localhost:8040
BB: Content-Length: 20
BB: Connection: Keep-Alive
BB: Cache-Control: no-cache
BB:
CC: {"COMMAND":"RESUME"}
DebugProcessor requestUrl : http://localhost:8040/jsdebug.debug?Wed Oct 30 14:26:34 UTC 0800 2013
DebugProcessor222222222222 : {"COMMAND":"RESUME"}
DebugProcessor response : org.ayound.js.debug.server.JsDebugResponse@39a3dc
DebugProcessor thread : org.ayound.js.debug.model.JsDebugThread@4ce898
DebugProcessor server : org.ayound.js.debug.server.JsDebugServer@1d1395
DebugProcessor requestHeader : {Accept-Language= zh-cn, Content-Length= 20, Accept-Encoding= gzip, deflate, User-Agent= Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729), Connection= Keep-Alive, Accept= */*, Cache-Control= no-cache}
param getCommand : RESUME
获取js中文件与断点的位置:{COMMAND:'BREAKPOINT',BREAKPOINTS:{'end':false}}
添加断点后:::
AA: POST /jsdebug.debug?Wed%20Oct%2030%2014:27:29%20UTC+0800%202013 HTTP/1.1
BB: Accept: */*
BB: Accept-Language: zh-cn
BB: Referer: http://localhost:8040/temp/test.html
BB: Accept-Encoding: gzip, deflate
BB: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
BB: Host: localhost:8040
BB: Content-Length: 94
BB: Connection: Keep-Alive
BB: Cache-Control: no-cache
BB:
CC: {"STACK":{"name":"undefined"},"COMMAND":"BREAKPOINT","RESOURCE":"/temp/transform.js","LINE":2}
AA: POST /jsdebug.debug?Wed%20Oct%2030%2014:31:08%20UTC+0800%202013 HTTP/1.1
BB: Accept: */*
BB: Accept-Language: zh-cn
BB: Referer: http://localhost:8040/temp/test.html
BB: Accept-Encoding: gzip, deflate
BB: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
BB: Host: localhost:8040
BB: Content-Length: 109
BB: Connection: Keep-Alive
BB: Cache-Control: no-cache
BB:
CC: {"STACK":{"name":"javascript debug toolkit"},"COMMAND":"BREAKPOINT","RESOURCE":"/temp/transform.js","LINE":3}
AA: POST /jsdebug.debug?Wed%20Oct%2030%2014:32:12%20UTC+0800%202013 HTTP/1.1
BB: Accept: */*
BB: Accept-Language: zh-cn
BB: Referer: http://localhost:8040/temp/test.html
BB: Accept-Encoding: gzip, deflate
BB: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
BB: Host: localhost:8040
BB: Content-Length: 119
BB: Connection: Keep-Alive
BB: Cache-Control: no-cache
BB:
CC: {"STACK":{"a":"hello,","b":"javascript debug toolkit"},"COMMAND":"BREAKPOINT","RESOURCE":"/temp/transform.js","LINE":7}
内嵌的js 编译后的内容为:
$jsd('/ddd/src/transform.js',1,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);});function test() {
$jsd('/ddd/src/transform.js',2,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); var name = "javascript debug toolkit";
$jsd('/ddd/src/transform.js',3,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); alert(add("hello,", name));
}
$jsd('/ddd/src/transform.js',6,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); function add(a, b) {
$jsd('/ddd/src/transform.js',7,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); return a + b;
}
$jsd('/ddd/src/transform.js',9,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); function cc(a) {
$jsd('/ddd/src/transform.js',10,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); return a + b;
}
$jsd('/ddd/src/transform.js',12,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); function bb() {
$jsd('/ddd/src/transform.js',13,this,((typeof(arguments)!="undefined"?arguments:null)),function(__text){return eval(__text);}); return "dddd@@@@@@@@@@@@@@";
}