Apply the attached patch to rhino cvs. (Note, this patch also has
patches for a modified version of dojo's compressor patch.)
It presently only works in interpreted mode (perhaps a good thing?), and
very simply adds a __jsdoc__ property to every function that has a /**
.. */ comment before it. The value of the property is a string with the
leading whitespace/asterisks removed as per javadoc spec.
The inspector/doc html-generator based on this patch will be released as
part of my upcoming alt framework technical preview release
(
http://marcello.cellosoft.com/projects/alt/).
Marcello
> Marcello Bastéa-Forte wrote:
>
>> If anyone is interested, I've made a patch to Rhino that records /**
>> javadoc */ tags into a __jsdoc__ property of all Function objects.
>> From there you can do whatever you like. (I made up a script that
>> analyzes the scope to find all classes/functions and uses the jsdoc
>> information accordingly.)
>
> Wow - I'm very interested! What do I have to do?
>
>
Index: src/org/mozilla/javascript/FunctionNode.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/FunctionNode.java,v
retrieving revision 1.29
diff -u -r1.29 FunctionNode.java
--- src/org/mozilla/javascript/FunctionNode.java 29 Aug 2005 13:25:31 -0000 1.29
+++ src/org/mozilla/javascript/FunctionNode.java 26 Jul 2006 23:16:25 -0000
@@ -77,7 +77,15 @@
public int getFunctionType() {
return itsFunctionType;
}
+
+ public void setJSDoc(String s) {
+ this.jsDoc = s;
+ }
+ public String getJSDoc() {
+ return jsDoc;
+ }
+ String jsDoc = null;
String functionName;
boolean itsNeedsActivation;
int itsFunctionType;
Index: src/org/mozilla/javascript/Interpreter.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/Interpreter.java,v
retrieving revision 1.307
diff -u -r1.307 Interpreter.java
--- src/org/mozilla/javascript/Interpreter.java 19 Nov 2005 22:57:49 -0000 1.307
+++ src/org/mozilla/javascript/Interpreter.java 26 Jul 2006 23:16:27 -0000
@@ -487,6 +487,7 @@
itsData.itsFunctionType = theFunction.getFunctionType();
itsData.itsNeedsActivation = theFunction.requiresActivation();
itsData.itsName = theFunction.getFunctionName();
+ itsData.jsDoc = theFunction.getJSDoc();
if (!theFunction.getIgnoreDynamicScope()) {
if (compilerEnv.isUseDynamicScope()) {
itsData.useDynamicScope = true;
Index: src/org/mozilla/javascript/InterpretedFunction.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/InterpretedFunction.java,v
retrieving revision 1.52
diff -u -r1.52 InterpretedFunction.java
--- src/org/mozilla/javascript/InterpretedFunction.java 28 May 2006 17:15:24 -0000 1.52
+++ src/org/mozilla/javascript/InterpretedFunction.java 26 Jul 2006 23:16:25 -0000
@@ -133,6 +133,17 @@
if (idata.itsRegExpLiterals != null) {
functionRegExps = createRegExpWraps(cx, scope);
}
+ if (idata.jsDoc != null) {
+ defineProperty("__jsdoc__",
+ Context.toString(idata.getJSDoc()),
+ ScriptableObject.DONTENUM);
+ defineProperty("__source__",
+ Context.toString(idata.getSourceName()),
+ ScriptableObject.DONTENUM);
+ defineProperty("__lines__",
+ Context.javaToJS(idata.getLineNumbers(), scope),
+ ScriptableObject.DONTENUM);
+ }
}
public String getFunctionName()
Index: src/org/mozilla/javascript/TokenStream.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/TokenStream.java,v
retrieving revision 1.63
diff -u -r1.63 TokenStream.java
--- src/org/mozilla/javascript/TokenStream.java 31 Jul 2005 13:48:46 -0000 1.63
+++ src/org/mozilla/javascript/TokenStream.java 26 Jul 2006 23:16:28 -0000
@@ -284,6 +284,8 @@
final int getLineno() { return lineno; }
final String getString() { return string; }
+
+ final String getJSDoc() { String s = jsDoc; jsDoc = null; return s; }
final double getNumber() { return number; }
@@ -742,6 +744,11 @@
}
if (matchChar('*')) {
boolean lookForSlash = false;
+ // Marcello: JSDoc patch
+ boolean potentialJSDoc = true;
+ boolean inJSDoc = false;
+ boolean readJSDoc = false;
+ int jsDocStep = 1; // 0: space, 1: star, 2: space, 3: text
for (;;) {
c = getChar();
if (c == EOF_CHAR) {
@@ -749,13 +756,47 @@
return Token.ERROR;
} else if (c == '*') {
lookForSlash = true;
+ if (potentialJSDoc) {
+ inJSDoc = true;
+ potentialJSDoc = false;
+ stringBufferTop = 0;
+ continue;
+ }
} else if (c == '/') {
if (lookForSlash) {
+ if (inJSDoc)
+ this.jsDoc = getStringFromBuffer();
continue retry;
}
} else {
lookForSlash = false;
}
+ potentialJSDoc = false;
+ if (inJSDoc) {
+ // If we hit a newline restart step
+ if (c=='\n' && jsDocStep>=1) {
+ jsDocStep = 0;
+ if (readJSDoc)
+ addToString(c);
+ continue;
+ } else if (jsDocStep<3) {
+ // Ignore asterisks if we're in step 0/1
+ if (c=='*') {
+ if (jsDocStep==0)
+ jsDocStep=1;
+ if (jsDocStep==1)
+ continue;
+ // Ignore spaces in steps 0,1,2
+ } else if (isJSSpace(c)) {
+ if (jsDocStep==1)
+ jsDocStep = 2;
+ continue;
+ }
+ }
+ jsDocStep = 3;
+ addToString(c);
+ readJSDoc = true;
+ }
}
}
@@ -1367,6 +1408,9 @@
// code.
private String string = "";
private double number;
+
+ // Marcello: store jsdoc
+ private String jsDoc = null;
private char[] stringBuffer = new char[128];
private int stringBufferTop;
Index: src/org/mozilla/javascript/BaseFunction.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/BaseFunction.java,v
retrieving revision 1.57
diff -u -r1.57 BaseFunction.java
--- src/org/mozilla/javascript/BaseFunction.java 30 Aug 2005 10:05:42 -0000 1.57
+++ src/org/mozilla/javascript/BaseFunction.java 26 Jul 2006 23:16:25 -0000
@@ -245,6 +245,8 @@
} else {
indent = 0;
}
+ if (args.length>=2)
+ flags |= ScriptRuntime.toInt32(args[1]);
}
return realf.decompile(indent, flags);
}
Index: src/org/mozilla/javascript/Decompiler.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/Decompiler.java,v
retrieving revision 1.19
diff -u -r1.19 Decompiler.java
--- src/org/mozilla/javascript/Decompiler.java 28 Aug 2005 23:25:22 -0000 1.19
+++ src/org/mozilla/javascript/Decompiler.java 26 Jul 2006 23:16:25 -0000
@@ -82,6 +82,17 @@
* Flag to indicate that the decompilation generates toSource result.
*/
public static final int TO_SOURCE_FLAG = 1 << 1;
+
+ /**
+ * Flag to indicate that the decompilation generates a compressed result.
+ */
+ public static final int COMPRESS_FLAG = 1 << 2;
+
+ /**
+ * Flag to indicate that the decompilation generates a compressed result.
+ */
+ public static final int COMPRESS_NEWLINES_FLAG = 1 << 3;
+
/**
* Decompilation property to specify initial ident value.
@@ -298,6 +309,10 @@
StringBuffer result = new StringBuffer();
boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG));
+ // Compress: features
+ boolean compress = (0 != (flags & Decompiler.COMPRESS_FLAG));
+ boolean compressnl = (0 != (flags & Decompiler.COMPRESS_NEWLINES_FLAG));
+ TokenMapper tm = new TokenMapper();
// Spew tokens in source, for debugging.
// as TYPE number char
@@ -329,6 +344,10 @@
int braceNesting = 0;
boolean afterFirstEOL = false;
int i = 0;
+ int prevToken = 0;
+ boolean primeFunctionNesting = false;
+ boolean inArgsList = false;
+ boolean primeInArgsList = false;
int topFunctionType;
if (source.charAt(i) == Token.SCRIPT) {
++i;
@@ -339,7 +358,9 @@
if (!toSource) {
// add an initial newline to exactly match js.
- result.append('\n');
+ // Compress: features
+ if (!compress)
+ result.append('\n');
for (int j = 0; j < indent; j++)
result.append(' ');
} else {
@@ -349,10 +370,20 @@
}
while (i < length) {
+ // Compress: features
+ if (i>0)
+ prevToken = source.charAt(i-1);
switch(source.charAt(i)) {
case Token.NAME:
case Token.REGEXP: // re-wrapped in '/'s in parser...
- i = printSourceString(source, i + 1, false, result);
+ // Compress:
+ int jumpPos = getSourceStringEnd(source, i+1);
+ if (!compress || Token.OBJECTLIT == source.charAt(jumpPos)) {
+ i = printSourceString(source, i + 1, false, result);
+ } else {
+ i = tm.printCompressed( source, i + 1, false, result, prevToken,
+ inArgsList, braceNesting);
+ }
continue;
case Token.STRING:
@@ -381,7 +412,11 @@
case Token.FUNCTION:
++i; // skip function type
- result.append("function ");
+ primeInArgsList = true;
+ primeFunctionNesting = true;
+ result.append("function");
+ if (Token.LP != getNext(source, length, i))
+ result.append(' ');
break;
case FUNCTION_END:
@@ -389,7 +424,7 @@
break;
case Token.COMMA:
- result.append(", ");
+ result.append(compress ? "," : ", ");
break;
case Token.LC:
@@ -400,6 +435,7 @@
break;
case Token.RC: {
+ tm.leaveNestingLevel(braceNesting);
--braceNesting;
/* don't print the closing RC if it closes the
* toplevel function and we're called from
@@ -417,18 +453,29 @@
case Token.WHILE:
case Token.ELSE:
indent -= indentGap;
- result.append(' ');
+ if (!compress)
+ result.append(' ');
break;
}
break;
}
case Token.LP:
+ if (primeInArgsList) {
+ inArgsList = true;
+ primeInArgsList = false;
+ }
+ if (primeFunctionNesting) {
+ tm.enterNestingLevel(braceNesting);
+ primeFunctionNesting = false;
+ }
result.append('(');
break;
case Token.RP:
+ if (inArgsList)
+ inArgsList = false;
result.append(')');
- if (Token.LC == getNext(source, length, i))
+ if (!compress && Token.LC == getNext(source, length, i))
result.append(' ');
break;
@@ -454,7 +501,7 @@
newLine = false;
}
}
- if (newLine) {
+ if (newLine && !compressnl) {
result.append('\n');
}
@@ -482,8 +529,9 @@
less = indentGap;
}
- for (; less < indent; less++)
- result.append(' ');
+ if (!compress)
+ for (; less < indent; less++)
+ result.append(' ');
}
break;
}
@@ -500,15 +548,17 @@
break;
case Token.IF:
- result.append("if ");
+ result.append(compress ? "if" : "if ");
break;
case Token.ELSE:
- result.append("else ");
+ result.append(compress ? "else" : "else ");
break;
case Token.FOR:
- result.append("for ");
+ result.append("for");
+ if (!compress || Token.NAME == getNext(source, length, i))
+ result.append(' ');
break;
case Token.IN:
@@ -516,27 +566,27 @@
break;
case Token.WITH:
- result.append("with ");
+ result.append(compress ? "with" : "with ");
break;
case Token.WHILE:
- result.append("while ");
+ result.append(compress ? "while" : "while ");
break;
case Token.DO:
- result.append("do ");
+ result.append(compress ? "do" : "do ");
break;
case Token.TRY:
- result.append("try ");
+ result.append(compress ? "try" : "try ");
break;
case Token.CATCH:
- result.append("catch ");
+ result.append(compress ? "catch" : "catch ");
break;
case Token.FINALLY:
- result.append("finally ");
+ result.append(compress ? "finally" : "finally ");
break;
case Token.THROW:
@@ -544,7 +594,7 @@
break;
case Token.SWITCH:
- result.append("switch ");
+ result.append(compress ? "switch" : "switch ");
break;
case Token.BREAK:
@@ -561,6 +611,8 @@
case Token.CASE:
result.append("case ");
+ if (!compress || Token.NAME == getNext(source, length, i))
+ result.append(' ');
break;
case Token.DEFAULT:
@@ -578,63 +630,65 @@
break;
case Token.SEMI:
- result.append(';');
- if (Token.EOL != getNext(source, length, i)) {
+ if (Token.EOL == getNext(source, length, i)) {
+ if (compressnl || !compress)
+ result.append(';');
+ } else {
// separators in FOR
- result.append(' ');
+ result.append(compress ? ";" : "; ");
}
break;
case Token.ASSIGN:
- result.append(" = ");
+ result.append(compress ? "=" : " = ");
break;
case Token.ASSIGN_ADD:
- result.append(" += ");
+ result.append(compress ? "+=" : " += ");
break;
case Token.ASSIGN_SUB:
- result.append(" -= ");
+ result.append(compress ? "-=" : " -= ");
break;
case Token.ASSIGN_MUL:
- result.append(" *= ");
+ result.append(compress ? "*=" : " *= ");
break;
case Token.ASSIGN_DIV:
- result.append(" /= ");
+ result.append(compress ? "/=" : " /= ");
break;
case Token.ASSIGN_MOD:
- result.append(" %= ");
+ result.append(compress ? "%=" : " %= ");
break;
case Token.ASSIGN_BITOR:
- result.append(" |= ");
+ result.append(compress ? "|=" : " |= ");
break;
case Token.ASSIGN_BITXOR:
- result.append(" ^= ");
+ result.append(compress ? "^=" : " ^= ");
break;
case Token.ASSIGN_BITAND:
- result.append(" &= ");
+ result.append(compress ? "&=" : " &= ");
break;
case Token.ASSIGN_LSH:
- result.append(" <<= ");
+ result.append(compress ? "<<=" : " <<= ");
break;
case Token.ASSIGN_RSH:
- result.append(" >>= ");
+ result.append(compress ? ">>=" : " >>= ");
break;
case Token.ASSIGN_URSH:
- result.append(" >>>= ");
+ result.append(compress ? ">>>=" : " >>>= ");
break;
case Token.HOOK:
- result.append(" ? ");
+ result.append(compress ? "?" : " ? ");
break;
case Token.OBJECTLIT:
@@ -652,59 +706,59 @@
result.append(':');
else
// it's the middle part of a ternary
- result.append(" : ");
+ result.append(compress ? ":" : " : ");
break;
case Token.OR:
- result.append(" || ");
+ result.append(compress ? "||" : " || ");
break;
case Token.AND:
- result.append(" && ");
+ result.append(compress ? "&&" : " && ");
break;
case Token.BITOR:
- result.append(" | ");
+ result.append(compress ? "|" : " | ");
break;
case Token.BITXOR:
- result.append(" ^ ");
+ result.append(compress ? "^" : " ^ ");
break;
case Token.BITAND:
- result.append(" & ");
+ result.append(compress ? "&" : " & ");
break;
case Token.SHEQ:
- result.append(" === ");
+ result.append(compress ? "===" : " === ");
break;
case Token.SHNE:
- result.append(" !== ");
+ result.append(compress ? "!==" : " !== ");
break;
case Token.EQ:
- result.append(" == ");
+ result.append(compress ? "==" : " == ");
break;
case Token.NE:
- result.append(" != ");
+ result.append(compress ? "!=" : " != ");
break;
case Token.LE:
- result.append(" <= ");
+ result.append(compress ? "<=" : " <= ");
break;
case Token.LT:
- result.append(" < ");
+ result.append(compress ? "<" : " < ");
break;
case Token.GE:
- result.append(" >= ");
+ result.append(compress ? ">=" : " >= ");
break;
case Token.GT:
- result.append(" > ");
+ result.append(compress ? ">" : " > ");
break;
case Token.INSTANCEOF:
@@ -712,15 +766,15 @@
break;
case Token.LSH:
- result.append(" << ");
+ result.append(compress ? "<<" : " << ");
break;
case Token.RSH:
- result.append(" >> ");
+ result.append(compress ? ">>" : " >> ");
break;
case Token.URSH:
- result.append(" >>> ");
+ result.append(compress ? ">>>" : " >>> ");
break;
case Token.TYPEOF:
@@ -748,31 +802,39 @@
break;
case Token.INC:
+ if (compress && Token.ADD == prevToken)
+ result.append(' ');
result.append("++");
+ if (compress && Token.ADD == getNext(source, length, i))
+ result.append(' ');
break;
case Token.DEC:
+ if (compress && Token.SUB == prevToken)
+ result.append(' ');
result.append("--");
+ if (compress && Token.SUB == getNext(source, length, i))
+ result.append(' ');
break;
case Token.ADD:
- result.append(" + ");
+ result.append(compress ? "+" : " + ");
break;
case Token.SUB:
- result.append(" - ");
+ result.append(compress ? "-" : " - ");
break;
case Token.MUL:
- result.append(" * ");
+ result.append(compress ? "*" : " * ");
break;
case Token.DIV:
- result.append(" / ");
+ result.append(compress ? "/" : " / ");
break;
case Token.MOD:
- result.append(" % ");
+ result.append(compress ? "%" : " % ");
break;
case Token.COLONCOLON:
@@ -800,7 +862,7 @@
if (!toSource) {
// add that trailing newline if it's an outermost function.
- if (!justFunctionBody)
+ if (!justFunctionBody && !compressnl)
result.append('\n');
} else {
if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
@@ -890,3 +952,111 @@
private static final boolean printSource = false;
}
+
+
+class TokenMapper {
+ private java.util.ArrayList functionBracePositions =
+ new java.util.ArrayList();
+ private java.util.ArrayList scopeReplacedTokens = new java.util.ArrayList();
+ private int tokenCount = 10;
+
+ // FIXME: this isn't the brightest way to accomplish this. Firstly, we need
+ // to be sure we aren't colliding with other things in the namespace!
+ private String getMappedToken(String token, boolean newMapping) {
+ String nt = null;
+ java.util.HashMap tokens = (java.util.HashMap)scopeReplacedTokens.get(scopeReplacedTokens.size()-1);
+ if (newMapping) {
+ nt = new String(Integer.toString(tokenCount++,26));
+ tokens.put(token, nt);
+ return nt;
+ }
+ String mapping = getTokenMapping(token);
+ if (mapping==null)
+ return token;
+ return mapping;
+ }
+
+ private boolean hasLocalTokenMapping(String token) {
+ if (scopeReplacedTokens.size() < 1)
+ return false;
+ java.util.HashMap tokens = (java.util.HashMap)(scopeReplacedTokens.get(scopeReplacedTokens.size()-1));
+ if (tokens.containsKey(token))
+ return true;
+ return false;
+ }
+
+ private String getTokenMapping(String token) {
+ for (int i=scopeReplacedTokens.size()-1; i>=0; i--) {
+ java.util.HashMap tokens = (java.util.HashMap)(scopeReplacedTokens.get(i));
+ if (tokens.containsKey(token))
+ return (String)tokens.get(token);
+ }
+ return null;
+ }
+
+ public int printCompressed(String source,
+ int offset,
+ boolean asQuotedString,
+ StringBuffer sb,
+ int prevToken,
+ boolean inArgsList,
+ int currentLevel) {
+ boolean newMapping = false;
+ int length = source.charAt(offset);
+ ++offset;
+ if ((0x8000 & length) != 0) {
+ length = ((0x7FFF & length) << 16) | source.charAt(offset);
+ ++offset;
+ }
+
+ if (sb != null) {
+ String str = source.substring(offset, offset + length);
+ String sourceStr = new String(str);
+ if (((prevToken == Token.VAR)&&(!hasLocalTokenMapping(sourceStr)))||(inArgsList))
+ newMapping = true;
+
+
+ if (((functionBracePositions.size()>0)&&(currentLevel>=(((Integer)functionBracePositions.get(functionBracePositions.size()-1)).intValue())))||(inArgsList))
+ if(prevToken != Token.DOT)
+ str = this.getMappedToken(str, newMapping);
+ if ((!inArgsList)&&(asQuotedString))
+ if((prevToken == Token.LC)||(prevToken == Token.COMMA))
+ str = sourceStr;
+
+ if(!asQuotedString){
+ sb.append(str);
+ } else {
+ sb.append('"');
+ sb.append(ScriptRuntime.escapeString(str));
+ sb.append('"');
+ }
+ }
+
+ return offset + length;
+ }
+
+ public void enterNestingLevel(int braceNesting){
+ functionBracePositions.add(new Integer(braceNesting+1));
+ scopeReplacedTokens.add(new java.util.HashMap());
+ }
+
+ public void leaveNestingLevel(int braceNesting){
+ Integer bn = new Integer(braceNesting);
+ if ((functionBracePositions.contains(bn))&&(scopeReplacedTokens.size()>0)) {
+ // remove our mappings now!
+ int scopedSize = scopeReplacedTokens.size();
+ /*
+ HashMap tokens = (HashMap)(scopeReplacedTokens.get(scopedSize-1));
+ Iterator titer = (tokens.keySet()).iterator();
+ String key = null;
+ while(titer.hasNext()){
+ key = (String)titer.next();
+ // System.out.println("removing: "+key);
+ tokenMappings.remove(key);
+ }
+ */
+ scopeReplacedTokens.remove(scopedSize-1);
+ functionBracePositions.remove(bn);
+ }
+ }
+}
Index: src/org/mozilla/javascript/InterpreterData.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/InterpreterData.java,v
retrieving revision 1.53
diff -u -r1.53 InterpreterData.java
--- src/org/mozilla/javascript/InterpreterData.java 30 Aug 2005 10:05:42 -0000 1.53
+++ src/org/mozilla/javascript/InterpreterData.java 26 Jul 2006 23:16:27 -0000
@@ -102,6 +102,9 @@
String encodedSource;
int encodedSourceStart;
int encodedSourceEnd;
+
+ // Marcello: Added JSDoc
+ String jsDoc;
int languageVersion;
@@ -154,6 +157,11 @@
return itsSourceFile;
}
+ public String getJSDoc()
+ {
+ return jsDoc;
+ }
+
public boolean isGeneratedScript()
{
return ScriptRuntime.isGeneratedScript(itsSourceFile);
Index: src/org/mozilla/javascript/Parser.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/Parser.java,v
retrieving revision 1.104
diff -u -r1.104 Parser.java
--- src/org/mozilla/javascript/Parser.java 1 Jun 2006 14:30:19 -0000 1.104
+++ src/org/mozilla/javascript/Parser.java 26 Jul 2006 23:16:28 -0000
@@ -430,6 +430,9 @@
{
int syntheticType = functionType;
int baseLineno = ts.getLineno(); // line number where source starts
+
+ // Marcello: JSDoc addition
+ String jsDoc = ts.getJSDoc();
int functionSourceStart = decompiler.markFunctionStart(functionType);
String name;
@@ -476,6 +479,9 @@
// of with object.
fnNode.itsIgnoreDynamicScope = true;
}
+
+ // Marcello: JSDoc addition
+ fnNode.setJSDoc(jsDoc);
int functionIndex = currentScriptOrFn.addFunction(fnNode);
_______________________________________________
dev-tech-js-engine mailing list
dev-tech-js-engine@...
https://lists.mozilla.org/listinfo/dev-tech-js-engine