diff --git a/rhino_modules/jsdoc/src/handlers.js b/rhino_modules/jsdoc/src/handlers.js index af214418..d12f9dfd 100644 --- a/rhino_modules/jsdoc/src/handlers.js +++ b/rhino_modules/jsdoc/src/handlers.js @@ -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; diff --git a/rhino_modules/jsdoc/src/parser.js b/rhino_modules/jsdoc/src/parser.js index 6dcf3a92..201bc411 100644 --- a/rhino_modules/jsdoc/src/parser.js +++ b/rhino_modules/jsdoc/src/parser.js @@ -8,11 +8,11 @@ var Token = Packages.org.mozilla.javascript.Token, currentParser = null, currentSourceName = ''; - -/** + +/** * @class * @mixes module:common/events - * + * * @example Create a new parser. * 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 Parse two source files. * 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 ''; // 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; } diff --git a/test/cases/memberoftag4.js b/test/cases/memberoftag4.js new file mode 100644 index 00000000..9069d949 --- /dev/null +++ b/test/cases/memberoftag4.js @@ -0,0 +1,12 @@ +Call( +{ + methodA: function() + { + this.id = this.createUUID(); + }, + + valueOf: function() + { + return this.id; + } +}); \ No newline at end of file diff --git a/test/runner.js b/test/runner.js index dbea1cb1..35c04d24 100644 --- a/test/runner.js +++ b/test/runner.js @@ -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'); diff --git a/test/t/cases/memberoftag4.js b/test/t/cases/memberoftag4.js new file mode 100644 index 00000000..afe14c42 --- /dev/null +++ b/test/t/cases/memberoftag4.js @@ -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); + }); + +})(); \ No newline at end of file