Merge pull request #109 from jannon/ObjectLiteralValueOfKeyFix

Fix for valueOf key in object literals
This commit is contained in:
Jannon Frank 2012-04-21 04:06:38 -07:00
commit f2b444bdb2
5 changed files with 106 additions and 81 deletions

View File

@ -11,33 +11,33 @@ var currentModule = null;
*/
exports.attachTo = function(parser) {
var jsdoc = {doclet: require('jsdoc/doclet'), name: require('jsdoc/name')};
// handles JSDoc comments that include a @name tag -- the code is ignored in such a case
parser.on('jsdocCommentFound', function(e) {
var newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
if (!newDoclet.name) {
return false; // only interested in virtual comments (with a @name) here
}
addDoclet.call(this, newDoclet);
if (newDoclet.kind === 'module') {
currentModule = newDoclet.longname;
}
e.doclet = newDoclet;
resolveProperties(newDoclet);
});
// handles named symbols in the code, may or may not have a JSDoc comment attached
parser.on('symbolFound', function(e) {
var subDoclets = e.comment.split(/@also\b/g);
for (var i = 0, l = subDoclets.length; i < l; i++) {
newSymbolDoclet.call(this, subDoclets[i], e);
}
});
function newSymbolDoclet(docletSrc, e) {
var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
@ -47,11 +47,11 @@ exports.attachTo = function(parser) {
e.comment = '@undocumented';
newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
}
if (newDoclet.alias) {
if (newDoclet.alias === '{@thisClass}') {
memberofName = this.resolveThis(e.astnode);
// "class" refers to the owner of the prototype, not the prototype itself
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
memberofName = RegExp.$1;
@ -69,7 +69,7 @@ exports.attachTo = function(parser) {
scope = '';
if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;
newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, '');
// like /** @module foo */ exports.bar = 1;
@ -86,14 +86,14 @@ exports.attachTo = function(parser) {
// or /** blah */ this.foo = 1;
memberofName = this.resolveThis(e.astnode);
scope = nameStartsWith === 'exports'? 'static' : 'instance';
// like /** @module foo */ this.bar = 1;
if (nameStartsWith === 'this' && currentModule && !memberofName) {
memberofName = currentModule;
scope = 'static';
}
}
if (memberofName) {
if (newDoclet.name) {
newDoclet.name = memberofName + (scope === 'instance'? '#' : '.') + newDoclet.name;
@ -108,8 +108,8 @@ exports.attachTo = function(parser) {
memberofName = memberofName[0];
}
}
if (memberofName) {
if (memberofName) {
newDoclet.addTag( 'memberof', memberofName);
if (basename) {
newDoclet.name = newDoclet.name.replace(new RegExp('^' + RegExp.escape(basename) + '.'), '');
@ -117,30 +117,30 @@ exports.attachTo = function(parser) {
}
else {
if (currentModule) {
if (!newDoclet.scope) newDoclet.addTag( 'inner');
if (!newDoclet.memberof && newDoclet.scope !== 'global') newDoclet.addTag( 'memberof', currentModule);
if (!newDoclet.scope){newDoclet.addTag( 'inner');}
if (!newDoclet.memberof && newDoclet.scope !== 'global'){newDoclet.addTag( 'memberof', currentModule);}
}
}
}
newDoclet.postProcess();
}
else {
return false;
}
resolveProperties(newDoclet);
if (!newDoclet.memberof) {
newDoclet.scope = 'global';
}
addDoclet.call(this, newDoclet);
e.doclet = newDoclet;
}
//parser.on('fileBegin', function(e) { });
parser.on('fileComplete', function(e) {
currentModule = null;
});
@ -149,7 +149,7 @@ exports.attachTo = function(parser) {
if (newDoclet) {
e = { doclet: newDoclet };
this.fire('newDoclet', e);
if (!e.defaultPrevented) {
if ( !filter(newDoclet) ) {
this.addResult(newDoclet);
@ -157,22 +157,26 @@ exports.attachTo = function(parser) {
}
}
}
function filter(doclet) {
// you can't document prototypes
if ( /#$/.test(doclet.longname) ) return true;
if ( /#$/.test(doclet.longname) ) {
return true;
}
// you can't document symbols added by the parser with a dummy name
if (doclet.meta.code && doclet.meta.code.name === '____') return true;
if (doclet.meta.code && doclet.meta.code.name === '____') {
return true;
}
return false;
}
function resolveProperties(newDoclet) {
// find name and description from each property tag text
if (newDoclet.properties) {
for (var i = 0, len = newDoclet.properties.length; i < len; i++) {
var property = newDoclet.properties[i];
var parts = jsdoc.name.splitName(property.description);
property.name = parts.name;
property.description = parts.description;

View File

@ -8,11 +8,11 @@
var Token = Packages.org.mozilla.javascript.Token,
currentParser = null,
currentSourceName = '';
/**
/**
* @class
* @mixes module:common/events
*
*
* @example <caption>Create a new parser.</caption>
* var jsdocParser = new (require('jsdoc/src/parser').Parser)();
*/
@ -38,7 +38,7 @@ require('common/util').mixin(exports.Parser.prototype, require('common/events'))
* @fires newDoclet
* @fires fileBegin
* @fires fileComplete
*
*
* @example <caption>Parse two source files.</caption>
* var myFiles = ['file1.js', 'file2.js'];
* var docs = jsdocParser.parse(myFiles);
@ -47,9 +47,9 @@ exports.Parser.prototype.parse = function(sourceFiles, encoding) {
const SCHEMA = 'javascript:';
var sourceCode = '',
filename = '';
if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; }
for (i = 0, leni = sourceFiles.length; i < leni; i++) {
if (sourceFiles[i].indexOf(SCHEMA) === 0) {
sourceCode = sourceFiles[i].substr(SCHEMA.length);
@ -65,12 +65,12 @@ exports.Parser.prototype.parse = function(sourceFiles, encoding) {
continue;
}
}
currentParser = this;
this._parseSourceCode(sourceCode, filename);
currentParser = null;
}
return this._resultBuffer;
}
@ -115,13 +115,13 @@ exports.Parser.prototype.getVisitors = function() {
exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) {
var e = {filename: sourceName};
this.fire('fileBegin', e);
if (!e.defaultPrevented) {
e = {filename: sourceName, source: sourceCode};
this.fire('beforeParse', e);
sourceCode = e.source;
currentSourceName = sourceName = e.filename;
sourceCode = pretreat(e.source);
var ast = parserFactory().parse(sourceCode, sourceName, 1);
@ -131,9 +131,9 @@ exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) {
})
);
}
this.fire('fileComplete', e);
currentSourceName = '';
}
@ -141,16 +141,16 @@ function pretreat(code) {
return code
// make starbangstar comments look like real jsdoc comments
.replace(/\/\*\!\*/g, '/**')
// make matching comment endings easier
.replace(/\*\//g, '»')
// merge adjacent doclets
.replace(/»\/\*\*+/g, '@also')
// make lent objectliterals documentable by giving them a dummy name
.replace(/(\/\*\*[^»]*?@lends\b[^»]*?»\s*)\{/g, '$1 ____ = {') // like return @lends {
.replace(/(\/\*\*[^»]*?@lends\b[^»]*?»)(\s*)return(\s*)\{/g, '$2$3 return $1 ____ = {') // like @lends return {
// make matching comment endings harder
.replace(/»/g, '*/');
}
@ -166,7 +166,7 @@ exports.Parser.tkn = tkn;
exports.Parser.prototype.astnodeToMemberof = function(node) {
var id,
doclet;
if (node.type === Token.VAR || node.type === Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) {
if (node.enclosingFunction) { // an inner var or func
id = 'astnode'+node.enclosingFunction.hashCode();
@ -195,13 +195,13 @@ exports.Parser.prototype.astnodeToMemberof = function(node) {
}
//First check to see if we have a global scope alias
doclet = this.refs["__global__"];
if (doclet && doclet.meta.vars && basename in doclet.meta.vars) {
if (doclet && doclet.meta.vars && doclet.meta.vars.hasOwnProperty(basename)) {
var alias = doclet.meta.vars[basename];
if (alias !== false) {
return [alias, basename];
}
}
id = 'astnode'+node.parent.hashCode();
doclet = this.refs[id];
if (!doclet) return ''; // global?
@ -216,7 +216,7 @@ exports.Parser.prototype.astnodeToMemberof = function(node) {
*/
exports.Parser.prototype.resolveThis = function(node) {
var memberof = {};
if (node.type !== Token.COLON && node.enclosingFunction) {
// get documentation for the enclosing function
memberof.id = 'astnode'+node.enclosingFunction.hashCode();
@ -225,7 +225,7 @@ exports.Parser.prototype.resolveThis = function(node) {
if (!memberof.doclet) {
return '<anonymous>'; // TODO handle global this?
}
if (memberof.doclet['this']) {
return memberof.doclet['this'];
}
@ -247,11 +247,11 @@ exports.Parser.prototype.resolveThis = function(node) {
else if (node.parent) {
var parent = node.parent;
if (parent.type === Token.COLON) parent = parent.parent; // go up one more
memberof.id = 'astnode'+parent.hashCode();
memberof.doclet = this.refs[memberof.id];
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) return ''; // global?
return memberof.doclet.longname||memberof.doclet.name;
}
else {
@ -264,14 +264,14 @@ exports.Parser.prototype.resolveThis = function(node) {
*/
exports.Parser.prototype.resolvePropertyParent = function(node) {
var memberof = {};
if (node.parent) {
var parent = node.parent;
if (parent.type === Token.COLON) parent = parent.parent; // go up one more
memberof.id = 'astnode'+parent.hashCode();
memberof.doclet = this.refs[memberof.id];
if (memberof.doclet) { return memberof; }
}
}
@ -284,14 +284,14 @@ exports.Parser.prototype.resolvePropertyParent = function(node) {
exports.Parser.prototype.resolveVar = function(node, basename) {
var doclet,
enclosingFunction = node.enclosingFunction;
if (!enclosingFunction) { return ''; } // global
doclet = this.refs['astnode'+enclosingFunction.hashCode()];
if ( doclet && doclet.meta.vars && basename in doclet.meta.vars ) {
return doclet.longname;
}
return this.resolveVar(enclosingFunction, basename);
}
@ -332,7 +332,7 @@ function visitNode(node) {
if (comment.commentType !== Token.CommentType.JSDOC) {
continue;
}
if (commentSrc = ''+comment.toSource()) {
e = {
@ -359,9 +359,9 @@ function visitNode(node) {
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
var basename = getBasename(e.code.name);
if (basename !== 'this') e.code.funcscope = currentParser.resolveVar(node, basename);
}
else if (node.type === Token.COLON) { // assignment within an object literal
@ -381,7 +381,7 @@ function visitNode(node) {
if (node.variables) {
return true; // we'll get each var separately on future visits
}
if (node.parent.variables.toArray()[0] === node) { // like /** blah */ var a=1, b=2, c=3;
// the first var assignment gets any jsDoc before the whole var series
node.jsDoc = node.parent.jsDoc;
@ -403,7 +403,7 @@ function visitNode(node) {
funcDoc = null;
if (node.enclosingFunction) {
func = 'astnode'+node.enclosingFunction.hashCode();
}
}
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || {};
@ -422,23 +422,23 @@ function visitNode(node) {
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
e.code.name = (node.type == tkn.NAMEDFUNCTIONSTATEMENT)? '' : String(node.name) || '';
//console.log(':: e.code.name is', e.code.name);
// keep track of vars in a function or global scope
var func = "__global__",
funcDoc = null;
if (node.enclosingFunction) {
func = 'astnode'+node.enclosingFunction.hashCode();
}
}
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || {};
funcDoc.meta.vars[e.code.name] = false;
e.finishers.push(makeVarsFinisher(funcDoc));
}
var basename = getBasename(e.code.name)
e.code.funcscope = currentParser.resolveVar(node, basename);
}
@ -463,12 +463,12 @@ function visitNode(node) {
/** @private */
function parserFactory() {
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext();
var ce = new Packages.org.mozilla.javascript.CompilerEnvirons();
ce.setRecordingComments(true);
ce.setRecordingLocalJsDocComments(true);
ce.setLanguageVersion(180);
ce.initFromContext(cx);
return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter());
}
@ -480,7 +480,7 @@ function parserFactory() {
*/
function aboutNode(node) {
about = {};
if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) {
about.name = node.type == tkn.NAMEDFUNCTIONSTATEMENT? '' : '' + node.name;
about.type = 'function';
@ -514,7 +514,7 @@ function aboutNode(node) {
about.node = node.right;
about.value = nodeToString(about.node);
about.type = getTypeName(node.right);
if (about.type === 'FUNCTION' && about.node.name) {
about.node.type = tkn.NAMEDFUNCTIONSTATEMENT;
}
@ -526,7 +526,7 @@ function aboutNode(node) {
about.name = string;
}
}
// get names of the formal parameters declared for this function
if (about.node && about.node.getParamCount) {
var paramCount = about.node.getParamCount();
@ -539,7 +539,7 @@ function aboutNode(node) {
about.paramnames = paramNames;
}
}
return about;
}
@ -548,9 +548,9 @@ function aboutNode(node) {
*/
function nodeToString(node) {
var str;
if (!node) return;
if (node.type === Token.GETPROP) {
str = [nodeToString(node.target), node.property.string].join('.');
}
@ -578,7 +578,7 @@ function nodeToString(node) {
else {
str = getTypeName(node);
}
return '' + str;
};
@ -587,11 +587,11 @@ function nodeToString(node) {
*/
function getTypeName(node) {
var type = '';
if (node) {
type = ''+ Packages.org.mozilla.javascript.Token.typeToName(node.getType());
}
return type;
}

View File

@ -0,0 +1,12 @@
Call(
{
methodA: function()
{
this.id = this.createUUID();
},
valueOf: function()
{
return this.id;
}
});

View File

@ -38,10 +38,10 @@ var testhelpers = {
var sourceCode = readFile(__dirname + '/' + filename),
testParser,
doclets;
testParser = new (require('jsdoc/src/parser')).Parser();
require('jsdoc/src/handlers').attachTo(testParser);
doclets = testParser.parse('javascript:' + sourceCode);
testhelpers.indexAll(doclets);
@ -49,7 +49,7 @@ var testhelpers = {
// test assume borrows have not yet been resolved
// require('jsdoc/borrow').resolveBorrows(doclets);
return {
doclets: doclets,
getByLongname: function(longname) {
@ -62,7 +62,7 @@ var testhelpers = {
indexAll: function(docs) {
var index = {};
docs.forEach(function(doc) {
if (!index[doc.longname]) index[doc.longname] = [];
if (!index.hasOwnProperty(doc.longname)){index[doc.longname] = [];}
index[doc.longname].push(doc);
});
docs.index = index;
@ -144,6 +144,7 @@ testFile('test/t/cases/moduleinner.js');
testFile('test/t/cases/memberoftag.js');
testFile('test/t/cases/memberoftag2.js');
testFile('test/t/cases/memberoftag3.js');
testFile('test/t/cases/memberoftag4.js');
testFile('test/t/cases/memberoftagforced.js');
testFile('test/t/cases/moduletag.js');
testFile('test/t/cases/moduletag2.js');

View File

@ -0,0 +1,8 @@
(function() {
test('Should not crash when valueOf property is used in an Object', function() {
var docSet = testhelpers.getDocSetFromFile('test/cases/memberoftag4.js');
assert.ok(true);
});
})();