📄 parser.java
字号:
/* * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */package com.caucho.es.parser;import com.caucho.es.ESBase;import com.caucho.es.ESException;import com.caucho.es.ESId;import com.caucho.es.ESParseException;import com.caucho.es.Script;import com.caucho.java.JavaCompiler;import com.caucho.java.LineMap;import com.caucho.loader.SimpleLoader;import com.caucho.log.Log;import com.caucho.server.util.CauchoSystem;import com.caucho.util.CharBuffer;import com.caucho.util.IntArray;import com.caucho.util.L10N;import com.caucho.vfs.MergePath;import com.caucho.vfs.Path;import com.caucho.vfs.ReadStream;import com.caucho.vfs.Vfs;import com.caucho.vfs.WriteStream;import java.io.IOException;import java.util.ArrayList;import java.util.logging.Logger;/** * Parser is a factory for generating compiled Script objects. * * <p>Most applications will use the <code>parse(String)</code> interface * to parse JavaScript. That method will try to load a precompiled * script from the work directory before trying to parse it. * * <p>Applications will often set the script path a directory for * script and include the classpath in the path. Applications will * often override the work dir for a more appropriate work directory. * * <code><pre> * package com.caucho.vfs.*; * package com.caucho.es.*; * * ... * * com.caucho.es.parser.Parser parser; * parser = new com.caucho.es.parser.Parser(); * * // configure the path to search for *.js files * MergePath scriptPath = new MergePath(); * scriptPath.addMergePath(Vfs.lookup("/home/ferg/js")); * ClassLoader loader = Thread.currentThread().contextClassLoader(); * scriptPath.addClassPath(loader); * parser.setScriptPath(scriptPath); * * // configure the directory storing compiled scripts * Path workPath = Vfs.lookup("/tmp/caucho/work"); * parser.setWorkDir(workPath); * * Script script = parser.parse("test.js"); * </pre></code> */public class Parser { private static final Logger log = Log.open(Parser.class); private static final L10N L = new L10N(Parser.class); private static final Object LOCK = new Object(); static ESId CLINIT = ESId.intern("__clinit__"); static ESId PROTOTYPE = ESId.intern("prototype"); static ESId FINALLY = ESId.intern("finally"); static ESId ANONYMOUS = ESId.intern("anonymous"); static ESId OBJECT = ESId.intern("Object"); static ESId REGEXP = ESId.intern("RegExp"); static ESId ARRAY = ESId.intern("Array"); static ESId LENGTH = ESId.intern("length"); static ESId PACKAGES = ESId.intern("Packages"); static ESId JAVA = ESId.intern("java"); static ESId COM = ESId.intern("com"); static ESId CAUCHO = ESId.intern("caucho"); static final int PREC_DOT = 1; static final int PREC_POST = PREC_DOT; static final int PREC_FUN = PREC_POST + 1; static final int PREC_UMINUS = PREC_FUN + 1; static final int PREC_TIMES = PREC_UMINUS + 1; static final int PREC_PLUS = PREC_TIMES + 1; static final int PREC_SHIFT = PREC_PLUS + 1; static final int PREC_CMP = PREC_SHIFT + 1; static final int PREC_EQ = PREC_CMP + 1; static final int PREC_BITAND = PREC_EQ + 1; static final int PREC_BITXOR = PREC_BITAND + 1; static final int PREC_BITOR = PREC_BITXOR + 1; static final int PREC_AND = PREC_BITOR + 1; static final int PREC_OR = PREC_AND + 1; static final int PREC_COND = PREC_OR + 1; static final int PREC_ASSIGN = PREC_COND + 1; static final int PREC_COMMA = PREC_ASSIGN + 1; static final int PREC_MAX = PREC_COMMA + 1; ClassLoader parentLoader; ClassLoader loader; Path scriptPath; boolean isEval; // Name of the generated class String className; Lexer lexer; IntArray hashes = new IntArray(); ArrayList importList = new ArrayList(); ParseClass parseClass; Function globalFunction; Function staticFunction; Function function; // the current prototype, i.e. class Block block; LineMap lineMap; Path workPath; //JavaCompiler compiler; boolean isFast; public Parser() { workPath = CauchoSystem.getWorkPath(); //compiler.setEncoding("utf8"); addImport("java.lang.*"); addImport("com.caucho.jslib.*"); } /** * Sets the parent class loader. If unspecified, defaults to the context * classloader. Most applications will just use the default. * * @param parentLoader the classloader to be used for the script's parent. */ public void setParentLoader(ClassLoader parentLoader) { this.parentLoader = parentLoader; } /** * Internal method to set the actual class loader. * Normally, this should only be called from com.caucho.es functions. */ public void setClassLoader(ClassLoader loader) { this.loader = loader; } /** * Returns the current class loader. If null, creates from the parent * loader and the work-dir. */ ClassLoader getClassLoader() { if (loader != null) return loader; if (parentLoader != null) loader = SimpleLoader.create(parentLoader, workPath, null); else loader = SimpleLoader.create(null, workPath, null); return loader; } /** * Sets the path to search for imported javascript source files. * Normally, ScriptPath will be a MergePath. If the MergePath * adds the classpath, then JavaScript files can be put in the * normal Java classpath. * * <p>If the ScriptPath is not specified, it will use the * current directory and the classpath from the context class loader. * * <code><pre> * MergePath scriptPath = new MergePath(); * scriptPath.addMergePath(Vfs.lookup("/home/ferg/js")); * * ClassLoader loader = Thread.currentThread().contextClassLoader(); * scriptPath.addClassPath(loader); * * parser.setScriptPath(scriptPath); * </pre></code> * * @param scriptPath path to search for imported scripts. */ public void setScriptPath(Path scriptPath) { this.scriptPath = scriptPath; } /** * Returns the path to search for imported javascript. Normally, scriptPath * will be a MergePath. * * @param scriptPath path to search for imported scripts. */ public Path getScriptPath() { return scriptPath; } /** * Adds a package/script to be automatically imported by the script. * Each import is the equivalent of adding the following javascript: * * <code><pre> * package <em>name</em>; * </pre></code> * * @param name package or script name to be automatically imported. */ public void addImport(String name) { if (! importList.contains(name)) importList.add(name); } /** * Sets "fast" mode, i.e. JavaScript 2.0. Fast mode lets the compiler * make assumptions about types and classes, e.g. that class methods * won't change dynamically. This lets the compiler generate more code * that directly translates to Java calls. */ public void setFast(boolean isFast) { this.isFast = isFast; } /** * Sets a line number map. For generated files like JSP or XTP, * the error messages need an extra translation to get to the original * line numbers. */ public void setLineMap(LineMap lineMap) { this.lineMap = lineMap; } /** * Sets the name of the generated java class. If unset, the parser will * mangle the input name. */ public void setClassName(String name) { this.className = name; } /** * Sets the directory for generated *.java and *.class files. * The parser will check this directory for any precompiled javascript * classes. The default work-dir is /tmp/caucho on unix and * c:\temp\caucho on windows. * * @param path the work directory. */ public void setWorkDir(Path path) { workPath = path; } /** * Returns the directory for generated *.java and *.class files. */ public Path getWorkDir() { return workPath; } /** * Main application parsing method. The parser will try to load * the compiled script. If the compiled script exists and the * source file has not changed, parse will return the old script. * Otherwise, it will parse and compile the javascript. * * @param name the name of the javascript source. * * @return the parsed script */ public Script parse(String name) throws ESException, IOException { Path path; try { String className; if (this.className != null) className = this.className; else className = "_js." + JavaCompiler.mangleName(name); if (scriptPath == null) { MergePath mergePath = new MergePath(); mergePath.addMergePath(Vfs.lookup()); ClassLoader parentLoader = this.parentLoader; if (parentLoader == null) parentLoader = Thread.currentThread().getContextClassLoader(); mergePath.addClassPath(parentLoader); scriptPath = mergePath; } Script script = loadScript(className); if (! script.isModified()) { script.setScriptPath(getScriptPath()); script.setClassDir(workPath); return script; } } catch (Throwable e) { } path = getScriptPath().lookup(name); ReadStream is = path.openRead(); try { return parse(is, name, 1); } finally { is.close(); } } /** * Alternative parsing method when the application only has an open * stream to the file. Since this method will always compile a new script, * it can be significantly slower than the <code>parse(String)</code> * method. * * @param is a read stream to the javascript source. * * @return the parsed script. */ public Script parse(ReadStream is) throws ESException, IOException { return parse(is, null, 1); } /** * An alternative parsing method given an open stream, a filename and * a line number. * * @param is a stream to the javascript source. * @param name filename to use for error messages. * @param line initial line number. * * @return the compiled script */ public Script parse(ReadStream is, String name, int line) throws ESException, IOException { if (name == null) name = is.getUserPath(); if (line <= 0) line = 1; return parse(is, name, line, false); } /** * Parses a script for the JavaScript "eval" expression. The * semantics for "eval" are slightly different from standard * scripts. * * @param is stream to the eval source. * @param name filename to use for error messages * @param line initial line number to use for error messages. * * @return the compiled script. */ public Script parseEval(ReadStream is, String name, int line) throws ESException, IOException { if (name == null) name = "eval"; if (line <= 0) line = 1; return parse(is, name, line, true); } /** * The main parser method. * * @param is stream to read the script. * @param name the filename to use for error messages. * @param line the line number to use for error messages. * @param isEval if true, parse for an eval expression. * * @return the compiled javascript */ private Script parse(ReadStream is, String name, int line, boolean isEval) throws ESException, IOException { if (name == null) name = "anonymous"; if (line <= 0) line = 1; lexer = new Lexer(is, name, line); if (lineMap != null) lexer.setLineMap(lineMap); if (className == null) className = "_js." + JavaCompiler.mangleName(name); if (scriptPath == null) { MergePath mergePath = new MergePath(); if (is.getPath() != null) mergePath.addMergePath(is.getPath().getParent()); else mergePath.addMergePath(Vfs.lookup()); ClassLoader parentLoader = this.parentLoader; if (parentLoader == null) parentLoader = Thread.currentThread().getContextClassLoader(); mergePath.addClassPath(parentLoader); scriptPath = mergePath; } block = null; JavaCompiler compiler = JavaCompiler.create(getClassLoader()); compiler.setClassDir(workPath); parseClass = new ParseClass(name, className); parseClass.setParser(this); if (is.getPath() != null && is.getPath().getLastModified() > 0) parseClass.setSourcePath(is.getPath()); globalFunction = parseClass.newFunction(null, ESId.intern("global"), false); globalFunction.setFast(isFast); staticFunction = parseClass.newFunction(null, ESId.intern("__es_static"), false); parseClass.setGlobal(globalFunction); if (isEval) { block = Block.create(this, globalFunction); block.finish(); function = parseClass.newFunction(globalFunction, ESId.intern("eval"), false); function.setEval(); } else function = globalFunction; block = Block.create(this, function); parseBlock(true); block.finish(); if (lexer.peek() != Lexer.EOF) throw expect(L.l("end of file")); block = Block.create(this, staticFunction); block.finish(); synchronized (LOCK) { Path path = workPath.lookup(className.replace('.', '/') + ".java"); path.getParent().mkdirs(); WriteStream os = path.openWrite(); os.setEncoding("JAVA"); parseClass.writeCode(os); os.close(); Script script; try { compiler.compile(className.replace('.', '/') + ".java", null);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -