node-inspector/lib/convert.js

250 строки
6.5 KiB
JavaScript

exports.v8LocationToInspectorLocation = function(v8loc) {
return {
scriptId: v8loc.script_id.toString(),
lineNumber: v8loc.line,
columnNumber: v8loc.column
};
};
// Conversions between v8 file paths and node-inspector urls
// Kind Path Url
// UNIX /dir/app.js file:///dir/app.js
// Windows c:\dir\app.js file:///C:/dir/app.js
// UNC \\SHARE\app.js file://SHARE/app.js
exports.v8NameToInspectorUrl = function(v8name) {
if (!v8name || v8name === 'repl') {
// Call to `evaluate` from user-land creates a new script with undefined URL.
// REPL has null main script file and calls `evaluate` with `repl`
// as the file name.
//
// When we send an empty string as URL, front-end opens the source
// as VM-only script (named "[VM] {script-id}").
//
// The empty name of the main script file is displayed as "(program)".
return '';
}
if (/^\//.test(v8name)) {
return 'file://' + v8name;
} else if (/^[a-zA-Z]:\\/.test(v8name)) {
return 'file:///' + v8name.replace(/\\/g, '/');
} else if (/^\\\\/.test(v8name)) {
return 'file://' + v8name.substring(2).replace(/\\/g, '/');
}
return v8name;
};
exports.inspectorUrlToV8Name = function(url) {
var path = url.replace(/^file:\/\//, '');
if (/^\/[a-zA-Z]:\//.test(path))
return path.substring(1).replace(/\//g, '\\'); // Windows disk path
if (/^\//.test(path))
return path; // UNIX-style
if (/^file:\/\//.test(url))
return '\\\\' + path.replace(/\//g, '\\'); // Windows UNC path
return url;
};
exports.v8ScopeTypeToString = function(v8ScopeType) {
switch (v8ScopeType) {
case 0:
return 'global';
case 1:
return 'local';
case 2:
return 'with';
case 3:
return 'closure';
case 4:
return 'catch';
default:
return 'unknown';
}
};
exports.v8RefToInspectorObject = function(ref) {
var desc = '',
type = ref.type,
subtype,
size,
name,
objectId,
inspectorResult;
switch (type) {
case 'object':
name = /#<(\w+)>/.exec(ref.text);
if (name && name.length > 1) {
desc = name[1];
if (desc === 'Array' || desc === 'Buffer') {
size = ref.properties.filter(function(p) { return /^\d+$/.test(p.name);}).length;
desc += '[' + size + ']';
subtype = 'array';
}
} else if (ref.className === 'Date') {
desc = new Date(ref.value || NaN).toString();
subtype = 'date';
} else {
desc = ref.className || 'Object';
}
break;
case 'regexp':
type = 'object';
subtype = 'regexp';
desc = ref.text || '';
/*
We need to collect RegExp flags and append they to description,
or open issue in NodeJS same as 'RegExp text serialized without flags'
*/
break;
case 'function':
desc = ref.text || 'function()';
break;
case 'error':
type = 'object';
desc = ref.text || 'Error';
break;
default:
desc = ref.text || '';
break;
}
if (desc.length > 100) {
desc = desc.substring(0, 100) + '\u2026';
}
objectId = ref.handle;
if (objectId === undefined)
objectId = ref.ref;
inspectorResult = {
type: type,
subtype: subtype,
objectId: String(objectId),
className: ref.className,
description: desc
};
return inspectorResult;
};
exports.v8ObjectToInspectorProperties = function(obj, refs, options) {
var proto = obj.protoObject,
props = obj.properties || [],
ownProperties = options.ownProperties,
accessorPropertiesOnly = options.accessorPropertiesOnly;
props = props.map(function(prop) {
var ref = refs[prop.ref];
var inspectorProperty = {
name: String(prop.name),
writable: !(prop.attributes & 1 << 0),
enumerable: !(prop.attributes & 1 << 1),
configurable: !(prop.attributes & 1 << 2),
value: exports.v8ResultToInspectorResult(ref)
};
return inspectorProperty;
});
if (ownProperties && proto) {
proto = refs[proto.ref];
if (proto.type !== 'undefined') {
props.push({
name: '__proto__',
value: exports.v8RefToInspectorObject(proto),
writable: true,
configurable: true,
enumerable: false,
isOwn: true
});
}
}
props = props.filter(function(prop) {
/*
Node.js does not return get/set property descriptors now (v0.11.11),
therefore we can't fully implement 'accessorPropertiesOnly'.
See https://github.com/joyent/node/issues/7139
*/
var isAccessorProperty = ('get' in prop || 'set' in prop);
return accessorPropertiesOnly ? isAccessorProperty : !isAccessorProperty;
});
return props;
};
exports.v8ErrorToInspectorError = function(message) {
var nameMatch = /^([^:]+):/.exec(message);
return {
type: 'object',
objectId: 'ERROR',
className: nameMatch ? nameMatch[1] : 'Error',
description: message
};
};
exports.v8ResultToInspectorResult = function(result) {
var subtype,
inspectorResult;
if (['object', 'function', 'regexp', 'error'].indexOf(result.type) > -1) {
return exports.v8RefToInspectorObject(result);
}
if (result.type == 'null') {
// workaround for the problem with front-end's setVariableValue
// implementation not preserving null type
result.value = null;
subtype = 'null';
}
inspectorResult = {
type: result.type,
subtype: subtype,
value: result.value,
description: String(result.value)
};
return inspectorResult;
};
exports.inspectorValueToV8Value = function(value) {
if (value.value === undefined && value.objectId === undefined)
return { type: 'undefined' };
if (value.objectId) {
return { handle: Number(value.objectId) };
}
return value;
};
exports.v8FunctionLookupToFunctionDetails = function(handleData) {
return {
details: {
location: {
scriptId: String(handleData.scriptId),
lineNumber: handleData.line,
columnNumber: handleData.column
},
name: handleData.name || handleData.inferredName,
// There is a list of scope ids in responseBody.scopes, but not scope
// details :( // We need to issue `scopes` request to fetch scopes
// details, but we don't have frame number where the function was defined.
// Let's leave the scopeChain empty for now.
scopeChain: []
}
};
};
exports.v8ScriptIdToInspectorId = function(scriptId) {
return String(scriptId);
};
exports.inspectorScriptIdToV8Id = function(scriptId) {
return Number(scriptId);
};