[richfaces-svn-commits] JBoss Rich Faces SVN: r4755 - in trunk: cdk/maven-javascript-plugin and 30 other directories.
richfaces-svn-commits at lists.jboss.org
richfaces-svn-commits at lists.jboss.org
Tue Dec 11 18:30:42 EST 2007
Author: alexsmirnov
Date: 2007-12-11 18:30:42 -0500 (Tue, 11 Dec 2007)
New Revision: 4755
Added:
trunk/cdk/maven-javascript-plugin/
trunk/cdk/maven-javascript-plugin/README-YUICompressor.txt
trunk/cdk/maven-javascript-plugin/README-yuicompressor.txt
trunk/cdk/maven-javascript-plugin/pom.xml
trunk/cdk/maven-javascript-plugin/src/
trunk/cdk/maven-javascript-plugin/src/changes/
trunk/cdk/maven-javascript-plugin/src/changes/changes.xml
trunk/cdk/maven-javascript-plugin/src/main/
trunk/cdk/maven-javascript-plugin/src/main/java/
trunk/cdk/maven-javascript-plugin/src/main/java/com/
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/CssCompressor.java
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptErrorReporter.java
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptToken.java
trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/Aggregation.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/BasicRhinoShell.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/ErrorReporter4Mojo.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintChecker.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintMojo.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/MojoSupport.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/SourceFile.java
trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/YuiCompressorMojo.java
trunk/cdk/maven-javascript-plugin/src/main/java/org/
trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/
trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/
trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Decompiler.java
trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Parser.java
trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Token.java
trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/TokenStream.java
trunk/cdk/maven-javascript-plugin/src/main/resources/
trunk/cdk/maven-javascript-plugin/src/main/resources/jslint.js
trunk/cdk/maven-javascript-plugin/src/test/
trunk/cdk/maven-javascript-plugin/src/test/java/
trunk/cdk/maven-javascript-plugin/src/test/java/net/
trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/
trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/
trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/mojo/
trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/mojo/yuicompressor/
trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/mojo/yuicompressor/AggregationTestCase.java
Modified:
trunk/cdk/pom.xml
trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletContextImpl.java
trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletRenderContextImpl.java
trunk/framework/impl/pom.xml
trunk/framework/test/pom.xml
trunk/samples/seamPortletEar/seamBookingPortlet/pom.xml
trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/portlet.xml
trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/seamBookingPortlet-object.xml
Log:
Import patched yuicompressor-maven-plugin into project, to aggregate and test JavaScripts.
Generate aggregatet scripts and styles.
Modify Seam Booking portlet to work with a refactored bridge.
Added: trunk/cdk/maven-javascript-plugin/README-YUICompressor.txt
===================================================================
--- trunk/cdk/maven-javascript-plugin/README-YUICompressor.txt (rev 0)
+++ trunk/cdk/maven-javascript-plugin/README-YUICompressor.txt 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,76 @@
+==============================================================================
+YUI Compressor
+==============================================================================
+
+NAME
+
+ YUI Compressor - The Yahoo! JavaScript and CSS Compressor
+
+SYNOPSIS
+
+ java -jar yuicompressor.jar
+ [-h, --help] [--line-break] [--nomunge]
+ [--warn] [--charset character-set]
+ [-o outfile] infile
+
+DESCRIPTION
+
+ The YUI Compressor is a JavaScript compressor which, in addition to removing
+ comments and white-spaces, obfuscates local variables using the smallest
+ possible variable name. This obfuscation is safe, even when using constructs
+ such as 'eval' or 'with' (although the compression is not optimal is those
+ cases) Compared to jsmin, the average savings is around 20%.
+
+ The YUI Compressor is also able to safely compress CSS files. The decision
+ on which compressor is being used is made on the file extension (js or css)
+
+GLOBAL OPTIONS
+
+ -h, --help
+ Prints help on how to use the YUI Compressor
+
+ --type js|css
+ The type of compressor (JavaScript or CSS) is chosen based on the
+ extension of the input file name (.js or .css) If the input file name
+ does not have an extension, or that extension is neither .js nor .css,
+ the type option MUST be specified.
+
+ --charset character-set
+ If a supported character set is specified, the YUI Compressor will use it
+ to read the input file. Otherwise, it will assume that the platform's
+ default character set is being used. The output file is encoded using
+ the same character set.
+
+ -o outfile
+ Place output in file outfile. If not specified, the YUI Compressor will
+ place the output in a file which name is made of the input file name,
+ the "-min" suffix and the "js" extension.
+
+JAVASCRIPT ONLY OPTIONS
+
+ --line-break
+ Adds a line feed character after each semi-colon character in the
+ output file (may help debugging with the MS Script debugger)
+
+ --nomunge
+ Minify only. Do not obfuscate local symbols.
+
+ --warn
+ Prints additional warnings such as duplicate variable declarations,
+ missing variable declaration, unrecommended practices, etc.
+
+NOTES
+
+ The YUI Compressor requires Java version >= 1.4.
+
+AUTHOR
+
+ The YUI Compressor was written and is maintained by:
+ Julien Lecomte <jlecomte at yahoo-inc.com>
+ The CSS portion is a port of Isaac Schlueter's cssmin utility.
+
+COPYRIGHT
+
+ Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ Code licensed under the BSD License:
+ http://developer.yahoo.net/yui/license.txt
Property changes on: trunk/cdk/maven-javascript-plugin/README-YUICompressor.txt
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/cdk/maven-javascript-plugin/README-yuicompressor.txt
===================================================================
--- trunk/cdk/maven-javascript-plugin/README-yuicompressor.txt (rev 0)
+++ trunk/cdk/maven-javascript-plugin/README-yuicompressor.txt 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,100 @@
+==============================================================================
+YUI Compressor
+==============================================================================
+
+NAME
+
+ YUI Compressor - The Yahoo! JavaScript and CSS Compressor
+
+SYNOPSIS
+
+ Usage: java -jar yuicompressor-x.y.z.jar [options] [input file]
+
+ Global Options
+ -h, --help Displays this information
+ --type <js|css> Specifies the type of the input file
+ --charset <charset> Read the input file using <charset>
+ --line-break <column> Insert a line break after the specified column number
+ -o <file> Place the output into <file>. Defaults to stdout.
+
+ JavaScript Options
+ --warn Display possible errors in the code
+ --nomunge Minify only, do not obfuscate
+ --preserve-semi Preserve all semicolons
+ --preserve-strings Do not merge concatenated string literals
+
+DESCRIPTION
+
+ The YUI Compressor is a JavaScript compressor which, in addition to removing
+ comments and white-spaces, obfuscates local variables using the smallest
+ possible variable name. This obfuscation is safe, even when using constructs
+ such as 'eval' or 'with' (although the compression is not optimal is those
+ cases) Compared to jsmin, the average savings is around 20%.
+
+ The YUI Compressor is also able to safely compress CSS files. The decision
+ on which compressor is being used is made on the file extension (js or css)
+
+GLOBAL OPTIONS
+
+ -h, --help
+ Prints help on how to use the YUI Compressor
+
+ --line-break
+ Some source control tools don't like files containing lines longer than,
+ say 8000 characters. The linebreak option is used in that case to split
+ long lines after a specific column. It can also be used to make the code
+ more readable, easier to debug (especially with the MS Script Debugger)
+ Specify 0 to get a line break after each semi-colon in JavaScript, and
+ after each rule in CSS.
+
+ --type js|css
+ The type of compressor (JavaScript or CSS) is chosen based on the
+ extension of the input file name (.js or .css) This option is required
+ if no input file has been specified. Otherwise, this option is only
+ required if the input file extension is neither 'js' nor 'css'.
+
+ --charset character-set
+ If a supported character set is specified, the YUI Compressor will use it
+ to read the input file. Otherwise, it will assume that the platform's
+ default character set is being used. The output file is encoded using
+ the same character set.
+
+ -o outfile
+ Place output in file outfile. If not specified, the YUI Compressor will
+ default to the standard output, which you can redirect to a file.
+
+JAVASCRIPT ONLY OPTIONS
+
+ --nomunge
+ Minify only. Do not obfuscate local symbols.
+
+ --warn
+ Prints additional warnings such as duplicate variable declarations,
+ missing variable declaration, unrecommended practices, etc.
+
+ --preserve-semi
+ Preserve unnecessary semicolons (such as right before a '}') This option
+ is useful when compressed code has to be run through JSLint (which is the
+ case of YUI for example)
+
+ --preserve-strings
+ Use this option to specify that concatenated string literals should
+ never be merged.
+
+NOTES
+
+ + If no input file is specified, it defaults to stdin.
+
+ + The YUI Compressor requires Java version >= 1.4.
+
+AUTHOR
+
+ The YUI Compressor was written and is maintained by:
+ Julien Lecomte <jlecomte at yahoo-inc.com>
+ The CSS portion is a port of Isaac Schlueter's cssmin utility.
+
+COPYRIGHT
+
+ Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ Code licensed under the BSD License:
+ http://developer.yahoo.net/yui/license.txt
Property changes on: trunk/cdk/maven-javascript-plugin/README-yuicompressor.txt
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/cdk/maven-javascript-plugin/pom.xml
===================================================================
--- trunk/cdk/maven-javascript-plugin/pom.xml (rev 0)
+++ trunk/cdk/maven-javascript-plugin/pom.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,56 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>cdk</artifactId>
+ <groupId>org.richfaces</groupId>
+ <version>3.2.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.richfaces.cdk</groupId>
+ <artifactId>maven-javascript-plugin</artifactId>
+ <packaging>maven-plugin</packaging>
+ <name>YUI Compressor Maven Mojo</name>
+ <description>
+ To compress (Minify + Ofuscate) Javascript files and CSS files
+ (using YUI Compressor from Julien Lecomte) and/or to check
+ Javascript files with jslint.
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>rhino</groupId>
+ <artifactId>js</artifactId>
+ <version>1.6R7</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>2.0.7</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-project</artifactId>
+ <version>2.0.7</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins></plugins>
+ </build>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-plugin-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </reporting>
+
+ <properties>
+ <java.src.version>1.5</java.src.version>
+ <runtime.log>target/velocity.log</runtime.log>
+ </properties>
+
+</project>
Property changes on: trunk/cdk/maven-javascript-plugin/pom.xml
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/changes/changes.xml
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/changes/changes.xml (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/changes/changes.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<document>
+ <properties>
+ <author email="dwayneb at users.sourceforge.net">David Bernard</author>
+ </properties>
+ <body>
+ <release version="TODO" description="Future">
+ <action dev="dwayneb" type="add">add support for offline mode (don't failed)</action>
+ <action dev="dwayneb" type="add">generate an xml report for integration with external tool (like QALab)</action>
+ <action dev="dwayneb" type="add">generate an html/xdoc report for integration with the site (like other static analyser : pmd, checkstyle,...)</action>
+ <action dev="dwayneb" type="add">more test</action>
+ <action dev="dwayneb" type="add">add option to stop maven processing if Spoon building failed (pre-compilation by JDTCompiler)</action>
+ <action dev="dwayneb" type="add">add logging information (about download,...)</action>
+ </release>
+ <release version="0.6.1" date="2007-06-27" description="spoon-1.3">
+ <action dev="dwayneb" type="update">move to spoon-1.3</action>
+ </release>
+ <release version="0.6" date="2007-06-14" description="testing">
+ <action dev="dwayneb" type="fix">keep annotation in the generated bytecode</action>
+ <action dev="dwayneb" type="update">enhance/fix support for using plugin for test phase with local spoonlet</action>
+ <action dev="dwayneb" type="add">add the goal 'test-recompile'</action>
+ <action dev="dwayneb" type="update">rename the goal 'test-run' to 'test-generate'</action>
+ </release>
+ <release version="0.5" date="2007-06-06" description="spoon-1.2">
+ <action dev="dwayneb" type="add">add option to stop maven processing if WARNING or ERROR are reported by Spoonlet(s)</action>
+ <action dev="dwayneb" type="fix">use maven logger for every report (include new from spoon-core-1.2)</action>
+ <action dev="dwayneb" type="update">change scm from subversion (svn) to mercurial (hg)</action>
+ <action dev="dwayneb" type="update">move to spoon-core-1.2.</action>
+ <action dev="dwayneb" type="fix">change the groupId from spoon.contrib to net.sf.alchim to follow Maven's convention</action>
+ </release>
+ <release version="0.4" description="launcher improvement">
+ <action dev="dwayneb" type="fix">support change in the api of spoon-1.1-SNAPSHOT.</action>
+ <action dev="dwayneb" type="fix">display classpath as debug message</action>
+ <action dev="dwayneb" type="add">add basic support SNAPSHOT (always downloaded)</action>
+ <action dev="dwayneb" type="add">add basic support for relocation of artifact's groupId (see http://maven.apache.org/guides/mini/guide-relocation.html)</action>
+ </release>
+ <release version="0.3" date="2006-12-03" description="Support spoon.cfg.xml">
+ <action dev="dwayneb" type="add">add support of "spoon.cfg.xml".</action>
+ <action dev="dwayneb" type="add">rename previous 'run' goal by 'generate' goal.</action>
+ <action dev="dwayneb" type="add">add 'recompile' goal.</action>
+ <action dev="dwayneb" type="fix">fix classloader and classpath issue when compile and run spoonlet.</action>
+ </release>
+ <release version="0.2" date="2006-11-23" description="First release">
+ <action dev="dwayneb" type="add">Publish the project on sourceforge (under alchim project)</action>
+ </release>
+ </body>
+</document>
Property changes on: trunk/cdk/maven-javascript-plugin/src/changes/changes.xml
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/CssCompressor.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/CssCompressor.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/CssCompressor.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,157 @@
+/*
+ * YUI Compressor
+ * Author: Julien Lecomte <jlecomte at yahoo-inc.com>
+ * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ *
+ * This code is a port of Isaac Schlueter's cssmin utility.
+ */
+
+package com.yahoo.platform.yui.compressor;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class CssCompressor {
+
+ private StringBuffer srcsb = new StringBuffer();
+
+ public CssCompressor(Reader in) throws IOException {
+ // Read the stream...
+ int c;
+ while ((c = in.read()) != -1) {
+ srcsb.append((char) c);
+ }
+ }
+
+ public void compress(Writer out, int linebreakpos)
+ throws IOException {
+
+ Pattern p;
+ Matcher m;
+ String css;
+ StringBuffer sb;
+ int startIndex, endIndex;
+
+ // Remove all comment blocks...
+ sb = new StringBuffer(srcsb.toString());
+ while ((startIndex = sb.indexOf("/*")) >= 0) {
+ endIndex = sb.indexOf("*/", startIndex + 2);
+ if (endIndex >= startIndex + 2)
+ sb.delete(startIndex, endIndex + 2);
+ }
+
+ css = sb.toString();
+
+ // Normalize all whitespace strings to single spaces. Easier to work with that way.
+ css = css.replaceAll("\\s+", " ");
+
+ // Remove the spaces before the things that should not have spaces before them.
+ // But, be careful not to turn "p :link {...}" into "p:link{...}"
+ // Swap out any pseudo-class colons with the token, and then swap back.
+ sb = new StringBuffer();
+ p = Pattern.compile("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
+ m = p.matcher(css);
+ while (m.find()) {
+ String s = m.group();
+ s = s.replaceAll(":", "___PSEUDOCLASSCOLON___");
+ m.appendReplacement(sb, s);
+ }
+ m.appendTail(sb);
+ css = sb.toString();
+ css = css.replaceAll("\\s+([!{};:>+\\(\\)\\],])", "$1");
+ css = css.replaceAll("___PSEUDOCLASSCOLON___", ":");
+
+ // Remove the spaces after the things that should not have spaces after them.
+ css = css.replaceAll("([!{}:;>+\\(\\[,])\\s+", "$1");
+
+ // Add the semicolon where it's missing.
+ css = css.replaceAll("([^;\\}])}", "$1;}");
+
+ // Replace 0(px,em,%) with 0.
+ css = css.replaceAll("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2");
+
+ // Replace 0 0 0 0; with 0.
+ css = css.replaceAll(":0 0 0 0;", ":0;");
+ css = css.replaceAll(":0 0 0;", ":0;");
+ css = css.replaceAll(":0 0;", ":0;");
+ // Replace background-position:0; with background-position:0 0;
+ css = css.replaceAll("background-position:0;", "background-position:0 0;");
+
+ // Replace 0.6 to .6, but only when preceded by : or a white-space
+ css = css.replaceAll("(:|\\s)0+\\.(\\d+)", "$1.$2");
+
+ // Shorten colors from rgb(51,102,153) to #336699
+ // This makes it more likely that it'll get further compressed in the next step.
+ p = Pattern.compile("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
+ m = p.matcher(css);
+ sb = new StringBuffer();
+ while (m.find()) {
+ String[] rgbcolors = m.group(1).split(",");
+ StringBuffer hexcolor = new StringBuffer("#");
+ for (int i = 0; i < rgbcolors.length; i++) {
+ int val = Integer.parseInt(rgbcolors[i]);
+ if (val < 16) {
+ hexcolor.append("0");
+ }
+ hexcolor.append(Integer.toHexString(val));
+ }
+ m.appendReplacement(sb, hexcolor.toString());
+ }
+ m.appendTail(sb);
+ css = sb.toString();
+
+ // Shorten colors from #AABBCC to #ABC. Note that we want to make sure
+ // the color is not preceded by either ", " or =. Indeed, the property
+ // filter: chroma(color="#FFFFFF");
+ // would become
+ // filter: chroma(color="#FFF");
+ // which makes the filter break in IE.
+ p = Pattern.compile("([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])");
+ m = p.matcher(css);
+ sb = new StringBuffer();
+ while (m.find()) {
+ // Test for AABBCC pattern
+ if (m.group(3).equalsIgnoreCase(m.group(4)) &&
+ m.group(5).equalsIgnoreCase(m.group(6)) &&
+ m.group(7).equalsIgnoreCase(m.group(8))) {
+ m.appendReplacement(sb, m.group(1) + m.group(2) + "#" + m.group(3) + m.group(5) + m.group(7));
+ } else {
+ m.appendReplacement(sb, m.group());
+ }
+ }
+ m.appendTail(sb);
+ css = sb.toString();
+
+ // Remove empty rules.
+ css = css.replaceAll("[^\\}]+\\{;\\}", "");
+
+ if (linebreakpos >= 0) {
+ // Some source control tools don't like it when files containing lines longer
+ // than, say 8000 characters, are checked in. The linebreak option is used in
+ // that case to split long lines after a specific column.
+ int i = 0;
+ int linestartpos = 0;
+ sb = new StringBuffer(css);
+ while (i < sb.length()) {
+ char c = sb.charAt(i++);
+ if (c == '}' && i - linestartpos > linebreakpos) {
+ sb.insert(i, '\n');
+ linestartpos = i;
+ }
+ }
+
+ css = sb.toString();
+ }
+
+ // Trim the final string (for any leading or trailing white spaces)
+ css = css.trim();
+
+ // Write the output...
+ out.write(css);
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/CssCompressor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,1151 @@
+/*
+ * YUI Compressor
+ * Author: Julien Lecomte <jlecomte at yahoo-inc.com>
+ * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+package com.yahoo.platform.yui.compressor;
+
+import org.mozilla.javascript.*;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.*;
+
+public class JavaScriptCompressor {
+
+ static final ArrayList ones;
+ static final ArrayList twos;
+ static final ArrayList threes;
+
+ static final Set builtin = new HashSet();
+ static final Map literals = new Hashtable();
+
+ static {
+
+ // This list contains all the 3 characters or less built-in global
+ // symbols available in a browser. Please add to this list if you
+ // see anything missing.
+ builtin.add("NaN");
+ builtin.add("top");
+
+ ones = new ArrayList();
+ for (char c = 'A'; c <= 'Z'; c++)
+ ones.add(Character.toString(c));
+ for (char c = 'a'; c <= 'z'; c++)
+ ones.add(Character.toString(c));
+
+ twos = new ArrayList();
+ for (int i = 0; i < ones.size(); i++) {
+ String one = (String) ones.get(i);
+ for (char c = 'A'; c <= 'Z'; c++)
+ twos.add(one + Character.toString(c));
+ for (char c = 'a'; c <= 'z'; c++)
+ twos.add(one + Character.toString(c));
+ for (char c = '0'; c <= '9'; c++)
+ twos.add(one + Character.toString(c));
+ }
+
+ // Remove two-letter JavaScript reserved words and built-in globals...
+ twos.remove("as");
+ twos.remove("is");
+ twos.remove("do");
+ twos.remove("if");
+ twos.remove("in");
+ twos.removeAll(builtin);
+
+ threes = new ArrayList();
+ for (int i = 0; i < twos.size(); i++) {
+ String two = (String) twos.get(i);
+ for (char c = 'A'; c <= 'Z'; c++)
+ threes.add(two + Character.toString(c));
+ for (char c = 'a'; c <= 'z'; c++)
+ threes.add(two + Character.toString(c));
+ for (char c = '0'; c <= '9'; c++)
+ threes.add(two + Character.toString(c));
+ }
+
+ // Remove three-letter JavaScript reserved words and built-in globals...
+ threes.remove("for");
+ threes.remove("int");
+ threes.remove("new");
+ threes.remove("try");
+ threes.remove("use");
+ threes.remove("var");
+ threes.removeAll(builtin);
+
+ // That's up to ((26+26)*(1+(26+26+10)))*(1+(26+26+10))-8
+ // (206,380 symbols per scope)
+
+ // The following list comes from org/mozilla/javascript/Decompiler.java...
+ literals.put(new Integer(Token.GET), "get ");
+ literals.put(new Integer(Token.SET), "set ");
+ literals.put(new Integer(Token.TRUE), "true");
+ literals.put(new Integer(Token.FALSE), "false");
+ literals.put(new Integer(Token.NULL), "null");
+ literals.put(new Integer(Token.THIS), "this");
+ literals.put(new Integer(Token.FUNCTION), "function ");
+ literals.put(new Integer(Token.COMMA), ",");
+ literals.put(new Integer(Token.LC), "{");
+ literals.put(new Integer(Token.RC), "}");
+ literals.put(new Integer(Token.LP), "(");
+ literals.put(new Integer(Token.RP), ")");
+ literals.put(new Integer(Token.LB), "[");
+ literals.put(new Integer(Token.RB), "]");
+ literals.put(new Integer(Token.DOT), ".");
+ literals.put(new Integer(Token.NEW), "new ");
+ literals.put(new Integer(Token.DELPROP), "delete ");
+ literals.put(new Integer(Token.IF), "if");
+ literals.put(new Integer(Token.ELSE), "else");
+ literals.put(new Integer(Token.FOR), "for");
+ literals.put(new Integer(Token.IN), " in ");
+ literals.put(new Integer(Token.WITH), "with");
+ literals.put(new Integer(Token.WHILE), "while");
+ literals.put(new Integer(Token.DO), "do");
+ literals.put(new Integer(Token.TRY), "try");
+ literals.put(new Integer(Token.CATCH), "catch");
+ literals.put(new Integer(Token.FINALLY), "finally");
+ literals.put(new Integer(Token.THROW), "throw ");
+ literals.put(new Integer(Token.SWITCH), "switch");
+ literals.put(new Integer(Token.BREAK), "break ");
+ literals.put(new Integer(Token.CONTINUE), "continue ");
+ literals.put(new Integer(Token.CASE), "case ");
+ literals.put(new Integer(Token.DEFAULT), "default");
+ literals.put(new Integer(Token.RETURN), "return ");
+ literals.put(new Integer(Token.VAR), "var ");
+ literals.put(new Integer(Token.SEMI), ";");
+ literals.put(new Integer(Token.ASSIGN), "=");
+ literals.put(new Integer(Token.ASSIGN_ADD), "+=");
+ literals.put(new Integer(Token.ASSIGN_SUB), "-=");
+ literals.put(new Integer(Token.ASSIGN_MUL), "*=");
+ literals.put(new Integer(Token.ASSIGN_DIV), "/=");
+ literals.put(new Integer(Token.ASSIGN_MOD), "%=");
+ literals.put(new Integer(Token.ASSIGN_BITOR), "|=");
+ literals.put(new Integer(Token.ASSIGN_BITXOR), "^=");
+ literals.put(new Integer(Token.ASSIGN_BITAND), "&=");
+ literals.put(new Integer(Token.ASSIGN_LSH), "<<=");
+ literals.put(new Integer(Token.ASSIGN_RSH), ">>=");
+ literals.put(new Integer(Token.ASSIGN_URSH), ">>>=");
+ literals.put(new Integer(Token.HOOK), "?");
+ literals.put(new Integer(Token.OBJECTLIT), ":");
+ literals.put(new Integer(Token.COLON), ":");
+ literals.put(new Integer(Token.OR), "||");
+ literals.put(new Integer(Token.AND), "&&");
+ literals.put(new Integer(Token.BITOR), "|");
+ literals.put(new Integer(Token.BITXOR), "^");
+ literals.put(new Integer(Token.BITAND), "&");
+ literals.put(new Integer(Token.SHEQ), "===");
+ literals.put(new Integer(Token.SHNE), "!==");
+ literals.put(new Integer(Token.EQ), "==");
+ literals.put(new Integer(Token.NE), "!=");
+ literals.put(new Integer(Token.LE), "<=");
+ literals.put(new Integer(Token.LT), "<");
+ literals.put(new Integer(Token.GE), ">=");
+ literals.put(new Integer(Token.GT), ">");
+ literals.put(new Integer(Token.INSTANCEOF), " instanceof ");
+ literals.put(new Integer(Token.LSH), "<<");
+ literals.put(new Integer(Token.RSH), ">>");
+ literals.put(new Integer(Token.URSH), ">>>");
+ literals.put(new Integer(Token.TYPEOF), "typeof ");
+ literals.put(new Integer(Token.VOID), "void ");
+ literals.put(new Integer(Token.CONST), "const ");
+ literals.put(new Integer(Token.NOT), "!");
+ literals.put(new Integer(Token.BITNOT), "~");
+ literals.put(new Integer(Token.POS), "+");
+ literals.put(new Integer(Token.NEG), "-");
+ literals.put(new Integer(Token.INC), "++");
+ literals.put(new Integer(Token.DEC), "--");
+ literals.put(new Integer(Token.ADD), "+");
+ literals.put(new Integer(Token.SUB), "-");
+ literals.put(new Integer(Token.MUL), "*");
+ literals.put(new Integer(Token.DIV), "/");
+ literals.put(new Integer(Token.MOD), "%");
+ literals.put(new Integer(Token.COLONCOLON), "::");
+ literals.put(new Integer(Token.DOTDOT), "..");
+ literals.put(new Integer(Token.DOTQUERY), ".(");
+ literals.put(new Integer(Token.XMLATTR), "@");
+ }
+
+ private static int countChar(String haystack, char needle) {
+ int idx = 0;
+ int count = 0;
+ int length = haystack.length();
+ while (idx < length) {
+ char c = haystack.charAt(idx++);
+ if (c == needle) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private static int printSourceString(String source, int offset, StringBuffer sb) {
+ 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);
+ sb.append(str);
+ }
+ return offset + length;
+ }
+
+ private static int printSourceNumber(String source,
+ int offset, StringBuffer sb) {
+ double number = 0.0;
+ char type = source.charAt(offset);
+ ++offset;
+ if (type == 'S') {
+ if (sb != null) {
+ number = source.charAt(offset);
+ }
+ ++offset;
+ } else if (type == 'J' || type == 'D') {
+ if (sb != null) {
+ long lbits;
+ lbits = (long) source.charAt(offset) << 48;
+ lbits |= (long) source.charAt(offset + 1) << 32;
+ lbits |= (long) source.charAt(offset + 2) << 16;
+ lbits |= (long) source.charAt(offset + 3);
+ if (type == 'J') {
+ number = lbits;
+ } else {
+ number = Double.longBitsToDouble(lbits);
+ }
+ }
+ offset += 4;
+ } else {
+ // Bad source
+ throw new RuntimeException();
+ }
+ if (sb != null) {
+ sb.append(ScriptRuntime.numberToString(number, 10));
+ }
+ return offset;
+ }
+
+ private static ArrayList parse(Reader in, ErrorReporter reporter)
+ throws IOException, EvaluatorException {
+
+ CompilerEnvirons env = new CompilerEnvirons();
+ Parser parser = new Parser(env, reporter);
+ parser.parse(in, null, 1);
+ String source = parser.getEncodedSource();
+
+ int offset = 0;
+ int length = source.length();
+ ArrayList tokens = new ArrayList();
+ StringBuffer sb = new StringBuffer();
+
+ while (offset < length) {
+ int tt = source.charAt(offset++);
+ switch (tt) {
+
+ case Token.IECC:
+ case Token.NAME:
+ case Token.REGEXP:
+ case Token.STRING:
+ sb.setLength(0);
+ offset = printSourceString(source, offset, sb);
+ tokens.add(new JavaScriptToken(tt, sb.toString()));
+ break;
+
+ case Token.NUMBER:
+ sb.setLength(0);
+ offset = printSourceNumber(source, offset, sb);
+ tokens.add(new JavaScriptToken(tt, sb.toString()));
+ break;
+
+ default:
+ String literal = (String) literals.get(new Integer(tt));
+ if (literal != null) {
+ tokens.add(new JavaScriptToken(tt, literal));
+ }
+ break;
+ }
+ }
+
+ return tokens;
+ }
+
+ private static ArrayList processStringLiterals(ArrayList tokens, boolean merge) {
+
+ String tv;
+ int i, length;
+ ArrayList result = new ArrayList();
+ JavaScriptToken token, prevToken, nextToken;
+
+ // Concatenate string literals that are being appended wherever
+ // it is safe to do so. Note that we take care of the case:
+ // "a" + "b".toUpperCase()
+
+ for (i = 0, length = tokens.size(); i < length; i++) {
+ token = (JavaScriptToken) tokens.get(i);
+ switch (token.getType()) {
+
+ case Token.ADD:
+ if (merge) {
+ if (i > 0 && i < length) {
+ prevToken = (JavaScriptToken) result.get(result.size() - 1);
+ nextToken = (JavaScriptToken) tokens.get(i + 1);
+ if (prevToken.getType() == Token.STRING && nextToken.getType() == Token.STRING &&
+ (i == length - 1 || ((JavaScriptToken) tokens.get(i + 2)).getType() != Token.DOT)) {
+ result.set(result.size() - 1, new JavaScriptToken(Token.STRING,
+ prevToken.getValue() + nextToken.getValue()));
+ i++; // not a good practice, but oh well...
+ break;
+ }
+ }
+ }
+
+ /* FALLSTHROUGH */
+
+ default:
+ result.add(token);
+ break;
+ }
+ }
+
+ // Second pass...
+
+ for (i = 0, length = result.size(); i < length; i++) {
+ token = (JavaScriptToken) result.get(i);
+ if (token.getType() == Token.STRING) {
+ tv = token.getValue();
+
+ // Finally, add the quoting characters and escape the string. We use
+ // the quoting character that minimizes the amount of escaping to save
+ // a few additional bytes.
+
+ char quotechar;
+ int singleQuoteCount = countChar(tv, '\'');
+ int doubleQuoteCount = countChar(tv, '"');
+ if (doubleQuoteCount <= singleQuoteCount) {
+ quotechar = '"';
+ } else {
+ quotechar = '\'';
+ }
+
+ tv = quotechar + escapeString(tv, quotechar) + quotechar;
+
+ // String concatenation transforms the old script scheme:
+ // '<scr'+'ipt ...><'+'/script>'
+ // into the following:
+ // '<script ...></script>'
+ // which breaks if this code is embedded inside an HTML document.
+ // Since this is not the right way to do this, let's fix the code by
+ // transforming all "</script" into "<\/script"
+
+ if (tv.indexOf("</script") >= 0) {
+ tv = tv.replaceAll("<\\/script", "<\\\\/script");
+ }
+
+ result.set(i, new JavaScriptToken(Token.STRING, tv));
+ }
+ }
+
+ return result;
+ }
+
+ // Add necessary escaping that was removed in Rhino's tokenizer.
+ private static String escapeString(String s, char quotechar) {
+
+ assert quotechar == '"' || quotechar == '\'';
+
+ if (s == null) {
+ return null;
+ }
+
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, L = s.length(); i < L; i++) {
+ int c = s.charAt(i);
+ if (c == quotechar) {
+ sb.append("\\");
+ }
+ sb.append((char) c);
+ }
+
+ return sb.toString();
+ }
+
+ private ErrorReporter logger;
+
+ private boolean munge;
+ private boolean warn;
+
+ private static final int BUILDING_SYMBOL_TREE = 1;
+ private static final int CHECKING_SYMBOL_TREE = 2;
+
+ private int mode;
+ private int offset;
+ private int braceNesting;
+ private ArrayList srctokens, tokens;
+ private Stack scopes = new Stack();
+ private ScriptOrFnScope globalScope = new ScriptOrFnScope(-1, null);
+ private Hashtable indexedScopes = new Hashtable();
+
+ public JavaScriptCompressor(Reader in, ErrorReporter reporter)
+ throws IOException, EvaluatorException {
+
+ this.logger = reporter;
+ this.srctokens = parse(in, reporter);
+ }
+
+ public void compress(Writer out, int linebreak, boolean munge, boolean warn,
+ boolean preserveAllSemiColons, boolean preserveStringLiterals)
+ throws IOException {
+
+ this.munge = munge;
+ this.warn = warn;
+
+ this.tokens = processStringLiterals(this.srctokens, !preserveStringLiterals);
+
+ buildSymbolTree();
+ mungeSymboltree();
+ StringBuffer sb = printSymbolTree(linebreak, preserveAllSemiColons);
+
+ out.write(sb.toString());
+ }
+
+ private ScriptOrFnScope getCurrentScope() {
+ return (ScriptOrFnScope) scopes.peek();
+ }
+
+ private void enterScope(ScriptOrFnScope scope) {
+ scopes.push(scope);
+ }
+
+ private void leaveCurrentScope() {
+ scopes.pop();
+ }
+
+ private JavaScriptToken consumeToken() {
+ return (JavaScriptToken) tokens.get(offset++);
+ }
+
+ private JavaScriptToken getToken(int delta) {
+ return (JavaScriptToken) tokens.get(offset + delta);
+ }
+
+ /*
+ * Returns the identifier for the specified symbol defined in
+ * the specified scope or in any scope above it. Returns null
+ * if this symbol does not have a corresponding identifier.
+ */
+ private JavaScriptIdentifier getIdentifier(String symbol, ScriptOrFnScope scope) {
+ JavaScriptIdentifier identifier;
+ while (scope != null) {
+ identifier = scope.getIdentifier(symbol);
+ if (identifier != null) {
+ return identifier;
+ }
+ scope = scope.getParentScope();
+ }
+ return null;
+ }
+
+ /*
+ * If either 'eval' or 'with' is used in a local scope, we must make
+ * sure that all containing local scopes don't get munged. Otherwise,
+ * the obfuscation would potentially introduce bugs.
+ */
+ private void protectScopeFromObfuscation(ScriptOrFnScope scope) {
+ assert scope != null;
+
+ if (scope == globalScope) {
+ // The global scope does not get obfuscated,
+ // so we don't need to worry about it...
+ return;
+ }
+
+ // Find the highest local scope containing the specified scope.
+ while (scope.getParentScope() != globalScope) {
+ scope = scope.getParentScope();
+ }
+
+ assert scope.getParentScope() == globalScope;
+ scope.preventMunging();
+ }
+
+ private String getDebugString(int max) {
+ assert max > 0;
+ StringBuffer result = new StringBuffer();
+ int start = Math.max(offset - max, 0);
+ int end = Math.min(offset + max, tokens.size());
+ for (int i = start; i < end; i++) {
+ JavaScriptToken token = (JavaScriptToken) tokens.get(i);
+ if (i == offset)
+ result.append(" ---> ");
+ result.append(token.getValue());
+ if (i == offset)
+ result.append(" <--- ");
+ }
+ return result.toString();
+ }
+
+ private void warn(String message, boolean showDebugString) {
+ if (warn) {
+ if (showDebugString) {
+ message = message + "\n" + getDebugString(10);
+ }
+ logger.warning(message, null, -1, null, -1);
+ }
+ }
+
+ private void parseFunctionDeclaration() {
+
+ String symbol;
+ JavaScriptToken token;
+ ScriptOrFnScope currentScope, fnScope;
+ JavaScriptIdentifier identifier;
+
+ currentScope = getCurrentScope();
+
+ token = consumeToken();
+ if (token.getType() == Token.NAME) {
+ if (mode == BUILDING_SYMBOL_TREE) {
+ // Get the name of the function and declare it in the current scope.
+ symbol = token.getValue();
+ if (currentScope.getIdentifier(symbol) != null) {
+ warn("[WARNING] The function " + symbol + " has already been declared in the same scope...", true);
+ }
+ currentScope.declareIdentifier(symbol);
+ }
+ token = consumeToken();
+ }
+
+ assert token.getType() == Token.LP;
+ if (mode == BUILDING_SYMBOL_TREE) {
+ fnScope = new ScriptOrFnScope(braceNesting, currentScope);
+ indexedScopes.put(new Integer(offset), fnScope);
+ } else {
+ fnScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset));
+ }
+
+ // Parse function arguments.
+ int argpos = 0;
+ while ((token = consumeToken()).getType() != Token.RP) {
+ assert token.getType() == Token.NAME ||
+ token.getType() == Token.COMMA;
+ if (token.getType() == Token.NAME && mode == BUILDING_SYMBOL_TREE) {
+ symbol = token.getValue();
+ identifier = fnScope.declareIdentifier(symbol);
+ if (symbol.equals("$super") && argpos == 0) {
+ // Exception for Prototype 1.6...
+ identifier.preventMunging();
+ }
+ argpos++;
+ }
+ }
+
+ token = consumeToken();
+ assert token.getType() == Token.LC;
+ braceNesting++;
+
+ token = getToken(0);
+ if (token.getType() == Token.STRING &&
+ getToken(1).getType() == Token.SEMI) {
+ // This is a hint. Hints are empty statements that look like
+ // "localvar1:nomunge, localvar2:nomunge"; They allow developers
+ // to prevent specific symbols from getting obfuscated (some heretic
+ // implementations, such as Prototype 1.6, require specific variable
+ // names, such as $super for example, in order to work appropriately.
+ // Note: right now, only "nomunge" is supported in the right hand side
+ // of a hint. However, in the future, the right hand side may contain
+ // other values.
+ consumeToken();
+ String hints = token.getValue();
+ // Remove the leading and trailing quotes...
+ hints = hints.substring(1, hints.length() - 1).trim();
+ StringTokenizer st1 = new StringTokenizer(hints, ",");
+ while (st1.hasMoreTokens()) {
+ String hint = st1.nextToken();
+ int idx = hint.indexOf(':');
+ if (idx <= 0 || idx >= hint.length() - 1) {
+ if (mode == BUILDING_SYMBOL_TREE) {
+ // No need to report the error twice, hence the test...
+ warn("[WARNING] Invalid hint syntax: " + hint, true);
+ }
+ break;
+ }
+ String variableName = hint.substring(0, idx).trim();
+ String variableType = hint.substring(idx + 1).trim();
+ if (mode == BUILDING_SYMBOL_TREE) {
+ fnScope.addHint(variableName, variableType);
+ } else if (mode == CHECKING_SYMBOL_TREE) {
+ identifier = fnScope.getIdentifier(variableName);
+ if (identifier != null) {
+ if (variableType.equals("nomunge")) {
+ identifier.preventMunging();
+ } else {
+ warn("[WARNING] Unsupported hint value: " + hint, true);
+ }
+ } else {
+ warn("[WARNING] Hint refers to an unknown identifier: " + hint, true);
+ }
+ }
+ }
+ }
+
+ parseScope(fnScope);
+ }
+
+ private void parseCatch() {
+
+ String symbol;
+ JavaScriptToken token;
+ ScriptOrFnScope currentScope;
+ JavaScriptIdentifier identifier;
+
+ token = getToken(-1);
+ assert token.getType() == Token.CATCH;
+ token = consumeToken();
+ assert token.getType() == Token.LP;
+ token = consumeToken();
+ assert token.getType() == Token.NAME;
+
+ symbol = token.getValue();
+ currentScope = getCurrentScope();
+
+ if (mode == BUILDING_SYMBOL_TREE) {
+ // We must declare the exception identifier in the containing function
+ // scope to avoid errors related to the obfuscation process. No need to
+ // display a warning if the symbol was already declared here...
+ currentScope.declareIdentifier(symbol);
+ } else {
+ identifier = getIdentifier(symbol, currentScope);
+ identifier.incrementRefcount();
+ }
+
+ token = consumeToken();
+ assert token.getType() == Token.RP;
+ }
+
+ private void parseExpression() {
+
+ // Parse the expression until we encounter a comma or a semi-colon
+ // in the same brace nesting, bracket nesting and paren nesting.
+ // Parse functions if any...
+
+ String symbol;
+ JavaScriptToken token;
+ ScriptOrFnScope currentScope;
+ JavaScriptIdentifier identifier;
+
+ int expressionBraceNesting = braceNesting;
+ int bracketNesting = 0;
+ int parensNesting = 0;
+
+ int length = tokens.size();
+
+ while (offset < length) {
+
+ token = consumeToken();
+ currentScope = getCurrentScope();
+
+ switch (token.getType()) {
+
+ case Token.SEMI:
+ case Token.COMMA:
+ if (braceNesting == expressionBraceNesting &&
+ bracketNesting == 0 &&
+ parensNesting == 0) {
+ return;
+ }
+ break;
+
+ case Token.FUNCTION:
+ parseFunctionDeclaration();
+ break;
+
+ case Token.LC:
+ braceNesting++;
+ break;
+
+ case Token.RC:
+ braceNesting--;
+ assert braceNesting >= expressionBraceNesting;
+ break;
+
+ case Token.LB:
+ bracketNesting++;
+ break;
+
+ case Token.RB:
+ bracketNesting--;
+ break;
+
+ case Token.LP:
+ parensNesting++;
+ break;
+
+ case Token.RP:
+ parensNesting--;
+ break;
+
+ case Token.IECC:
+ if (mode == BUILDING_SYMBOL_TREE) {
+ protectScopeFromObfuscation(currentScope);
+ warn("[WARNING] Using JScript conditional comments is not recommended..." + (munge ? "\n[INFO] Using JSCript conditional comments reduces the level of compression!" : ""), true);
+ }
+ break;
+
+ case Token.NAME:
+ symbol = token.getValue();
+
+ if (mode == BUILDING_SYMBOL_TREE) {
+
+ if (symbol.equals("eval")) {
+
+ protectScopeFromObfuscation(currentScope);
+ warn("[WARNING] Using 'eval' is not recommended..." + (munge ? "\n[INFO] Using 'eval' reduces the level of compression!" : ""), true);
+
+ }
+
+ } else if (mode == CHECKING_SYMBOL_TREE) {
+
+ if ((offset < 2 ||
+ (getToken(-2).getType() != Token.DOT &&
+ getToken(-2).getType() != Token.GET &&
+ getToken(-2).getType() != Token.SET)) &&
+ getToken(0).getType() != Token.OBJECTLIT) {
+
+ identifier = getIdentifier(symbol, currentScope);
+
+ if (identifier == null) {
+
+ if (symbol.length() <= 3 && !builtin.contains(symbol)) {
+ // Here, we found an undeclared and un-namespaced symbol that is
+ // 3 characters or less in length. Declare it in the global scope.
+ // We don't need to declare longer symbols since they won't cause
+ // any conflict with other munged symbols.
+ globalScope.declareIdentifier(symbol);
+ warn("[WARNING] Found an undeclared symbol: " + symbol, true);
+ }
+
+ } else {
+
+ identifier.incrementRefcount();
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private void parseScope(ScriptOrFnScope scope) {
+
+ String symbol;
+ JavaScriptToken token;
+ JavaScriptIdentifier identifier;
+
+ int length = tokens.size();
+
+ enterScope(scope);
+
+ while (offset < length) {
+
+ token = consumeToken();
+
+ switch (token.getType()) {
+
+ case Token.VAR:
+ case Token.CONST:
+
+ // The var keyword is followed by at least one symbol name.
+ // If several symbols follow, they are comma separated.
+ for (; ;) {
+ token = consumeToken();
+
+ assert token.getType() == Token.NAME;
+
+ if (mode == BUILDING_SYMBOL_TREE) {
+ symbol = token.getValue();
+ if (scope.getIdentifier(symbol) == null) {
+ scope.declareIdentifier(symbol);
+ } else {
+ warn("[WARNING] The variable " + symbol + " has already been declared in the same scope...", true);
+ }
+ }
+
+ token = getToken(0);
+
+ assert token.getType() == Token.SEMI ||
+ token.getType() == Token.ASSIGN ||
+ token.getType() == Token.COMMA ||
+ token.getType() == Token.IN;
+
+ if (token.getType() == Token.IN) {
+ break;
+ } else {
+ parseExpression();
+ token = getToken(-1);
+ if (token.getType() == Token.SEMI) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case Token.FUNCTION:
+ parseFunctionDeclaration();
+ break;
+
+ case Token.LC:
+ braceNesting++;
+ break;
+
+ case Token.RC:
+ braceNesting--;
+ assert braceNesting >= scope.getBraceNesting();
+ if (braceNesting == scope.getBraceNesting()) {
+ leaveCurrentScope();
+ return;
+ }
+ break;
+
+ case Token.WITH:
+ if (mode == BUILDING_SYMBOL_TREE) {
+ // Inside a 'with' block, it is impossible to figure out
+ // statically whether a symbol is a local variable or an
+ // object member. As a consequence, the only thing we can
+ // do is turn the obfuscation off for the highest scope
+ // containing the 'with' block.
+ protectScopeFromObfuscation(scope);
+ warn("[WARNING] Using 'with' is not recommended" + (munge ? "(and it reduces the level of compression)" : ""), true);
+ }
+ break;
+
+ case Token.CATCH:
+ parseCatch();
+ break;
+
+ case Token.IECC:
+ if (mode == BUILDING_SYMBOL_TREE) {
+ protectScopeFromObfuscation(scope);
+ warn("[WARNING] Using JScript conditional comments is not recommended..." + (munge ? "\n[INFO] Using JSCript conditional comments reduces the level of compression!" : ""), true);
+ }
+ break;
+
+ case Token.NAME:
+ symbol = token.getValue();
+
+ if (mode == BUILDING_SYMBOL_TREE) {
+
+ if (symbol.equals("eval")) {
+
+ protectScopeFromObfuscation(scope);
+ warn("[WARNING] Using 'eval' is not recommended..." + (munge ? "\n[INFO] Using 'eval' reduces the level of compression!" : ""), true);
+
+ }
+
+ } else if (mode == CHECKING_SYMBOL_TREE) {
+
+ if ((offset < 2 || getToken(-2).getType() != Token.DOT) &&
+ getToken(0).getType() != Token.OBJECTLIT) {
+
+ identifier = getIdentifier(symbol, scope);
+
+ if (identifier == null) {
+
+ if (symbol.length() <= 3 && !builtin.contains(symbol)) {
+ // Here, we found an undeclared and un-namespaced symbol that is
+ // 3 characters or less in length. Declare it in the global scope.
+ // We don't need to declare longer symbols since they won't cause
+ // any conflict with other munged symbols.
+ globalScope.declareIdentifier(symbol);
+ warn("[WARNING] Found an undeclared symbol: " + symbol, true);
+ }
+
+ } else {
+
+ identifier.incrementRefcount();
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private void buildSymbolTree() {
+ offset = 0;
+ braceNesting = 0;
+ scopes.clear();
+ indexedScopes.clear();
+ indexedScopes.put(new Integer(0), globalScope);
+ mode = BUILDING_SYMBOL_TREE;
+ parseScope(globalScope);
+ }
+
+ private void mungeSymboltree() {
+
+ if (!munge) {
+ return;
+ }
+
+ // One problem with obfuscation resides in the use of undeclared
+ // and un-namespaced global symbols that are 3 characters or less
+ // in length. Here is an example:
+ //
+ // var declaredGlobalVar;
+ //
+ // function declaredGlobalFn() {
+ // var localvar;
+ // localvar = abc; // abc is an undeclared global symbol
+ // }
+ //
+ // In the example above, there is a slim chance that localvar may be
+ // munged to 'abc', conflicting with the undeclared global symbol
+ // abc, creating a potential bug. The following code detects such
+ // global symbols. This must be done AFTER the entire file has been
+ // parsed, and BEFORE munging the symbol tree. Note that declaring
+ // extra symbols in the global scope won't hurt.
+ //
+ // Note: Since we go through all the tokens to do this, we also use
+ // the opportunity to count how many times each identifier is used.
+
+ offset = 0;
+ braceNesting = 0;
+ scopes.clear();
+ mode = CHECKING_SYMBOL_TREE;
+ parseScope(globalScope);
+ globalScope.munge();
+ }
+
+ private StringBuffer printSymbolTree(int linebreakpos, boolean preserveAllSemiColons)
+ throws IOException {
+
+ offset = 0;
+ braceNesting = 0;
+ scopes.clear();
+
+ String symbol;
+ JavaScriptToken token;
+ ScriptOrFnScope currentScope;
+ JavaScriptIdentifier identifier;
+
+ int length = tokens.size();
+ StringBuffer result = new StringBuffer();
+
+ int linestartpos = 0;
+
+ enterScope(globalScope);
+
+ while (offset < length) {
+
+ token = consumeToken();
+ symbol = token.getValue();
+ currentScope = getCurrentScope();
+
+ switch (token.getType()) {
+
+ case Token.NAME:
+
+ if (offset >= 2 && getToken(-2).getType() == Token.DOT ||
+ getToken(0).getType() == Token.OBJECTLIT) {
+
+ result.append(symbol);
+
+ } else {
+
+ identifier = getIdentifier(symbol, currentScope);
+ if (identifier != null) {
+ if (identifier.getMungedValue() != null) {
+ result.append(identifier.getMungedValue());
+ } else {
+ result.append(symbol);
+ }
+ if (currentScope != globalScope && identifier.getRefcount() == 0) {
+ warn("[WARNING] The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more efficient way.", true);
+ }
+ } else {
+ result.append(symbol);
+ }
+ }
+ break;
+
+ case Token.REGEXP:
+ case Token.NUMBER:
+ case Token.STRING:
+ result.append(symbol);
+ break;
+
+ case Token.ADD:
+ case Token.SUB:
+ result.append((String) literals.get(new Integer(token.getType())));
+ if (offset < length) {
+ token = getToken(0);
+ if (token.getType() == Token.INC ||
+ token.getType() == Token.DEC ||
+ token.getType() == Token.ADD ||
+ token.getType() == Token.DEC) {
+ // Handle the case x +/- ++/-- y
+ // We must keep a white space here. Otherwise, x +++ y would be
+ // interpreted as x ++ + y by the compiler, which is a bug (due
+ // to the implicit assignment being done on the wrong variable)
+ result.append(" ");
+ } else if (token.getType() == Token.POS && getToken(-1).getType() == Token.ADD ||
+ token.getType() == Token.NEG && getToken(-1).getType() == Token.SUB) {
+ // Handle the case x + + y and x - - y
+ result.append(" ");
+ }
+ }
+ break;
+
+ case Token.FUNCTION:
+ result.append("function");
+ token = consumeToken();
+ if (token.getType() == Token.NAME) {
+ result.append(" ");
+ symbol = token.getValue();
+ identifier = getIdentifier(symbol, currentScope);
+ assert identifier != null;
+ if (identifier.getMungedValue() != null) {
+ result.append(identifier.getMungedValue());
+ } else {
+ result.append(symbol);
+ }
+ if (currentScope != globalScope && identifier.getRefcount() == 0) {
+ warn("[WARNING] The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more efficient way.", true);
+ }
+ token = consumeToken();
+ }
+ assert token.getType() == Token.LP;
+ result.append("(");
+ currentScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset));
+ enterScope(currentScope);
+ while ((token = consumeToken()).getType() != Token.RP) {
+ assert token.getType() == Token.NAME || token.getType() == Token.COMMA;
+ if (token.getType() == Token.NAME) {
+ symbol = token.getValue();
+ identifier = getIdentifier(symbol, currentScope);
+ assert identifier != null;
+ if (identifier.getMungedValue() != null) {
+ result.append(identifier.getMungedValue());
+ } else {
+ result.append(symbol);
+ }
+ } else if (token.getType() == Token.COMMA) {
+ result.append(",");
+ }
+ }
+ result.append(")");
+ token = consumeToken();
+ assert token.getType() == Token.LC;
+ result.append("{");
+ braceNesting++;
+ token = getToken(0);
+ if (token.getType() == Token.STRING) {
+ // This is a hint. Skip it!
+ consumeToken();
+ token = getToken(0);
+ assert token.getType() == Token.SEMI;
+ consumeToken();
+ }
+ break;
+
+ case Token.RETURN:
+ result.append("return");
+ // No space needed after 'return' when followed
+ // by '(', '[', '{', a string or a regexp.
+ if (offset < length) {
+ token = getToken(0);
+ if (token.getType() != Token.LP &&
+ token.getType() != Token.LB &&
+ token.getType() != Token.LC &&
+ token.getType() != Token.STRING &&
+ token.getType() != Token.REGEXP) {
+ result.append(" ");
+ }
+ }
+ break;
+
+ case Token.CASE:
+ result.append("case");
+ // White-space needed after 'case' when not followed by a string.
+ if (offset < length && getToken(0).getType() != Token.STRING) {
+ result.append(" ");
+ }
+ break;
+
+ case Token.THROW:
+ // White-space needed after 'throw' when not followed by a string.
+ result.append("throw");
+ if (offset < length && getToken(0).getType() != Token.STRING) {
+ result.append(" ");
+ }
+ break;
+
+ case Token.BREAK:
+ result.append("break");
+ if (offset < length && getToken(0).getType() != Token.SEMI) {
+ // If 'break' is not followed by a semi-colon, it must be
+ // followed by a label, hence the need for a white space.
+ result.append(" ");
+ }
+ break;
+
+ case Token.CONTINUE:
+ result.append("continue");
+ if (offset < length && getToken(0).getType() != Token.SEMI) {
+ // If 'continue' is not followed by a semi-colon, it must be
+ // followed by a label, hence the need for a white space.
+ result.append(" ");
+ }
+ break;
+
+ case Token.LC:
+ result.append("{");
+ braceNesting++;
+ break;
+
+ case Token.RC:
+ result.append("}");
+ braceNesting--;
+ assert braceNesting >= currentScope.getBraceNesting();
+ if (braceNesting == currentScope.getBraceNesting()) {
+ leaveCurrentScope();
+ }
+ break;
+
+ case Token.SEMI:
+ // No need to output a semi-colon if the next character is a right-curly...
+ if (preserveAllSemiColons || offset < length && getToken(0).getType() != Token.RC)
+ result.append(";");
+ if (linebreakpos >= 0 && result.length() - linestartpos > linebreakpos) {
+ // Some source control tools don't like it when files containing lines longer
+ // than, say 8000 characters, are checked in. The linebreak option is used in
+ // that case to split long lines after a specific column.
+ result.append("\n");
+ linestartpos = result.length();
+ }
+ break;
+
+ case Token.IECC:
+ result.append("/*");
+ result.append(symbol);
+ result.append("*/");
+ break;
+
+ default:
+ String literal = (String) literals.get(new Integer(token.getType()));
+ if (literal != null) {
+ result.append(literal);
+ } else {
+ warn("[WARNING] This symbol cannot be printed: " + symbol, true);
+ }
+ break;
+ }
+ }
+
+ return result;
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptErrorReporter.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptErrorReporter.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptErrorReporter.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,56 @@
+/*
+ * YUI Compressor
+ * Author: Julien Lecomte <jlecomte at yahoo-inc.com>
+ * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+package com.yahoo.platform.yui.compressor;
+
+import org.mozilla.javascript.ErrorReporter;
+import org.mozilla.javascript.EvaluatorException;
+
+import java.io.PrintStream;
+
+class JavaScriptErrorReporter implements ErrorReporter {
+
+ private boolean reportWarnings;
+ private PrintStream err;
+
+ JavaScriptErrorReporter(PrintStream err, boolean reportWarnings) {
+ this.err = err;
+ this.reportWarnings = reportWarnings;
+ }
+
+ public void warning(String message, String sourceName,
+ int line, String lineSource, int lineOffset) {
+ if (reportWarnings) {
+ reportMessage(message, sourceName, line, lineSource, lineOffset);
+ }
+ }
+
+ public EvaluatorException runtimeError(String message, String sourceName,
+ int line, String lineSource, int lineOffset) {
+ error(message, sourceName, line, lineSource, lineOffset);
+ return new EvaluatorException(message);
+ }
+
+ public void error(String message, String sourceName,
+ int line, String lineSource, int lineOffset) {
+ reportMessage(message, sourceName, line, lineSource, lineOffset);
+ }
+
+ private void reportMessage(String message, String sourceName,
+ int line, String lineSource, int lineOffset) {
+ if (line < 0) {
+ if (message.length() == 0) {
+ err.println("An unknown error occurred...");
+ } else {
+ err.println(message);
+ }
+ } else {
+ err.println(line + ':' + lineOffset + ':' + message);
+ }
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptErrorReporter.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,55 @@
+/*
+ * YUI Compressor
+ * Author: Julien Lecomte <jlecomte at yahoo-inc.com>
+ * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+package com.yahoo.platform.yui.compressor;
+
+import org.mozilla.javascript.Token;
+
+/**
+ * JavaScriptIdentifier represents a variable/function identifier.
+ */
+class JavaScriptIdentifier extends JavaScriptToken {
+
+ private int refcount = 0;
+ private String mungedValue;
+ private ScriptOrFnScope declaredScope;
+ private boolean markedForMunging = true;
+
+ JavaScriptIdentifier(String value, ScriptOrFnScope declaredScope) {
+ super(Token.NAME, value);
+ this.declaredScope = declaredScope;
+ }
+
+ ScriptOrFnScope getDeclaredScope() {
+ return declaredScope;
+ }
+
+ void setMungedValue(String value) {
+ mungedValue = value;
+ }
+
+ String getMungedValue() {
+ return mungedValue;
+ }
+
+ void preventMunging() {
+ markedForMunging = false;
+ }
+
+ boolean isMarkedForMunging() {
+ return markedForMunging;
+ }
+
+ void incrementRefcount() {
+ refcount++;
+ }
+
+ int getRefcount() {
+ return refcount;
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptToken.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptToken.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptToken.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,28 @@
+/*
+ * YUI Compressor
+ * Author: Julien Lecomte <jlecomte at yahoo-inc.com>
+ * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+package com.yahoo.platform.yui.compressor;
+
+public class JavaScriptToken {
+
+ private int type;
+ private String value;
+
+ JavaScriptToken(int type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ int getType() {
+ return type;
+ }
+
+ String getValue() {
+ return value;
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/JavaScriptToken.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,156 @@
+/*
+ * YUI Compressor
+ * Author: Julien Lecomte <jlecomte at yahoo-inc.com>
+ * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+package com.yahoo.platform.yui.compressor;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+class ScriptOrFnScope {
+
+ private int braceNesting;
+ private ScriptOrFnScope parentScope;
+ private ArrayList subScopes;
+ private Hashtable identifiers = new Hashtable();
+ private Hashtable hints = new Hashtable();
+ private boolean markedForMunging = true;
+
+ ScriptOrFnScope(int braceNesting, ScriptOrFnScope parentScope) {
+ this.braceNesting = braceNesting;
+ this.parentScope = parentScope;
+ this.subScopes = new ArrayList();
+ if (parentScope != null) {
+ parentScope.subScopes.add(this);
+ }
+ }
+
+ int getBraceNesting() {
+ return braceNesting;
+ }
+
+ ScriptOrFnScope getParentScope() {
+ return parentScope;
+ }
+
+ JavaScriptIdentifier declareIdentifier(String symbol) {
+ JavaScriptIdentifier identifier = (JavaScriptIdentifier) identifiers.get(symbol);
+ if (identifier == null) {
+ identifier = new JavaScriptIdentifier(symbol, this);
+ identifiers.put(symbol, identifier);
+ }
+ return identifier;
+ }
+
+ JavaScriptIdentifier getIdentifier(String symbol) {
+ return (JavaScriptIdentifier) identifiers.get(symbol);
+ }
+
+ void addHint(String variableName, String variableType) {
+ hints.put(variableName, variableType);
+ }
+
+ void preventMunging() {
+ if (parentScope != null) {
+ // The symbols in the global scope don't get munged,
+ // but the sub-scopes it contains do get munged.
+ markedForMunging = false;
+ }
+ }
+
+ private ArrayList getUsedSymbols() {
+ ArrayList result = new ArrayList();
+ Enumeration elements = identifiers.elements();
+ while (elements.hasMoreElements()) {
+ JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.nextElement();
+ String mungedValue = identifier.getMungedValue();
+ if (mungedValue == null) {
+ mungedValue = identifier.getValue();
+ }
+ result.add(mungedValue);
+ }
+ return result;
+ }
+
+ private ArrayList getAllUsedSymbols() {
+ ArrayList result = new ArrayList();
+ ScriptOrFnScope scope = this;
+ while (scope != null) {
+ result.addAll(scope.getUsedSymbols());
+ scope = scope.parentScope;
+ }
+ return result;
+ }
+
+ void munge() {
+
+ if (!markedForMunging) {
+ // Stop right here if this scope was flagged as unsafe for munging.
+ return;
+ }
+
+ int pickFromSet = 1;
+
+ // Do not munge symbols in the global scope!
+ if (parentScope != null) {
+
+ ArrayList freeSymbols = new ArrayList();
+
+ freeSymbols.addAll(JavaScriptCompressor.ones);
+ freeSymbols.removeAll(getAllUsedSymbols());
+ if (freeSymbols.size() == 0) {
+ pickFromSet = 2;
+ freeSymbols.addAll(JavaScriptCompressor.twos);
+ freeSymbols.removeAll(getAllUsedSymbols());
+ }
+ if (freeSymbols.size() == 0) {
+ pickFromSet = 3;
+ freeSymbols.addAll(JavaScriptCompressor.threes);
+ freeSymbols.removeAll(getAllUsedSymbols());
+ }
+ if (freeSymbols.size() == 0) {
+ System.err.println("The YUI Compressor ran out of symbols. Aborting...");
+ System.exit(1);
+ }
+
+ Enumeration elements = identifiers.elements();
+ while (elements.hasMoreElements()) {
+ if (freeSymbols.size() == 0) {
+ pickFromSet++;
+ if (pickFromSet == 2) {
+ freeSymbols.addAll(JavaScriptCompressor.twos);
+ } else if (pickFromSet == 3) {
+ freeSymbols.addAll(JavaScriptCompressor.threes);
+ } else {
+ System.err.println("The YUI Compressor ran out of symbols. Aborting...");
+ System.exit(1);
+ }
+ // It is essential to remove the symbols already used in
+ // the containing scopes, or some of the variables declared
+ // in the containing scopes will be redeclared, which can
+ // lead to errors.
+ freeSymbols.removeAll(getAllUsedSymbols());
+ }
+
+ String mungedValue;
+ JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.nextElement();
+ if (identifier.isMarkedForMunging()) {
+ mungedValue = (String) freeSymbols.remove(0);
+ } else {
+ mungedValue = identifier.getValue();
+ }
+ identifier.setMungedValue(mungedValue);
+ }
+ }
+
+ for (int i = 0; i < subScopes.size(); i++) {
+ ScriptOrFnScope scope = (ScriptOrFnScope) subScopes.get(i);
+ scope.munge();
+ }
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/Aggregation.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/Aggregation.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/Aggregation.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,92 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.plexus.util.DirectoryScanner;
+import org.codehaus.plexus.util.IOUtil;
+
+public class Aggregation {
+ public File output;
+ public File sourceDirectory;
+ public String[] includes;
+ public String[] excludes;
+ public boolean removeIncluded = false;
+ public boolean insertNewLine = false;
+
+ public void run(File outputDirectory) throws Exception {
+ if(null == sourceDirectory){
+ sourceDirectory = outputDirectory;
+ }
+ List<File> files = getIncludedFiles();
+ if (files.size() != 0) {
+ FileOutputStream out = new FileOutputStream(output);
+ try {
+ for (File file : files) {
+ FileInputStream in = new FileInputStream(file);
+ try {
+ IOUtil.copy(in, out);
+ if (insertNewLine) {
+ out.write('\n');
+ }
+ if (removeIncluded) {
+ file.delete();
+ }
+ } finally {
+ IOUtil.close(in);
+ in = null;
+ }
+ }
+ } finally {
+ IOUtil.close(out);
+ out = null;
+ }
+ }
+ }
+
+ protected List<File> getIncludedFiles() throws Exception {
+ ArrayList<File> back = new ArrayList<File>();
+ if (includes != null) {
+ for (String include : includes) {
+ addInto(include, back);
+ }
+ }
+ return back;
+ }
+
+ private void addInto(String include, List<File> includedFiles) throws Exception {
+ if (include.indexOf('*') > -1) {
+ DirectoryScanner scanner = newScanner();
+ scanner.setIncludes(new String[] { include });
+ scanner.scan();
+ String[] rpaths = scanner.getIncludedFiles();
+ for (String rpath : rpaths) {
+ File file = new File(scanner.getBasedir(), rpath);
+ if (!includedFiles.contains(file)) {
+ includedFiles.add(file);
+ }
+ }
+ } else {
+ File file = new File(include);
+ if (!file.isAbsolute()) {
+ file = new File(output.getParentFile(), include);
+ }
+ if (!includedFiles.contains(file)) {
+ includedFiles.add(file);
+ }
+ }
+ }
+
+ private DirectoryScanner newScanner() throws Exception {
+ DirectoryScanner scanner = new DirectoryScanner();
+ scanner.setBasedir(sourceDirectory);
+ if ((excludes != null) && (excludes.length != 0)) {
+ scanner.setExcludes(excludes);
+ }
+ scanner.addDefaultExcludes();
+ return scanner;
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/Aggregation.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/BasicRhinoShell.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/BasicRhinoShell.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/BasicRhinoShell.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,354 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package net.sf.alchim.mojo.yuicompressor;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ErrorReporter;
+import org.mozilla.javascript.EvaluatorException;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.WrappedException;
+
+/**
+ * The BasicRhinoShell program.
+ *
+ * Can execute scripts interactively or in batch mode at the command line. An
+ * example of controlling the JavaScript engine.
+ *
+ * @author Norris Boyd
+ * @based http://lxr.mozilla.org/mozilla/source/js/rhino/examples/BasicRhinoShell.java
+ * (2007-08-30)
+ */
+ at SuppressWarnings("serial")
+public class BasicRhinoShell extends ScriptableObject {
+
+ @Override
+ public String getClassName() {
+ return "global";
+ }
+
+ /**
+ * Main entry point.
+ *
+ * Process arguments as would a normal Java program. Also create a new
+ * Context and associate it with the current thread. Then set up the
+ * execution environment and begin to execute scripts.
+ */
+ public static void exec(String args[], ErrorReporter reporter) {
+ // Associate a new Context with this thread
+ Context cx = Context.enter();
+ cx.setErrorReporter(reporter);
+ try {
+ // Initialize the standard objects (Object, Function, etc.)
+ // This must be done before scripts can be executed.
+ BasicRhinoShell BasicRhinoShell = new BasicRhinoShell();
+ cx.initStandardObjects(BasicRhinoShell);
+
+ // Define some global functions particular to the BasicRhinoShell.
+ // Note
+ // that these functions are not part of ECMA.
+ String[] names = { "print", "quit", "version", "load", "help", "readFile" };
+ BasicRhinoShell.defineFunctionProperties(names, BasicRhinoShell.class, ScriptableObject.DONTENUM);
+
+ args = processOptions(cx, args);
+
+ // Set up "arguments" in the global scope to contain the command
+ // line arguments after the name of the script to execute
+ Object[] array;
+ if (args.length == 0) {
+ array = new Object[0];
+ } else {
+ int length = args.length - 1;
+ array = new Object[length];
+ System.arraycopy(args, 1, array, 0, length);
+ }
+ Scriptable argsObj = cx.newArray(BasicRhinoShell, array);
+ BasicRhinoShell.defineProperty("arguments", argsObj, ScriptableObject.DONTENUM);
+
+ BasicRhinoShell.processSource(cx, args.length == 0 ? null : args[0]);
+ } finally {
+ Context.exit();
+ }
+ }
+
+ /**
+ * Parse arguments.
+ */
+ public static String[] processOptions(Context cx, String args[]) {
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (!arg.startsWith("-")) {
+ String[] result = new String[args.length - i];
+ for (int j = i; j < args.length; j++) {
+ result[j - i] = args[j];
+ }
+ return result;
+ }
+ if (arg.equals("-version")) {
+ if (++i == args.length) {
+ usage(arg);
+ }
+ double d = Context.toNumber(args[i]);
+ if (d != d) {
+ usage(arg);
+ }
+ cx.setLanguageVersion((int) d);
+ continue;
+ }
+ usage(arg);
+ }
+ return new String[0];
+ }
+
+ /**
+ * Print a usage message.
+ */
+ private static void usage(String s) {
+ p("Didn't understand \"" + s + "\".");
+ p("Valid arguments are:");
+ p("-version 100|110|120|130|140|150|160|170");
+ System.exit(1);
+ }
+
+ /**
+ * Print a help message.
+ *
+ * This method is defined as a JavaScript function.
+ */
+ public void help() {
+ p("");
+ p("Command Description");
+ p("======= ===========");
+ p("help() Display usage and help messages. ");
+ p("defineClass(className) Define an extension using the Java class");
+ p(" named with the string argument. ");
+ p(" Uses ScriptableObject.defineClass(). ");
+ p("load(['foo.js', ...]) Load JavaScript source files named by ");
+ p(" string arguments. ");
+ p("loadClass(className) Load a class named by a string argument.");
+ p(" The class must be a script compiled to a");
+ p(" class file. ");
+ p("print([expr ...]) Evaluate and print expressions. ");
+ p("quit() Quit the BasicRhinoShell. ");
+ p("version([number]) Get or set the JavaScript version number.");
+ p("");
+ }
+
+ /**
+ * Print the string values of its arguments.
+ *
+ * This method is defined as a JavaScript function. Note that its arguments
+ * are of the "varargs" form, which allows it to handle an arbitrary number
+ * of arguments supplied to the JavaScript function.
+ *
+ */
+ public static void print(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) {
+ System.out.print(" ");
+ }
+
+ // Convert the arbitrary JavaScript value into a string form.
+ String s = Context.toString(args[i]);
+
+ System.out.print(s);
+ }
+ System.out.println();
+ }
+
+ /**
+ * Quit the BasicRhinoShell.
+ *
+ * This only affects the interactive mode.
+ *
+ * This method is defined as a JavaScript function.
+ */
+ public void quit() {
+ quitting = true;
+ }
+
+ /**
+ * This method is defined as a JavaScript function.
+ */
+ public String readFile(String path) {
+ try {
+ return IOUtil.toString(new FileInputStream(path));
+ } catch (RuntimeException exc) {
+ throw exc;
+ } catch (Exception exc) {
+ throw new RuntimeException("wrap: " + exc.getMessage(), exc);
+ }
+ }
+
+ /**
+ * Get and set the language version.
+ *
+ * This method is defined as a JavaScript function.
+ */
+ public static double version(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
+ double result = cx.getLanguageVersion();
+ if (args.length > 0) {
+ double d = Context.toNumber(args[0]);
+ cx.setLanguageVersion((int) d);
+ }
+ return result;
+ }
+
+ /**
+ * Load and execute a set of JavaScript source files.
+ *
+ * This method is defined as a JavaScript function.
+ *
+ */
+ public static void load(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
+ BasicRhinoShell BasicRhinoShell = (BasicRhinoShell) getTopLevelScope(thisObj);
+ for (Object element : args) {
+ BasicRhinoShell.processSource(cx, Context.toString(element));
+ }
+ }
+
+ /**
+ * Evaluate JavaScript source.
+ *
+ * @param cx the current context
+ * @param filename the name of the file to compile, or null for interactive
+ * mode.
+ */
+ private void processSource(Context cx, String filename) {
+ if (filename == null) {
+ BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+ String sourceName = "<stdin>";
+ int lineno = 1;
+ boolean hitEOF = false;
+ do {
+ int startline = lineno;
+ System.err.print("js> ");
+ System.err.flush();
+ try {
+ String source = "";
+ // Collect lines of source to compile.
+ while (true) {
+ String newline;
+ newline = in.readLine();
+ if (newline == null) {
+ hitEOF = true;
+ break;
+ }
+ source = source + newline + "\n";
+ lineno++;
+ // Continue collecting as long as more lines
+ // are needed to complete the current
+ // statement. stringIsCompilableUnit is also
+ // true if the source statement will result in
+ // any error other than one that might be
+ // resolved by appending more source.
+ if (cx.stringIsCompilableUnit(source)) {
+ break;
+ }
+ }
+ Object result = cx.evaluateString(this, source, sourceName, startline, null);
+ if (result != Context.getUndefinedValue()) {
+ System.err.println(Context.toString(result));
+ }
+ } catch (WrappedException we) {
+ // Some form of exception was caught by JavaScript and
+ // propagated up.
+ System.err.println(we.getWrappedException().toString());
+ we.printStackTrace();
+ } catch (EvaluatorException ee) {
+ // Some form of JavaScript error.
+ System.err.println("js: " + ee.getMessage());
+ } catch (JavaScriptException jse) {
+ // Some form of JavaScript error.
+ System.err.println("js: " + jse.getMessage());
+ } catch (IOException ioe) {
+ System.err.println(ioe.toString());
+ }
+ if (quitting) {
+ // The user executed the quit() function.
+ break;
+ }
+ } while (!hitEOF);
+ System.err.println();
+ } else {
+ FileReader in = null;
+ try {
+ in = new FileReader(filename);
+ } catch (FileNotFoundException ex) {
+ Context.reportError("Couldn't open file \"" + filename + "\".");
+ return;
+ }
+
+ try {
+ // Here we evalute the entire contents of the file as
+ // a script. Text is printed only if the print() function
+ // is called.
+ cx.evaluateReader(this, in, filename, 1, null);
+ } catch (WrappedException we) {
+ System.err.println(we.getWrappedException().toString());
+ we.printStackTrace();
+ } catch (EvaluatorException ee) {
+ System.err.println("js: " + ee.getMessage());
+ } catch (JavaScriptException jse) {
+ System.err.println("js: " + jse.getMessage());
+ } catch (IOException ioe) {
+ System.err.println(ioe.toString());
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ioe) {
+ System.err.println(ioe.toString());
+ }
+ }
+ }
+ }
+
+ private static void p(String s) {
+ System.out.println(s);
+ }
+
+ private boolean quitting;
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/BasicRhinoShell.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/ErrorReporter4Mojo.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/ErrorReporter4Mojo.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/ErrorReporter4Mojo.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,81 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+import org.apache.maven.plugin.logging.Log;
+import org.mozilla.javascript.ErrorReporter;
+import org.mozilla.javascript.EvaluatorException;
+
+public class ErrorReporter4Mojo implements ErrorReporter {
+
+ private String defaultFilename_;
+ private boolean acceptWarn_;
+ private Log log_;
+ private int warningCnt_;
+ private int errorCnt_;
+
+ public ErrorReporter4Mojo(Log log, boolean jswarn) {
+ log_ = log;
+ acceptWarn_ = jswarn;
+ }
+
+ public void setDefaultFileName(String v) {
+ if (v.length() == 0) {
+ v = null;
+ }
+ defaultFilename_ = v;
+ }
+
+ public int getErrorCnt() {
+ return errorCnt_;
+ }
+
+ public int getWarningCnt() {
+ return warningCnt_;
+ }
+
+ public void error(String message, String sourceName, int line, String lineSource, int lineOffset) {
+ String fullMessage = newMessage(message, sourceName, line, lineSource, lineOffset);
+ log_.error(fullMessage);
+ errorCnt_++;
+ }
+
+ public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) {
+ error(message, sourceName, line, lineSource, lineOffset);
+ throw new EvaluatorException(message, sourceName, line, lineSource, lineOffset);
+ }
+
+ public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) {
+ if (acceptWarn_) {
+ String fullMessage = newMessage(message, sourceName, line, lineSource, lineOffset);
+ log_.warn(fullMessage);
+ warningCnt_++;
+ }
+ }
+
+ private String newMessage(String message, String sourceName, int line, String lineSource, int lineOffset) {
+ StringBuilder back = new StringBuilder();
+ if ((sourceName == null) || (sourceName.length() == 0)) {
+ sourceName = defaultFilename_;
+ }
+ if (sourceName != null) {
+ back.append(sourceName)
+ .append(":line ")
+ .append(line)
+ .append(":column ")
+ .append(lineOffset)
+ .append(':')
+ ;
+ }
+ if ((message != null) && (message.length() != 0)) {
+ back.append(message);
+ } else {
+ back.append("unknown error");
+ }
+ if ((lineSource != null) && (lineSource.length() != 0)) {
+ back.append("\n\t")
+ .append(lineSource)
+ ;
+ }
+ return back.toString();
+ }
+
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/ErrorReporter4Mojo.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintChecker.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintChecker.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintChecker.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,38 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.mozilla.javascript.ErrorReporter;
+
+//TODO: use MojoErrorReporter
+class JSLintChecker {
+ private String jslintPath_;
+
+ public JSLintChecker() throws Exception {
+ FileOutputStream out = null;
+ InputStream in = null;
+ try {
+ File jslint = File.createTempFile("jslint", ".js");
+ in = getClass().getResourceAsStream("/jslint.js");
+ out = new FileOutputStream(jslint);
+ IOUtil.copy(in, out);
+ jslintPath_ = jslint.getAbsolutePath();
+ } finally {
+ IOUtil.close(in);
+ IOUtil.close(out);
+ }
+ }
+
+ public void check(File jsFile, ErrorReporter reporter) {
+ String[] args = new String[2];
+ args[0] = jslintPath_;
+ args[1] = jsFile.getAbsolutePath();
+ BasicRhinoShell.exec(args, reporter);
+ //if (Main.exec(args) != 0) {
+ // reporter.warning("warnings during checking of :" + jsFile.getAbsolutePath(), null, -1, null, -1);
+ //}
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintChecker.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintMojo.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintMojo.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintMojo.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,36 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+
+/**
+ * Check JS files with jslint.
+ *
+ * @goal jslint
+ * @phase process-resources
+ *
+ * @author David Bernard
+ * @created 2007-08-29
+ */
+// @SuppressWarnings("unchecked")
+public class JSLintMojo extends MojoSupport {
+ private JSLintChecker jslint_;
+
+ @Override
+ protected String[] getDefaultIncludes() throws Exception {
+ return new String[] { "**/**.js" };
+ }
+
+ @Override
+ public void beforeProcess() throws Exception {
+ jslint_ = new JSLintChecker();
+ }
+
+ @Override
+ public void afterProcess() throws Exception {
+ }
+
+ @Override
+ protected void processFile(SourceFile src) throws Exception {
+ getLog().info("check file :" + src.toFile());
+ jslint_.check(src.toFile(), jsErrorReporter_);
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/JSLintMojo.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/MojoSupport.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/MojoSupport.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/MojoSupport.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,184 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.DirectoryScanner;
+
+/**
+ * Common class for mojos.
+ *
+ * @author David Bernard
+ * @created 2007-08-29
+ */
+// @SuppressWarnings("unchecked")
+public abstract class MojoSupport extends AbstractMojo {
+ private static final String[] EMPTY_STRING_ARRAY = {};
+
+ /**
+ * Javascript source directory. (result will be put to outputDirectory).
+ * This allow project with "src/main/js" structure.
+ *
+ * @parameter default-value="${project.build.sourceDirectory}/../js"
+ */
+ private File sourceDirectory;
+
+ /**
+ * Single directory for extra files to include in the WAR.
+ *
+ * @parameter default-value="${basedir}/src/main/webapp"
+ */
+ private File warSourceDirectory;
+
+ /**
+ * The directory where the webapp is built.
+ *
+ * @parameter default-value="${project.build.directory}/${project.build.finalName}"
+ */
+ protected File webappDirectory;
+
+ /**
+ * The output directory into which to copy the resources.
+ *
+ * @parameter default-value="${project.build.outputDirectory}"
+ */
+ protected File outputDirectory;
+
+ /**
+ * The list of resources we want to transfer.
+ *
+ * @parameter default-value="${project.resources}"
+ */
+ private List<Resource> resources;
+
+ /**
+ * list of additionnal excludes
+ *
+ * @parameter
+ */
+ private List<String> excludes;
+
+ /**
+ * list of additionnal includes
+ *
+ * @parameter
+ */
+ private List<String> includes;
+
+ /**
+ * @parameter expression="${project}"
+ * @readonly
+ * @required
+ */
+ protected MavenProject project;
+
+ /**
+ * [js only] Display possible errors in the code
+ *
+ * @parameter expression="${maven.yuicompressor.jswarm}"
+ * default-value="true"
+ */
+ protected boolean jswarn;
+
+ /**
+ * define if plugin must stop/fail on warnings.
+ *
+ * @parameter expression="${maven.yuicompressor.failOnWarning}"
+ * default-value="false"
+ */
+ protected boolean failOnWarning;
+ protected ErrorReporter4Mojo jsErrorReporter_;
+
+ @SuppressWarnings("unchecked")
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ try {
+ if (failOnWarning) {
+ jswarn = true;
+ }
+ jsErrorReporter_ = new ErrorReporter4Mojo(getLog(), jswarn);
+ beforeProcess();
+ processDir(sourceDirectory, outputDirectory, null, null,
+ true);
+ for (Resource resource : resources) {
+ File destRoot = outputDirectory;
+ if (resource.getTargetPath() != null) {
+ destRoot = new File(outputDirectory, resource
+ .getTargetPath());
+ }
+ processDir(new File(resource.getDirectory()), destRoot,
+ resource.getIncludes(), resource.getExcludes(), true);
+ }
+ processDir(warSourceDirectory, webappDirectory, null, null,
+ false);
+ afterProcess();
+ getLog().info(
+ String.format("nb warnings: %d, nb errors: %d",
+ jsErrorReporter_.getWarningCnt(), jsErrorReporter_
+ .getErrorCnt()));
+ if (failOnWarning && (jsErrorReporter_.getWarningCnt() > 0)) {
+ throw new MojoFailureException("warnings on "
+ + this.getClass().getSimpleName()
+ + "=> failure ! (see log)");
+ }
+ } catch (RuntimeException exc) {
+ throw exc;
+ } catch (MojoFailureException exc) {
+ throw exc;
+ } catch (MojoExecutionException exc) {
+ throw exc;
+ } catch (Exception exc) {
+ throw new MojoExecutionException("wrap: " + exc.getMessage(), exc);
+ }
+ }
+
+ protected abstract String[] getDefaultIncludes() throws Exception;
+
+ protected abstract void beforeProcess() throws Exception;
+
+ protected abstract void afterProcess() throws Exception;
+
+ protected void processDir(File srcRoot, File destRoot,
+ List<String> srcIncludes, List<String> srcExcludes,
+ boolean destAsSource) throws Exception {
+ if ((srcRoot == null) || (!srcRoot.exists())) {
+ return;
+ }
+ if (destRoot == null) {
+ throw new MojoFailureException("destination directory for "
+ + srcRoot + " is null");
+ }
+ DirectoryScanner scanner = new DirectoryScanner();
+ scanner.setBasedir(srcRoot);
+ if ((srcIncludes != null) && !srcIncludes.isEmpty()) {
+ scanner.setIncludes(srcIncludes.toArray(EMPTY_STRING_ARRAY));
+ }
+ if ((includes != null) && !includes.isEmpty()) {
+ scanner.setIncludes(includes.toArray(EMPTY_STRING_ARRAY));
+ } else {
+ scanner.setIncludes(getDefaultIncludes());
+ }
+ if ((srcExcludes != null) && !srcExcludes.isEmpty()) {
+ scanner.setExcludes(srcExcludes.toArray(EMPTY_STRING_ARRAY));
+ }
+ if ((excludes != null) && !excludes.isEmpty()) {
+ scanner.setExcludes(excludes.toArray(EMPTY_STRING_ARRAY));
+ }
+ scanner.addDefaultExcludes();
+ scanner.scan();
+ for (String name : scanner.getIncludedFiles()) {
+ SourceFile src = new SourceFile(srcRoot, destRoot, name,
+ destAsSource);
+ jsErrorReporter_.setDefaultFileName("..."
+ + src.toFile().getAbsolutePath().substring(
+ project.getBasedir().getAbsolutePath().length()));
+ processFile(src);
+ }
+ }
+
+ protected abstract void processFile(SourceFile src) throws Exception;
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/MojoSupport.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/SourceFile.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/SourceFile.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/SourceFile.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,45 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+import java.io.File;
+
+public class SourceFile {
+
+ private File srcRoot_;
+ private File destRoot_;
+ private boolean destAsSource_;
+ private String rpath_;
+ private String extension_;
+
+ public SourceFile(File srcRoot, File destRoot, String name, boolean destAsSource) throws Exception {
+ srcRoot_ = srcRoot;
+ destRoot_ = destRoot;
+ destAsSource_ = destAsSource;
+ rpath_ = name;
+ int sep = rpath_.lastIndexOf('.');
+ if (sep>0) {
+ extension_ = rpath_.substring(sep);
+ rpath_ = rpath_.substring(0, sep);
+ } else {
+ extension_ = "";
+ }
+ }
+
+ public File toFile() {
+ String frpath = rpath_ + extension_;
+ if (destAsSource_) {
+ File defaultDest = new File(destRoot_, frpath);
+ if (defaultDest.exists() && defaultDest.canRead()) {
+ return defaultDest;
+ }
+ }
+ return new File(srcRoot_, frpath);
+ }
+
+ public File toDestFile(String suffix) {
+ return new File(destRoot_, rpath_ + suffix + extension_);
+ }
+
+ public String getExtension() {
+ return extension_;
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/SourceFile.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/YuiCompressorMojo.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/YuiCompressorMojo.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/YuiCompressorMojo.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,229 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+
+import com.yahoo.platform.yui.compressor.CssCompressor;
+import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
+
+/**
+ * Apply compression on JS and CSS (using YUI Compressor).
+ *
+ * @goal compress
+ * @phase process-resources
+ *
+ * @author David Bernard
+ * @created 2007-08-28
+ */
+// @SuppressWarnings("unchecked")
+public class YuiCompressorMojo extends MojoSupport {
+
+ /**
+ * Read the input file using "encoding".
+ *
+ * @parameter expression="${file.encoding}" default-value="UTF-8"
+ */
+ private String encoding;
+
+ /**
+ * The output filename suffix.
+ *
+ * @parameter expression="${maven.yuicompressor.suffix}" default-value="-min"
+ */
+ private String suffix;
+
+ /**
+ * If no "suffix" must be add to output filename (maven's configuration manage empty suffix like default).
+ *
+ * @parameter expression="${maven.yuicompressor.nosuffix}" default-value="false"
+ */
+ private boolean nosuffix;
+
+ /**
+ * Insert line breaks in output after the specified column number.
+ *
+ * @parameter expression="${maven.yuicompressor.linebreakpos}" default-value="0"
+ */
+ private int linebreakpos;
+
+ /**
+ * [js only] Minify only, do not obfuscate.
+ *
+ * @parameter expression="${maven.yuicompressor.nomunge}" default-value="false"
+ */
+ private boolean nomunge;
+
+ /**
+ * [js only] Preserve unnecessary semicolons.
+ *
+ * @parameter expression="${maven.yuicompressor.preserveAllSemiColons}" default-value="false"
+ */
+ private boolean preserveAllSemiColons;
+
+ /**
+ * [js only] Preserve string (no optimization).
+ *
+ * @parameter expression="${maven.yuicompressor.preserveStringLiterals}" default-value="false"
+ */
+ private boolean preserveStringLiterals;
+
+ /**
+ * force the compression of every files,
+ * else if compressed file already exists and is younger than source file, nothing is done.
+ *
+ * @parameter expression="${maven.yuicompressor.force}" default-value="false"
+ */
+ private boolean force;
+
+ /**
+ * a list of aggregation/concatenation to do after processing,
+ * for example to create big js files that contain several small js files.
+ * Aggregation could be done on any type of file (js, css, ...).
+ *
+ * @parameter
+ */
+ private Aggregation[] aggregations;
+
+ /**
+ * request to create a gzipped version of the yuicompressed/aggregation files.
+ *
+ * @parameter expression="${maven.yuicompressor.gzip}" default-value="false"
+ */
+ private boolean gzip;
+
+ /**
+ * show statistics (compression ratio).
+ *
+ * @parameter expression="${maven.yuicompressor.statistics}" default-value="true"
+ */
+ private boolean statistics;
+
+ private long inSizeTotal_;
+ private long outSizeTotal_;
+
+ @Override
+ protected String[] getDefaultIncludes() throws Exception {
+ return new String[]{"**/*.css", "**/*.js"};
+ }
+
+ @Override
+ public void beforeProcess() throws Exception {
+ if (nosuffix) {
+ suffix = "";
+ }
+ }
+
+ @Override
+ protected void afterProcess() throws Exception {
+ if (statistics && (inSizeTotal_ > 0)) {
+ getLog().info(String.format("total input (%db) -> output (%db)[%d%%]", inSizeTotal_, outSizeTotal_, ((outSizeTotal_ * 100)/inSizeTotal_)));
+ }
+ if (aggregations != null) {
+ for(Aggregation aggregation : aggregations) {
+ getLog().info("generate aggregation : " + aggregation.output);
+ aggregation.run(outputDirectory);
+ File gzipped = gzipIfRequested(aggregation.output);
+ if (statistics) {
+ if (gzipped != null) {
+ getLog().info(String.format("%s (%db) -> %s (%db)[%d%%]", aggregation.output.getName(), aggregation.output.length(), gzipped.getName(), gzipped.length(), ratioOfSize(aggregation.output, gzipped)));
+ } else if (aggregation.output.exists()){
+ getLog().info(String.format("%s (%db)", aggregation.output.getName(), aggregation.output.length()));
+ } else {
+ getLog().warn(String.format("%s not created", aggregation.output.getName()));
+ }
+ }
+ }
+ }
+ }
+
+
+ @Override
+ protected void processFile(SourceFile src) throws Exception {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("compress file :" + src.toFile()+ " to " + src.toDestFile(suffix));
+ }
+ File inFile = src.toFile();
+ File outFile = src.toDestFile(suffix);
+
+ getLog().debug("only compress if input file is youger than existing output file");
+ if (!force && outFile.exists() && (outFile.lastModified() > inFile.lastModified())) {
+ if (getLog().isInfoEnabled()) {
+ getLog().info("nothing to do, " + outFile + " is younger than original, use 'force' option or clean your target");
+ }
+ return;
+ }
+
+ InputStreamReader in = null;
+ OutputStreamWriter out = null;
+ try {
+ in = new InputStreamReader(new FileInputStream(inFile), encoding);
+ if (!outFile.getParentFile().exists() && !outFile.getParentFile().mkdirs()) {
+ throw new MojoExecutionException( "Cannot create resource output directory: " + outFile.getParentFile() );
+ }
+ getLog().debug("use a temporary outputfile (in case in == out)");
+ File outFileTmp = new File(outFile.getAbsolutePath() + ".tmp");
+ FileUtils.forceDelete(outFileTmp);
+
+ getLog().debug("start compression");
+ out = new OutputStreamWriter(new FileOutputStream(outFileTmp), encoding);
+ if (".js".equalsIgnoreCase(src.getExtension())) {
+ JavaScriptCompressor compressor = new JavaScriptCompressor(in, jsErrorReporter_);
+ compressor.compress(out, linebreakpos, !nomunge, jswarn, preserveAllSemiColons, preserveStringLiterals);
+ } else if (".css".equalsIgnoreCase(src.getExtension())) {
+ CssCompressor compressor = new CssCompressor(in);
+ compressor.compress(out, linebreakpos);
+ }
+ getLog().debug("end compression");
+ FileUtils.forceDelete(outFile);
+ FileUtils.rename(outFileTmp, outFile);
+ } finally {
+ IOUtil.close(in);
+ IOUtil.close(out);
+ }
+ File gzipped = gzipIfRequested(outFile);
+ if (statistics) {
+ inSizeTotal_ += inFile.length();
+ outSizeTotal_ += outFile.length();
+ getLog().info(String.format("%s (%db) -> %s (%db)[%d%%]", inFile.getName(), inFile.length(), outFile.getName(), outFile.length(), ratioOfSize(inFile, outFile)));
+ if (gzipped != null) {
+ getLog().info(String.format("%s (%db) -> %s (%db)[%d%%]", inFile.getName(), inFile.length(), gzipped.getName(), gzipped.length(), ratioOfSize(inFile, gzipped)));
+ }
+ }
+ }
+
+ protected File gzipIfRequested(File file) throws Exception {
+ if (!gzip || (file == null) || (!file.exists())) {
+ return null;
+ }
+ if (".gz".equalsIgnoreCase(FileUtils.getExtension(file.getName()))) {
+ return null;
+ }
+ File gzipped = new File(file.getAbsolutePath()+".gz");
+ getLog().debug(String.format("create gzip version : %s", gzipped.getName()));
+ GZIPOutputStream out = null;
+ FileInputStream in = null;
+ try {
+ out = new GZIPOutputStream(new FileOutputStream(gzipped));
+ in = new FileInputStream(file);
+ IOUtil.copy(in, out);
+ } finally {
+ IOUtil.close(in);
+ IOUtil.close(out);
+ }
+ return gzipped;
+ }
+
+ protected long ratioOfSize(File file100, File fileX) throws Exception {
+ long v100 = Math.max(file100.length(), 1);
+ long vX = Math.max(fileX.length(), 1);
+ return (vX * 100)/v100;
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/net/sf/alchim/mojo/yuicompressor/YuiCompressorMojo.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Decompiler.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Decompiler.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Decompiler.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,916 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Ang
+ * Igor Bukanov
+ * Bob Jervis
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * The following class save decompilation information about the source.
+ * Source information is returned from the parser as a String
+ * associated with function nodes and with the toplevel script. When
+ * saved in the constant pool of a class, this string will be UTF-8
+ * encoded, and token values will occupy a single byte.
+
+ * Source is saved (mostly) as token numbers. The tokens saved pretty
+ * much correspond to the token stream of a 'canonical' representation
+ * of the input program, as directed by the parser. (There were a few
+ * cases where tokens could have been left out where decompiler could
+ * easily reconstruct them, but I left them in for clarity). (I also
+ * looked adding source collection to TokenStream instead, where I
+ * could have limited the changes to a few lines in getToken... but
+ * this wouldn't have saved any space in the resulting source
+ * representation, and would have meant that I'd have to duplicate
+ * parser logic in the decompiler to disambiguate situations where
+ * newlines are important.) The function decompile expands the
+ * tokens back into their string representations, using simple
+ * lookahead to correct spacing and indentation.
+ *
+ * Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens
+ * are stored inline, as a NUMBER token, a character representing the type, and
+ * either 1 or 4 characters representing the bit-encoding of the number. String
+ * types NAME, STRING and OBJECT are currently stored as a token type,
+ * followed by a character giving the length of the string (assumed to
+ * be less than 2^16), followed by the characters of the string
+ * inlined into the source string. Changing this to some reference to
+ * to the string in the compiled class' constant pool would probably
+ * save a lot of space... but would require some method of deriving
+ * the final constant pool entry from information available at parse
+ * time.
+ */
+public class Decompiler
+{
+ /**
+ * Flag to indicate that the decompilation should omit the
+ * function header and trailing brace.
+ */
+ public static final int ONLY_BODY_FLAG = 1 << 0;
+
+ /**
+ * Flag to indicate that the decompilation generates toSource result.
+ */
+ public static final int TO_SOURCE_FLAG = 1 << 1;
+
+ /**
+ * Decompilation property to specify initial ident value.
+ */
+ public static final int INITIAL_INDENT_PROP = 1;
+
+ /**
+ * Decompilation property to specify default identation offset.
+ */
+ public static final int INDENT_GAP_PROP = 2;
+
+ /**
+ * Decompilation property to specify identation offset for case labels.
+ */
+ public static final int CASE_GAP_PROP = 3;
+
+ // Marker to denote the last RC of function so it can be distinguished from
+ // the last RC of object literals in case of function expressions
+ private static final int FUNCTION_END = Token.LAST_TOKEN + 1;
+
+ String getEncodedSource()
+ {
+ return sourceToString(0);
+ }
+
+ int getCurrentOffset()
+ {
+ return sourceTop;
+ }
+
+ int markFunctionStart(int functionType)
+ {
+ int savedOffset = getCurrentOffset();
+ addToken(Token.FUNCTION);
+ append((char)functionType);
+ return savedOffset;
+ }
+
+ int markFunctionEnd(int functionStart)
+ {
+ int offset = getCurrentOffset();
+ append((char)FUNCTION_END);
+ return offset;
+ }
+
+ void addToken(int token)
+ {
+ if (!(0 <= token && token <= Token.LAST_TOKEN))
+ throw new IllegalArgumentException();
+
+ append((char)token);
+ }
+
+ void addEOL(int token)
+ {
+ if (!(0 <= token && token <= Token.LAST_TOKEN))
+ throw new IllegalArgumentException();
+
+ append((char)token);
+ append((char)Token.EOL);
+ }
+
+ void addName(String str)
+ {
+ addToken(Token.NAME);
+ appendString(str);
+ }
+
+ void addString(String str)
+ {
+ addToken(Token.STRING);
+ appendString(str);
+ }
+
+ void addRegexp(String regexp, String flags)
+ {
+ addToken(Token.REGEXP);
+ appendString('/' + regexp + '/' + flags);
+ }
+
+ void addJScriptConditionalComment(String str)
+ {
+ addToken(Token.IECC);
+ appendString(str);
+ }
+
+ void addNumber(double n)
+ {
+ addToken(Token.NUMBER);
+
+ /* encode the number in the source stream.
+ * Save as NUMBER type (char | char char char char)
+ * where type is
+ * 'D' - double, 'S' - short, 'J' - long.
+
+ * We need to retain float vs. integer type info to keep the
+ * behavior of liveconnect type-guessing the same after
+ * decompilation. (Liveconnect tries to present 1.0 to Java
+ * as a float/double)
+ * OPT: This is no longer true. We could compress the format.
+
+ * This may not be the most space-efficient encoding;
+ * the chars created below may take up to 3 bytes in
+ * constant pool UTF-8 encoding, so a Double could take
+ * up to 12 bytes.
+ */
+
+ long lbits = (long)n;
+ if (lbits != n) {
+ // if it's floating point, save as a Double bit pattern.
+ // (12/15/97 our scanner only returns Double for f.p.)
+ lbits = Double.doubleToLongBits(n);
+ append('D');
+ append((char)(lbits >> 48));
+ append((char)(lbits >> 32));
+ append((char)(lbits >> 16));
+ append((char)lbits);
+ }
+ else {
+ // we can ignore negative values, bc they're already prefixed
+ // by NEG
+ if (lbits < 0) Kit.codeBug();
+
+ // will it fit in a char?
+ // this gives a short encoding for integer values up to 2^16.
+ if (lbits <= Character.MAX_VALUE) {
+ append('S');
+ append((char)lbits);
+ }
+ else { // Integral, but won't fit in a char. Store as a long.
+ append('J');
+ append((char)(lbits >> 48));
+ append((char)(lbits >> 32));
+ append((char)(lbits >> 16));
+ append((char)lbits);
+ }
+ }
+ }
+
+ private void appendString(String str)
+ {
+ int L = str.length();
+ int lengthEncodingSize = 1;
+ if (L >= 0x8000) {
+ lengthEncodingSize = 2;
+ }
+ int nextTop = sourceTop + lengthEncodingSize + L;
+ if (nextTop > sourceBuffer.length) {
+ increaseSourceCapacity(nextTop);
+ }
+ if (L >= 0x8000) {
+ // Use 2 chars to encode strings exceeding 32K, were the highest
+ // bit in the first char indicates presence of the next byte
+ sourceBuffer[sourceTop] = (char)(0x8000 | (L >>> 16));
+ ++sourceTop;
+ }
+ sourceBuffer[sourceTop] = (char)L;
+ ++sourceTop;
+ str.getChars(0, L, sourceBuffer, sourceTop);
+ sourceTop = nextTop;
+ }
+
+ private void append(char c)
+ {
+ if (sourceTop == sourceBuffer.length) {
+ increaseSourceCapacity(sourceTop + 1);
+ }
+ sourceBuffer[sourceTop] = c;
+ ++sourceTop;
+ }
+
+ private void increaseSourceCapacity(int minimalCapacity)
+ {
+ // Call this only when capacity increase is must
+ if (minimalCapacity <= sourceBuffer.length) Kit.codeBug();
+ int newCapacity = sourceBuffer.length * 2;
+ if (newCapacity < minimalCapacity) {
+ newCapacity = minimalCapacity;
+ }
+ char[] tmp = new char[newCapacity];
+ System.arraycopy(sourceBuffer, 0, tmp, 0, sourceTop);
+ sourceBuffer = tmp;
+ }
+
+ private String sourceToString(int offset)
+ {
+ if (offset < 0 || sourceTop < offset) Kit.codeBug();
+ return new String(sourceBuffer, offset, sourceTop - offset);
+ }
+
+ /**
+ * Decompile the source information associated with this js
+ * function/script back into a string. For the most part, this
+ * just means translating tokens back to their string
+ * representations; there's a little bit of lookahead logic to
+ * decide the proper spacing/indentation. Most of the work in
+ * mapping the original source to the prettyprinted decompiled
+ * version is done by the parser.
+ *
+ * @param source encoded source tree presentation
+ *
+ * @param flags flags to select output format
+ *
+ * @param properties indentation properties
+ *
+ */
+ public static String decompile(String source, int flags,
+ UintMap properties)
+ {
+ int length = source.length();
+ if (length == 0) { return ""; }
+
+ int indent = properties.getInt(INITIAL_INDENT_PROP, 0);
+ if (indent < 0) throw new IllegalArgumentException();
+ int indentGap = properties.getInt(INDENT_GAP_PROP, 4);
+ if (indentGap < 0) throw new IllegalArgumentException();
+ int caseGap = properties.getInt(CASE_GAP_PROP, 2);
+ if (caseGap < 0) throw new IllegalArgumentException();
+
+ StringBuffer result = new StringBuffer();
+ boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
+ boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG));
+
+ // Spew tokens in source, for debugging.
+ // as TYPE number char
+ if (printSource) {
+ System.err.println("length:" + length);
+ for (int i = 0; i < length; ++i) {
+ // Note that tokenToName will fail unless Context.printTrees
+ // is true.
+ String tokenname = null;
+ if (Token.printNames) {
+ tokenname = Token.name(source.charAt(i));
+ }
+ if (tokenname == null) {
+ tokenname = "---";
+ }
+ String pad = tokenname.length() > 7
+ ? "\t"
+ : "\t\t";
+ System.err.println
+ (tokenname
+ + pad + (int)source.charAt(i)
+ + "\t'" + ScriptRuntime.escapeString
+ (source.substring(i, i+1))
+ + "'");
+ }
+ System.err.println();
+ }
+
+ int braceNesting = 0;
+ boolean afterFirstEOL = false;
+ int i = 0;
+ int topFunctionType;
+ if (source.charAt(i) == Token.SCRIPT) {
+ ++i;
+ topFunctionType = -1;
+ } else {
+ topFunctionType = source.charAt(i + 1);
+ }
+
+ if (!toSource) {
+ // add an initial newline to exactly match js.
+ result.append('\n');
+ for (int j = 0; j < indent; j++)
+ result.append(' ');
+ } else {
+ if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
+ result.append('(');
+ }
+ }
+
+ while (i < length) {
+ switch(source.charAt(i)) {
+ case Token.GET:
+ case Token.SET:
+ result.append(source.charAt(i) == Token.GET ? "get " : "set ");
+ ++i;
+ i = printSourceString(source, i + 1, false, result);
+ // Now increment one more to get past the FUNCTION token
+ ++i;
+ break;
+
+ case Token.NAME:
+ case Token.REGEXP: // re-wrapped in '/'s in parser...
+ i = printSourceString(source, i + 1, false, result);
+ continue;
+
+ case Token.STRING:
+ i = printSourceString(source, i + 1, true, result);
+ continue;
+
+ case Token.NUMBER:
+ i = printSourceNumber(source, i + 1, result);
+ continue;
+
+ case Token.TRUE:
+ result.append("true");
+ break;
+
+ case Token.FALSE:
+ result.append("false");
+ break;
+
+ case Token.NULL:
+ result.append("null");
+ break;
+
+ case Token.THIS:
+ result.append("this");
+ break;
+
+ case Token.FUNCTION:
+ ++i; // skip function type
+ result.append("function ");
+ break;
+
+ case FUNCTION_END:
+ // Do nothing
+ break;
+
+ case Token.COMMA:
+ result.append(", ");
+ break;
+
+ case Token.LC:
+ ++braceNesting;
+ if (Token.EOL == getNext(source, length, i))
+ indent += indentGap;
+ result.append('{');
+ break;
+
+ case Token.RC: {
+ --braceNesting;
+ /* don't print the closing RC if it closes the
+ * toplevel function and we're called from
+ * decompileFunctionBody.
+ */
+ if (justFunctionBody && braceNesting == 0)
+ break;
+
+ result.append('}');
+ switch (getNext(source, length, i)) {
+ case Token.EOL:
+ case FUNCTION_END:
+ indent -= indentGap;
+ break;
+ case Token.WHILE:
+ case Token.ELSE:
+ indent -= indentGap;
+ result.append(' ');
+ break;
+ }
+ break;
+ }
+ case Token.LP:
+ result.append('(');
+ break;
+
+ case Token.RP:
+ result.append(')');
+ if (Token.LC == getNext(source, length, i))
+ result.append(' ');
+ break;
+
+ case Token.LB:
+ result.append('[');
+ break;
+
+ case Token.RB:
+ result.append(']');
+ break;
+
+ case Token.EOL: {
+ if (toSource) break;
+ boolean newLine = true;
+ if (!afterFirstEOL) {
+ afterFirstEOL = true;
+ if (justFunctionBody) {
+ /* throw away just added 'function name(...) {'
+ * and restore the original indent
+ */
+ result.setLength(0);
+ indent -= indentGap;
+ newLine = false;
+ }
+ }
+ if (newLine) {
+ result.append('\n');
+ }
+
+ /* add indent if any tokens remain,
+ * less setback if next token is
+ * a label, case or default.
+ */
+ if (i + 1 < length) {
+ int less = 0;
+ int nextToken = source.charAt(i + 1);
+ if (nextToken == Token.CASE
+ || nextToken == Token.DEFAULT)
+ {
+ less = indentGap - caseGap;
+ } else if (nextToken == Token.RC) {
+ less = indentGap;
+ }
+
+ /* elaborate check against label... skip past a
+ * following inlined NAME and look for a COLON.
+ */
+ else if (nextToken == Token.NAME) {
+ int afterName = getSourceStringEnd(source, i + 2);
+ if (source.charAt(afterName) == Token.COLON)
+ less = indentGap;
+ }
+
+ for (; less < indent; less++)
+ result.append(' ');
+ }
+ break;
+ }
+ case Token.DOT:
+ result.append('.');
+ break;
+
+ case Token.NEW:
+ result.append("new ");
+ break;
+
+ case Token.DELPROP:
+ result.append("delete ");
+ break;
+
+ case Token.IF:
+ result.append("if ");
+ break;
+
+ case Token.ELSE:
+ result.append("else ");
+ break;
+
+ case Token.FOR:
+ result.append("for ");
+ break;
+
+ case Token.IN:
+ result.append(" in ");
+ break;
+
+ case Token.WITH:
+ result.append("with ");
+ break;
+
+ case Token.WHILE:
+ result.append("while ");
+ break;
+
+ case Token.DO:
+ result.append("do ");
+ break;
+
+ case Token.TRY:
+ result.append("try ");
+ break;
+
+ case Token.CATCH:
+ result.append("catch ");
+ break;
+
+ case Token.FINALLY:
+ result.append("finally ");
+ break;
+
+ case Token.THROW:
+ result.append("throw ");
+ break;
+
+ case Token.SWITCH:
+ result.append("switch ");
+ break;
+
+ case Token.BREAK:
+ result.append("break");
+ if (Token.NAME == getNext(source, length, i))
+ result.append(' ');
+ break;
+
+ case Token.CONTINUE:
+ result.append("continue");
+ if (Token.NAME == getNext(source, length, i))
+ result.append(' ');
+ break;
+
+ case Token.CASE:
+ result.append("case ");
+ break;
+
+ case Token.DEFAULT:
+ result.append("default");
+ break;
+
+ case Token.RETURN:
+ result.append("return");
+ if (Token.SEMI != getNext(source, length, i))
+ result.append(' ');
+ break;
+
+ case Token.VAR:
+ result.append("var ");
+ break;
+
+ case Token.SEMI:
+ result.append(';');
+ if (Token.EOL != getNext(source, length, i)) {
+ // separators in FOR
+ result.append(' ');
+ }
+ break;
+
+ case Token.ASSIGN:
+ result.append(" = ");
+ break;
+
+ case Token.ASSIGN_ADD:
+ result.append(" += ");
+ break;
+
+ case Token.ASSIGN_SUB:
+ result.append(" -= ");
+ break;
+
+ case Token.ASSIGN_MUL:
+ result.append(" *= ");
+ break;
+
+ case Token.ASSIGN_DIV:
+ result.append(" /= ");
+ break;
+
+ case Token.ASSIGN_MOD:
+ result.append(" %= ");
+ break;
+
+ case Token.ASSIGN_BITOR:
+ result.append(" |= ");
+ break;
+
+ case Token.ASSIGN_BITXOR:
+ result.append(" ^= ");
+ break;
+
+ case Token.ASSIGN_BITAND:
+ result.append(" &= ");
+ break;
+
+ case Token.ASSIGN_LSH:
+ result.append(" <<= ");
+ break;
+
+ case Token.ASSIGN_RSH:
+ result.append(" >>= ");
+ break;
+
+ case Token.ASSIGN_URSH:
+ result.append(" >>>= ");
+ break;
+
+ case Token.HOOK:
+ result.append(" ? ");
+ break;
+
+ case Token.OBJECTLIT:
+ // pun OBJECTLIT to mean colon in objlit property
+ // initialization.
+ // This needs to be distinct from COLON in the general case
+ // to distinguish from the colon in a ternary... which needs
+ // different spacing.
+ result.append(':');
+ break;
+
+ case Token.COLON:
+ if (Token.EOL == getNext(source, length, i))
+ // it's the end of a label
+ result.append(':');
+ else
+ // it's the middle part of a ternary
+ result.append(" : ");
+ break;
+
+ case Token.OR:
+ result.append(" || ");
+ break;
+
+ case Token.AND:
+ result.append(" && ");
+ break;
+
+ case Token.BITOR:
+ result.append(" | ");
+ break;
+
+ case Token.BITXOR:
+ result.append(" ^ ");
+ break;
+
+ case Token.BITAND:
+ result.append(" & ");
+ break;
+
+ case Token.SHEQ:
+ result.append(" === ");
+ break;
+
+ case Token.SHNE:
+ result.append(" !== ");
+ break;
+
+ case Token.EQ:
+ result.append(" == ");
+ break;
+
+ case Token.NE:
+ result.append(" != ");
+ break;
+
+ case Token.LE:
+ result.append(" <= ");
+ break;
+
+ case Token.LT:
+ result.append(" < ");
+ break;
+
+ case Token.GE:
+ result.append(" >= ");
+ break;
+
+ case Token.GT:
+ result.append(" > ");
+ break;
+
+ case Token.INSTANCEOF:
+ result.append(" instanceof ");
+ break;
+
+ case Token.LSH:
+ result.append(" << ");
+ break;
+
+ case Token.RSH:
+ result.append(" >> ");
+ break;
+
+ case Token.URSH:
+ result.append(" >>> ");
+ break;
+
+ case Token.TYPEOF:
+ result.append("typeof ");
+ break;
+
+ case Token.VOID:
+ result.append("void ");
+ break;
+
+ case Token.CONST:
+ result.append("const ");
+ break;
+
+ case Token.NOT:
+ result.append('!');
+ break;
+
+ case Token.BITNOT:
+ result.append('~');
+ break;
+
+ case Token.POS:
+ result.append('+');
+ break;
+
+ case Token.NEG:
+ result.append('-');
+ break;
+
+ case Token.INC:
+ result.append("++");
+ break;
+
+ case Token.DEC:
+ result.append("--");
+ break;
+
+ case Token.ADD:
+ result.append(" + ");
+ break;
+
+ case Token.SUB:
+ result.append(" - ");
+ break;
+
+ case Token.MUL:
+ result.append(" * ");
+ break;
+
+ case Token.DIV:
+ result.append(" / ");
+ break;
+
+ case Token.MOD:
+ result.append(" % ");
+ break;
+
+ case Token.COLONCOLON:
+ result.append("::");
+ break;
+
+ case Token.DOTDOT:
+ result.append("..");
+ break;
+
+ case Token.DOTQUERY:
+ result.append(".(");
+ break;
+
+ case Token.XMLATTR:
+ result.append('@');
+ break;
+
+ default:
+ // If we don't know how to decompile it, raise an exception.
+ throw new RuntimeException("Token: " +
+ Token.name(source.charAt(i)));
+ }
+ ++i;
+ }
+
+ if (!toSource) {
+ // add that trailing newline if it's an outermost function.
+ if (!justFunctionBody)
+ result.append('\n');
+ } else {
+ if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
+ result.append(')');
+ }
+ }
+
+ return result.toString();
+ }
+
+ private static int getNext(String source, int length, int i)
+ {
+ return (i + 1 < length) ? source.charAt(i + 1) : Token.EOF;
+ }
+
+ private static int getSourceStringEnd(String source, int offset)
+ {
+ return printSourceString(source, offset, false, null);
+ }
+
+ private static int printSourceString(String source, int offset,
+ boolean asQuotedString,
+ StringBuffer sb)
+ {
+ 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);
+ if (!asQuotedString) {
+ sb.append(str);
+ } else {
+ sb.append('"');
+ sb.append(ScriptRuntime.escapeString(str));
+ sb.append('"');
+ }
+ }
+ return offset + length;
+ }
+
+ private static int printSourceNumber(String source, int offset,
+ StringBuffer sb)
+ {
+ double number = 0.0;
+ char type = source.charAt(offset);
+ ++offset;
+ if (type == 'S') {
+ if (sb != null) {
+ int ival = source.charAt(offset);
+ number = ival;
+ }
+ ++offset;
+ } else if (type == 'J' || type == 'D') {
+ if (sb != null) {
+ long lbits;
+ lbits = (long)source.charAt(offset) << 48;
+ lbits |= (long)source.charAt(offset + 1) << 32;
+ lbits |= (long)source.charAt(offset + 2) << 16;
+ lbits |= source.charAt(offset + 3);
+ if (type == 'J') {
+ number = lbits;
+ } else {
+ number = Double.longBitsToDouble(lbits);
+ }
+ }
+ offset += 4;
+ } else {
+ // Bad source
+ throw new RuntimeException();
+ }
+ if (sb != null) {
+ sb.append(ScriptRuntime.numberToString(number, 10));
+ }
+ return offset;
+ }
+
+ private char[] sourceBuffer = new char[128];
+
+// Per script/function source buffer top: parent source does not include a
+// nested functions source and uses function index as a reference instead.
+ private int sourceTop;
+
+// whether to do a debug print of the source information, when decompiling.
+ private static final boolean printSource = false;
+
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Decompiler.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Parser.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Parser.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Parser.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,2170 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Ang
+ * Igor Bukanov
+ * Yuh-Ruey Chen
+ * Ethan Hugg
+ * Bob Jervis
+ * Terry Lucas
+ * Mike McCabe
+ * Milen Nankov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Reader;
+import java.io.IOException;
+import java.util.Hashtable;
+
+/**
+ * This class implements the JavaScript parser.
+ *
+ * It is based on the C source files jsparse.c and jsparse.h
+ * in the jsref package.
+ *
+ * @see TokenStream
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+public class Parser
+{
+ // TokenInformation flags : currentFlaggedToken stores them together
+ // with token type
+ final static int
+ CLEAR_TI_MASK = 0xFFFF, // mask to clear token information bits
+ TI_AFTER_EOL = 1 << 16, // first token of the source line
+ TI_CHECK_LABEL = 1 << 17; // indicates to check for label
+
+ CompilerEnvirons compilerEnv;
+ private ErrorReporter errorReporter;
+ private String sourceURI;
+ boolean calledByCompileFunction;
+
+ private TokenStream ts;
+ private int currentFlaggedToken;
+ private int syntaxErrorCount;
+
+ private IRFactory nf;
+
+ private int nestingOfFunction;
+
+ private Decompiler decompiler;
+ private String encodedSource;
+
+// The following are per function variables and should be saved/restored
+// during function parsing.
+// XXX Move to separated class?
+ ScriptOrFnNode currentScriptOrFn;
+ private int nestingOfWith;
+ private Hashtable labelSet; // map of label names into nodes
+ private ObjArray loopSet;
+ private ObjArray loopAndSwitchSet;
+ private boolean hasReturnValue;
+ private int functionEndFlags;
+// end of per function variables
+
+ // Exception to unwind
+ private static class ParserException extends RuntimeException
+ {
+ static final long serialVersionUID = 5882582646773765630L;
+ }
+
+ public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter)
+ {
+ this.compilerEnv = compilerEnv;
+ this.errorReporter = errorReporter;
+ }
+
+ protected Decompiler createDecompiler(CompilerEnvirons compilerEnv)
+ {
+ return new Decompiler();
+ }
+
+ void addStrictWarning(String messageId, String messageArg)
+ {
+ if (compilerEnv.isStrictMode())
+ addWarning(messageId, messageArg);
+ }
+
+ void addWarning(String messageId, String messageArg)
+ {
+ String message = ScriptRuntime.getMessage1(messageId, messageArg);
+ if (compilerEnv.reportWarningAsError()) {
+ ++syntaxErrorCount;
+ errorReporter.error(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ } else
+ errorReporter.warning(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ void addError(String messageId)
+ {
+ ++syntaxErrorCount;
+ String message = ScriptRuntime.getMessage0(messageId);
+ errorReporter.error(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ void addError(String messageId, String messageArg)
+ {
+ ++syntaxErrorCount;
+ String message = ScriptRuntime.getMessage1(messageId, messageArg);
+ errorReporter.error(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ RuntimeException reportError(String messageId)
+ {
+ addError(messageId);
+
+ // Throw a ParserException exception to unwind the recursive descent
+ // parse.
+ throw new ParserException();
+ }
+
+ private int peekToken()
+ throws IOException
+ {
+ int tt = currentFlaggedToken;
+ if (tt == Token.EOF) {
+
+ while ((tt = ts.getToken()) == Token.IECC) {
+ /* Support for JScript conditional comments */
+ decompiler.addJScriptConditionalComment(ts.getString());
+ }
+
+ if (tt == Token.EOL) {
+ do {
+ tt = ts.getToken();
+
+ if (tt == Token.IECC) {
+ /* Support for JScript conditional comments */
+ decompiler.addJScriptConditionalComment(ts.getString());
+ }
+
+ } while (tt == Token.EOL || tt == Token.IECC);
+ tt |= TI_AFTER_EOL;
+ }
+ currentFlaggedToken = tt;
+ }
+ return tt & CLEAR_TI_MASK;
+ }
+
+ private int peekFlaggedToken()
+ throws IOException
+ {
+ peekToken();
+ return currentFlaggedToken;
+ }
+
+ private void consumeToken()
+ {
+ currentFlaggedToken = Token.EOF;
+ }
+
+ private int nextToken()
+ throws IOException
+ {
+ int tt = peekToken();
+ consumeToken();
+ return tt;
+ }
+
+ private int nextFlaggedToken()
+ throws IOException
+ {
+ peekToken();
+ int ttFlagged = currentFlaggedToken;
+ consumeToken();
+ return ttFlagged;
+ }
+
+ private boolean matchToken(int toMatch)
+ throws IOException
+ {
+ int tt = peekToken();
+ if (tt != toMatch) {
+ return false;
+ }
+ consumeToken();
+ return true;
+ }
+
+ private int peekTokenOrEOL()
+ throws IOException
+ {
+ int tt = peekToken();
+ // Check for last peeked token flags
+ if ((currentFlaggedToken & TI_AFTER_EOL) != 0) {
+ tt = Token.EOL;
+ }
+ return tt;
+ }
+
+ private void setCheckForLabel()
+ {
+ if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME)
+ throw Kit.codeBug();
+ currentFlaggedToken |= TI_CHECK_LABEL;
+ }
+
+ private void mustMatchToken(int toMatch, String messageId)
+ throws IOException, ParserException
+ {
+ if (!matchToken(toMatch)) {
+ reportError(messageId);
+ }
+ }
+
+ private void mustHaveXML()
+ {
+ if (!compilerEnv.isXmlAvailable()) {
+ reportError("msg.XML.not.available");
+ }
+ }
+
+ public String getEncodedSource()
+ {
+ return encodedSource;
+ }
+
+ public boolean eof()
+ {
+ return ts.eof();
+ }
+
+ boolean insideFunction()
+ {
+ return nestingOfFunction != 0;
+ }
+
+ private Node enterLoop(Node loopLabel)
+ {
+ Node loop = nf.createLoopNode(loopLabel, ts.getLineno());
+ if (loopSet == null) {
+ loopSet = new ObjArray();
+ if (loopAndSwitchSet == null) {
+ loopAndSwitchSet = new ObjArray();
+ }
+ }
+ loopSet.push(loop);
+ loopAndSwitchSet.push(loop);
+ return loop;
+ }
+
+ private void exitLoop()
+ {
+ loopSet.pop();
+ loopAndSwitchSet.pop();
+ }
+
+ private Node enterSwitch(Node switchSelector, int lineno)
+ {
+ Node switchNode = nf.createSwitch(switchSelector, lineno);
+ if (loopAndSwitchSet == null) {
+ loopAndSwitchSet = new ObjArray();
+ }
+ loopAndSwitchSet.push(switchNode);
+ return switchNode;
+ }
+
+ private void exitSwitch()
+ {
+ loopAndSwitchSet.pop();
+ }
+
+ /*
+ * Build a parse tree from the given sourceString.
+ *
+ * @return an Object representing the parsed
+ * program. If the parse fails, null will be returned. (The
+ * parse failure will result in a call to the ErrorReporter from
+ * CompilerEnvirons.)
+ */
+ public ScriptOrFnNode parse(String sourceString,
+ String sourceURI, int lineno)
+ {
+ this.sourceURI = sourceURI;
+ this.ts = new TokenStream(this, null, sourceString, lineno);
+ try {
+ return parse();
+ } catch (IOException ex) {
+ // Should never happen
+ throw new IllegalStateException();
+ }
+ }
+
+ /*
+ * Build a parse tree from the given sourceString.
+ *
+ * @return an Object representing the parsed
+ * program. If the parse fails, null will be returned. (The
+ * parse failure will result in a call to the ErrorReporter from
+ * CompilerEnvirons.)
+ */
+ public ScriptOrFnNode parse(Reader sourceReader,
+ String sourceURI, int lineno)
+ throws IOException
+ {
+ this.sourceURI = sourceURI;
+ this.ts = new TokenStream(this, sourceReader, null, lineno);
+ return parse();
+ }
+
+ private ScriptOrFnNode parse()
+ throws IOException
+ {
+ this.decompiler = createDecompiler(compilerEnv);
+ this.nf = new IRFactory(this);
+ currentScriptOrFn = nf.createScript();
+ int sourceStartOffset = decompiler.getCurrentOffset();
+ this.encodedSource = null;
+ decompiler.addToken(Token.SCRIPT);
+
+ this.currentFlaggedToken = Token.EOF;
+ this.syntaxErrorCount = 0;
+
+ int baseLineno = ts.getLineno(); // line number where source starts
+
+ /* so we have something to add nodes to until
+ * we've collected all the source */
+ Node pn = nf.createLeaf(Token.BLOCK);
+
+ try {
+ for (;;) {
+ int tt = peekToken();
+
+ if (tt <= Token.EOF) {
+ break;
+ }
+
+ Node n;
+ if (tt == Token.FUNCTION) {
+ consumeToken();
+ try {
+ n = function(calledByCompileFunction
+ ? FunctionNode.FUNCTION_EXPRESSION
+ : FunctionNode.FUNCTION_STATEMENT);
+ } catch (ParserException e) {
+ break;
+ }
+ } else {
+ n = statement();
+ }
+ nf.addChildToBack(pn, n);
+ }
+ } catch (StackOverflowError ex) {
+ String msg = ScriptRuntime.getMessage0(
+ "msg.too.deep.parser.recursion");
+ throw Context.reportRuntimeError(msg, sourceURI,
+ ts.getLineno(), null, 0);
+ }
+
+ if (this.syntaxErrorCount != 0) {
+ String msg = String.valueOf(this.syntaxErrorCount);
+ msg = ScriptRuntime.getMessage1("msg.got.syntax.errors", msg);
+ throw errorReporter.runtimeError(msg, sourceURI, baseLineno,
+ null, 0);
+ }
+
+ currentScriptOrFn.setSourceName(sourceURI);
+ currentScriptOrFn.setBaseLineno(baseLineno);
+ currentScriptOrFn.setEndLineno(ts.getLineno());
+
+ int sourceEndOffset = decompiler.getCurrentOffset();
+ currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset,
+ sourceEndOffset);
+
+ nf.initScript(currentScriptOrFn, pn);
+
+ if (compilerEnv.isGeneratingSource()) {
+ encodedSource = decompiler.getEncodedSource();
+ }
+ this.decompiler = null; // It helps GC
+
+ return currentScriptOrFn;
+ }
+
+ /*
+ * The C version of this function takes an argument list,
+ * which doesn't seem to be needed for tree generation...
+ * it'd only be useful for checking argument hiding, which
+ * I'm not doing anyway...
+ */
+ private Node parseFunctionBody()
+ throws IOException
+ {
+ ++nestingOfFunction;
+ Node pn = nf.createBlock(ts.getLineno());
+ try {
+ bodyLoop: for (;;) {
+ Node n;
+ int tt = peekToken();
+ switch (tt) {
+ case Token.ERROR:
+ case Token.EOF:
+ case Token.RC:
+ break bodyLoop;
+
+ case Token.FUNCTION:
+ consumeToken();
+ n = function(FunctionNode.FUNCTION_STATEMENT);
+ break;
+ default:
+ n = statement();
+ break;
+ }
+ nf.addChildToBack(pn, n);
+ }
+ } catch (ParserException e) {
+ // Ignore it
+ } finally {
+ --nestingOfFunction;
+ }
+
+ return pn;
+ }
+
+ private Node function(int functionType)
+ throws IOException, ParserException
+ {
+ int syntheticType = functionType;
+ int baseLineno = ts.getLineno(); // line number where source starts
+
+ int functionSourceStart = decompiler.markFunctionStart(functionType);
+ String name;
+ Node memberExprNode = null;
+ if (matchToken(Token.NAME)) {
+ name = ts.getString();
+ decompiler.addName(name);
+ if (!matchToken(Token.LP)) {
+ if (compilerEnv.isAllowMemberExprAsFunctionName()) {
+ // Extension to ECMA: if 'function <name>' does not follow
+ // by '(', assume <name> starts memberExpr
+ Node memberExprHead = nf.createName(name);
+ name = "";
+ memberExprNode = memberExprTail(false, memberExprHead);
+ }
+ mustMatchToken(Token.LP, "msg.no.paren.parms");
+ }
+ } else if (matchToken(Token.LP)) {
+ // Anonymous function
+ name = "";
+ } else {
+ name = "";
+ if (compilerEnv.isAllowMemberExprAsFunctionName()) {
+ // Note that memberExpr can not start with '(' like
+ // in function (1+2).toString(), because 'function (' already
+ // processed as anonymous function
+ memberExprNode = memberExpr(false);
+ }
+ mustMatchToken(Token.LP, "msg.no.paren.parms");
+ }
+
+ if (memberExprNode != null) {
+ syntheticType = FunctionNode.FUNCTION_EXPRESSION;
+ }
+
+ boolean nested = insideFunction();
+
+ FunctionNode fnNode = nf.createFunction(name);
+ if (nested || nestingOfWith > 0) {
+ // 1. Nested functions are not affected by the dynamic scope flag
+ // as dynamic scope is already a parent of their scope.
+ // 2. Functions defined under the with statement also immune to
+ // this setup, in which case dynamic scope is ignored in favor
+ // of with object.
+ fnNode.itsIgnoreDynamicScope = true;
+ }
+
+ int functionIndex = currentScriptOrFn.addFunction(fnNode);
+
+ int functionSourceEnd;
+
+ ScriptOrFnNode savedScriptOrFn = currentScriptOrFn;
+ currentScriptOrFn = fnNode;
+ int savedNestingOfWith = nestingOfWith;
+ nestingOfWith = 0;
+ Hashtable savedLabelSet = labelSet;
+ labelSet = null;
+ ObjArray savedLoopSet = loopSet;
+ loopSet = null;
+ ObjArray savedLoopAndSwitchSet = loopAndSwitchSet;
+ loopAndSwitchSet = null;
+ boolean savedHasReturnValue = hasReturnValue;
+ int savedFunctionEndFlags = functionEndFlags;
+
+ Node body;
+ try {
+ decompiler.addToken(Token.LP);
+ if (!matchToken(Token.RP)) {
+ boolean first = true;
+ do {
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ first = false;
+ mustMatchToken(Token.NAME, "msg.no.parm");
+ String s = ts.getString();
+ if (fnNode.hasParamOrVar(s)) {
+ addWarning("msg.dup.parms", s);
+ }
+ fnNode.addParam(s);
+ decompiler.addName(s);
+ } while (matchToken(Token.COMMA));
+
+ mustMatchToken(Token.RP, "msg.no.paren.after.parms");
+ }
+ decompiler.addToken(Token.RP);
+
+ mustMatchToken(Token.LC, "msg.no.brace.body");
+ decompiler.addEOL(Token.LC);
+ body = parseFunctionBody();
+ mustMatchToken(Token.RC, "msg.no.brace.after.body");
+
+ if (compilerEnv.isStrictMode() && !body.hasConsistentReturnUsage())
+ {
+ String msg = name.length() > 0 ? "msg.no.return.value"
+ : "msg.anon.no.return.value";
+ addStrictWarning(msg, name);
+ }
+
+ decompiler.addToken(Token.RC);
+ functionSourceEnd = decompiler.markFunctionEnd(functionSourceStart);
+ if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
+ // Add EOL only if function is not part of expression
+ // since it gets SEMI + EOL from Statement in that case
+ decompiler.addToken(Token.EOL);
+ }
+ }
+ finally {
+ hasReturnValue = savedHasReturnValue;
+ functionEndFlags = savedFunctionEndFlags;
+ loopAndSwitchSet = savedLoopAndSwitchSet;
+ loopSet = savedLoopSet;
+ labelSet = savedLabelSet;
+ nestingOfWith = savedNestingOfWith;
+ currentScriptOrFn = savedScriptOrFn;
+ }
+
+ fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd);
+ fnNode.setSourceName(sourceURI);
+ fnNode.setBaseLineno(baseLineno);
+ fnNode.setEndLineno(ts.getLineno());
+
+ if (name != null) {
+ int index = currentScriptOrFn.getParamOrVarIndex(name);
+ if (index >= 0 && index < currentScriptOrFn.getParamCount())
+ addStrictWarning("msg.var.hides.arg", name);
+ }
+
+ Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType);
+ if (memberExprNode != null) {
+ pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
+ if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
+ // XXX check JScript behavior: should it be createExprStatement?
+ pn = nf.createExprStatementNoReturn(pn, baseLineno);
+ }
+ }
+ return pn;
+ }
+
+ private Node statements()
+ throws IOException
+ {
+ Node pn = nf.createBlock(ts.getLineno());
+
+ int tt;
+ while((tt = peekToken()) > Token.EOF && tt != Token.RC) {
+ nf.addChildToBack(pn, statement());
+ }
+
+ return pn;
+ }
+
+ private Node condition()
+ throws IOException, ParserException
+ {
+ mustMatchToken(Token.LP, "msg.no.paren.cond");
+ decompiler.addToken(Token.LP);
+ Node pn = expr(false);
+ mustMatchToken(Token.RP, "msg.no.paren.after.cond");
+ decompiler.addToken(Token.RP);
+
+ // Report strict warning on code like "if (a = 7) ...". Suppress the
+ // warning if the condition is parenthesized, like "if ((a = 7)) ...".
+ if (pn.getProp(Node.PARENTHESIZED_PROP) == null &&
+ (pn.getType() == Token.SETNAME || pn.getType() == Token.SETPROP ||
+ pn.getType() == Token.SETELEM))
+ {
+ addStrictWarning("msg.equal.as.assign", "");
+ }
+ return pn;
+ }
+
+ // match a NAME; return null if no match.
+ private Node matchJumpLabelName()
+ throws IOException, ParserException
+ {
+ Node label = null;
+
+ int tt = peekTokenOrEOL();
+ if (tt == Token.NAME) {
+ consumeToken();
+ String name = ts.getString();
+ decompiler.addName(name);
+ if (labelSet != null) {
+ label = (Node)labelSet.get(name);
+ }
+ if (label == null) {
+ reportError("msg.undef.label");
+ }
+ }
+
+ return label;
+ }
+
+ private Node statement()
+ throws IOException
+ {
+ try {
+ Node pn = statementHelper(null);
+ if (pn != null) {
+ if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
+ addStrictWarning("msg.no.side.effects", "");
+ return pn;
+ }
+ } catch (ParserException e) { }
+
+ // skip to end of statement
+ int lineno = ts.getLineno();
+ guessingStatementEnd: for (;;) {
+ int tt = peekTokenOrEOL();
+ consumeToken();
+ switch (tt) {
+ case Token.ERROR:
+ case Token.EOF:
+ case Token.EOL:
+ case Token.SEMI:
+ break guessingStatementEnd;
+ }
+ }
+ return nf.createExprStatement(nf.createName("error"), lineno);
+ }
+
+ /**
+ * Whether the "catch (e: e instanceof Exception) { ... }" syntax
+ * is implemented.
+ */
+
+ private Node statementHelper(Node statementLabel)
+ throws IOException, ParserException
+ {
+ Node pn = null;
+
+ int tt;
+
+ tt = peekToken();
+
+ switch(tt) {
+ case Token.IF: {
+ consumeToken();
+
+ decompiler.addToken(Token.IF);
+ int lineno = ts.getLineno();
+ Node cond = condition();
+ decompiler.addEOL(Token.LC);
+ Node ifTrue = statement();
+ Node ifFalse = null;
+ if (matchToken(Token.ELSE)) {
+ decompiler.addToken(Token.RC);
+ decompiler.addToken(Token.ELSE);
+ decompiler.addEOL(Token.LC);
+ ifFalse = statement();
+ }
+ decompiler.addEOL(Token.RC);
+ pn = nf.createIf(cond, ifTrue, ifFalse, lineno);
+ return pn;
+ }
+
+ case Token.SWITCH: {
+ consumeToken();
+
+ decompiler.addToken(Token.SWITCH);
+ int lineno = ts.getLineno();
+ mustMatchToken(Token.LP, "msg.no.paren.switch");
+ decompiler.addToken(Token.LP);
+ pn = enterSwitch(expr(false), lineno);
+ try {
+ mustMatchToken(Token.RP, "msg.no.paren.after.switch");
+ decompiler.addToken(Token.RP);
+ mustMatchToken(Token.LC, "msg.no.brace.switch");
+ decompiler.addEOL(Token.LC);
+
+ boolean hasDefault = false;
+ switchLoop: for (;;) {
+ tt = nextToken();
+ Node caseExpression;
+ switch (tt) {
+ case Token.RC:
+ break switchLoop;
+
+ case Token.CASE:
+ decompiler.addToken(Token.CASE);
+ caseExpression = expr(false);
+ mustMatchToken(Token.COLON, "msg.no.colon.case");
+ decompiler.addEOL(Token.COLON);
+ break;
+
+ case Token.DEFAULT:
+ if (hasDefault) {
+ reportError("msg.double.switch.default");
+ }
+ decompiler.addToken(Token.DEFAULT);
+ hasDefault = true;
+ caseExpression = null;
+ mustMatchToken(Token.COLON, "msg.no.colon.case");
+ decompiler.addEOL(Token.COLON);
+ break;
+
+ default:
+ reportError("msg.bad.switch");
+ break switchLoop;
+ }
+
+ Node block = nf.createLeaf(Token.BLOCK);
+ while ((tt = peekToken()) != Token.RC
+ && tt != Token.CASE
+ && tt != Token.DEFAULT
+ && tt != Token.EOF)
+ {
+ nf.addChildToBack(block, statement());
+ }
+
+ // caseExpression == null => add default lable
+ nf.addSwitchCase(pn, caseExpression, block);
+ }
+ decompiler.addEOL(Token.RC);
+ nf.closeSwitch(pn);
+ } finally {
+ exitSwitch();
+ }
+ return pn;
+ }
+
+ case Token.WHILE: {
+ consumeToken();
+ decompiler.addToken(Token.WHILE);
+
+ Node loop = enterLoop(statementLabel);
+ try {
+ Node cond = condition();
+ decompiler.addEOL(Token.LC);
+ Node body = statement();
+ decompiler.addEOL(Token.RC);
+ pn = nf.createWhile(loop, cond, body);
+ } finally {
+ exitLoop();
+ }
+ return pn;
+ }
+
+ case Token.DO: {
+ consumeToken();
+ decompiler.addToken(Token.DO);
+ decompiler.addEOL(Token.LC);
+
+ Node loop = enterLoop(statementLabel);
+ try {
+ Node body = statement();
+ decompiler.addToken(Token.RC);
+ mustMatchToken(Token.WHILE, "msg.no.while.do");
+ decompiler.addToken(Token.WHILE);
+ Node cond = condition();
+ pn = nf.createDoWhile(loop, body, cond);
+ } finally {
+ exitLoop();
+ }
+ // Always auto-insert semicon to follow SpiderMonkey:
+ // It is required by EMAScript but is ignored by the rest of
+ // world, see bug 238945
+ matchToken(Token.SEMI);
+ decompiler.addEOL(Token.SEMI);
+ return pn;
+ }
+
+ case Token.FOR: {
+ consumeToken();
+ boolean isForEach = false;
+ decompiler.addToken(Token.FOR);
+
+ Node loop = enterLoop(statementLabel);
+ try {
+
+ Node init; // Node init is also foo in 'foo in Object'
+ Node cond; // Node cond is also object in 'foo in Object'
+ Node incr = null; // to kill warning
+ Node body;
+
+ // See if this is a for each () instead of just a for ()
+ if (matchToken(Token.NAME)) {
+ decompiler.addName(ts.getString());
+ if (ts.getString().equals("each")) {
+ isForEach = true;
+ } else {
+ reportError("msg.no.paren.for");
+ }
+ }
+
+ mustMatchToken(Token.LP, "msg.no.paren.for");
+ decompiler.addToken(Token.LP);
+ tt = peekToken();
+ if (tt == Token.SEMI) {
+ init = nf.createLeaf(Token.EMPTY);
+ } else {
+ if (tt == Token.VAR) {
+ // set init to a var list or initial
+ consumeToken(); // consume the 'var' token
+ init = variables(Token.FOR);
+ }
+ else {
+ init = expr(true);
+ }
+ }
+
+ if (matchToken(Token.IN)) {
+ decompiler.addToken(Token.IN);
+ // 'cond' is the object over which we're iterating
+ cond = expr(false);
+ } else { // ordinary for loop
+ mustMatchToken(Token.SEMI, "msg.no.semi.for");
+ decompiler.addToken(Token.SEMI);
+ if (peekToken() == Token.SEMI) {
+ // no loop condition
+ cond = nf.createLeaf(Token.EMPTY);
+ } else {
+ cond = expr(false);
+ }
+
+ mustMatchToken(Token.SEMI, "msg.no.semi.for.cond");
+ decompiler.addToken(Token.SEMI);
+ if (peekToken() == Token.RP) {
+ incr = nf.createLeaf(Token.EMPTY);
+ } else {
+ incr = expr(false);
+ }
+ }
+
+ mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
+ decompiler.addToken(Token.RP);
+ decompiler.addEOL(Token.LC);
+ body = statement();
+ decompiler.addEOL(Token.RC);
+
+ if (incr == null) {
+ // cond could be null if 'in obj' got eaten
+ // by the init node.
+ pn = nf.createForIn(loop, init, cond, body, isForEach);
+ } else {
+ pn = nf.createFor(loop, init, cond, incr, body);
+ }
+ } finally {
+ exitLoop();
+ }
+ return pn;
+ }
+
+ case Token.TRY: {
+ consumeToken();
+ int lineno = ts.getLineno();
+
+ Node tryblock;
+ Node catchblocks = null;
+ Node finallyblock = null;
+
+ decompiler.addToken(Token.TRY);
+ decompiler.addEOL(Token.LC);
+ tryblock = statement();
+ decompiler.addEOL(Token.RC);
+
+ catchblocks = nf.createLeaf(Token.BLOCK);
+
+ boolean sawDefaultCatch = false;
+ int peek = peekToken();
+ if (peek == Token.CATCH) {
+ while (matchToken(Token.CATCH)) {
+ if (sawDefaultCatch) {
+ reportError("msg.catch.unreachable");
+ }
+ decompiler.addToken(Token.CATCH);
+ mustMatchToken(Token.LP, "msg.no.paren.catch");
+ decompiler.addToken(Token.LP);
+
+ mustMatchToken(Token.NAME, "msg.bad.catchcond");
+ String varName = ts.getString();
+ decompiler.addName(varName);
+
+ Node catchCond = null;
+ if (matchToken(Token.IF)) {
+ decompiler.addToken(Token.IF);
+ catchCond = expr(false);
+ } else {
+ sawDefaultCatch = true;
+ }
+
+ mustMatchToken(Token.RP, "msg.bad.catchcond");
+ decompiler.addToken(Token.RP);
+ mustMatchToken(Token.LC, "msg.no.brace.catchblock");
+ decompiler.addEOL(Token.LC);
+
+ nf.addChildToBack(catchblocks,
+ nf.createCatch(varName, catchCond,
+ statements(),
+ ts.getLineno()));
+
+ mustMatchToken(Token.RC, "msg.no.brace.after.body");
+ decompiler.addEOL(Token.RC);
+ }
+ } else if (peek != Token.FINALLY) {
+ mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally");
+ }
+
+ if (matchToken(Token.FINALLY)) {
+ decompiler.addToken(Token.FINALLY);
+ decompiler.addEOL(Token.LC);
+ finallyblock = statement();
+ decompiler.addEOL(Token.RC);
+ }
+
+ pn = nf.createTryCatchFinally(tryblock, catchblocks,
+ finallyblock, lineno);
+
+ return pn;
+ }
+
+ case Token.THROW: {
+ consumeToken();
+ if (peekTokenOrEOL() == Token.EOL) {
+ // ECMAScript does not allow new lines before throw expression,
+ // see bug 256617
+ reportError("msg.bad.throw.eol");
+ }
+
+ int lineno = ts.getLineno();
+ decompiler.addToken(Token.THROW);
+ pn = nf.createThrow(expr(false), lineno);
+ break;
+ }
+
+ case Token.BREAK: {
+ consumeToken();
+ int lineno = ts.getLineno();
+
+ decompiler.addToken(Token.BREAK);
+
+ // matchJumpLabelName only matches if there is one
+ Node breakStatement = matchJumpLabelName();
+ if (breakStatement == null) {
+ if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) {
+ reportError("msg.bad.break");
+ return null;
+ }
+ breakStatement = (Node)loopAndSwitchSet.peek();
+ }
+ pn = nf.createBreak(breakStatement, lineno);
+ break;
+ }
+
+ case Token.CONTINUE: {
+ consumeToken();
+ int lineno = ts.getLineno();
+
+ decompiler.addToken(Token.CONTINUE);
+
+ Node loop;
+ // matchJumpLabelName only matches if there is one
+ Node label = matchJumpLabelName();
+ if (label == null) {
+ if (loopSet == null || loopSet.size() == 0) {
+ reportError("msg.continue.outside");
+ return null;
+ }
+ loop = (Node)loopSet.peek();
+ } else {
+ loop = nf.getLabelLoop(label);
+ if (loop == null) {
+ reportError("msg.continue.nonloop");
+ return null;
+ }
+ }
+ pn = nf.createContinue(loop, lineno);
+ break;
+ }
+
+ case Token.WITH: {
+ consumeToken();
+
+ decompiler.addToken(Token.WITH);
+ int lineno = ts.getLineno();
+ mustMatchToken(Token.LP, "msg.no.paren.with");
+ decompiler.addToken(Token.LP);
+ Node obj = expr(false);
+ mustMatchToken(Token.RP, "msg.no.paren.after.with");
+ decompiler.addToken(Token.RP);
+ decompiler.addEOL(Token.LC);
+
+ ++nestingOfWith;
+ Node body;
+ try {
+ body = statement();
+ } finally {
+ --nestingOfWith;
+ }
+
+ decompiler.addEOL(Token.RC);
+
+ pn = nf.createWith(obj, body, lineno);
+ return pn;
+ }
+
+ case Token.CONST:
+ case Token.VAR: {
+ consumeToken();
+ pn = variables(tt);
+ break;
+ }
+
+ case Token.RETURN: {
+ if (!insideFunction()) {
+ reportError("msg.bad.return");
+ }
+ consumeToken();
+ decompiler.addToken(Token.RETURN);
+ int lineno = ts.getLineno();
+
+ Node retExpr;
+ /* This is ugly, but we don't want to require a semicolon. */
+ tt = peekTokenOrEOL();
+ switch (tt) {
+ case Token.SEMI:
+ case Token.RC:
+ case Token.EOF:
+ case Token.EOL:
+ case Token.ERROR:
+ retExpr = null;
+ break;
+ default:
+ retExpr = expr(false);
+ hasReturnValue = true;
+ }
+ pn = nf.createReturn(retExpr, lineno);
+
+ // see if we need a strict mode warning
+ if (retExpr == null) {
+ if (functionEndFlags == Node.END_RETURNS_VALUE)
+ addStrictWarning("msg.return.inconsistent", "");
+
+ functionEndFlags |= Node.END_RETURNS;
+ } else {
+ if (functionEndFlags == Node.END_RETURNS)
+ addStrictWarning("msg.return.inconsistent", "");
+
+ functionEndFlags |= Node.END_RETURNS_VALUE;
+ }
+
+ break;
+ }
+
+ case Token.LC:
+ consumeToken();
+ if (statementLabel != null) {
+ decompiler.addToken(Token.LC);
+ }
+ pn = statements();
+ mustMatchToken(Token.RC, "msg.no.brace.block");
+ if (statementLabel != null) {
+ decompiler.addEOL(Token.RC);
+ }
+ return pn;
+
+ case Token.ERROR:
+ // Fall thru, to have a node for error recovery to work on
+ case Token.SEMI:
+ consumeToken();
+ pn = nf.createLeaf(Token.EMPTY);
+ return pn;
+
+ case Token.FUNCTION: {
+ consumeToken();
+ pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
+ return pn;
+ }
+
+ case Token.DEFAULT :
+ consumeToken();
+ mustHaveXML();
+
+ decompiler.addToken(Token.DEFAULT);
+ int nsLine = ts.getLineno();
+
+ if (!(matchToken(Token.NAME)
+ && ts.getString().equals("xml")))
+ {
+ reportError("msg.bad.namespace");
+ }
+ decompiler.addName(" xml");
+
+ if (!(matchToken(Token.NAME)
+ && ts.getString().equals("namespace")))
+ {
+ reportError("msg.bad.namespace");
+ }
+ decompiler.addName(" namespace");
+
+ if (!matchToken(Token.ASSIGN)) {
+ reportError("msg.bad.namespace");
+ }
+ decompiler.addToken(Token.ASSIGN);
+
+ Node expr = expr(false);
+ pn = nf.createDefaultNamespace(expr, nsLine);
+ break;
+
+ case Token.NAME: {
+ int lineno = ts.getLineno();
+ String name = ts.getString();
+ setCheckForLabel();
+ pn = expr(false);
+ if (pn.getType() != Token.LABEL) {
+ pn = nf.createExprStatement(pn, lineno);
+ } else {
+ // Parsed the label: push back token should be
+ // colon that primaryExpr left untouched.
+ if (peekToken() != Token.COLON) Kit.codeBug();
+ consumeToken();
+ // depend on decompiling lookahead to guess that that
+ // last name was a label.
+ decompiler.addName(name);
+ decompiler.addEOL(Token.COLON);
+
+ if (labelSet == null) {
+ labelSet = new Hashtable();
+ } else if (labelSet.containsKey(name)) {
+ reportError("msg.dup.label");
+ }
+
+ boolean firstLabel;
+ if (statementLabel == null) {
+ firstLabel = true;
+ statementLabel = pn;
+ } else {
+ // Discard multiple label nodes and use only
+ // the first: it allows to simplify IRFactory
+ firstLabel = false;
+ }
+ labelSet.put(name, statementLabel);
+ try {
+ pn = statementHelper(statementLabel);
+ } finally {
+ labelSet.remove(name);
+ }
+ if (firstLabel) {
+ pn = nf.createLabeledStatement(statementLabel, pn);
+ }
+ return pn;
+ }
+ break;
+ }
+
+ default: {
+ int lineno = ts.getLineno();
+ pn = expr(false);
+ pn = nf.createExprStatement(pn, lineno);
+ break;
+ }
+ }
+
+ int ttFlagged = peekFlaggedToken();
+ switch (ttFlagged & CLEAR_TI_MASK) {
+ case Token.SEMI:
+ // Consume ';' as a part of expression
+ consumeToken();
+ break;
+ case Token.ERROR:
+ case Token.EOF:
+ case Token.RC:
+ // Autoinsert ;
+ break;
+ default:
+ if ((ttFlagged & TI_AFTER_EOL) == 0) {
+ // Report error if no EOL or autoinsert ; otherwise
+ reportError("msg.no.semi.stmt");
+ }
+ break;
+ }
+ decompiler.addEOL(Token.SEMI);
+
+ return pn;
+ }
+
+ /**
+ * Parse a 'var' or 'const' statement, or a 'var' init list in a for
+ * statement.
+ * @param context A token value: either VAR, CONST or FOR depending on
+ * context.
+ * @return The parsed statement
+ * @throws IOException
+ * @throws ParserException
+ */
+ private Node variables(int context)
+ throws IOException, ParserException
+ {
+ Node pn;
+ boolean first = true;
+
+ if (context == Token.CONST){
+ pn = nf.createVariables(Token.CONST, ts.getLineno());
+ decompiler.addToken(Token.CONST);
+ } else {
+ pn = nf.createVariables(Token.VAR, ts.getLineno());
+ decompiler.addToken(Token.VAR);
+ }
+
+ for (;;) {
+ Node name;
+ Node init;
+ mustMatchToken(Token.NAME, "msg.bad.var");
+ String s = ts.getString();
+
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ first = false;
+
+ decompiler.addName(s);
+
+ if (context == Token.CONST) {
+ if (!currentScriptOrFn.addConst(s)) {
+ // We know it's already defined, since addConst passes if
+ // it's not defined at all. The addVar call just confirms
+ // what it is.
+ if (currentScriptOrFn.addVar(s) != ScriptOrFnNode.DUPLICATE_CONST)
+ addError("msg.var.redecl", s);
+ else
+ addError("msg.const.redecl", s);
+ }
+ } else {
+ int dupState = currentScriptOrFn.addVar(s);
+ if (dupState == ScriptOrFnNode.DUPLICATE_CONST)
+ addError("msg.const.redecl", s);
+ else if (dupState == ScriptOrFnNode.DUPLICATE_PARAMETER)
+ addStrictWarning("msg.var.hides.arg", s);
+ else if (dupState == ScriptOrFnNode.DUPLICATE_VAR)
+ addStrictWarning("msg.var.redecl", s);
+ }
+ name = nf.createName(s);
+
+ // omitted check for argument hiding
+
+ if (matchToken(Token.ASSIGN)) {
+ decompiler.addToken(Token.ASSIGN);
+
+ init = assignExpr(context == Token.FOR);
+ nf.addChildToBack(name, init);
+ }
+ nf.addChildToBack(pn, name);
+ if (!matchToken(Token.COMMA))
+ break;
+ }
+ return pn;
+ }
+
+ private Node expr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = assignExpr(inForInit);
+ while (matchToken(Token.COMMA)) {
+ decompiler.addToken(Token.COMMA);
+ if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
+ addStrictWarning("msg.no.side.effects", "");
+ pn = nf.createBinary(Token.COMMA, pn, assignExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node assignExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = condExpr(inForInit);
+
+ int tt = peekToken();
+ if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) {
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createAssignment(tt, pn, assignExpr(inForInit));
+ }
+
+ return pn;
+ }
+
+ private Node condExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = orExpr(inForInit);
+
+ if (matchToken(Token.HOOK)) {
+ decompiler.addToken(Token.HOOK);
+ Node ifTrue = assignExpr(false);
+ mustMatchToken(Token.COLON, "msg.no.colon.cond");
+ decompiler.addToken(Token.COLON);
+ Node ifFalse = assignExpr(inForInit);
+ return nf.createCondExpr(pn, ifTrue, ifFalse);
+ }
+
+ return pn;
+ }
+
+ private Node orExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = andExpr(inForInit);
+ if (matchToken(Token.OR)) {
+ decompiler.addToken(Token.OR);
+ pn = nf.createBinary(Token.OR, pn, orExpr(inForInit));
+ }
+
+ return pn;
+ }
+
+ private Node andExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = bitOrExpr(inForInit);
+ if (matchToken(Token.AND)) {
+ decompiler.addToken(Token.AND);
+ pn = nf.createBinary(Token.AND, pn, andExpr(inForInit));
+ }
+
+ return pn;
+ }
+
+ private Node bitOrExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = bitXorExpr(inForInit);
+ while (matchToken(Token.BITOR)) {
+ decompiler.addToken(Token.BITOR);
+ pn = nf.createBinary(Token.BITOR, pn, bitXorExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node bitXorExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = bitAndExpr(inForInit);
+ while (matchToken(Token.BITXOR)) {
+ decompiler.addToken(Token.BITXOR);
+ pn = nf.createBinary(Token.BITXOR, pn, bitAndExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node bitAndExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = eqExpr(inForInit);
+ while (matchToken(Token.BITAND)) {
+ decompiler.addToken(Token.BITAND);
+ pn = nf.createBinary(Token.BITAND, pn, eqExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node eqExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = relExpr(inForInit);
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.EQ:
+ case Token.NE:
+ case Token.SHEQ:
+ case Token.SHNE:
+ consumeToken();
+ int decompilerToken = tt;
+ int parseToken = tt;
+ if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
+ // JavaScript 1.2 uses shallow equality for == and != .
+ // In addition, convert === and !== for decompiler into
+ // == and != since the decompiler is supposed to show
+ // canonical source and in 1.2 ===, !== are allowed
+ // only as an alias to ==, !=.
+ switch (tt) {
+ case Token.EQ:
+ parseToken = Token.SHEQ;
+ break;
+ case Token.NE:
+ parseToken = Token.SHNE;
+ break;
+ case Token.SHEQ:
+ decompilerToken = Token.EQ;
+ break;
+ case Token.SHNE:
+ decompilerToken = Token.NE;
+ break;
+ }
+ }
+ decompiler.addToken(decompilerToken);
+ pn = nf.createBinary(parseToken, pn, relExpr(inForInit));
+ continue;
+ }
+ break;
+ }
+ return pn;
+ }
+
+ private Node relExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = shiftExpr();
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.IN:
+ if (inForInit)
+ break;
+ // fall through
+ case Token.INSTANCEOF:
+ case Token.LE:
+ case Token.LT:
+ case Token.GE:
+ case Token.GT:
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createBinary(tt, pn, shiftExpr());
+ continue;
+ }
+ break;
+ }
+ return pn;
+ }
+
+ private Node shiftExpr()
+ throws IOException, ParserException
+ {
+ Node pn = addExpr();
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.LSH:
+ case Token.URSH:
+ case Token.RSH:
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createBinary(tt, pn, addExpr());
+ continue;
+ }
+ break;
+ }
+ return pn;
+ }
+
+ private Node addExpr()
+ throws IOException, ParserException
+ {
+ Node pn = mulExpr();
+ for (;;) {
+ int tt = peekToken();
+ if (tt == Token.ADD || tt == Token.SUB) {
+ consumeToken();
+ decompiler.addToken(tt);
+ // flushNewLines
+ pn = nf.createBinary(tt, pn, mulExpr());
+ continue;
+ }
+ break;
+ }
+
+ return pn;
+ }
+
+ private Node mulExpr()
+ throws IOException, ParserException
+ {
+ Node pn = unaryExpr();
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.MUL:
+ case Token.DIV:
+ case Token.MOD:
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createBinary(tt, pn, unaryExpr());
+ continue;
+ }
+ break;
+ }
+
+ return pn;
+ }
+
+ private Node unaryExpr()
+ throws IOException, ParserException
+ {
+ int tt;
+
+ tt = peekToken();
+
+ switch(tt) {
+ case Token.VOID:
+ case Token.NOT:
+ case Token.BITNOT:
+ case Token.TYPEOF:
+ consumeToken();
+ decompiler.addToken(tt);
+ return nf.createUnary(tt, unaryExpr());
+
+ case Token.ADD:
+ consumeToken();
+ // Convert to special POS token in decompiler and parse tree
+ decompiler.addToken(Token.POS);
+ return nf.createUnary(Token.POS, unaryExpr());
+
+ case Token.SUB:
+ consumeToken();
+ // Convert to special NEG token in decompiler and parse tree
+ decompiler.addToken(Token.NEG);
+ return nf.createUnary(Token.NEG, unaryExpr());
+
+ case Token.INC:
+ case Token.DEC:
+ consumeToken();
+ decompiler.addToken(tt);
+ return nf.createIncDec(tt, false, memberExpr(true));
+
+ case Token.DELPROP:
+ consumeToken();
+ decompiler.addToken(Token.DELPROP);
+ return nf.createUnary(Token.DELPROP, unaryExpr());
+
+ case Token.ERROR:
+ consumeToken();
+ break;
+
+ // XML stream encountered in expression.
+ case Token.LT:
+ if (compilerEnv.isXmlAvailable()) {
+ consumeToken();
+ Node pn = xmlInitializer();
+ return memberExprTail(true, pn);
+ }
+ // Fall thru to the default handling of RELOP
+
+ default:
+ Node pn = memberExpr(true);
+
+ // Don't look across a newline boundary for a postfix incop.
+ tt = peekTokenOrEOL();
+ if (tt == Token.INC || tt == Token.DEC) {
+ consumeToken();
+ decompiler.addToken(tt);
+ return nf.createIncDec(tt, true, pn);
+ }
+ return pn;
+ }
+ return nf.createName("err"); // Only reached on error. Try to continue.
+
+ }
+
+ private Node xmlInitializer() throws IOException
+ {
+ int tt = ts.getFirstXMLToken();
+ if (tt != Token.XML && tt != Token.XMLEND) {
+ reportError("msg.syntax");
+ return null;
+ }
+
+ /* Make a NEW node to append to. */
+ Node pnXML = nf.createLeaf(Token.NEW);
+
+ String xml = ts.getString();
+ boolean fAnonymous = xml.trim().startsWith("<>");
+
+ Node pn = nf.createName(fAnonymous ? "XMLList" : "XML");
+ nf.addChildToBack(pnXML, pn);
+
+ pn = null;
+ Node expr;
+ for (;;tt = ts.getNextXMLToken()) {
+ switch (tt) {
+ case Token.XML:
+ xml = ts.getString();
+ decompiler.addName(xml);
+ mustMatchToken(Token.LC, "msg.syntax");
+ decompiler.addToken(Token.LC);
+ expr = (peekToken() == Token.RC)
+ ? nf.createString("")
+ : expr(false);
+ mustMatchToken(Token.RC, "msg.syntax");
+ decompiler.addToken(Token.RC);
+ if (pn == null) {
+ pn = nf.createString(xml);
+ } else {
+ pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
+ }
+ if (ts.isXMLAttribute()) {
+ /* Need to put the result in double quotes */
+ expr = nf.createUnary(Token.ESCXMLATTR, expr);
+ Node prepend = nf.createBinary(Token.ADD,
+ nf.createString("\""),
+ expr);
+ expr = nf.createBinary(Token.ADD,
+ prepend,
+ nf.createString("\""));
+ } else {
+ expr = nf.createUnary(Token.ESCXMLTEXT, expr);
+ }
+ pn = nf.createBinary(Token.ADD, pn, expr);
+ break;
+ case Token.XMLEND:
+ xml = ts.getString();
+ decompiler.addName(xml);
+ if (pn == null) {
+ pn = nf.createString(xml);
+ } else {
+ pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
+ }
+
+ nf.addChildToBack(pnXML, pn);
+ return pnXML;
+ default:
+ reportError("msg.syntax");
+ return null;
+ }
+ }
+ }
+
+ private void argumentList(Node listNode)
+ throws IOException, ParserException
+ {
+ boolean matched;
+ matched = matchToken(Token.RP);
+ if (!matched) {
+ boolean first = true;
+ do {
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ first = false;
+ nf.addChildToBack(listNode, assignExpr(false));
+ } while (matchToken(Token.COMMA));
+
+ mustMatchToken(Token.RP, "msg.no.paren.arg");
+ }
+ decompiler.addToken(Token.RP);
+ }
+
+ private Node memberExpr(boolean allowCallSyntax)
+ throws IOException, ParserException
+ {
+ int tt;
+
+ Node pn;
+
+ /* Check for new expressions. */
+ tt = peekToken();
+ if (tt == Token.NEW) {
+ /* Eat the NEW token. */
+ consumeToken();
+ decompiler.addToken(Token.NEW);
+
+ /* Make a NEW node to append to. */
+ pn = nf.createCallOrNew(Token.NEW, memberExpr(false));
+
+ if (matchToken(Token.LP)) {
+ decompiler.addToken(Token.LP);
+ /* Add the arguments to pn, if any are supplied. */
+ argumentList(pn);
+ }
+
+ /* XXX there's a check in the C source against
+ * "too many constructor arguments" - how many
+ * do we claim to support?
+ */
+
+ /* Experimental syntax: allow an object literal to follow a new expression,
+ * which will mean a kind of anonymous class built with the JavaAdapter.
+ * the object literal will be passed as an additional argument to the constructor.
+ */
+ tt = peekToken();
+ if (tt == Token.LC) {
+ nf.addChildToBack(pn, primaryExpr());
+ }
+ } else {
+ pn = primaryExpr();
+ }
+
+ return memberExprTail(allowCallSyntax, pn);
+ }
+
+ private Node memberExprTail(boolean allowCallSyntax, Node pn)
+ throws IOException, ParserException
+ {
+ tailLoop:
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+
+ case Token.DOT:
+ case Token.DOTDOT:
+ {
+ int memberTypeFlags;
+ String s;
+
+ consumeToken();
+ decompiler.addToken(tt);
+ memberTypeFlags = 0;
+ if (tt == Token.DOTDOT) {
+ mustHaveXML();
+ memberTypeFlags = Node.DESCENDANTS_FLAG;
+ }
+ if (!compilerEnv.isXmlAvailable()) {
+ mustMatchToken(Token.NAME, "msg.no.name.after.dot");
+ s = ts.getString();
+ decompiler.addName(s);
+ pn = nf.createPropertyGet(pn, null, s, memberTypeFlags);
+ break;
+ }
+
+ tt = nextToken();
+ switch (tt) {
+ // handles: name, ns::name, ns::*, ns::[expr]
+ case Token.NAME:
+ s = ts.getString();
+ decompiler.addName(s);
+ pn = propertyName(pn, s, memberTypeFlags);
+ break;
+
+ // handles: *, *::name, *::*, *::[expr]
+ case Token.MUL:
+ decompiler.addName("*");
+ pn = propertyName(pn, "*", memberTypeFlags);
+ break;
+
+ // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
+ // '@::attr', '@::*', '@*', '@*::attr', '@*::*'
+ case Token.XMLATTR:
+ decompiler.addToken(Token.XMLATTR);
+ pn = attributeAccess(pn, memberTypeFlags);
+ break;
+
+ default:
+ reportError("msg.no.name.after.dot");
+ }
+ }
+ break;
+
+ case Token.DOTQUERY:
+ consumeToken();
+ mustHaveXML();
+ decompiler.addToken(Token.DOTQUERY);
+ pn = nf.createDotQuery(pn, expr(false), ts.getLineno());
+ mustMatchToken(Token.RP, "msg.no.paren");
+ decompiler.addToken(Token.RP);
+ break;
+
+ case Token.LB:
+ consumeToken();
+ decompiler.addToken(Token.LB);
+ pn = nf.createElementGet(pn, null, expr(false), 0);
+ mustMatchToken(Token.RB, "msg.no.bracket.index");
+ decompiler.addToken(Token.RB);
+ break;
+
+ case Token.LP:
+ if (!allowCallSyntax) {
+ break tailLoop;
+ }
+ consumeToken();
+ decompiler.addToken(Token.LP);
+ pn = nf.createCallOrNew(Token.CALL, pn);
+ /* Add the arguments to pn, if any are supplied. */
+ argumentList(pn);
+ break;
+
+ default:
+ break tailLoop;
+ }
+ }
+ return pn;
+ }
+
+ /*
+ * Xml attribute expression:
+ * '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*'
+ */
+ private Node attributeAccess(Node pn, int memberTypeFlags)
+ throws IOException
+ {
+ memberTypeFlags |= Node.ATTRIBUTE_FLAG;
+ int tt = nextToken();
+
+ switch (tt) {
+ // handles: @name, @ns::name, @ns::*, @ns::[expr]
+ case Token.NAME:
+ {
+ String s = ts.getString();
+ decompiler.addName(s);
+ pn = propertyName(pn, s, memberTypeFlags);
+ }
+ break;
+
+ // handles: @*, @*::name, @*::*, @*::[expr]
+ case Token.MUL:
+ decompiler.addName("*");
+ pn = propertyName(pn, "*", memberTypeFlags);
+ break;
+
+ // handles @[expr]
+ case Token.LB:
+ decompiler.addToken(Token.LB);
+ pn = nf.createElementGet(pn, null, expr(false), memberTypeFlags);
+ mustMatchToken(Token.RB, "msg.no.bracket.index");
+ decompiler.addToken(Token.RB);
+ break;
+
+ default:
+ reportError("msg.no.name.after.xmlAttr");
+ pn = nf.createPropertyGet(pn, null, "?", memberTypeFlags);
+ break;
+ }
+
+ return pn;
+ }
+
+ /**
+ * Check if :: follows name in which case it becomes qualified name
+ */
+ private Node propertyName(Node pn, String name, int memberTypeFlags)
+ throws IOException, ParserException
+ {
+ String namespace = null;
+ if (matchToken(Token.COLONCOLON)) {
+ decompiler.addToken(Token.COLONCOLON);
+ namespace = name;
+
+ int tt = nextToken();
+ switch (tt) {
+ // handles name::name
+ case Token.NAME:
+ name = ts.getString();
+ decompiler.addName(name);
+ break;
+
+ // handles name::*
+ case Token.MUL:
+ decompiler.addName("*");
+ name = "*";
+ break;
+
+ // handles name::[expr]
+ case Token.LB:
+ decompiler.addToken(Token.LB);
+ pn = nf.createElementGet(pn, namespace, expr(false),
+ memberTypeFlags);
+ mustMatchToken(Token.RB, "msg.no.bracket.index");
+ decompiler.addToken(Token.RB);
+ return pn;
+
+ default:
+ reportError("msg.no.name.after.coloncolon");
+ name = "?";
+ }
+ }
+
+ pn = nf.createPropertyGet(pn, namespace, name, memberTypeFlags);
+ return pn;
+ }
+
+ private Node primaryExpr()
+ throws IOException, ParserException
+ {
+ Node pn;
+
+ int ttFlagged = nextFlaggedToken();
+ int tt = ttFlagged & CLEAR_TI_MASK;
+
+ switch(tt) {
+
+ case Token.FUNCTION:
+ return function(FunctionNode.FUNCTION_EXPRESSION);
+
+ case Token.LB: {
+ ObjArray elems = new ObjArray();
+ int skipCount = 0;
+ decompiler.addToken(Token.LB);
+ boolean after_lb_or_comma = true;
+ for (;;) {
+ tt = peekToken();
+
+ if (tt == Token.COMMA) {
+ consumeToken();
+ decompiler.addToken(Token.COMMA);
+ if (!after_lb_or_comma) {
+ after_lb_or_comma = true;
+ } else {
+ elems.add(null);
+ ++skipCount;
+ }
+ } else if (tt == Token.RB) {
+ consumeToken();
+ decompiler.addToken(Token.RB);
+ break;
+ } else {
+ if (!after_lb_or_comma) {
+ reportError("msg.no.bracket.arg");
+ }
+ elems.add(assignExpr(false));
+ after_lb_or_comma = false;
+ }
+ }
+ return nf.createArrayLiteral(elems, skipCount);
+ }
+
+ case Token.LC: {
+ ObjArray elems = new ObjArray();
+ decompiler.addToken(Token.LC);
+ if (!matchToken(Token.RC)) {
+
+ boolean first = true;
+ commaloop:
+ do {
+ Object property;
+
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ else
+ first = false;
+
+ tt = peekToken();
+ switch(tt) {
+ case Token.NAME:
+ case Token.STRING:
+ consumeToken();
+ // map NAMEs to STRINGs in object literal context
+ // but tell the decompiler the proper type
+ String s = ts.getString();
+ if (tt == Token.NAME) {
+ if (s.equals("get") &&
+ peekToken() == Token.NAME) {
+ decompiler.addToken(Token.GET);
+ consumeToken();
+ s = ts.getString();
+ decompiler.addName(s);
+ property = ScriptRuntime.getIndexObject(s);
+ if (!getterSetterProperty(elems, property,
+ true))
+ break commaloop;
+ break;
+ } else if (s.equals("set") &&
+ peekToken() == Token.NAME) {
+ decompiler.addToken(Token.SET);
+ consumeToken();
+ s = ts.getString();
+ decompiler.addName(s);
+ property = ScriptRuntime.getIndexObject(s);
+ if (!getterSetterProperty(elems, property,
+ false))
+ break commaloop;
+ break;
+ }
+ decompiler.addName(s);
+ } else {
+ decompiler.addString(s);
+ }
+ property = ScriptRuntime.getIndexObject(s);
+ plainProperty(elems, property);
+ break;
+
+ case Token.NUMBER:
+ consumeToken();
+ double n = ts.getNumber();
+ decompiler.addNumber(n);
+ property = ScriptRuntime.getIndexObject(n);
+ plainProperty(elems, property);
+ break;
+
+ case Token.RC:
+ // trailing comma is OK.
+ break commaloop;
+ default:
+ reportError("msg.bad.prop");
+ break commaloop;
+ }
+ } while (matchToken(Token.COMMA));
+
+ mustMatchToken(Token.RC, "msg.no.brace.prop");
+ }
+ decompiler.addToken(Token.RC);
+ return nf.createObjectLiteral(elems);
+ }
+
+ case Token.LP:
+
+ /* Brendan's IR-jsparse.c makes a new node tagged with
+ * TOK_LP here... I'm not sure I understand why. Isn't
+ * the grouping already implicit in the structure of the
+ * parse tree? also TOK_LP is already overloaded (I
+ * think) in the C IR as 'function call.' */
+ decompiler.addToken(Token.LP);
+ pn = expr(false);
+ pn.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
+ decompiler.addToken(Token.RP);
+ mustMatchToken(Token.RP, "msg.no.paren");
+ return pn;
+
+ case Token.XMLATTR:
+ mustHaveXML();
+ decompiler.addToken(Token.XMLATTR);
+ pn = attributeAccess(null, 0);
+ return pn;
+
+ case Token.NAME: {
+ String name = ts.getString();
+ if ((ttFlagged & TI_CHECK_LABEL) != 0) {
+ if (peekToken() == Token.COLON) {
+ // Do not consume colon, it is used as unwind indicator
+ // to return to statementHelper.
+ // XXX Better way?
+ return nf.createLabel(ts.getLineno());
+ }
+ }
+
+ decompiler.addName(name);
+ if (compilerEnv.isXmlAvailable()) {
+ pn = propertyName(null, name, 0);
+ } else {
+ pn = nf.createName(name);
+ }
+ return pn;
+ }
+
+ case Token.NUMBER: {
+ double n = ts.getNumber();
+ decompiler.addNumber(n);
+ return nf.createNumber(n);
+ }
+
+ case Token.STRING: {
+ String s = ts.getString();
+ decompiler.addString(s);
+ return nf.createString(s);
+ }
+
+ case Token.DIV:
+ case Token.ASSIGN_DIV: {
+ // Got / or /= which should be treated as regexp in fact
+ ts.readRegExp(tt);
+ String flags = ts.regExpFlags;
+ ts.regExpFlags = null;
+ String re = ts.getString();
+ decompiler.addRegexp(re, flags);
+ int index = currentScriptOrFn.addRegexp(re, flags);
+ return nf.createRegExp(index);
+ }
+
+ case Token.NULL:
+ case Token.THIS:
+ case Token.FALSE:
+ case Token.TRUE:
+ decompiler.addToken(tt);
+ return nf.createLeaf(tt);
+
+ case Token.RESERVED:
+ reportError("msg.reserved.id");
+ break;
+
+ case Token.ERROR:
+ /* the scanner or one of its subroutines reported the error. */
+ break;
+
+ case Token.EOF:
+ reportError("msg.unexpected.eof");
+ break;
+
+ default:
+ reportError("msg.syntax");
+ break;
+ }
+ return null; // should never reach here
+ }
+
+ private void plainProperty(ObjArray elems, Object property)
+ throws IOException {
+ mustMatchToken(Token.COLON, "msg.no.colon.prop");
+
+ // OBJLIT is used as ':' in object literal for
+ // decompilation to solve spacing ambiguity.
+ decompiler.addToken(Token.OBJECTLIT);
+ elems.add(property);
+ elems.add(assignExpr(false));
+ }
+
+ private boolean getterSetterProperty(ObjArray elems, Object property,
+ boolean isGetter) throws IOException {
+ Node f = function(FunctionNode.FUNCTION_EXPRESSION);
+ if (f.getType() != Token.FUNCTION) {
+ reportError("msg.bad.prop");
+ return false;
+ }
+ int fnIndex = f.getExistingIntProp(Node.FUNCTION_PROP);
+ FunctionNode fn = currentScriptOrFn.getFunctionNode(fnIndex);
+ if (fn.getFunctionName().length() != 0) {
+ reportError("msg.bad.prop");
+ return false;
+ }
+ elems.add(property);
+ if (isGetter) {
+ elems.add(nf.createUnary(Token.GET, f));
+ } else {
+ elems.add(nf.createUnary(Token.SET, f));
+ }
+ return true;
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Parser.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Token.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Token.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Token.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,420 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ * Mike McCabe
+ * Igor Bukanov
+ * Bob Jervis
+ * Milen Nankov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the JavaScript scanner.
+ *
+ * It is based on the C source files jsscan.c and jsscan.h
+ * in the jsref package.
+ *
+ * @see org.mozilla.javascript.Parser
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+public class Token
+{
+
+ // debug flags
+ public static final boolean printTrees = false;
+ static final boolean printICode = false;
+ static final boolean printNames = printTrees || printICode;
+
+ /**
+ * Token types. These values correspond to JSTokenType values in
+ * jsscan.c.
+ */
+
+ public final static int
+ // start enum
+ ERROR = -1, // well-known as the only code < EOF
+ EOF = 0, // end of file token - (not EOF_CHAR)
+ EOL = 1, // end of line
+
+ // Interpreter reuses the following as bytecodes
+ FIRST_BYTECODE_TOKEN = 2,
+
+ ENTERWITH = 2,
+ LEAVEWITH = 3,
+ RETURN = 4,
+ GOTO = 5,
+ IFEQ = 6,
+ IFNE = 7,
+ SETNAME = 8,
+ BITOR = 9,
+ BITXOR = 10,
+ BITAND = 11,
+ EQ = 12,
+ NE = 13,
+ LT = 14,
+ LE = 15,
+ GT = 16,
+ GE = 17,
+ LSH = 18,
+ RSH = 19,
+ URSH = 20,
+ ADD = 21,
+ SUB = 22,
+ MUL = 23,
+ DIV = 24,
+ MOD = 25,
+ NOT = 26,
+ BITNOT = 27,
+ POS = 28,
+ NEG = 29,
+ NEW = 30,
+ DELPROP = 31,
+ TYPEOF = 32,
+ GETPROP = 33,
+ SETPROP = 34,
+ GETELEM = 35,
+ SETELEM = 36,
+ CALL = 37,
+ NAME = 38,
+ NUMBER = 39,
+ STRING = 40,
+ NULL = 41,
+ THIS = 42,
+ FALSE = 43,
+ TRUE = 44,
+ SHEQ = 45, // shallow equality (===)
+ SHNE = 46, // shallow inequality (!==)
+ REGEXP = 47,
+ BINDNAME = 48,
+ THROW = 49,
+ RETHROW = 50, // rethrow caught execetion: catch (e if ) use it
+ IN = 51,
+ INSTANCEOF = 52,
+ LOCAL_LOAD = 53,
+ GETVAR = 54,
+ SETVAR = 55,
+ CATCH_SCOPE = 56,
+ ENUM_INIT_KEYS = 57,
+ ENUM_INIT_VALUES = 58,
+ ENUM_NEXT = 59,
+ ENUM_ID = 60,
+ THISFN = 61,
+ RETURN_RESULT = 62, // to return prevoisly stored return result
+ ARRAYLIT = 63, // array literal
+ OBJECTLIT = 64, // object literal
+ GET_REF = 65, // *reference
+ SET_REF = 66, // *reference = something
+ DEL_REF = 67, // delete reference
+ REF_CALL = 68, // f(args) = something or f(args)++
+ REF_SPECIAL = 69, // reference for special properties like __proto
+
+ // For XML support:
+ DEFAULTNAMESPACE = 70, // default xml namespace =
+ ESCXMLATTR = 71,
+ ESCXMLTEXT = 72,
+ REF_MEMBER = 73, // Reference for x. at y, x..y etc.
+ REF_NS_MEMBER = 74, // Reference for x.ns::y, x..ns::y etc.
+ REF_NAME = 75, // Reference for @y, @[y] etc.
+ REF_NS_NAME = 76; // Reference for ns::y, @ns::y@[y] etc.
+
+ // End of interpreter bytecodes
+ public final static int
+ LAST_BYTECODE_TOKEN = REF_NS_NAME,
+
+ TRY = 77,
+ SEMI = 78, // semicolon
+ LB = 79, // left and right brackets
+ RB = 80,
+ LC = 81, // left and right curlies (braces)
+ RC = 82,
+ LP = 83, // left and right parentheses
+ RP = 84,
+ COMMA = 85, // comma operator
+
+ ASSIGN = 86, // simple assignment (=)
+ ASSIGN_BITOR = 87, // |=
+ ASSIGN_BITXOR = 88, // ^=
+ ASSIGN_BITAND = 89, // |=
+ ASSIGN_LSH = 90, // <<=
+ ASSIGN_RSH = 91, // >>=
+ ASSIGN_URSH = 92, // >>>=
+ ASSIGN_ADD = 93, // +=
+ ASSIGN_SUB = 94, // -=
+ ASSIGN_MUL = 95, // *=
+ ASSIGN_DIV = 96, // /=
+ ASSIGN_MOD = 97; // %=
+
+ public final static int
+ FIRST_ASSIGN = ASSIGN,
+ LAST_ASSIGN = ASSIGN_MOD,
+
+ HOOK = 98, // conditional (?:)
+ COLON = 99,
+ OR = 100, // logical or (||)
+ AND = 101, // logical and (&&)
+ INC = 102, // increment/decrement (++ --)
+ DEC = 103,
+ DOT = 104, // member operator (.)
+ FUNCTION = 105, // function keyword
+ EXPORT = 106, // export keyword
+ IMPORT = 107, // import keyword
+ IF = 108, // if keyword
+ ELSE = 109, // else keyword
+ SWITCH = 110, // switch keyword
+ CASE = 111, // case keyword
+ DEFAULT = 112, // default keyword
+ WHILE = 113, // while keyword
+ DO = 114, // do keyword
+ FOR = 115, // for keyword
+ BREAK = 116, // break keyword
+ CONTINUE = 117, // continue keyword
+ VAR = 118, // var keyword
+ WITH = 119, // with keyword
+ CATCH = 120, // catch keyword
+ FINALLY = 121, // finally keyword
+ VOID = 122, // void keyword
+ RESERVED = 123, // reserved keywords
+
+ EMPTY = 124,
+
+ /* types used for the parse tree - these never get returned
+ * by the scanner.
+ */
+
+ BLOCK = 125, // statement block
+ LABEL = 126, // label
+ TARGET = 127,
+ LOOP = 128,
+ EXPR_VOID = 129, // expression statement in functions
+ EXPR_RESULT = 130, // expression statement in scripts
+ JSR = 131,
+ SCRIPT = 132, // top-level node for entire script
+ TYPEOFNAME = 133, // for typeof(simple-name)
+ USE_STACK = 134,
+ SETPROP_OP = 135, // x.y op= something
+ SETELEM_OP = 136, // x[y] op= something
+ LOCAL_BLOCK = 137,
+ SET_REF_OP = 138, // *reference op= something
+
+ // For XML support:
+ DOTDOT = 139, // member operator (..)
+ COLONCOLON = 140, // namespace::name
+ XML = 141, // XML type
+ DOTQUERY = 142, // .() -- e.g., x.emps.emp.(name == "terry")
+ XMLATTR = 143, // @
+ XMLEND = 144,
+
+ // Optimizer-only-tokens
+ TO_OBJECT = 145,
+ TO_DOUBLE = 146,
+
+ GET = 147, // JS 1.5 get pseudo keyword
+ SET = 148, // JS 1.5 set pseudo keyword
+ CONST = 149,
+ SETCONST = 150,
+ SETCONSTVAR = 151,
+
+ IECC = 152, // Internet Explorer conditional comment
+
+ LAST_TOKEN = 153;
+
+ public static String name(int token)
+ {
+ if (!printNames) {
+ return String.valueOf(token);
+ }
+ switch (token) {
+ case ERROR: return "ERROR";
+ case EOF: return "EOF";
+ case EOL: return "EOL";
+ case ENTERWITH: return "ENTERWITH";
+ case LEAVEWITH: return "LEAVEWITH";
+ case RETURN: return "RETURN";
+ case GOTO: return "GOTO";
+ case IFEQ: return "IFEQ";
+ case IFNE: return "IFNE";
+ case SETNAME: return "SETNAME";
+ case BITOR: return "BITOR";
+ case BITXOR: return "BITXOR";
+ case BITAND: return "BITAND";
+ case EQ: return "EQ";
+ case NE: return "NE";
+ case LT: return "LT";
+ case LE: return "LE";
+ case GT: return "GT";
+ case GE: return "GE";
+ case LSH: return "LSH";
+ case RSH: return "RSH";
+ case URSH: return "URSH";
+ case ADD: return "ADD";
+ case SUB: return "SUB";
+ case MUL: return "MUL";
+ case DIV: return "DIV";
+ case MOD: return "MOD";
+ case NOT: return "NOT";
+ case BITNOT: return "BITNOT";
+ case POS: return "POS";
+ case NEG: return "NEG";
+ case NEW: return "NEW";
+ case DELPROP: return "DELPROP";
+ case TYPEOF: return "TYPEOF";
+ case GETPROP: return "GETPROP";
+ case SETPROP: return "SETPROP";
+ case GETELEM: return "GETELEM";
+ case SETELEM: return "SETELEM";
+ case CALL: return "CALL";
+ case NAME: return "NAME";
+ case NUMBER: return "NUMBER";
+ case STRING: return "STRING";
+ case NULL: return "NULL";
+ case THIS: return "THIS";
+ case FALSE: return "FALSE";
+ case TRUE: return "TRUE";
+ case SHEQ: return "SHEQ";
+ case SHNE: return "SHNE";
+ case REGEXP: return "OBJECT";
+ case BINDNAME: return "BINDNAME";
+ case THROW: return "THROW";
+ case RETHROW: return "RETHROW";
+ case IN: return "IN";
+ case INSTANCEOF: return "INSTANCEOF";
+ case LOCAL_LOAD: return "LOCAL_LOAD";
+ case GETVAR: return "GETVAR";
+ case SETVAR: return "SETVAR";
+ case CATCH_SCOPE: return "CATCH_SCOPE";
+ case ENUM_INIT_KEYS: return "ENUM_INIT_KEYS";
+ case ENUM_INIT_VALUES: return "ENUM_INIT_VALUES";
+ case ENUM_NEXT: return "ENUM_NEXT";
+ case ENUM_ID: return "ENUM_ID";
+ case THISFN: return "THISFN";
+ case RETURN_RESULT: return "RETURN_RESULT";
+ case ARRAYLIT: return "ARRAYLIT";
+ case OBJECTLIT: return "OBJECTLIT";
+ case GET_REF: return "GET_REF";
+ case SET_REF: return "SET_REF";
+ case DEL_REF: return "DEL_REF";
+ case REF_CALL: return "REF_CALL";
+ case REF_SPECIAL: return "REF_SPECIAL";
+ case DEFAULTNAMESPACE:return "DEFAULTNAMESPACE";
+ case ESCXMLTEXT: return "ESCXMLTEXT";
+ case ESCXMLATTR: return "ESCXMLATTR";
+ case REF_MEMBER: return "REF_MEMBER";
+ case REF_NS_MEMBER: return "REF_NS_MEMBER";
+ case REF_NAME: return "REF_NAME";
+ case REF_NS_NAME: return "REF_NS_NAME";
+ case TRY: return "TRY";
+ case SEMI: return "SEMI";
+ case LB: return "LB";
+ case RB: return "RB";
+ case LC: return "LC";
+ case RC: return "RC";
+ case LP: return "LP";
+ case RP: return "RP";
+ case COMMA: return "COMMA";
+ case ASSIGN: return "ASSIGN";
+ case ASSIGN_BITOR: return "ASSIGN_BITOR";
+ case ASSIGN_BITXOR: return "ASSIGN_BITXOR";
+ case ASSIGN_BITAND: return "ASSIGN_BITAND";
+ case ASSIGN_LSH: return "ASSIGN_LSH";
+ case ASSIGN_RSH: return "ASSIGN_RSH";
+ case ASSIGN_URSH: return "ASSIGN_URSH";
+ case ASSIGN_ADD: return "ASSIGN_ADD";
+ case ASSIGN_SUB: return "ASSIGN_SUB";
+ case ASSIGN_MUL: return "ASSIGN_MUL";
+ case ASSIGN_DIV: return "ASSIGN_DIV";
+ case ASSIGN_MOD: return "ASSIGN_MOD";
+ case HOOK: return "HOOK";
+ case COLON: return "COLON";
+ case OR: return "OR";
+ case AND: return "AND";
+ case INC: return "INC";
+ case DEC: return "DEC";
+ case DOT: return "DOT";
+ case FUNCTION: return "FUNCTION";
+ case EXPORT: return "EXPORT";
+ case IMPORT: return "IMPORT";
+ case IF: return "IF";
+ case ELSE: return "ELSE";
+ case SWITCH: return "SWITCH";
+ case CASE: return "CASE";
+ case DEFAULT: return "DEFAULT";
+ case WHILE: return "WHILE";
+ case DO: return "DO";
+ case FOR: return "FOR";
+ case BREAK: return "BREAK";
+ case CONTINUE: return "CONTINUE";
+ case VAR: return "VAR";
+ case WITH: return "WITH";
+ case CATCH: return "CATCH";
+ case FINALLY: return "FINALLY";
+ case RESERVED: return "RESERVED";
+ case EMPTY: return "EMPTY";
+ case BLOCK: return "BLOCK";
+ case LABEL: return "LABEL";
+ case TARGET: return "TARGET";
+ case LOOP: return "LOOP";
+ case EXPR_VOID: return "EXPR_VOID";
+ case EXPR_RESULT: return "EXPR_RESULT";
+ case JSR: return "JSR";
+ case SCRIPT: return "SCRIPT";
+ case TYPEOFNAME: return "TYPEOFNAME";
+ case USE_STACK: return "USE_STACK";
+ case SETPROP_OP: return "SETPROP_OP";
+ case SETELEM_OP: return "SETELEM_OP";
+ case LOCAL_BLOCK: return "LOCAL_BLOCK";
+ case SET_REF_OP: return "SET_REF_OP";
+ case DOTDOT: return "DOTDOT";
+ case COLONCOLON: return "COLONCOLON";
+ case XML: return "XML";
+ case DOTQUERY: return "DOTQUERY";
+ case XMLATTR: return "XMLATTR";
+ case XMLEND: return "XMLEND";
+ case TO_OBJECT: return "TO_OBJECT";
+ case TO_DOUBLE: return "TO_DOUBLE";
+ case GET: return "GET";
+ case SET: return "SET";
+ case CONST: return "CONST";
+ case SETCONST: return "SETCONST";
+ }
+
+ // Token without name
+ throw new IllegalStateException(String.valueOf(token));
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/Token.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/TokenStream.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/TokenStream.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/TokenStream.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,1359 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ * Mike McCabe
+ * Igor Bukanov
+ * Ethan Hugg
+ * Bob Jervis
+ * Terry Lucas
+ * Milen Nankov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.*;
+
+/**
+ * This class implements the JavaScript scanner.
+ *
+ * It is based on the C source files jsscan.c and jsscan.h
+ * in the jsref package.
+ *
+ * @see org.mozilla.javascript.Parser
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+class TokenStream
+{
+ /*
+ * For chars - because we need something out-of-range
+ * to check. (And checking EOF by exception is annoying.)
+ * Note distinction from EOF token type!
+ */
+ private final static int
+ EOF_CHAR = -1;
+
+ TokenStream(Parser parser, Reader sourceReader, String sourceString,
+ int lineno)
+ {
+ this.parser = parser;
+ this.lineno = lineno;
+ if (sourceReader != null) {
+ if (sourceString != null) Kit.codeBug();
+ this.sourceReader = sourceReader;
+ this.sourceBuffer = new char[512];
+ this.sourceEnd = 0;
+ } else {
+ if (sourceString == null) Kit.codeBug();
+ this.sourceString = sourceString;
+ this.sourceEnd = sourceString.length();
+ }
+ this.sourceCursor = 0;
+ }
+
+ /* This function uses the cached op, string and number fields in
+ * TokenStream; if getToken has been called since the passed token
+ * was scanned, the op or string printed may be incorrect.
+ */
+ String tokenToString(int token)
+ {
+ if (Token.printTrees) {
+ String name = Token.name(token);
+
+ switch (token) {
+ case Token.STRING:
+ case Token.REGEXP:
+ case Token.NAME:
+ return name + " `" + this.string + "'";
+
+ case Token.NUMBER:
+ return "NUMBER " + this.number;
+ }
+
+ return name;
+ }
+ return "";
+ }
+
+ static boolean isKeyword(String s)
+ {
+ return Token.EOF != stringToKeyword(s);
+ }
+
+ private static int stringToKeyword(String name)
+ {
+// #string_id_map#
+// The following assumes that Token.EOF == 0
+ final int
+ Id_break = Token.BREAK,
+ Id_case = Token.CASE,
+ Id_continue = Token.CONTINUE,
+ Id_default = Token.DEFAULT,
+ Id_delete = Token.DELPROP,
+ Id_do = Token.DO,
+ Id_else = Token.ELSE,
+ Id_export = Token.EXPORT,
+ Id_false = Token.FALSE,
+ Id_for = Token.FOR,
+ Id_function = Token.FUNCTION,
+ Id_if = Token.IF,
+ Id_in = Token.IN,
+ Id_new = Token.NEW,
+ Id_null = Token.NULL,
+ Id_return = Token.RETURN,
+ Id_switch = Token.SWITCH,
+ Id_this = Token.THIS,
+ Id_true = Token.TRUE,
+ Id_typeof = Token.TYPEOF,
+ Id_var = Token.VAR,
+ Id_void = Token.VOID,
+ Id_while = Token.WHILE,
+ Id_with = Token.WITH,
+
+ // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
+ Id_abstract = Token.RESERVED,
+ Id_boolean = Token.RESERVED,
+ Id_byte = Token.RESERVED,
+ Id_catch = Token.CATCH,
+ Id_char = Token.RESERVED,
+ Id_class = Token.RESERVED,
+ Id_const = Token.CONST,
+ Id_debugger = Token.RESERVED,
+ Id_double = Token.RESERVED,
+ Id_enum = Token.RESERVED,
+ Id_extends = Token.RESERVED,
+ Id_final = Token.RESERVED,
+ Id_finally = Token.FINALLY,
+ Id_float = Token.RESERVED,
+ Id_goto = Token.RESERVED,
+ Id_implements = Token.RESERVED,
+ Id_import = Token.IMPORT,
+ Id_instanceof = Token.INSTANCEOF,
+ Id_int = Token.RESERVED,
+ Id_interface = Token.RESERVED,
+ Id_long = Token.RESERVED,
+ Id_native = Token.RESERVED,
+ Id_package = Token.RESERVED,
+ Id_private = Token.RESERVED,
+ Id_protected = Token.RESERVED,
+ Id_public = Token.RESERVED,
+ Id_short = Token.RESERVED,
+ Id_static = Token.RESERVED,
+ Id_super = Token.RESERVED,
+ Id_synchronized = Token.RESERVED,
+ Id_throw = Token.THROW,
+ Id_throws = Token.RESERVED,
+ Id_transient = Token.RESERVED,
+ Id_try = Token.TRY,
+ Id_volatile = Token.RESERVED;
+
+ int id;
+ String s = name;
+// #generated# Last update: 2001-06-01 17:45:01 CEST
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 2: c=s.charAt(1);
+ if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} }
+ else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} }
+ else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} }
+ break L;
+ case 3: switch (s.charAt(0)) {
+ case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L;
+ case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L;
+ case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L;
+ case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L;
+ case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L;
+ } break L;
+ case 4: switch (s.charAt(0)) {
+ case 'b': X="byte";id=Id_byte; break L;
+ case 'c': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} }
+ else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') {id=Id_char; break L0;} }
+ break L;
+ case 'e': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} }
+ else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} }
+ break L;
+ case 'g': X="goto";id=Id_goto; break L;
+ case 'l': X="long";id=Id_long; break L;
+ case 'n': X="null";id=Id_null; break L;
+ case 't': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} }
+ else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} }
+ break L;
+ case 'v': X="void";id=Id_void; break L;
+ case 'w': X="with";id=Id_with; break L;
+ } break L;
+ case 5: switch (s.charAt(2)) {
+ case 'a': X="class";id=Id_class; break L;
+ case 'e': X="break";id=Id_break; break L;
+ case 'i': X="while";id=Id_while; break L;
+ case 'l': X="false";id=Id_false; break L;
+ case 'n': c=s.charAt(0);
+ if (c=='c') { X="const";id=Id_const; }
+ else if (c=='f') { X="final";id=Id_final; }
+ break L;
+ case 'o': c=s.charAt(0);
+ if (c=='f') { X="float";id=Id_float; }
+ else if (c=='s') { X="short";id=Id_short; }
+ break L;
+ case 'p': X="super";id=Id_super; break L;
+ case 'r': X="throw";id=Id_throw; break L;
+ case 't': X="catch";id=Id_catch; break L;
+ } break L;
+ case 6: switch (s.charAt(1)) {
+ case 'a': X="native";id=Id_native; break L;
+ case 'e': c=s.charAt(0);
+ if (c=='d') { X="delete";id=Id_delete; }
+ else if (c=='r') { X="return";id=Id_return; }
+ break L;
+ case 'h': X="throws";id=Id_throws; break L;
+ case 'm': X="import";id=Id_import; break L;
+ case 'o': X="double";id=Id_double; break L;
+ case 't': X="static";id=Id_static; break L;
+ case 'u': X="public";id=Id_public; break L;
+ case 'w': X="switch";id=Id_switch; break L;
+ case 'x': X="export";id=Id_export; break L;
+ case 'y': X="typeof";id=Id_typeof; break L;
+ } break L;
+ case 7: switch (s.charAt(1)) {
+ case 'a': X="package";id=Id_package; break L;
+ case 'e': X="default";id=Id_default; break L;
+ case 'i': X="finally";id=Id_finally; break L;
+ case 'o': X="boolean";id=Id_boolean; break L;
+ case 'r': X="private";id=Id_private; break L;
+ case 'x': X="extends";id=Id_extends; break L;
+ } break L;
+ case 8: switch (s.charAt(0)) {
+ case 'a': X="abstract";id=Id_abstract; break L;
+ case 'c': X="continue";id=Id_continue; break L;
+ case 'd': X="debugger";id=Id_debugger; break L;
+ case 'f': X="function";id=Id_function; break L;
+ case 'v': X="volatile";id=Id_volatile; break L;
+ } break L;
+ case 9: c=s.charAt(0);
+ if (c=='i') { X="interface";id=Id_interface; }
+ else if (c=='p') { X="protected";id=Id_protected; }
+ else if (c=='t') { X="transient";id=Id_transient; }
+ break L;
+ case 10: c=s.charAt(1);
+ if (c=='m') { X="implements";id=Id_implements; }
+ else if (c=='n') { X="instanceof";id=Id_instanceof; }
+ break L;
+ case 12: X="synchronized";id=Id_synchronized; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ }
+// #/generated#
+// #/string_id_map#
+ if (id == 0) { return Token.EOF; }
+ return id & 0xff;
+ }
+
+ final int getLineno() { return lineno; }
+
+ final String getString() { return string; }
+
+ final double getNumber() { return number; }
+
+ final boolean eof() { return hitEOF; }
+
+ final int getToken() throws IOException
+ {
+ int c;
+
+ retry:
+ for (;;) {
+ // Eat whitespace, possibly sensitive to newlines.
+ for (;;) {
+ c = getChar();
+ if (c == EOF_CHAR) {
+ return Token.EOF;
+ } else if (c == '\n') {
+ dirtyLine = false;
+ return Token.EOL;
+ } else if (!isJSSpace(c)) {
+ if (c != '-') {
+ dirtyLine = true;
+ }
+ break;
+ }
+ }
+
+ if (c == '@') return Token.XMLATTR;
+
+ // identifier/keyword/instanceof?
+ // watch out for starting with a <backslash>
+ boolean identifierStart;
+ boolean isUnicodeEscapeStart = false;
+ if (c == '\\') {
+ c = getChar();
+ if (c == 'u') {
+ identifierStart = true;
+ isUnicodeEscapeStart = true;
+ stringBufferTop = 0;
+ } else {
+ identifierStart = false;
+ ungetChar(c);
+ c = '\\';
+ }
+ } else {
+ identifierStart = Character.isJavaIdentifierStart((char)c);
+ if (identifierStart) {
+ stringBufferTop = 0;
+ addToString(c);
+ }
+ }
+
+ if (identifierStart) {
+ boolean containsEscape = isUnicodeEscapeStart;
+ for (;;) {
+ if (isUnicodeEscapeStart) {
+ // strictly speaking we should probably push-back
+ // all the bad characters if the <backslash>uXXXX
+ // sequence is malformed. But since there isn't a
+ // correct context(is there?) for a bad Unicode
+ // escape sequence in an identifier, we can report
+ // an error here.
+ int escapeVal = 0;
+ for (int i = 0; i != 4; ++i) {
+ c = getChar();
+ escapeVal = Kit.xDigitToInt(c, escapeVal);
+ // Next check takes care about c < 0 and bad escape
+ if (escapeVal < 0) { break; }
+ }
+ if (escapeVal < 0) {
+ parser.addError("msg.invalid.escape");
+ return Token.ERROR;
+ }
+ addToString(escapeVal);
+ isUnicodeEscapeStart = false;
+ } else {
+ c = getChar();
+ if (c == '\\') {
+ c = getChar();
+ if (c == 'u') {
+ isUnicodeEscapeStart = true;
+ containsEscape = true;
+ } else {
+ parser.addError("msg.illegal.character");
+ return Token.ERROR;
+ }
+ } else {
+ if (c == EOF_CHAR
+ || !Character.isJavaIdentifierPart((char)c))
+ {
+ break;
+ }
+ addToString(c);
+ }
+ }
+ }
+ ungetChar(c);
+
+ String str = getStringFromBuffer();
+ if (!containsEscape) {
+ // OPT we shouldn't have to make a string (object!) to
+ // check if it's a keyword.
+
+ // Return the corresponding token if it's a keyword
+ int result = stringToKeyword(str);
+ if (result != Token.EOF) {
+ if (result != Token.RESERVED) {
+ return result;
+ } else if (!parser.compilerEnv.
+ isReservedKeywordAsIdentifier())
+ {
+ return result;
+ } else {
+ // If implementation permits to use future reserved
+ // keywords in violation with the EcmaScript,
+ // treat it as name but issue warning
+ parser.addWarning("msg.reserved.keyword", str);
+ }
+ }
+ }
+ this.string = (String)allStrings.intern(str);
+ return Token.NAME;
+ }
+
+ // is it a number?
+ if (isDigit(c) || (c == '.' && isDigit(peekChar()))) {
+
+ stringBufferTop = 0;
+ int base = 10;
+
+ if (c == '0') {
+ c = getChar();
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = getChar();
+ } else if (isDigit(c)) {
+ base = 8;
+ } else {
+ addToString('0');
+ }
+ }
+
+ if (base == 16) {
+ while (0 <= Kit.xDigitToInt(c, 0)) {
+ addToString(c);
+ c = getChar();
+ }
+ } else {
+ while ('0' <= c && c <= '9') {
+ /*
+ * We permit 08 and 09 as decimal numbers, which
+ * makes our behavior a superset of the ECMA
+ * numeric grammar. We might not always be so
+ * permissive, so we warn about it.
+ */
+ if (base == 8 && c >= '8') {
+ parser.addWarning("msg.bad.octal.literal",
+ c == '8' ? "8" : "9");
+ base = 10;
+ }
+ addToString(c);
+ c = getChar();
+ }
+ }
+
+ boolean isInteger = true;
+
+ if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
+ isInteger = false;
+ if (c == '.') {
+ do {
+ addToString(c);
+ c = getChar();
+ } while (isDigit(c));
+ }
+ if (c == 'e' || c == 'E') {
+ addToString(c);
+ c = getChar();
+ if (c == '+' || c == '-') {
+ addToString(c);
+ c = getChar();
+ }
+ if (!isDigit(c)) {
+ parser.addError("msg.missing.exponent");
+ return Token.ERROR;
+ }
+ do {
+ addToString(c);
+ c = getChar();
+ } while (isDigit(c));
+ }
+ }
+ ungetChar(c);
+ String numString = getStringFromBuffer();
+
+ double dval;
+ if (base == 10 && !isInteger) {
+ try {
+ // Use Java conversion to number from string...
+ dval = Double.valueOf(numString).doubleValue();
+ }
+ catch (NumberFormatException ex) {
+ parser.addError("msg.caught.nfe");
+ return Token.ERROR;
+ }
+ } else {
+ dval = ScriptRuntime.stringToNumber(numString, 0, base);
+ }
+
+ this.number = dval;
+ return Token.NUMBER;
+ }
+
+ // is it a string?
+ if (c == '"' || c == '\'') {
+ // We attempt to accumulate a string the fast way, by
+ // building it directly out of the reader. But if there
+ // are any escaped characters in the string, we revert to
+ // building it out of a StringBuffer.
+
+ int quoteChar = c;
+ stringBufferTop = 0;
+
+ c = getChar();
+ while (c != quoteChar) {
+ if (c == '\n' || c == EOF_CHAR) {
+ ungetChar(c);
+ parser.addError("msg.unterminated.string.lit");
+ return Token.ERROR;
+ }
+
+ if (c == '\\') {
+ // We've hit an escaped character
+
+ c = getChar();
+
+ switch (c) {
+
+ case '\\': // backslash
+ case 'b': // backspace
+ case 'f': // form feed
+ case 'n': // line feed
+ case 'r': // carriage return
+ case 't': // horizontal tab
+ case 'v': // vertical tab
+ case 'd': // octal sequence
+ case 'u': // unicode sequence
+ case 'x': // hexadecimal sequence
+
+ // Only keep the '\' character for those
+ // characters that need to be escaped...
+ // Don't escape quoting characters...
+ addToString('\\');
+ break;
+
+ case '\n':
+ // Remove line terminator after escape
+ c = getChar();
+ break;
+ }
+ }
+
+ addToString(c);
+ c = getChar();
+ }
+
+ String str = getStringFromBuffer();
+ this.string = (String)allStrings.intern(str);
+ return Token.STRING;
+ }
+
+ switch (c) {
+ case ';': return Token.SEMI;
+ case '[': return Token.LB;
+ case ']': return Token.RB;
+ case '{': return Token.LC;
+ case '}': return Token.RC;
+ case '(': return Token.LP;
+ case ')': return Token.RP;
+ case ',': return Token.COMMA;
+ case '?': return Token.HOOK;
+ case ':':
+ if (matchChar(':')) {
+ return Token.COLONCOLON;
+ } else {
+ return Token.COLON;
+ }
+ case '.':
+ if (matchChar('.')) {
+ return Token.DOTDOT;
+ } else if (matchChar('(')) {
+ return Token.DOTQUERY;
+ } else {
+ return Token.DOT;
+ }
+
+ case '|':
+ if (matchChar('|')) {
+ return Token.OR;
+ } else if (matchChar('=')) {
+ return Token.ASSIGN_BITOR;
+ } else {
+ return Token.BITOR;
+ }
+
+ case '^':
+ if (matchChar('=')) {
+ return Token.ASSIGN_BITXOR;
+ } else {
+ return Token.BITXOR;
+ }
+
+ case '&':
+ if (matchChar('&')) {
+ return Token.AND;
+ } else if (matchChar('=')) {
+ return Token.ASSIGN_BITAND;
+ } else {
+ return Token.BITAND;
+ }
+
+ case '=':
+ if (matchChar('=')) {
+ if (matchChar('='))
+ return Token.SHEQ;
+ else
+ return Token.EQ;
+ } else {
+ return Token.ASSIGN;
+ }
+
+ case '!':
+ if (matchChar('=')) {
+ if (matchChar('='))
+ return Token.SHNE;
+ else
+ return Token.NE;
+ } else {
+ return Token.NOT;
+ }
+
+ case '<':
+ /* NB:treat HTML begin-comment as comment-till-eol */
+ if (matchChar('!')) {
+ if (matchChar('-')) {
+ if (matchChar('-')) {
+ skipLine();
+ continue retry;
+ }
+ ungetChar('-');
+ }
+ ungetChar('!');
+ }
+ if (matchChar('<')) {
+ if (matchChar('=')) {
+ return Token.ASSIGN_LSH;
+ } else {
+ return Token.LSH;
+ }
+ } else {
+ if (matchChar('=')) {
+ return Token.LE;
+ } else {
+ return Token.LT;
+ }
+ }
+
+ case '>':
+ if (matchChar('>')) {
+ if (matchChar('>')) {
+ if (matchChar('=')) {
+ return Token.ASSIGN_URSH;
+ } else {
+ return Token.URSH;
+ }
+ } else {
+ if (matchChar('=')) {
+ return Token.ASSIGN_RSH;
+ } else {
+ return Token.RSH;
+ }
+ }
+ } else {
+ if (matchChar('=')) {
+ return Token.GE;
+ } else {
+ return Token.GT;
+ }
+ }
+
+ case '*':
+ if (matchChar('=')) {
+ return Token.ASSIGN_MUL;
+ } else {
+ return Token.MUL;
+ }
+
+ case '/':
+ // is it a // comment?
+ if (matchChar('/')) {
+ skipLine();
+ continue retry;
+ }
+ if (matchChar('*')) {
+ boolean lookForSlash = false;
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ c = getChar();
+ if (c == EOF_CHAR) {
+ parser.addError("msg.unterminated.comment");
+ return Token.ERROR;
+ }
+ sb.append((char) c);
+ if (c == '*') {
+ lookForSlash = true;
+ } else if (c == '/') {
+ if (lookForSlash) {
+ sb.delete(sb.length()-2, sb.length());
+ String s = sb.toString();
+ if (s.startsWith("@cc_on") ||
+ s.startsWith("@if") ||
+ s.startsWith("@elif") ||
+ s.startsWith("@else") ||
+ s.startsWith("@end")) {
+ this.string = s;
+ return Token.IECC;
+ } else {
+ continue retry;
+ }
+ }
+ } else {
+ lookForSlash = false;
+ }
+ }
+ }
+
+ if (matchChar('=')) {
+ return Token.ASSIGN_DIV;
+ } else {
+ return Token.DIV;
+ }
+
+ case '%':
+ if (matchChar('=')) {
+ return Token.ASSIGN_MOD;
+ } else {
+ return Token.MOD;
+ }
+
+ case '~':
+ return Token.BITNOT;
+
+ case '+':
+ if (matchChar('=')) {
+ return Token.ASSIGN_ADD;
+ } else if (matchChar('+')) {
+ return Token.INC;
+ } else {
+ return Token.ADD;
+ }
+
+ case '-':
+ if (matchChar('=')) {
+ c = Token.ASSIGN_SUB;
+ } else if (matchChar('-')) {
+ if (!dirtyLine) {
+ // treat HTML end-comment after possible whitespace
+ // after line start as comment-utill-eol
+ if (matchChar('>')) {
+ skipLine();
+ continue retry;
+ }
+ }
+ c = Token.DEC;
+ } else {
+ c = Token.SUB;
+ }
+ dirtyLine = true;
+ return c;
+
+ default:
+ parser.addError("msg.illegal.character");
+ return Token.ERROR;
+ }
+ }
+ }
+
+ private static boolean isAlpha(int c)
+ {
+ // Use 'Z' < 'a'
+ if (c <= 'Z') {
+ return 'A' <= c;
+ } else {
+ return 'a' <= c && c <= 'z';
+ }
+ }
+
+ static boolean isDigit(int c)
+ {
+ return '0' <= c && c <= '9';
+ }
+
+ /* As defined in ECMA. jsscan.c uses C isspace() (which allows
+ * \v, I think.) note that code in getChar() implicitly accepts
+ * '\r' == \u000D as well.
+ */
+ static boolean isJSSpace(int c)
+ {
+ if (c <= 127) {
+ return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB;
+ } else {
+ return c == 0xA0
+ || Character.getType((char)c) == Character.SPACE_SEPARATOR;
+ }
+ }
+
+ private static boolean isJSFormatChar(int c)
+ {
+ return c > 127 && Character.getType((char)c) == Character.FORMAT;
+ }
+
+ /**
+ * Parser calls the method when it gets / or /= in literal context.
+ */
+ void readRegExp(int startToken)
+ throws IOException
+ {
+ stringBufferTop = 0;
+ if (startToken == Token.ASSIGN_DIV) {
+ // Miss-scanned /=
+ addToString('=');
+ } else {
+ if (startToken != Token.DIV) Kit.codeBug();
+ }
+
+ int c;
+ while ((c = getChar()) != '/') {
+ if (c == '\n' || c == EOF_CHAR) {
+ ungetChar(c);
+ throw parser.reportError("msg.unterminated.re.lit");
+ }
+ if (c == '\\') {
+ addToString(c);
+ c = getChar();
+ }
+
+ addToString(c);
+ }
+ int reEnd = stringBufferTop;
+
+ while (true) {
+ if (matchChar('g'))
+ addToString('g');
+ else if (matchChar('i'))
+ addToString('i');
+ else if (matchChar('m'))
+ addToString('m');
+ else
+ break;
+ }
+
+ if (isAlpha(peekChar())) {
+ throw parser.reportError("msg.invalid.re.flag");
+ }
+
+ this.string = new String(stringBuffer, 0, reEnd);
+ this.regExpFlags = new String(stringBuffer, reEnd,
+ stringBufferTop - reEnd);
+ }
+
+ boolean isXMLAttribute()
+ {
+ return xmlIsAttribute;
+ }
+
+ int getFirstXMLToken() throws IOException
+ {
+ xmlOpenTagsCount = 0;
+ xmlIsAttribute = false;
+ xmlIsTagContent = false;
+ ungetChar('<');
+ return getNextXMLToken();
+ }
+
+ int getNextXMLToken() throws IOException
+ {
+ stringBufferTop = 0; // remember the XML
+
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ if (xmlIsTagContent) {
+ switch (c) {
+ case '>':
+ addToString(c);
+ xmlIsTagContent = false;
+ xmlIsAttribute = false;
+ break;
+ case '/':
+ addToString(c);
+ if (peekChar() == '>') {
+ c = getChar();
+ addToString(c);
+ xmlIsTagContent = false;
+ xmlOpenTagsCount--;
+ }
+ break;
+ case '{':
+ ungetChar(c);
+ this.string = getStringFromBuffer();
+ return Token.XML;
+ case '\'':
+ case '"':
+ addToString(c);
+ if (!readQuotedString(c)) return Token.ERROR;
+ break;
+ case '=':
+ addToString(c);
+ xmlIsAttribute = true;
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ addToString(c);
+ break;
+ default:
+ addToString(c);
+ xmlIsAttribute = false;
+ break;
+ }
+
+ if (!xmlIsTagContent && xmlOpenTagsCount == 0) {
+ this.string = getStringFromBuffer();
+ return Token.XMLEND;
+ }
+ } else {
+ switch (c) {
+ case '<':
+ addToString(c);
+ c = peekChar();
+ switch (c) {
+ case '!':
+ c = getChar(); // Skip !
+ addToString(c);
+ c = peekChar();
+ switch (c) {
+ case '-':
+ c = getChar(); // Skip -
+ addToString(c);
+ c = getChar();
+ if (c == '-') {
+ addToString(c);
+ if(!readXmlComment()) return Token.ERROR;
+ } else {
+ // throw away the string in progress
+ stringBufferTop = 0;
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+ break;
+ case '[':
+ c = getChar(); // Skip [
+ addToString(c);
+ if (getChar() == 'C' &&
+ getChar() == 'D' &&
+ getChar() == 'A' &&
+ getChar() == 'T' &&
+ getChar() == 'A' &&
+ getChar() == '[')
+ {
+ addToString('C');
+ addToString('D');
+ addToString('A');
+ addToString('T');
+ addToString('A');
+ addToString('[');
+ if (!readCDATA()) return Token.ERROR;
+
+ } else {
+ // throw away the string in progress
+ stringBufferTop = 0;
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+ break;
+ default:
+ if(!readEntity()) return Token.ERROR;
+ break;
+ }
+ break;
+ case '?':
+ c = getChar(); // Skip ?
+ addToString(c);
+ if (!readPI()) return Token.ERROR;
+ break;
+ case '/':
+ // End tag
+ c = getChar(); // Skip /
+ addToString(c);
+ if (xmlOpenTagsCount == 0) {
+ // throw away the string in progress
+ stringBufferTop = 0;
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+ xmlIsTagContent = true;
+ xmlOpenTagsCount--;
+ break;
+ default:
+ // Start tag
+ xmlIsTagContent = true;
+ xmlOpenTagsCount++;
+ break;
+ }
+ break;
+ case '{':
+ ungetChar(c);
+ this.string = getStringFromBuffer();
+ return Token.XML;
+ default:
+ addToString(c);
+ break;
+ }
+ }
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+
+ /**
+ *
+ */
+ private boolean readQuotedString(int quote) throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ addToString(c);
+ if (c == quote) return true;
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readXmlComment() throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR;) {
+ addToString(c);
+ if (c == '-' && peekChar() == '-') {
+ c = getChar();
+ addToString(c);
+ if (peekChar() == '>') {
+ c = getChar(); // Skip >
+ addToString(c);
+ return true;
+ } else {
+ continue;
+ }
+ }
+ c = getChar();
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readCDATA() throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR;) {
+ addToString(c);
+ if (c == ']' && peekChar() == ']') {
+ c = getChar();
+ addToString(c);
+ if (peekChar() == '>') {
+ c = getChar(); // Skip >
+ addToString(c);
+ return true;
+ } else {
+ continue;
+ }
+ }
+ c = getChar();
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readEntity() throws IOException
+ {
+ int declTags = 1;
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ addToString(c);
+ switch (c) {
+ case '<':
+ declTags++;
+ break;
+ case '>':
+ declTags--;
+ if (declTags == 0) return true;
+ break;
+ }
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readPI() throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ addToString(c);
+ if (c == '?' && peekChar() == '>') {
+ c = getChar(); // Skip >
+ addToString(c);
+ return true;
+ }
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ private String getStringFromBuffer()
+ {
+ return new String(stringBuffer, 0, stringBufferTop);
+ }
+
+ private void addToString(int c)
+ {
+ int N = stringBufferTop;
+ if (N == stringBuffer.length) {
+ char[] tmp = new char[stringBuffer.length * 2];
+ System.arraycopy(stringBuffer, 0, tmp, 0, N);
+ stringBuffer = tmp;
+ }
+ stringBuffer[N] = (char)c;
+ stringBufferTop = N + 1;
+ }
+
+ private void ungetChar(int c)
+ {
+ // can not unread past across line boundary
+ if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n')
+ Kit.codeBug();
+ ungetBuffer[ungetCursor++] = c;
+ }
+
+ private boolean matchChar(int test) throws IOException
+ {
+ int c = getChar();
+ if (c == test) {
+ return true;
+ } else {
+ ungetChar(c);
+ return false;
+ }
+ }
+
+ private int peekChar() throws IOException
+ {
+ int c = getChar();
+ ungetChar(c);
+ return c;
+ }
+
+ private int getChar() throws IOException
+ {
+ if (ungetCursor != 0) {
+ return ungetBuffer[--ungetCursor];
+ }
+
+ for(;;) {
+ int c;
+ if (sourceString != null) {
+ if (sourceCursor == sourceEnd) {
+ hitEOF = true;
+ return EOF_CHAR;
+ }
+ c = sourceString.charAt(sourceCursor++);
+ } else {
+ if (sourceCursor == sourceEnd) {
+ if (!fillSourceBuffer()) {
+ hitEOF = true;
+ return EOF_CHAR;
+ }
+ }
+ c = sourceBuffer[sourceCursor++];
+ }
+
+ if (lineEndChar >= 0) {
+ if (lineEndChar == '\r' && c == '\n') {
+ lineEndChar = '\n';
+ continue;
+ }
+ lineEndChar = -1;
+ lineStart = sourceCursor - 1;
+ lineno++;
+ }
+
+ if (c <= 127) {
+ if (c == '\n' || c == '\r') {
+ lineEndChar = c;
+ c = '\n';
+ }
+ } else {
+ if (isJSFormatChar(c)) {
+ continue;
+ }
+ if (ScriptRuntime.isJSLineTerminator(c)) {
+ lineEndChar = c;
+ c = '\n';
+ }
+ }
+ return c;
+ }
+ }
+
+ private void skipLine() throws IOException
+ {
+ // skip to end of line
+ int c;
+ while ((c = getChar()) != EOF_CHAR && c != '\n') { }
+ ungetChar(c);
+ }
+
+ final int getOffset()
+ {
+ int n = sourceCursor - lineStart;
+ if (lineEndChar >= 0) { --n; }
+ return n;
+ }
+
+ final String getLine()
+ {
+ if (sourceString != null) {
+ // String case
+ int lineEnd = sourceCursor;
+ if (lineEndChar >= 0) {
+ --lineEnd;
+ } else {
+ for (; lineEnd != sourceEnd; ++lineEnd) {
+ int c = sourceString.charAt(lineEnd);
+ if (ScriptRuntime.isJSLineTerminator(c)) {
+ break;
+ }
+ }
+ }
+ return sourceString.substring(lineStart, lineEnd);
+ } else {
+ // Reader case
+ int lineLength = sourceCursor - lineStart;
+ if (lineEndChar >= 0) {
+ --lineLength;
+ } else {
+ // Read until the end of line
+ for (;; ++lineLength) {
+ int i = lineStart + lineLength;
+ if (i == sourceEnd) {
+ try {
+ if (!fillSourceBuffer()) { break; }
+ } catch (IOException ioe) {
+ // ignore it, we're already displaying an error...
+ break;
+ }
+ // i recalculuation as fillSourceBuffer can move saved
+ // line buffer and change lineStart
+ i = lineStart + lineLength;
+ }
+ int c = sourceBuffer[i];
+ if (ScriptRuntime.isJSLineTerminator(c)) {
+ break;
+ }
+ }
+ }
+ return new String(sourceBuffer, lineStart, lineLength);
+ }
+ }
+
+ private boolean fillSourceBuffer() throws IOException
+ {
+ if (sourceString != null) Kit.codeBug();
+ if (sourceEnd == sourceBuffer.length) {
+ if (lineStart != 0) {
+ System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0,
+ sourceEnd - lineStart);
+ sourceEnd -= lineStart;
+ sourceCursor -= lineStart;
+ lineStart = 0;
+ } else {
+ char[] tmp = new char[sourceBuffer.length * 2];
+ System.arraycopy(sourceBuffer, 0, tmp, 0, sourceEnd);
+ sourceBuffer = tmp;
+ }
+ }
+ int n = sourceReader.read(sourceBuffer, sourceEnd,
+ sourceBuffer.length - sourceEnd);
+ if (n < 0) {
+ return false;
+ }
+ sourceEnd += n;
+ return true;
+ }
+
+ // stuff other than whitespace since start of line
+ private boolean dirtyLine;
+
+ String regExpFlags;
+
+ // Set this to an inital non-null value so that the Parser has
+ // something to retrieve even if an error has occured and no
+ // string is found. Fosters one class of error, but saves lots of
+ // code.
+ private String string = "";
+ private double number;
+
+ private char[] stringBuffer = new char[128];
+ private int stringBufferTop;
+ private ObjToIntMap allStrings = new ObjToIntMap(50);
+
+ // Room to backtrace from to < on failed match of the last - in <!--
+ private final int[] ungetBuffer = new int[3];
+ private int ungetCursor;
+
+ private boolean hitEOF = false;
+
+ private int lineStart = 0;
+ private int lineno;
+ private int lineEndChar = -1;
+
+ private String sourceString;
+ private Reader sourceReader;
+ private char[] sourceBuffer;
+ private int sourceEnd;
+ private int sourceCursor;
+
+ // for xml tokenizer
+ private boolean xmlIsAttribute;
+ private boolean xmlIsTagContent;
+ private int xmlOpenTagsCount;
+
+ private Parser parser;
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/main/java/org/mozilla/javascript/TokenStream.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Added: trunk/cdk/maven-javascript-plugin/src/main/resources/jslint.js
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/main/resources/jslint.js (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/main/resources/jslint.js 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,295 @@
+// (C)2002 Douglas Crockford
+// www.JSLint.com
+// Rhino Edition
+
+var JSLINT;JSLINT=function(){var adsafe={activexobject:true,alert:true,back:true,body:true,close:true,confirm:true,cookie:true,constructor:true,createpopup:true,defaultstatus:true,defaultview:true,document:true,documentelement:true,domain:true,'eval':true,execScript:true,external:true,forms:true,forward:true,frameelement:true,fromcharcode:true,history:true,home:true,location:true,moveby:true,moveto:true,navigate:true,opener:true,parent:true,print:true,prompt:true,prototype:true,referrer:true,resizeby:true,resizeto:true,self:true,showhelp:true,showmodaldialog:true,status:true,stop:true,top:true,window:true,write:true,writeln:true,__proto__:true},allOptions={adsafe:true,bitwise:true,browser:true,cap:true,debug:true,eqeqeq:true,evil:true,fragment:true,laxbreak:true,nomen:true,passfail:true,plusplus:true,rhino:true,undef:true,white:true,widget:true},anonname,browser={alert:true,blur:true,clearInterval:true,clearTimeout:true,close:true,closed:true,confirm:true,console:true,Debug!
:true,defaultStatus:true,document:true,event:true,focus:true,frames:true,getComputedStyle:true,history:true,Image:true,length:true,location:true,moveBy:true,moveTo:true,name:true,navigator:true,onblur:true,onerror:true,onfocus:true,onload:true,onresize:true,onunload:true,open:true,opener:true,opera:true,parent:true,print:true,prompt:true,resizeBy:true,resizeTo:true,screen:true,scroll:true,scrollBy:true,scrollTo:true,self:true,setInterval:true,setTimeout:true,status:true,top:true,window:true,XMLHttpRequest:true},escapes={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},funct,functions,globals,implied,inblock,indent,jsonmode,lines,lookahead,member,membersOnly,nexttoken,noreach,option,prereg,prevtoken,rhino={defineClass:true,deserialize:true,gc:true,help:true,load:true,loadClass:true,print:true,quit:true,readFile:true,readUrl:true,runCommand:true,seal:true,serialize:true,spawn:true,sync:true,toint32:true,version:true},scope,stack,standard={Array:tr!
ue,Boolean:true,Date:true,decodeURI:true,decodeURIComponent:true,encod
eURI:true,encodeURIComponent:true,Error:true,escape:true,'eval':true,EvalError:true,Function:true,isFinite:true,isNaN:true,Math:true,Number:true,Object:true,parseInt:true,parseFloat:true,RangeError:true,ReferenceError:true,RegExp:true,String:true,SyntaxError:true,TypeError:true,unescape:true,URIError:true},syntax={},token,verb,warnings,widget={alert:true,appleScript:true,animator:true,appleScript:true,beep:true,bytesToUIString:true,Canvas:true,chooseColor:true,chooseFile:true,chooseFolder:true,convertPathToHFS:true,convertPathToPlatform:true,closeWidget:true,COM:true,CustomAnimation:true,escape:true,FadeAnimation:true,filesystem:true,focusWidget:true,form:true,Frame:true,HotKey:true,Image:true,include:true,isApplicationRunning:true,iTunes:true,konfabulatorVersion:true,log:true,MenuItem:true,MoveAnimation:true,openURL:true,play:true,Point:true,popupMenu:true,preferenceGroups:true,preferences:true,print:true,prompt:true,random:true,reloadWidget:true,resolvePath:true,resumeUpda!
tes:true,RotateAnimation:true,runCommand:true,runCommandInBg:true,saveAs:true,savePreferences:true,screen:true,ScrollBar:true,showWidgetPreferences:true,sleep:true,speak:true,suppressUpdates:true,system:true,tellWidget:true,Text:true,TextArea:true,unescape:true,updateNow:true,URL:true,widget:true,Window:true,XMLDOM:true,XMLHttpRequest:true,yahooCheckLogin:true,yahooLogin:true,yahooLogout:true},wmode,xmode,xtype,tx=/^\s*([(){}[.,:;'"~]|\](\]>)?|\?>?|==?=?|\/(\*(global|extern|jslint|member|members)?|=|\/)?|\*[\/=]?|\+[+=]?|-[-=]?|%[=>]?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=%\?]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+-]?[0-9]+)?)/,rx=/^(\\[^\x00-\x1f]|\[(\\[^\x00-\x1f]|[^\x00-\x1f\\\/])*\]|[^\x00-\x1f\\\/\[])+\/[gim]*/,lx=/\*\/|\/\*/,ix=/^([a-zA-Z_$][a-zA-Z0-9_$]*$)/,jx=/(javascript|jscript|ecmascript)\s*:/i;Object.prototype.begetObject=function(){function F(){}
+F.prototype=this;return new F();};Object.prototype.combine=function(o){var n;for(n in o){if(o.hasOwnProperty(n)){this[n]=o[n];}}};String.prototype.entityify=function(){return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');};String.prototype.isAlpha=function(){return(this>='a'&&this<='z\uffff')||(this>='A'&&this<='Z\uffff');};String.prototype.isDigit=function(){return(this>='0'&&this<='9');};String.prototype.supplant=function(o){return this.replace(/{([^{}]*)}/g,function(a,b){var r=o[b];return typeof r==='string'||typeof r==='number'?r:a;});};String.prototype.name=function(){if(ix.test(this)){return this;}
+if(/[&<"\\\x00-\x1f]/.test(this)){return'"'+this.replace(/[&<"\\\x00-\x1f]/g,function(a){var c=escapes[a];if(c){return c;}
+c=a.charCodeAt();return'\\u00'+
+Math.floor(c/16).toString(16)+
+(c%16).toString(16);})+'"';}
+return'"'+this+'"';};function populateGlobals(){if(option.rhino){globals.combine(rhino);}
+if(option.browser){globals.combine(browser);}
+if(option.widget){globals.combine(widget);}}
+function quit(m,l,ch){throw{name:'JSLintError',line:l,character:ch,message:m+" ("+Math.floor((l/lines.length)*100)+"% scanned)."};}
+function warning(m,t,a,b,c,d){var ch,l,w;t=t||nexttoken;if(t.id==='(end)'){t=token;}
+l=t.line||0;ch=t.from||0;w={id:'(error)',raw:m,evidence:lines[l]||'',line:l,character:ch,a:a,b:b,c:c,d:d};w.reason=m.supplant(w);JSLINT.errors.push(w);if(option.passfail){quit('Stopping. ',l,ch);}
+warnings+=1;if(warnings===50){quit("Too many errors.",l,ch);}
+return w;}
+function warningAt(m,l,ch,a,b,c,d){return warning(m,{line:l,from:ch},a,b,c,d);}
+function error(m,t,a,b,c,d){var w=warning(m,t,a,b,c,d);quit("Stopping, unable to continue.",w.line,w.character);}
+function errorAt(m,l,ch,a,b,c,d){return error(m,{line:l,from:ch},a,b,c,d);}
+var lex=function(){var character,from,line,s;function nextLine(){line+=1;if(line>=lines.length){return false;}
+character=0;s=lines[line];return true;}
+function it(type,value){var i,t;if(option.adsafe&&adsafe[value.toLowerCase()]===true){warning("Adsafe restricted word '{a}'.",{line:line,from:character},value);}
+if(type==='(punctuator)'||(type==='(identifier)'&&syntax.hasOwnProperty(value))){t=syntax[value];if(!t.id){t=syntax[type];}}else{t=syntax[type];}
+t=t.begetObject();if(type==='(string)'){if(/(javascript|jscript|ecmascript)\s*:/i.test(value)){warningAt("JavaScript URL.",line,from);}}
+t.value=value;t.line=line;t.character=character;t.from=from;i=t.id;prereg=i&&(('(,=:[!&|?{};'.indexOf(i.charAt(i.length-1))>=0)||i==='return');return t;}
+return{init:function(source){if(typeof source==='string'){lines=source.replace(/\r\n/g,'\n').replace(/\r/g,'\n').split('\n');}else{lines=source;}
+line=0;character=0;from=0;s=lines[0];},token:function(){var c,d,i,l,r,t;function match(x){var r=x.exec(s),r1;if(r){l=r[0].length;r1=r[1];c=r1.charAt(0);s=s.substr(l);character+=l;from=character-r1.length;return r1;}}
+function string(x){var c,j,r='';if(jsonmode&&x!=='"'){warningAt("Strings must use doublequote.",line,character);}
+if(xmode===x||xmode==='string'){return it('(punctuator)',x);}
+function esc(n){var i=parseInt(s.substr(j+1,n),16);j+=n;if(i>=32&&i<=127&&i!==34&&i!==92&&i!==39){warningAt("Unnecessary escapement.",line,character);}
+character+=n;c=String.fromCharCode(i);}
+for(j=0;j<s.length;j+=1){c=s.charAt(j);if(c===x){character+=1;s=s.substr(j+1);return it('(string)',r,x);}
+if(c<' '){if(c==='\n'||c==='\r'){break;}
+warningAt("Control character in string: {a}.",line,character+j,s.substring(0,j));}else if(c==='<'){if(s.charAt(j+1)==='/'&&xmode&&xmode!=='CDATA'){warningAt("Expected '<\\/' and instead saw '</'.",line,character);}}else if(c==='\\'){j+=1;character+=1;c=s.charAt(j);switch(c){case'\\':case'\'':case'"':case'/':break;case'b':c='\b';break;case'f':c='\f';break;case'n':c='\n';break;case'r':c='\r';break;case't':c='\t';break;case'u':esc(4);break;case'v':c='\v';break;case'x':if(jsonmode){warningAt("Avoid \\x-.",line,character);}
+esc(2);break;default:warningAt("Bad escapement.",line,character);}}
+r+=c;character+=1;}
+errorAt("Unclosed string.",line,from);}
+for(;;){if(!s){return it(nextLine()?'(endline)':'(end)','');}
+t=match(tx);if(!t){t='';c='';while(s&&s<'!'){s=s.substr(1);}
+if(s){errorAt("Unexpected '{a}'.",line,character,s.substr(0,1));}}
+if(c.isAlpha()||c==='_'||c==='$'){return it('(identifier)',t);}
+if(c.isDigit()){if(!isFinite(Number(t))){warningAt("Bad number '{a}'.",line,character,t);}
+if(s.substr(0,1).isAlpha()){warningAt("Missing space after '{a}'.",line,character,t);}
+if(c==='0'){d=t.substr(1,1);if(d.isDigit()){warningAt("Don't use extra leading zeros '{a}'.",line,character,t);}else if(jsonmode&&(d==='x'||d==='X')){warningAt("Avoid 0x-. '{a}'.",line,character,t);}}
+if(t.substr(t.length-1)==='.'){warningAt("A trailing decimal point can be confused with a dot '{a}'.",line,character,t);}
+return it('(number)',t);}
+switch(t){case'"':case"'":return string(t);case'//':s='';break;case'/*':for(;;){i=s.search(lx);if(i>=0){break;}
+if(!nextLine()){errorAt("Unclosed comment.",line,character);}}
+character+=i+2;if(s.substr(i,1)==='/'){errorAt("Nested comment.",line,character);}
+s=s.substr(i+2);break;case'/*extern':case'/*global':case'/*members':case'/*member':case'/*jslint':case'*/':return{value:t,type:'special',line:line,character:character,from:from};case'':break;case'/':if(prereg){r=rx.exec(s);if(r){c=r[0];l=c.length;character+=l;s=s.substr(l);return it('(regex)',c);}
+errorAt("Bad regular expression.",line,character);}
+return it('(punctuator)',t);default:return it('(punctuator)',t);}}},skip:function(p){var i,t=p;if(nexttoken.id){if(!t){t='';if(nexttoken.id.substr(0,1)==='<'){lookahead.push(nexttoken);return true;}}else if(nexttoken.id.indexOf(t)>=0){return true;}}
+token=nexttoken;nexttoken=syntax['(end)'];for(;;){i=s.indexOf(t||'<');if(i>=0){character+=i+t.length;s=s.substr(i+t.length);return true;}
+if(!nextLine()){break;}}
+return false;}};}();function addlabel(t,type){if(t==='hasOwnProperty'){error("'hasOwnProperty' is a really bad name.");}
+if(funct===true){scope[t]=true;}else{if(funct.hasOwnProperty(t)){warning(funct[t]===true?"'{a}' was used before it was defined.":"'{a}' is already defined.",nexttoken,t);}
+scope[t]=funct;funct[t]=type;}}
+function doOption(){var b,obj,filter,t,v;switch(nexttoken.value){case'*/':error("Unbegun comment.");break;case'/*extern':case'/*global':obj=globals;break;case'/*members':case'/*member':if(!membersOnly){membersOnly={};}
+obj=membersOnly;break;case'/*jslint':if(option.adsafe){error("Adsafe restriction.");}
+obj=option;filter=allOptions;}
+for(;;){t=lex.token();if(t.id===','){t=lex.token();}
+while(t.id==='(endline)'){t=lex.token();}
+if(t.type==='special'&&t.value==='*/'){break;}
+if(t.type!=='(string)'&&t.type!=='(identifier)'){error("Bad option.",t);}
+if(filter){if(filter[t.value]!==true){error("Bad option.",t);}
+v=lex.token();if(v.id!==':'){error("Expected '{a}' and instead saw '{b}'.",t,':',t.value);}
+v=lex.token();if(v.value==='true'){b=true;}else if(v.value==='false'){b=false;}else{error("Expected '{a}' and instead saw '{b}'.",t,'true',t.value);}}else{b=true;}
+obj[t.value]=b;}
+if(filter){populateGlobals();}}
+function peek(p){var i=p||0,j=0,t;while(j<=i){t=lookahead[j];if(!t){t=lookahead[j]=lex.token();}
+j+=1;}
+return t;}
+var badbreak={')':true,']':true,'++':true,'--':true};function advance(id,t){var l;switch(token.id){case'(number)':if(nexttoken.id==='.'){warning("A dot following a number can be confused with a decimal point.",token);}
+break;case'-':if(nexttoken.id==='-'||nexttoken.id==='--'){warning("Confusing minusses.");}
+break;case'+':if(nexttoken.id==='+'||nexttoken.id==='++'){warning("Confusing plusses.");}
+break;}
+if(token.type==='(string)'||token.identifier){anonname=token.value;}
+if(id&&nexttoken.id!==id){if(t){if(nexttoken.id==='(end)'){warning("Unmatched '{a}'.",t,t.id);}else{warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",nexttoken,id,t.id,t.line+1,nexttoken.value);}}else{warning("Expected '{a}' and instead saw '{b}'.",nexttoken,id,nexttoken.value);}}
+prevtoken=token;token=nexttoken;for(;;){nexttoken=lookahead.shift()||lex.token();if(nexttoken.type==='special'){doOption();}else{if(nexttoken.id==='<!['){if(xtype==='html'){error("Unexpected '{a}'.",nexttoken,'<![');}
+if(xmode==='script'){nexttoken=lex.token();if(nexttoken.value!=='CDATA'){error("Missing '{a}'.",nexttoken,'CDATA');}
+nexttoken=lex.token();if(nexttoken.id!=='['){error("Missing '{a}'.",nexttoken,'[');}
+xmode='CDATA';}else if(xmode==='xml'){lex.skip(']]>');}else{error("Unexpected '{a}'.",nexttoken,'<![');}}else if(nexttoken.id===']]>'){if(xmode==='CDATA'){xmode='script';}else{error("Unexpected '{a}'.",nexttoken,']]>');}}else if(nexttoken.id!=='(endline)'){break;}
+if(xmode==='"'||xmode==="'"){error("Missing '{a}'.",token,xmode);}
+l=!xmode&&!option.laxbreak&&(token.type==='(string)'||token.type==='(number)'||token.type==='(identifier)'||badbreak[token.id]);}}
+if(l){switch(nexttoken.id){case'{':case'}':case']':break;case')':switch(token.id){case')':case'}':case']':break;default:warning("Line breaking error '{a}'.",token,')');}
+break;default:warning("Line breaking error '{a}'.",token,token.value);}}
+if(xtype==='widget'&&xmode==='script'&&nexttoken.id){l=nexttoken.id.charAt(0);if(l==='<'||l==='&'){nexttoken.nud=nexttoken.led=null;nexttoken.lbp=0;nexttoken.reach=true;}}}
+function parse(rbp,initial){var left;var o;if(nexttoken.id==='(end)'){error("Unexpected early end of program.",token);}
+advance();if(initial){anonname='anonymous';verb=token.value;}
+if(initial&&token.fud){token.fud();}else{if(token.nud){o=token.exps;left=token.nud();}else{if(nexttoken.type==='(number)'&&token.id==='.'){warning("A leading decimal point can be confused with a dot: '.{a}'.",token,nexttoken.value);advance();return token;}else{error("Expected an identifier and instead saw '{a}'.",token,token.id);}}
+while(rbp<nexttoken.lbp){o=nexttoken.exps;advance();if(token.led){left=token.led(left);}else{error("Expected an operator and instead saw '{a}'.",token,token.id);}}
+if(initial&&!o){warning("Expected an assignment or function call and instead saw an expression.",token);}}
+if(!option.evil&&left&&left.value==='eval'){warning("eval is evil.",left);}
+return left;}
+function adjacent(left,right){left=left||token;right=right||nexttoken;if(option.white){if(left.character!==right.from){warning("Unexpected space after '{a}'.",nexttoken,left.value);}}}
+function nospace(left,right){left=left||token;right=right||nexttoken;if(option.white){if(left.line===right.line){adjacent(left,right);}}}
+function nonadjacent(left,right){left=left||token;right=right||nexttoken;if(option.white){if(left.character===right.from){warning("Missing space after '{a}'.",nexttoken,left.value);}}}
+function indentation(bias){var i;if(option.white&&nexttoken.id!=='(end)'){i=indent+(bias||0);if(nexttoken.from!==i){warning("Expected '{a}' to have an indentation of {b} instead of {c}.",nexttoken,nexttoken.value,i,nexttoken.from);}}}
+function symbol(s,p){return syntax[s]||(syntax[s]={id:s,lbp:p,value:s});}
+function delim(s){return symbol(s,0);}
+function stmt(s,f){var x=delim(s);x.identifier=x.reserved=true;x.fud=f;return x;}
+function blockstmt(s,f){var x=stmt(s,f);x.block=true;return x;}
+function reserveName(x){var c=x.id.charAt(0);if((c>='a'&&c<='z')||(c>='A'&&c<='Z')){x.identifier=x.reserved=true;}
+return x;}
+function prefix(s,f){var x=symbol(s,150);reserveName(x);x.nud=(typeof f==='function')?f:function(){if(option.plusplus&&(this.id==='++'||this.id==='--')){warning("Unexpected use of '{a}'.",this,this.id);}
+parse(150);return this;};return x;}
+function type(s,f){var x=delim(s);x.type=s;x.nud=f;return x;}
+function reserve(s,f){var x=type(s,f);x.identifier=x.reserved=true;return x;}
+function reservevar(s){return reserve(s,function(){return this;});}
+function infix(s,f,p){var x=symbol(s,p);reserveName(x);x.led=(typeof f==='function')?f:function(left){nonadjacent(prevtoken,token);nonadjacent(token,nexttoken);return[this.id,left,parse(p)];};return x;}
+function relation(s,f){var x=symbol(s,100);x.led=function(left){nonadjacent(prevtoken,token);nonadjacent(token,nexttoken);var right=parse(100);if((left&&left.id==='NaN')||(right&&right.id==='NaN')){warning("Use the isNaN function to compare with NaN.",this);}else if(f){f.apply(this,[left,right]);}
+return[this.id,left,right];};return x;}
+function isPoorRelation(node){return(node.type==='(number)'&&!+node.value)||(node.type==='(string)'&&!node.value)||node.type==='true'||node.type==='false'||node.type==='undefined'||node.type==='null';}
+function assignop(s,f){symbol(s,20).exps=true;return infix(s,function(left){nonadjacent(prevtoken,token);nonadjacent(token,nexttoken);if(left){if(left.id==='.'||left.id==='['||(left.identifier&&!left.reserved)){parse(19);return left;}
+if(left===syntax['function']){warning("Expected an identifier in an assignment and instead saw a function invocation.",token);}}
+error("Bad assignment.",this);},20);}
+function bitwise(s,f,p){var x=symbol(s,p);reserveName(x);x.led=(typeof f==='function')?f:function(left){if(option.bitwise){warning("Unexpected use of '{a}'.",this,this.id);}
+nonadjacent(prevtoken,token);nonadjacent(token,nexttoken);return[this.id,left,parse(p)];};return x;}
+function bitwiseassignop(s){symbol(s,20).exps=true;return infix(s,function(left){if(option.bitwise){warning("Unexpected use of '{a}'.",this,this.id);}
+nonadjacent(prevtoken,token);nonadjacent(token,nexttoken);if(left){if(left.id==='.'||left.id==='['||(left.identifier&&!left.reserved)){parse(19);return left;}
+if(left===syntax['function']){warning("Expected an identifier in an assignment, and instead saw a function invocation.",token);}}
+error("Bad assignment.",this);},20);}
+function suffix(s,f){var x=symbol(s,150);x.led=function(left){if(option.plusplus){warning("Unexpected use of '{a}'.",this,this.id);}
+return[f,left];};return x;}
+function optionalidentifier(){if(nexttoken.reserved){warning("Expected an identifier and instead saw '{a}' (a reserved word).",nexttoken,nexttoken.id);}
+if(nexttoken.identifier){if(option.nomen){if(nexttoken.value.charAt(0)==='_'||nexttoken.value.indexOf('$')>=0){warning("Unexpected characters in '{a}'.",nexttoken,nexttoken.value);}}
+advance();return token.value;}}
+function identifier(){var i=optionalidentifier();if(i){return i;}
+if(token.id==='function'&&nexttoken.id==='('){warning("Missing name in function statement.");}else{error("Expected an identifier and instead saw '{a}'.",nexttoken,nexttoken.value);}}
+function reachable(s){var i=0;var t;if(nexttoken.id!==';'||noreach){return;}
+for(;;){t=peek(i);if(t.reach){return;}
+if(t.id!=='(endline)'){if(t.id==='function'){warning("Inner functions should be listed at the top of the outer function.",t);break;}
+warning("Unreachable '{a}' after '{b}'.",t,t.value,s);break;}
+i+=1;}}
+function statement(){var i=indent,s=scope,t=nexttoken;if(t.id===';'){warning("Unnecessary semicolon.",t);advance(';');return;}
+if(t.identifier&&!t.reserved&&peek().id===':'){advance();advance(':');scope=s.begetObject();addlabel(t.value,'label');if(!nexttoken.labelled){warning("Label '{a}' on {b} statement.",nexttoken,t.value,nexttoken.value);}
+if(jx.test(t.value+':')){warning("Label '{a}' looks like a javascript url.",t,t.value);}
+nexttoken.label=t.value;t=nexttoken;}
+parse(0,true);if(!t.block){if(nexttoken.id!==';'){warningAt("Missing semicolon.",token.line,token.from+token.value.length);}else{adjacent(token,nexttoken);advance(';');nonadjacent(token,nexttoken);}}
+indent=i;scope=s;}
+function statements(){while(!nexttoken.reach&&nexttoken.id!=='(end)'){if(nexttoken.id===';'){warning("Unnecessary semicolon.");advance(';');}
+indentation();statement();}}
+function block(f){var b=inblock,s=scope;inblock=f;if(f){scope=scope.begetObject();}
+nonadjacent(token,nexttoken);var t=nexttoken;if(nexttoken.id==='{'){advance('{');if(nexttoken.id!=='}'||token.line!==nexttoken.line){indent+=4;if(!f&&nexttoken.from===indent+4){indent+=4;}
+statements();indent-=4;indentation();}
+advance('}',t);}else{warning("Expected '{a}' and instead saw '{b}'.",nexttoken,'{',nexttoken.value);noreach=true;statement();noreach=false;}
+verb=null;scope=s;inblock=b;}
+function idValue(){return this;}
+function countMember(m){if(membersOnly&&membersOnly[m]!==true){warning("Unexpected member '{a}'.",nexttoken,m);}
+if(typeof member[m]==='number'){member[m]+=1;}else{member[m]=1;}}
+var scriptstring={onblur:true,onchange:true,onclick:true,ondblclick:true,onfocus:true,onkeydown:true,onkeypress:true,onkeyup:true,onload:true,onmousedown:true,onmousemove:true,onmouseout:true,onmouseover:true,onmouseup:true,onreset:true,onselect:true,onsubmit:true,onunload:true};var xmltype={HTML:{doBegin:function(n){if(!option.cap){warning("HTML case error.");}
+xmltype.html.doBegin();}},html:{doBegin:function(n){xtype='html';xmltype.html.script=false;option.browser=true;populateGlobals();},doTagName:function(n,p){var i;var t=xmltype.html.tag[n];var x;if(!t){error("Unrecognized tag '<{a}>'.",nexttoken,n===n.toLowerCase()?n:n+' (capitalization error)');}
+x=t.parent;if(x){if(x.indexOf(' '+p+' ')<0){error("A '<{a}>' must be within '<{b}>'.",token,n,x);}}else{i=stack.length;do{if(i<=0){error("A '<{a}>' must be within '<{b}>'.",token,n,'body');}
+i-=1;}while(stack[i].name!=='body');}
+xmltype.html.script=n==='script';return t.empty;},doAttribute:function(n,a){if(n==='script'){if(a==='src'){xmltype.html.script=false;return'string';}else if(a==='language'){warning("The 'language' attribute is deprecated.",token);return false;}}
+if(a==='href'){return'href';}
+return scriptstring[a]&&'script';},doIt:function(n){return xmltype.html.script?'script':n!=='html'&&xmltype.html.tag[n].special&&'special';},tag:{a:{},abbr:{},acronym:{},address:{},applet:{},area:{empty:true,parent:' map '},b:{},base:{empty:true,parent:' head '},bdo:{},big:{},blockquote:{},body:{parent:' html noframes '},br:{empty:true},button:{},canvas:{parent:' body p div th td '},caption:{parent:' table '},center:{},cite:{},code:{},col:{empty:true,parent:' table colgroup '},colgroup:{parent:' table '},dd:{parent:' dl '},del:{},dfn:{},dir:{},div:{},dl:{},dt:{parent:' dl '},em:{},embed:{},fieldset:{},font:{},form:{},frame:{empty:true,parent:' frameset '},frameset:{parent:' html frameset '},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},head:{parent:' html '},html:{},hr:{empty:true},i:{},iframe:{},img:{empty:true},input:{empty:true},ins:{},kbd:{},label:{},legend:{parent:' fieldset '},li:{parent:' dir menu ol ul '},link:{empty:true,parent:' head '},map:{},menu:{},meta:{empty:true,paren!
t:' head noscript '},noframes:{parent:' html body '},noscript:{parent:' applet blockquote body button center dd del div fieldset form frameset head html iframe ins li map noframes noscript object td th '},object:{},ol:{},optgroup:{parent:' select '},option:{parent:' optgroup select '},p:{},param:{empty:true,parent:' applet object '},pre:{},q:{},samp:{},script:{parent:' head body p div span abbr acronym address bdo blockquote cite code del dfn em ins kbd pre samp strong table tbody td th tr var '},select:{},small:{},span:{},strong:{},style:{parent:' head ',special:true},sub:{},sup:{},table:{},tbody:{parent:' table '},td:{parent:' tr '},textarea:{},tfoot:{parent:' table '},th:{parent:' tr '},thead:{parent:' table '},title:{parent:' head '},tr:{parent:' table tbody thead tfoot '},tt:{},u:{},ul:{},'var':{}}},widget:{doBegin:function(n){xtype='widget';option.widget=true;populateGlobals();},doTagName:function(n,p){var t=xmltype.widget.tag[n];if(!t){error("Unrecognized tag '<{a}>'!
.",nexttoken,n);}
+var x=t.parent;if(x.indexOf(' '+p+' ')<0){error("A '<{a}>' must be within '<{b}>'.",token,n,x);}},doAttribute:function(n,a){var t=xmltype.widget.tag[a];if(!t){error("Unrecognized attribute '<{a} {b}>'.",nexttoken,n,a);}
+var x=t.parent;if(x.indexOf(' '+n+' ')<0){error("Attribute '{a}' does not belong in '<{b}>'.",nexttoken,a,n);}
+return t.script?'script':a==='name'&&n!=='setting'?'define':'string';},doIt:function(n){var x=xmltype.widget.tag[n];return x&&x.script&&'script';},tag:{"about-box":{parent:' widget '},"about-image":{parent:' about-box '},"about-text":{parent:' about-box '},"about-version":{parent:' about-box '},action:{parent:' widget ',script:true},alignment:{parent:' canvas frame image scrollbar text textarea window '},anchorStyle:{parent:' text '},author:{parent:' widget '},autoHide:{parent:' scrollbar '},beget:{parent:' canvas frame image scrollbar text window '},bgColor:{parent:' text textarea '},bgColour:{parent:' text textarea '},bgOpacity:{parent:' text textarea '},canvas:{parent:' frame window '},checked:{parent:' image menuItem '},clipRect:{parent:' image '},color:{parent:' about-text about-version shadow text textarea '},colorize:{parent:' image '},colour:{parent:' about-text about-version shadow text textarea '},columns:{parent:' textarea '},company:{parent:' widget '},contextMe!
nuItems:{parent:' canvas frame image scrollbar text textarea window '},copyright:{parent:' widget '},data:{parent:' about-text about-version text textarea '},debug:{parent:' widget '},defaultValue:{parent:' preference '},defaultTracking:{parent:' widget '},description:{parent:' preference '},directory:{parent:' preference '},editable:{parent:' textarea '},enabled:{parent:' menuItem '},extension:{parent:' preference '},file:{parent:' action preference '},fillMode:{parent:' image '},font:{parent:' about-text about-version text textarea '},fontStyle:{parent:' textarea '},frame:{parent:' frame window '},group:{parent:' preference '},hAlign:{parent:' canvas frame image scrollbar text textarea '},handleLinks:{parent:' textArea '},height:{parent:' canvas frame image scrollbar text textarea window '},hidden:{parent:' preference '},hLineSize:{parent:' frame '},hOffset:{parent:' about-text about-version canvas frame image scrollbar shadow text textarea window '},hotkey:{parent:' widg!
et '},hRegistrationPoint:{parent:' canvas frame image scrollbar text '
},hScrollBar:{parent:' frame '},hslAdjustment:{parent:' image '},hslTinting:{parent:' image '},icon:{parent:' preferenceGroup '},id:{parent:' canvas frame hotkey image preference text textarea timer scrollbar widget window '},image:{parent:' about-box frame window widget '},interval:{parent:' action timer '},key:{parent:' hotkey '},kind:{parent:' preference '},level:{parent:' window '},lines:{parent:' textarea '},loadingSrc:{parent:' image '},locked:{parent:' window '},max:{parent:' scrollbar '},maxLength:{parent:' preference '},menuItem:{parent:' contextMenuItems '},min:{parent:' scrollbar '},minimumVersion:{parent:' widget '},minLength:{parent:' preference '},missingSrc:{parent:' image '},modifier:{parent:' hotkey '},name:{parent:' canvas frame hotkey image preference preferenceGroup scrollbar setting text textarea timer widget window '},notSaved:{parent:' preference '},onClick:{parent:' canvas frame image scrollbar text textarea ',script:true},onContextMenu:{parent:' canv!
as frame image scrollbar text textarea window ',script:true},onDragDrop:{parent:' canvas frame image scrollbar text textarea ',script:true},onDragEnter:{parent:' canvas frame image scrollbar text textarea ',script:true},onDragExit:{parent:' canvas frame image scrollbar text textarea ',script:true},onFirstDisplay:{parent:' window ',script:true},onGainFocus:{parent:' textarea window ',script:true},onKeyDown:{parent:' hotkey text textarea window ',script:true},onKeyPress:{parent:' textarea window ',script:true},onKeyUp:{parent:' hotkey text textarea window ',script:true},onImageLoaded:{parent:' image ',script:true},onLoseFocus:{parent:' textarea window ',script:true},onMouseDown:{parent:' canvas frame image scrollbar text textarea window ',script:true},onMouseDrag:{parent:' canvas frame image scrollbar text textArea window ',script:true},onMouseEnter:{parent:' canvas frame image scrollbar text textarea window ',script:true},onMouseExit:{parent:' canvas frame image scrollbar te!
xt textarea window ',script:true},onMouseMove:{parent:' canvas frame i
mage scrollbar text textarea window ',script:true},onMouseUp:{parent:' canvas frame image scrollbar text textarea window ',script:true},onMouseWheel:{parent:' frame ',script:true},onMultiClick:{parent:' canvas frame image scrollbar text textarea window ',script:true},onSelect:{parent:' menuItem ',script:true},onTextInput:{parent:' window ',script:true},onTimerFired:{parent:' timer ',script:true},onValueChanged:{parent:' scrollbar ',script:true},opacity:{parent:' canvas frame image scrollbar shadow text textarea window '},option:{parent:' preference widget '},optionValue:{parent:' preference '},order:{parent:' preferenceGroup '},orientation:{parent:' scrollbar '},pageSize:{parent:' scrollbar '},preference:{parent:' widget '},preferenceGroup:{parent:' widget '},remoteAsync:{parent:' image '},requiredPlatform:{parent:' widget '},root:{parent:' window '},rotation:{parent:' canvas frame image scrollbar text '},scrollbar:{parent:' frame text textarea window '},scrolling:{parent:' !
text '},scrollX:{parent:' frame '},scrollY:{parent:' frame '},secure:{parent:' preference textarea '},setting:{parent:' settings '},settings:{parent:' widget '},shadow:{parent:' about-text about-version text window '},size:{parent:' about-text about-version text textarea '},spellcheck:{parent:' textarea '},src:{parent:' image '},srcHeight:{parent:' image '},srcWidth:{parent:' image '},style:{parent:' about-text about-version canvas frame image preference scrollbar text textarea window '},subviews:{parent:' frame '},superview:{parent:' canvas frame image scrollbar text textarea '},text:{parent:' frame text textarea window '},textarea:{parent:' frame window '},timer:{parent:' widget '},thumbColor:{parent:' scrollbar textarea '},ticking:{parent:' timer '},ticks:{parent:' preference '},tickLabel:{parent:' preference '},tileOrigin:{parent:' image '},title:{parent:' menuItem preference preferenceGroup window '},tooltip:{parent:' frame image text textarea '},tracking:{parent:' can!
vas image '},trigger:{parent:' action '},truncation:{parent:' text '},
type:{parent:' preference '},url:{parent:' about-box about-text about-version '},useFileIcon:{parent:' image '},vAlign:{parent:' canvas frame image scrollbar text textarea '},value:{parent:' preference scrollbar setting '},version:{parent:' widget '},visible:{parent:' canvas frame image scrollbar text textarea window '},vLineSize:{parent:' frame '},vOffset:{parent:' about-text about-version canvas frame image scrollbar shadow text textarea window '},vRegistrationPoint:{parent:' canvas frame image scrollbar text '},vScrollBar:{parent:' frame '},width:{parent:' canvas frame image scrollbar text textarea window '},window:{parent:' canvas frame image scrollbar text textarea widget '},wrap:{parent:' text '},zOrder:{parent:' canvas frame image scrollbar text textarea window '}}}};function xmlword(tag){var w=nexttoken.value;if(!nexttoken.identifier){if(nexttoken.id==='<'){if(tag){error("Expected '{a}' and instead saw '{b}'.",token,'<','<');}else{error("Missing '{a}'.",token,'>')!
;}}else if(nexttoken.id==='(end)'){error("Bad structure.");}else{warning("Missing quote.",token);}}
+advance();while(nexttoken.id==='-'||nexttoken.id===':'){w+=nexttoken.id;advance();if(!nexttoken.identifier){error("Bad name '{a}'.",nexttoken,w+nexttoken.value);}
+w+=nexttoken.value;advance();}
+return w;}
+function closetag(n){return'</'+n+'>';}
+function xml(){var a,e,n,q,t;xmode='xml';stack=null;for(;;){switch(nexttoken.value){case'<':if(!stack){stack=[];}
+advance('<');t=nexttoken;n=xmlword(true);t.name=n;if(!xtype){if(xmltype[n]){xmltype[n].doBegin();n=xtype;e=false;}else{if(option.fragment){xmltype.html.doBegin();stack=[{name:'body'}];e=xmltype[xtype].doTagName(n,'body');}else{error("Unrecognized tag '<{a}>'.",nexttoken,n);}}}else{if(option.cap&&xtype==='html'){n=n.toLowerCase();}
+if(stack.length===0){error("What the hell is this?");}
+e=xmltype[xtype].doTagName(n,stack[stack.length-1].name);}
+t.type=n;for(;;){if(nexttoken.id==='/'){advance('/');e=true;break;}
+if(nexttoken.id&&nexttoken.id.substr(0,1)==='>'){break;}
+a=xmlword();switch(xmltype[xtype].doAttribute(n,a)){case'script':xmode='string';advance('=');q=nexttoken.id;if(q!=='"'&&q!=="'"){error("Missing quote.");}
+xmode=q;wmode=option.white;option.white=false;advance(q);statements();option.white=wmode;if(nexttoken.id!==q){error("Missing close quote on script attribute.");}
+xmode='xml';advance(q);break;case'value':advance('=');if(!nexttoken.identifier&&nexttoken.type!=='(string)'&&nexttoken.type!=='(number)'){error("Bad value '{a}'.",nexttoken,nexttoken.value);}
+advance();break;case'string':case'href':advance('=');if(nexttoken.type!=='(string)'){error("Bad value '{a}'.",nexttoken,nexttoken.value);}
+advance();break;case'define':advance('=');if(nexttoken.type!=='(string)'){error("Bad value '{a}'.",nexttoken,nexttoken.value);}
+addlabel(nexttoken.value,'var');advance();break;default:if(nexttoken.id==='='){advance('=');if(!nexttoken.identifier&&nexttoken.type!=='(string)'&&nexttoken.type!=='(number)'){}
+advance();}}}
+switch(xmltype[xtype].doIt(n)){case'script':xmode='script';advance('>');indent=nexttoken.from;statements();if(nexttoken.id!=='</'&&nexttoken.id!=='(end)'){warning("Unexpected '{a}'.",nexttoken,nexttoken.id);}
+xmode='xml';break;case'special':e=true;n=closetag(t.name);if(!lex.skip(n)){error("Missing '{a}'.",t,n);}
+break;default:lex.skip('>');}
+if(!e){stack.push(t);}
+break;case'</':advance('</');n=xmlword(true);t=stack.pop();if(!t){error("Unexpected '{a}'.",nexttoken,closetag(n));}
+if(t.name!==n){error("Expected '{a}' and instead saw '{b}'.",nexttoken,closetag(t.name),closetag(n));}
+if(nexttoken.id!=='>'){error("Missing '{a}'.",nexttoken,'>');}
+if(stack.length>0){lex.skip('>');}else{advance('>');}
+break;case'<!':for(;;){advance();if(nexttoken.id==='>'){break;}
+if(nexttoken.id==='<'||nexttoken.id==='(end)'){error("Missing '{a}'.",token,'>');}}
+lex.skip('>');break;case'<!--':lex.skip('-->');break;case'<%':lex.skip('%>');break;case'<?':for(;;){advance();if(nexttoken.id==='?>'){break;}
+if(nexttoken.id==='<?'||nexttoken.id==='<'||nexttoken.id==='>'||nexttoken.id==='(end)'){error("Missing '{a}'.",token,'?>');}}
+lex.skip('?>');break;case'<=':case'<<':case'<<=':error("Missing '{a}'.",nexttoken,'<');break;case'(end)':return;}
+if(stack&&stack.length===0){return;}
+if(!lex.skip('')){t=stack.pop();if(t.value){error("Missing '{a}'.",t,closetag(t.name));}else{return;}}
+advance();}}
+type('(number)',idValue);type('(string)',idValue);syntax['(identifier)']={type:'(identifier)',lbp:0,identifier:true,nud:function(){var v=this.value,s=scope[v];if(s===funct){if(funct!==true){switch(funct[v]){case'unused':funct[v]='var';break;case'label':warning("'{a}' is a statement label.",token,v);break;}}}else if(funct===true){if(option.undef){warning("'{a}' is undefined.",token,v);}else{implied[v]=true;globals[v]=true;}}else{switch(funct[v]){case'closure':case'function':case'var':case'unused':warning("'{a}' used out of scope.",token,v);break;case'label':warning("'{a}' is a statement label.",token,v);break;case'outer':case true:break;default:if(s===true){funct[v]=true;}else if(typeof s!=='object'){if(option.undef){warning("'{a}' is undefined.",token,v);}else{implied[v]=true;globals[v]=true;funct[v]=true;}}else{switch(s[v]){case'function':case'var':case'unused':s[v]='closure';funct[v]='outer';break;case'closure':case'parameter':funct[v]='outer';break;case'label':warning("'!
{a}' is a statement label.",token,v);}}}}
+return this;},led:function(){error("Expected an operator and instead saw '{a}'.",nexttoken,nexttoken.value);}};type('(regex)',function(){return[this.id,this.value,this.flags];});delim('(endline)');delim('(begin)');delim('(end)').reach=true;delim('</').reach=true;delim('<![').reach=true;delim('<%');delim('<?');delim('<!');delim('<!--');delim('%>');delim('?>');delim('(error)').reach=true;delim('}').reach=true;delim(')');delim(']');delim(']]>').reach=true;delim('"').reach=true;delim("'").reach=true;delim(';');delim(':').reach=true;delim(',');reserve('else');reserve('case').reach=true;reserve('catch');reserve('default').reach=true;reserve('finally');reservevar('arguments');reservevar('eval');reservevar('false');reservevar('Infinity');reservevar('NaN');reservevar('null');reservevar('this');reservevar('true');reservevar('undefined');assignop('=','assign',20);assignop('+=','assignadd',20);assignop('-=','assignsub',20);assignop('*=','assignmult',20);assignop('/=','assigndiv',20).nu!
d=function(){error("A regular expression literal can be confused with '/='.");};assignop('%=','assignmod',20);bitwiseassignop('&=','assignbitand',20);bitwiseassignop('|=','assignbitor',20);bitwiseassignop('^=','assignbitxor',20);bitwiseassignop('<<=','assignshiftleft',20);bitwiseassignop('>>=','assignshiftright',20);bitwiseassignop('>>>=','assignshiftrightunsigned',20);infix('?',function(left){parse(10);advance(':');parse(10);},30);infix('||','or',40);infix('&&','and',50);bitwise('|','bitor',70);bitwise('^','bitxor',80);bitwise('&','bitand',90);relation('==',function(left,right){if(option.eqeqeq){warning("Expected '{a}' and instead saw '{b}'.",this,'===','==');}else if(isPoorRelation(left)){warning("Use '{a}' to compare with '{b}'.",this,'===',left.value);}else if(isPoorRelation(right)){warning("Use '{a}' to compare with '{b}'.",this,'===',right.value);}
+return['==',left,right];});relation('===');relation('!=',function(left,right){if(option.eqeqeq){warning("Expected '{a}' and instead saw '{b}'.",this,'!==','!=');}else if(isPoorRelation(left)){warning("Use '{a}' to compare with '{b}'.",this,'!==',left.value);}else if(isPoorRelation(right)){warning("Use '{a}' to compare with '{b}'.",this,'!==',right.value);}
+return['!=',left,right];});relation('!==');relation('<');relation('>');relation('<=');relation('>=');bitwise('<<','shiftleft',120);bitwise('>>','shiftright',120);bitwise('>>>','shiftrightunsigned',120);infix('in','in',120);infix('instanceof','instanceof',120);infix('+',function(left){nonadjacent(prevtoken,token);nonadjacent(token,nexttoken);var right=parse(130);if(left&&right&&left.id==='(string)'&&right.id==='(string)'){left.value+=right.value;left.character=right.character;if(option.adsafe&&adsafe[left.value.toLowerCase()]===true){warning("Adsafe restricted word '{a}'.",left,left.value);}
+if(jx.test(left.value)){warning("JavaScript URL.",left);}
+return left;}
+return[this.id,left,right];},130);prefix('+','num');infix('-','sub',130);prefix('-','neg');infix('*','mult',140);infix('/','div',140);infix('%','mod',140);suffix('++','postinc');prefix('++','preinc');syntax['++'].exps=true;suffix('--','postdec');prefix('--','predec');syntax['--'].exps=true;prefix('delete',function(){var p=parse(0);if(p.id!=='.'&&p.id!=='['){warning("Expected '{a}' and instead saw '{b}'.",nexttoken,'.',nexttoken.value);}}).exps=true;prefix('~',function(){if(option.bitwise){warning("Unexpected '{a}'.",this,'~');}
+parse(150);return this;});prefix('!','not');prefix('typeof','typeof');prefix('new',function(){var c=parse(155),i;if(c){if(c.identifier){c['new']=true;switch(c.value){case'Object':warning("Use the object literal notation {}.",token);break;case'Array':warning("Use the array literal notation [].",token);break;case'Number':case'String':case'Boolean':warning("Do not use the {a} function as a constructor.",token,c.value);break;case'Function':if(!option.evil){warning("The Function constructor is eval.");}
+break;default:if(c.id!=='function'){i=c.value.substr(0,1);if(i<'A'||i>'Z'){warning("A constructor name should start with an uppercase letter.",token);}}}}else{if(c.id!=='.'&&c.id!=='['&&c.id!=='('){warning("Bad constructor.",token);}}}else{warning("Weird construction. Delete 'new'.",this);}
+adjacent(token,nexttoken);if(nexttoken.id==='('){advance('(');nospace();if(nexttoken.id!==')'){for(;;){parse(10);if(nexttoken.id!==','){break;}
+advance(',');}}
+advance(')');nospace(prevtoken,token);}else{warning("Missing '()' invoking a constructor.");}
+return syntax['function'];});syntax['new'].exps=true;infix('.',function(left){adjacent(prevtoken,token);var m=identifier();if(typeof m==='string'){countMember(m);}
+if(!option.evil&&left&&left.value==='document'&&(m==='write'||m==='writeln')){warning("document.write can be a form of eval.",left);}
+this.left=left;this.right=m;return this;},160);infix('(',function(left){adjacent(prevtoken,token);nospace();var n=0;var p=[];if(left&&left.type==='(identifier)'){if(left.value.match(/^[A-Z](.*[a-z].*)?$/)){if(left.value!=='Number'&&left.value!=='String'&&left.value!=='Boolean'&&left.value!=='Date'){warning("Missing 'new' prefix when invoking a constructor.",left);}}}
+if(nexttoken.id!==')'){for(;;){p[p.length]=parse(10);n+=1;if(nexttoken.id!==','){break;}
+advance(',');nonadjacent(token,nexttoken);}}
+advance(')');nospace(prevtoken,token);if(typeof left==='object'){if(left.value==='parseInt'&&n===1){warning("Missing radix parameter.",left);}
+if(!option.evil){if(left.value==='eval'||left.value==='Function'){warning("eval is evil.",left);}else if(p[0]&&p[0].id==='(string)'&&(left.value==='setTimeout'||left.value==='setInterval')){warning("Implied eval is evil. Pass a function instead of a string.",left);}}
+if(!left.identifier&&left.id!=='.'&&left.id!=='['&&left.id!=='('){warning("Bad invocation.",left);}}
+return syntax['function'];},155).exps=true;prefix('(',function(){nospace();var v=parse(0);advance(')',this);nospace(prevtoken,token);return v;});infix('[',function(left){nospace();var e=parse(0),s;if(e&&e.type==='(string)'){countMember(e.value);if(ix.test(e.value)){s=syntax[e.value];if(!s||!s.reserved){warning("['{a}'] is better written in dot notation.",e,e.value);}}}
+advance(']',this);nospace(prevtoken,token);this.left=left;this.right=e;return this;},160);prefix('[',function(){if(nexttoken.id===']'){advance(']');return;}
+var b=token.line!==nexttoken.line;if(b){indent+=4;if(nexttoken.from===indent+4){indent+=4;}}
+for(;;){if(b&&token.line!==nexttoken.line){indentation();}
+parse(10);if(nexttoken.id===','){adjacent(token,nexttoken);advance(',');if(nexttoken.id===','||nexttoken.id===']'){warning("Extra comma.",token);}
+nonadjacent(token,nexttoken);}else{if(b){indent-=4;indentation();}
+advance(']',this);return;}}},160);(function(x){x.nud=function(){var i,s;if(nexttoken.id==='}'){advance('}');return;}
+var b=token.line!==nexttoken.line;if(b){indent+=4;if(nexttoken.from===indent+4){indent+=4;}}
+for(;;){if(b){indentation();}
+i=optionalidentifier(true);if(!i){if(nexttoken.id==='(string)'){i=nexttoken.value;if(ix.test(i)){s=syntax[i];}
+advance();}else if(nexttoken.id==='(number)'){i=nexttoken.value.toString();advance();}else{error("Expected '{a}' and instead saw '{b}'.",nexttoken,'}',nexttoken.value);}}
+countMember(i);advance(':');nonadjacent(token,nexttoken);parse(10);if(nexttoken.id===','){adjacent(token,nexttoken);advance(',');if(nexttoken.id===','||nexttoken.id==='}'){warning("Extra comma.",token);}
+nonadjacent(token,nexttoken);}else{if(b){indent-=4;indentation();}
+advance('}',this);return;}}};x.fud=function(){error("Expected to see a statement and instead saw a block.");};})(delim('{'));function varstatement(){for(;;){nonadjacent(token,nexttoken);addlabel(identifier(),'unused');if(nexttoken.id==='='){nonadjacent(token,nexttoken);advance('=');nonadjacent(token,nexttoken);if(peek(0).id==='='){error("Variable {a} was not declared correctly.",nexttoken,nexttoken.value);}
+parse(20);}
+if(nexttoken.id!==','){return;}
+adjacent(token,nexttoken);advance(',');nonadjacent(token,nexttoken);}}
+stmt('var',varstatement);stmt('new',function(){error("'new' should not be used as a statement.");});function functionparams(){var i,t=nexttoken,p=[];advance('(');nospace();if(nexttoken.id===')'){advance(')');nospace(prevtoken,token);return;}
+for(;;){i=identifier();p.push(i);addlabel(i,'parameter');if(nexttoken.id===','){advance(',');nonadjacent(token,nexttoken);}else{advance(')',t);nospace(prevtoken,token);return p.join(', ');}}}
+function doFunction(i){var s=scope;scope=s.begetObject();funct={'(name)':i||'"'+anonname+'"','(line)':nexttoken.line+1,'(context)':funct};functions.push(funct);if(i){addlabel(i,'function');}
+funct['(params)']=functionparams();block(false);scope=s;funct=funct['(context)'];}
+blockstmt('function',function(){if(inblock){warning("Function statements cannot be placed in blocks. Use a function expression or move the statement to the top of the outer function.",token);}
+var i=identifier();adjacent(token,nexttoken);addlabel(i,'unused');doFunction(i);if(nexttoken.id==='('&&nexttoken.line===token.line){error("Function statements are not invocable. Wrap the function expression in parens.");}});prefix('function',function(){var i=optionalidentifier();if(i){adjacent(token,nexttoken);}else{nonadjacent(token,nexttoken);}
+doFunction(i);});blockstmt('if',function(){var t=nexttoken;advance('(');nonadjacent(this,t);nospace();parse(20);if(nexttoken.id==='='){warning("Assignment in control part.");advance('=');parse(20);}
+advance(')',t);nospace(prevtoken,token);block(true);if(nexttoken.id==='else'){nonadjacent(token,nexttoken);advance('else');if(nexttoken.id==='if'||nexttoken.id==='switch'){statement();}else{block(true);}}});blockstmt('try',function(){var b,e;block(true);if(nexttoken.id==='catch'){advance('catch');nonadjacent(token,nexttoken);advance('(');e=nexttoken.value;if(nexttoken.type!=='(identifier)'){warning("Expected an identifier and instead saw '{a}'.",nexttoken,e);}else{addlabel(e,'unused');}
+advance();advance(')');block(false);b=true;}
+if(nexttoken.id==='finally'){advance('finally');block(false);return;}else if(!b){error("Expected '{a}' and instead saw '{b}'.",nexttoken,'catch',nexttoken.value);}});blockstmt('while',function(){var t=nexttoken;advance('(');nonadjacent(this,t);nospace();parse(20);if(nexttoken.id==='='){warning("Assignment in control part.");advance('=');parse(20);}
+advance(')',t);nospace(prevtoken,token);block(true);}).labelled=true;reserve('with');blockstmt('switch',function(){var t=nexttoken;var g=false;advance('(');nonadjacent(this,t);nospace();this.condition=parse(20);advance(')',t);nospace(prevtoken,token);nonadjacent(token,nexttoken);t=nexttoken;advance('{');nonadjacent(token,nexttoken);indent+=4;this.cases=[];for(;;){switch(nexttoken.id){case'case':switch(verb){case'break':case'case':case'continue':case'return':case'switch':case'throw':break;default:warning("Expected a 'break' statement before 'case'.",token);}
+indentation(-4);advance('case');this.cases.push(parse(20));g=true;advance(':');verb='case';break;case'default':switch(verb){case'break':case'continue':case'return':case'throw':break;default:warning("Expected a 'break' statement before 'default'.",token);}
+indentation(-4);advance('default');g=true;advance(':');break;case'}':indent-=4;indentation();advance('}',t);if(this.cases.length===1||this.condition.id==='true'||this.condition.id==='false'){warning("This 'switch' should be an 'if'.",this);}
+return;case'(end)':error("Missing '{a}'.",nexttoken,'}');return;default:if(g){switch(token.id){case',':error("Each value should have its own case label.");return;case':':statements();break;default:error("Missing ':' on a case clause.",token);}}else{error("Expected '{a}' and instead saw '{b}'.",nexttoken,'case',nexttoken.value);}}}}).labelled=true;stmt('debugger',function(){if(!option.debug){warning("All 'debugger' statements should be removed.");}});stmt('do',function(){block(true);advance('while');var t=nexttoken;nonadjacent(token,t);advance('(');nospace();parse(20);advance(')',t);nospace(prevtoken,token);}).labelled=true;blockstmt('for',function(){var t=nexttoken;advance('(');nonadjacent(this,t);nospace();if(peek(nexttoken.id==='var'?1:0).id==='in'){if(nexttoken.id==='var'){advance('var');addlabel(identifier(),'var');}else{advance();}
+advance('in');parse(20);advance(')',t);block(true);return;}else{if(nexttoken.id!==';'){if(nexttoken.id==='var'){advance('var');varstatement();}else{for(;;){parse(0);if(nexttoken.id!==','){break;}
+advance(',');}}}
+advance(';');if(nexttoken.id!==';'){parse(20);}
+advance(';');if(nexttoken.id===';'){error("Expected '{a}' and instead saw '{b}'.",nexttoken,')',';');}
+if(nexttoken.id!==')'){for(;;){parse(0);if(nexttoken.id!==','){break;}
+advance(',');}}
+advance(')',t);nospace(prevtoken,token);block(true);}}).labelled=true;function nolinebreak(t){if(t.line!==nexttoken.line){warning("Line breaking error '{a}'.",t,t.id);}}
+stmt('break',function(){var v=nexttoken.value;nolinebreak(this);if(nexttoken.id!==';'){if(funct===true){warning("Put '{a}' and the statement it labels in a function.",nexttoken,v);}else if(funct[v]!=='label'){warning("'{a}' is not a statement label.",nexttoken,v);}else if(scope[v]!==funct){warning("'{a}' is out of scope.",nexttoken,v);}
+advance();}
+reachable('break');});stmt('continue',function(){var v=nexttoken.value;nolinebreak(this);if(nexttoken.id!==';'){if(funct===true){warning("Put '{a}' and the statement it labels in a function.",nexttoken,v);}else if(funct[v]!=='label'){warning("'{a}' is not a statement label.",nexttoken,v);}else if(scope[v]!==funct){warning("'{a}' is out of scope.",nexttoken,v);}
+advance();}
+reachable('continue');});stmt('return',function(){nolinebreak(this);if(nexttoken.id!==';'&&!nexttoken.reach){nonadjacent(token,nexttoken);parse(20);}
+reachable('return');});stmt('throw',function(){nolinebreak(this);nonadjacent(token,nexttoken);parse(20);reachable('throw');});reserve('abstract');reserve('boolean');reserve('byte');reserve('char');reserve('class');reserve('const');reserve('double');reserve('enum');reserve('export');reserve('extends');reserve('final');reserve('float');reserve('goto');reserve('implements');reserve('import');reserve('int');reserve('interface');reserve('long');reserve('native');reserve('package');reserve('private');reserve('protected');reserve('public');reserve('short');reserve('static');reserve('super');reserve('synchronized');reserve('throws');reserve('transient');reserve('void');reserve('volatile');function jsonValue(){function jsonObject(){var t=nexttoken;advance('{');if(nexttoken.id!=='}'){for(;;){if(nexttoken.id==='(end)'){error("Missing '}' to match '{' from line {a}.",nexttoken,t.line+1);}else if(nexttoken.id==='}'){warning("Unexpected comma.",token);break;}else if(nexttoken.id===','){e!
rror("Unexpected comma.",nexttoken);}else if(nexttoken.id!=='(string)'){warning("Expected a string and instead saw {a}.",nexttoken,nexttoken.value);}
+advance();advance(':');jsonValue();if(nexttoken.id!==','){break;}
+advance(',');}}
+advance('}');}
+function jsonArray(){var t=nexttoken;advance('[');if(nexttoken.id!==']'){for(;;){if(nexttoken.id==='(end)'){error("Missing ']' to match '[' from line {a}.",nexttoken,t.line+1);}else if(nexttoken.id===']'){warning("Unexpected comma.",token);break;}else if(nexttoken.id===','){error("Unexpected comma.",nexttoken);}
+jsonValue();if(nexttoken.id!==','){break;}
+advance(',');}}
+advance(']');}
+switch(nexttoken.id){case'{':jsonObject();break;case'[':jsonArray();break;case'true':case'false':case'null':case'(number)':case'(string)':advance();break;case'-':advance('-');if(token.character!==nexttoken.from){warning("Unexpected space after '-'.",token);}
+adjacent(token,nexttoken);advance('(number)');break;default:error("Expected a JSON value.",nexttoken);}}
+var itself=function(s,o){option=o||{};JSLINT.errors=[];globals=standard.begetObject();scope=globals.begetObject();funct=true;functions=[];xmode=false;xtype='';stack=null;member={};membersOnly=null;implied={};inblock=false;lookahead=[];indent=0;jsonmode=false;warnings=0;lex.init(s);prereg=true;prevtoken=token=nexttoken=syntax['(begin)'];populateGlobals();try{advance();if(nexttoken.value.charAt(0)==='<'){xml();}else if(nexttoken.id==='{'||nexttoken.id==='['){option.laxbreak=true;jsonmode=true;jsonValue();}else{statements();}
+advance('(end)');}catch(e){if(e){JSLINT.errors.push({reason:e.message,line:e.line||nexttoken.line,character:e.character||nexttoken.from},null);}}
+return JSLINT.errors.length===0;};itself.report=function(option){var a=[],c,e,f,i,k,l,m='',n,o=[],s,v,cl,va,un,ou,gl,la;function detail(h,s){if(s.length){o.push('<div><i>'+h+'</i> '+
+s.sort().join(', ')+'</div>');}}
+s=[];for(k in implied){if(implied.hasOwnProperty(k)){s.push(k);}}
+k=JSLINT.errors.length;if(k||s.length>0){o.push('<div id=errors><i>Error:</i>');if(s.length>0){o.push('<p><i>Implied global:</i> '+s.sort().join(', ')+'</p>');c=true;}
+for(i=0;i<k;i+=1){c=JSLINT.errors[i];if(c){e=c.evidence||'';o.push('<p>Problem at line '+(c.line+1)+' character '+(c.character+1)+': '+c.reason.entityify()+'</p><p class=evidence>'+
+(e&&(e.length>80?e.substring(0,77)+'...':e).entityify())+'</p>');}}
+o.push('</div>');if(!c){return o.join('');}}
+if(!option){o.push('<div id=functions>');s=[];for(k in scope){if(scope.hasOwnProperty(k)){s.push(k);}}
+if(s.length===0){o.push('<div><i>No new global variables introduced.</i></div>');}else{o.push('<div><i>Global</i> '+s.join(', ')+'</div>');}
+for(i=0;i<functions.length;i+=1){f=functions[i];cl=[];va=[];un=[];ou=[];gl=[];la=[];for(k in f){if(f.hasOwnProperty(k)){v=f[k];switch(v){case'closure':cl.push(k);break;case'var':va.push(k);break;case'unused':un.push(k);break;case'label':la.push(k);break;case'outer':ou.push(k);break;case true:if(k!=='(context)'){gl.push(k);}
+break;}}}
+o.push('<br><div class=function><i>'+f['(line)']+'</i> '+
+(f['(name)']||'')+'('+
+(f['(params)']||'')+')</div>');detail('Closure',cl);detail('Variable',va);detail('Unused',un);detail('Label',la);detail('Outer',ou);detail('Global',gl);}
+for(k in member){if(typeof member[k]==='number'){a.push(k);}}
+if(a.length){a=a.sort();m='<br><div class=function>/*members ';l=10;for(i=0;i<a.length;i+=1){k=a[i];n=k.name();if(l+n.length>72){o.push(m+'</div>');m='<div> ';l=1;}
+l+=n.length+2;if(member[k]===1){n='<i>'+n+'</i>';}
+if(i<a.length-1){n+=', ';}
+m+=n;}
+o.push(m+' */</div>');}
+o.push('</div>');}
+return o.join('');};return itself;}();(function(a){if(!a[0]){print("Usage: jslint.js file.js");quit(1);}
+var input=readFile(a[0]);if(!input){print("jslint: Couldn't open file '"+a[0]+"'.");quit(1);}
+if(!JSLINT(input,{rhino:true,passfail:false})){for(var i=0;i<JSLINT.errors.length;i+=1){var e=JSLINT.errors[i];if(e){print('Lint at line '+(e.line+1)+' character '+
+(e.character+1)+': '+e.reason);print((e.evidence||'').replace(/^\s*(\S*(\s+\S+)*)\s*$/,"$1"));print('');}}}else{print("jslint: No problems found in "+a[0]);quit();}})(arguments);
\ No newline at end of file
Added: trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/mojo/yuicompressor/AggregationTestCase.java
===================================================================
--- trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/mojo/yuicompressor/AggregationTestCase.java (rev 0)
+++ trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/mojo/yuicompressor/AggregationTestCase.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -0,0 +1,177 @@
+package net.sf.alchim.mojo.yuicompressor;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+
+import org.codehaus.plexus.util.FileUtils;
+
+public class AggregationTestCase extends TestCase {
+ private File dir_;
+
+ @Override
+ protected void setUp() throws Exception {
+ dir_ = File.createTempFile(this.getClass().getName(), "-test");
+ dir_.delete();
+ dir_.mkdirs();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ FileUtils.deleteDirectory(dir_);
+ }
+
+ public void test0to1() throws Exception {
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertFalse(target.output.exists());
+
+ target.includes = new String[]{};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertFalse(target.output.exists());
+
+ target.includes = new String[]{"**/*.js"};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertFalse(target.output.exists());
+ }
+
+
+ public void test1to1() throws Exception {
+ File f1 = new File(dir_, "01.js");
+ FileUtils.fileWrite(f1.getAbsolutePath(), "1");
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+ target.includes = new String[]{f1.getName()};
+
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f1), FileUtils.fileRead(target.output));
+ }
+
+ public void test2to1() throws Exception {
+ File f1 = new File(dir_, "01.js");
+ FileUtils.fileWrite(f1.getAbsolutePath(), "1");
+
+ File f2 = new File(dir_, "02.js");
+ FileUtils.fileWrite(f2.getAbsolutePath(), "22\n22");
+
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+
+ target.includes = new String[]{f1.getName(), f2.getName()};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f1) + FileUtils.fileRead(f2), FileUtils.fileRead(target.output));
+
+ target.output.delete();
+ target.includes = new String[]{"*.js"};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+// assertEquals(FileUtils.fileRead(f1) + FileUtils.fileRead(f2), FileUtils.fileRead(target.output));
+ }
+
+ public void testNoDuplicateAggregation() throws Exception {
+ File f1 = new File(dir_, "01.js");
+ FileUtils.fileWrite(f1.getAbsolutePath(), "1");
+
+ File f2 = new File(dir_, "02.js");
+ FileUtils.fileWrite(f2.getAbsolutePath(), "22\n22");
+
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+
+ target.includes = new String[]{f1.getName(), f1.getName(), f2.getName()};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f1) + FileUtils.fileRead(f2), FileUtils.fileRead(target.output));
+
+ target.output.delete();
+ target.includes = new String[]{f1.getName(), "*.js"};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f1) + FileUtils.fileRead(f2), FileUtils.fileRead(target.output));
+ }
+
+ public void test2to1Order() throws Exception {
+ File f1 = new File(dir_, "01.js");
+ FileUtils.fileWrite(f1.getAbsolutePath(), "1");
+
+ File f2 = new File(dir_, "02.js");
+ FileUtils.fileWrite(f2.getAbsolutePath(), "2");
+
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+
+ target.includes = new String[]{f2.getName(), f1.getName()};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f2) + FileUtils.fileRead(f1), FileUtils.fileRead(target.output));
+ }
+
+ public void test2to1WithNewLine() throws Exception {
+ File f1 = new File(dir_, "01.js");
+ FileUtils.fileWrite(f1.getAbsolutePath(), "1");
+
+ File f2 = new File(dir_, "02.js");
+ FileUtils.fileWrite(f2.getAbsolutePath(), "22\n22");
+
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+ target.insertNewLine = true;
+ target.includes = new String[]{f1.getName(), f2.getName()};
+
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f1) + "\n" + FileUtils.fileRead(f2) + "\n", FileUtils.fileRead(target.output));
+ }
+
+ public void testAbsolutePathFromInside() throws Exception {
+ File f1 = new File(dir_, "01.js");
+ FileUtils.fileWrite(f1.getAbsolutePath(), "1");
+
+ File f2 = new File(dir_, "02.js");
+ FileUtils.fileWrite(f2.getAbsolutePath(), "22\n22");
+
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+
+ target.includes = new String[]{f1.getAbsolutePath(), f2.getName()};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f1) + FileUtils.fileRead(f2), FileUtils.fileRead(target.output));
+ }
+
+ public void testAbsolutePathFromOutside() throws Exception {
+ File f1 = File.createTempFile("test-01", ".js");
+ try {
+ FileUtils.fileWrite(f1.getAbsolutePath(), "1");
+
+ File f2 = new File(dir_, "02.js");
+ FileUtils.fileWrite(f2.getAbsolutePath(), "22\n22");
+
+ Aggregation target = new Aggregation();
+ target.output = new File(dir_, "output.js");
+
+ target.includes = new String[]{f1.getAbsolutePath(), f2.getName()};
+ assertFalse(target.output.exists());
+ target.run(dir_);
+ assertTrue(target.output.exists());
+ assertEquals(FileUtils.fileRead(f1) + FileUtils.fileRead(f2), FileUtils.fileRead(target.output));
+ } finally {
+ f1.delete();
+ }
+ }
+}
Property changes on: trunk/cdk/maven-javascript-plugin/src/test/java/net/sf/alchim/mojo/yuicompressor/AggregationTestCase.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Revision Author
Modified: trunk/cdk/pom.xml
===================================================================
--- trunk/cdk/pom.xml 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/cdk/pom.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -27,9 +27,11 @@
</pluginManagement>
</build>
<modules>
+ <module>generator</module>
<module>maven-cdk-plugin</module>
- <module>generator</module>
+ <module>maven-javascript-plugin</module>
<module>maven-archetype-jsf-component</module>
<module>maven-archetype-jsfwebapp</module>
+
</modules>
</project>
\ No newline at end of file
Modified: trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletContextImpl.java
===================================================================
--- trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletContextImpl.java 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletContextImpl.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -31,6 +31,7 @@
import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
+import javax.portlet.PortletURL;
import javax.portlet.faces.Bridge;
import org.ajax4jsf.webapp.WebXml;
@@ -235,8 +236,17 @@
*/
@Override
protected String createActionUrl(Map<String, String> parameters){
- return "/ajax4jsfPortletBridge/actionUrl/do/nothitg";
- }
+ RenderResponse renderResponse = (RenderResponse) getPortletResponse();
+ PortletURL portletURL = renderResponse.createActionURL();
+ portletURL.setParameter(AbstractExternalContext.NAMESPACE_PARAMETER,
+ renderResponse.getNamespace());
+ for (Iterator<Map.Entry<String, String>> param = parameters.entrySet()
+ .iterator(); param.hasNext();) {
+ Map.Entry<String, String> parameter = param.next();
+ portletURL.setParameter(parameter.getKey(), parameter.getValue());
+ }
+ return portletURL.toString();
+ }
public String encodeResourceURL(String url) {
return encodeURL(url);
@@ -247,7 +257,7 @@
* @return
*/
protected String encodeURL(String url) {
- return getPortletResponse().encodeURL(url);
+ return getPortletResponse().encodeURL(url);
}
public String getAuthType() {
@@ -307,11 +317,11 @@
}
public void redirect(String url) throws IOException {
- if (null == url) {
+ if (null == url || url.length() < 0) {
throw new NullPointerException("Path to redirect is null");
}
PortalActionURL actionURL = new PortalActionURL(url);
- if(url.startsWith("#") || (!actionURL.isInContext(getRequestContextPath())) || "true".equalsIgnoreCase(actionURL.getParameter(Bridge.DIRECT_LINK))){
+ if(url.startsWith("#") || (!actionURL.isInContext(getRequestContextPath())) || "true".equalsIgnoreCase(actionURL.getParameter(Bridge.DIRECT_LINK))){
((ActionResponse) getResponse()).sendRedirect(url);
}
}
Modified: trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletRenderContextImpl.java
===================================================================
--- trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletRenderContextImpl.java 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/extensions/portletbridge/portletbridge-impl/src/main/java/org/ajax4jsf/portlet/context/PortletRenderContextImpl.java 2007-12-11 23:30:42 UTC (rev 4755)
@@ -91,9 +91,11 @@
throw new NullPointerException("Path to redirect is null");
}
PortalActionURL actionURL = new PortalActionURL(url);
- if(!actionURL.isInContext(getRequestContextPath()) || "true".equalsIgnoreCase(actionURL.getParameter(Bridge.DIRECT_LINK))){
- throw new IllegalStateException(
- "Redirect to new url not at action phase");
+ if(!actionURL.isInContext(getRequestContextPath()) || "true".equalsIgnoreCase(actionURL.getParameter(Bridge.DIRECT_LINK))){
+ dispatch(url);
+
+// throw new IllegalStateException(
+// "Redirect to new url not at action phase");
}
// HACK - if page is in the context, just treat it as navigation case
}
@@ -109,14 +111,14 @@
Entry<String, String> parameter = param.next();
portletURL.setParameter(parameter.getKey(), parameter.getValue());
}
- return portletURL.toString();
+ return portletURL.toString();
}
public void dispatch(String path) throws IOException {
if (null == path) {
throw new NullPointerException("Path to new view is null");
}
- PortletRequestDispatcher dispatcher = getPortletContext()
+ PortletRequestDispatcher dispatcher = getPortletContext()
.getRequestDispatcher(path);
if (null == dispatcher) {
throw new IllegalStateException(
Modified: trunk/framework/impl/pom.xml
===================================================================
--- trunk/framework/impl/pom.xml 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/framework/impl/pom.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -64,30 +64,17 @@
</resourceRoot>
</configuration>
</execution>
- <execution>
- <id>framevork-javascript</id>
- <phase>compile</phase>
- <goals>
- <goal>run</goal>
- </goals>
- <configuration>
- <tasks>
- <copy todir="target/classes/org/ajax4jsf" file="target/compressed/framework.pack.js"/>
- </tasks>
- </configuration>
- </execution>
</executions>
</plugin>
<plugin>
- <groupId>net.sf.alchim</groupId>
- <artifactId>yuicompressor-maven-plugin</artifactId>
+ <groupId>org.richfaces.cdk</groupId>
+ <artifactId>maven-javascript-plugin</artifactId>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<goal>compress</goal>
</goals>
- </execution>
- </executions>
<configuration>
<nosuffix>false</nosuffix>
<outputDirectory>${project.build.directory}/compressed/</outputDirectory>
@@ -98,7 +85,7 @@
-->
<!-- insert new line after each concatenation (default: false) -->
<insertNewLine>true</insertNewLine>
- <output>${project.build.directory}/compressed/framework.pack.js</output>
+ <output>${project.build.outputDirectory}/org/ajax4jsf/framework.pack.js</output>
<!-- files to include, path relative to output's directory or absolute path-->
<includes>
<include>${project.build.directory}/compressed/org/ajax4jsf/javascript/scripts/prototype-min.js</include>
@@ -126,6 +113,25 @@
</resource>
</resources>
</configuration>
+
+ </execution>
+ <execution>
+ <id>jslint</id>
+ <goals>
+ <goal>jslint</goal>
+ </goals>
+ <phase>test</phase>
+ <configuration>
+ <includes><include>**/framework.pack.js</include></includes>
+ <resources>
+ <resource>
+ <directory>${project.build.outputDirectory}</directory>
+ </resource>
+ </resources>
+ </configuration>
+
+ </execution>
+ </executions>
</plugin>
</plugins>
</build>
Modified: trunk/framework/test/pom.xml
===================================================================
--- trunk/framework/test/pom.xml 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/framework/test/pom.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -68,5 +68,10 @@
<version>2.2.1</version>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>rhino</groupId>
+ <artifactId>js</artifactId>
+ <version>1.6R7</version>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
Modified: trunk/samples/seamPortletEar/seamBookingPortlet/pom.xml
===================================================================
--- trunk/samples/seamPortletEar/seamBookingPortlet/pom.xml 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/samples/seamPortletEar/seamBookingPortlet/pom.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -106,8 +106,8 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.richfaces.extensions</groupId>
- <artifactId>portlet</artifactId>
+ <groupId>org.richfaces.extensions.portletbridge</groupId>
+ <artifactId>portletbridge-api</artifactId>
<version>3.2.0-SNAPSHOT</version>
<exclusions>
<exclusion>
@@ -121,6 +121,21 @@
</exclusions>
</dependency>
<dependency>
+ <groupId>org.richfaces.extensions.portletbridge</groupId>
+ <artifactId>portletbridge-impl</artifactId>
+ <version>3.2.0-SNAPSHOT</version>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.faces</groupId>
+ <artifactId>jsf-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>javax.faces</groupId>
+ <artifactId>jsf-impl</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-ui</artifactId>
<version>3.2.0-SNAPSHOT</version>
Modified: trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/portlet.xml
===================================================================
--- trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/portlet.xml 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/portlet.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -19,7 +19,7 @@
-->
<expiration-cache>-0</expiration-cache>
<portlet-info>
- <title>Ajax Portlet</title>
+ <title>Seam Booking Example Portlet</title>
</portlet-info>
<supports>
<mime-type>text/html</mime-type>
Modified: trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/seamBookingPortlet-object.xml
===================================================================
--- trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/seamBookingPortlet-object.xml 2007-12-11 23:02:12 UTC (rev 4754)
+++ trunk/samples/seamPortletEar/seamBookingPortlet/src/main/webapp/WEB-INF/seamBookingPortlet-object.xml 2007-12-11 23:30:42 UTC (rev 4755)
@@ -35,7 +35,7 @@
<if-exists>overwrite</if-exists>
<parent-ref>default.default</parent-ref>
<window>
- <window-name>otherAjaxportletWindow</window-name>
+ <window-name>seamBookingPortletWindow</window-name>
<instance-ref>SeamBookingPortletInstance</instance-ref>
<default>true</default>
<region>center</region>
More information about the richfaces-svn-commits
mailing list