0001: /*
0002: * Context.java
0003: *
0004: * Copyright (c) 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: *
0006: * See the file "LICENSE.txt" for information on usage and redistribution
0007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0008: */
0009: package pnuts.lang;
0010:
0011: import java.io.BufferedReader;
0012: import java.io.FileNotFoundException;
0013: import java.io.IOException;
0014: import java.io.InputStreamReader;
0015: import java.io.OutputStream;
0016: import java.io.PrintWriter;
0017: import java.io.Writer;
0018: import java.net.URL;
0019: import java.net.URLConnection;
0020: import java.util.Enumeration;
0021: import java.util.HashSet;
0022: import java.util.Map;
0023: import java.util.HashMap;
0024: import java.util.Hashtable;
0025: import java.util.Properties;
0026: import java.util.Set;
0027:
0028: import org.pnuts.util.Cell;
0029:
0030: /**
0031: * <em>Context</em> represents an internal state of a particular script
0032: * execution. A Context is created when start executing a script and passed
0033: * around during the execution.
0034: *
0035: * A pnuts.lang.Context object contains the following information.
0036: * <ol>
0037: * <li>Current Package (which Pnuts-package being used)
0038: * <li>Imported Java-package list
0039: * <li>Writer to which print() write data
0040: * <li>Writer to which error() write message
0041: * <li>ClassLoader
0042: * <li>Modules added with use() function.
0043: * <li>Units
0044: * <li>Environments (accessed by Context.get() and set())
0045: * <li>Stack frame (for the pure interpreter)
0046: * <li>Encoding
0047: * </ol>
0048: *
0049: * A clone is created when eval(), load(), or loadFile() is called in a script.
0050: * When a clone is created, (1) and (2) of the clone are reset to the default
0051: * value.
0052: *
0053: */
0054: public class Context implements Cloneable {
0055: private static final boolean DEBUG = false;
0056:
0057: public final static PrintWriter defaultOutputStream = new PrintWriter(
0058: System.out, false);
0059:
0060: public final static PrintWriter defaultTerminalStream = defaultOutputStream;
0061:
0062: public final static PrintWriter defaultErrorStream = new PrintWriter(
0063: System.err, true);
0064:
0065: /**
0066: * internal name for undefined state.
0067: */
0068: private final static Object UNDEF = new Object[0];
0069:
0070: static String exceptionHandlerTableSymbol = Runtime.EXCEPTOIN_FIELD_SYMBOL;
0071:
0072: static String finallyFunctionSymbol = "!finally".intern();
0073:
0074: private ImportEnv defaultImports;
0075:
0076: static Configuration defaultConfig = Configuration.getDefault();
0077:
0078: static boolean defaultVerboseMode = false;
0079: static {
0080: try {
0081: String vb = Runtime.getProperty("pnuts.verbose");
0082: if (vb != null) {
0083: defaultVerboseMode = true;
0084: }
0085: } catch (Throwable t) {
0086: }
0087: }
0088:
0089: private static Runtime defaultRuntime = new Runtime();
0090:
0091: private static PnutsImpl defaultPnutsImpl = PnutsImpl.getDefault();
0092:
0093: Implementation pnutsImpl = defaultPnutsImpl;
0094:
0095: /*
0096: * A flag to tell if this context is created with eval() builtin function
0097: */
0098: boolean eval = false;
0099:
0100: /*
0101: * parent context from which this context is created
0102: */
0103: Context parent;
0104:
0105: // eval depth
0106: protected int depth = 0;
0107:
0108: // caller
0109:
0110: Function frame;
0111:
0112: // the stack frame
0113: StackFrame stackFrame;
0114:
0115: Cell evalFrameStack;
0116:
0117: // the stack which tracks load/loadFile chain
0118: protected Cell loadingResource;
0119:
0120: // line information
0121: protected int beginLine = -1;
0122:
0123: // line information
0124: protected int endLine = -1;
0125:
0126: // column information
0127: protected int beginColumn = -1;
0128:
0129: // import() state
0130: protected ImportEnv importEnv;
0131:
0132: // list of added modules
0133: protected ModuleList moduleList;
0134:
0135: // the module list copied from moduleList
0136: ModuleList localModuleList;
0137:
0138: private Set pendingModules;
0139:
0140: // list of loaded files
0141: protected SymbolTable provideTable = new SymbolTable();
0142:
0143: // set of unit symbols
0144: protected Hashtable unitTable;
0145:
0146: // context-local variables
0147: protected SymbolTable environment;
0148:
0149: // script encoding
0150: String encoding;
0151:
0152: static SymbolTable globals = new SymbolTable();
0153: static {
0154: globals.set("pnuts_version".intern(), Pnuts.pnuts_version);
0155: globals.set(Runtime.INT_SYMBOL, int.class);
0156: globals.set(Runtime.SHORT_SYMBOL, short.class);
0157: globals.set(Runtime.CHAR_SYMBOL, char.class);
0158: globals.set(Runtime.BYTE_SYMBOL, byte.class);
0159: globals.set(Runtime.LONG_SYMBOL, long.class);
0160: globals.set(Runtime.FLOAT_SYMBOL, float.class);
0161: globals.set(Runtime.DOUBLE_SYMBOL, double.class);
0162: globals.set(Runtime.BOOLEAN_SYMBOL, boolean.class);
0163: globals.set(Runtime.VOID_SYMBOL, void.class);
0164: PnutsFunction[] builtins = PnutsFunction.primitives;
0165: for (int i = 0; i < builtins.length; i++) {
0166: globals.set(builtins[i].getName().intern(), builtins[i]);
0167: }
0168: }
0169:
0170: // streams for println()
0171: private PrintWriter outputWriter;
0172:
0173: private OutputStream outputStream;
0174:
0175: // stream for error reporting
0176: private PrintWriter errorWriter;
0177:
0178: // stream for terminal (read-eval-print)
0179: private PrintWriter terminalWriter;
0180:
0181: // the name of the context
0182: private String name;
0183:
0184: // class loader
0185: ClassLoader classLoader[];
0186:
0187: // class loader for class definition
0188: ClassLoader codeLoader;
0189:
0190: boolean namespaceRefreshed[] = { false }; // shared by all clones
0191:
0192: Runtime runtime = defaultRuntime;
0193:
0194: boolean inGeneratorClosure; // for AST interpreter
0195:
0196: BinaryOperator _add; // +
0197: BinaryOperator _subtract; // -
0198: BinaryOperator _multiply; // *
0199: BinaryOperator _mod; // %
0200: BinaryOperator _divide; // /
0201: BinaryOperator _shiftArithmetic; // >>>
0202: BinaryOperator _shiftLeft; // <<
0203: BinaryOperator _shiftRight; // >>
0204: BinaryOperator _and; // &
0205: BinaryOperator _or; // |
0206: BinaryOperator _xor; // ^
0207: UnaryOperator _add1; // ++
0208: UnaryOperator _subtract1; // --
0209: UnaryOperator _not; // ~
0210: UnaryOperator _negate; // -
0211: BooleanOperator _eq; // ==
0212: BooleanOperator _lt; // <
0213: BooleanOperator _le; // <=
0214: BooleanOperator _gt; // >
0215: BooleanOperator _ge; // >=
0216:
0217: /*
0218: * Hook to be executed at the end of a script
0219: */
0220: Executable exitHook;
0221:
0222: /*
0223: * the current package
0224: */
0225: Package currentPackage = Package.getGlobalPackage();
0226: {
0227: setConfiguration(defaultConfig);
0228: currentPackage.init(this );
0229: }
0230:
0231: Package rootPackage = currentPackage;
0232:
0233: /*
0234: * configuration of this context
0235: */
0236: Configuration config;
0237:
0238: boolean verbose = defaultVerboseMode;
0239:
0240: /**
0241: * Create a new context
0242: */
0243: public Context() {
0244: this (Package.getGlobalPackage());
0245: }
0246:
0247: /**
0248: * Creates a context.
0249: *
0250: * @param pkg
0251: * the name of the package.
0252: */
0253: public Context(String pkg) {
0254: this (Package.getPackage(pkg, null));
0255: }
0256:
0257: /**
0258: * Creates a context.
0259: *
0260: * @param pkg
0261: * the initial package of the context. If null, the global
0262: * package is used.
0263: */
0264: public Context(Package pkg) {
0265: if (pkg != null) {
0266: setCurrentPackage(pkg);
0267: }
0268: this .outputWriter = defaultOutputStream;
0269: this .outputStream = System.out;
0270: this .terminalWriter = defaultTerminalStream;
0271: this .errorWriter = defaultErrorStream;
0272: this .classLoader = new ClassLoader[] { config
0273: .getInitialClassLoader() };
0274: this .importEnv = defaultImports;
0275: }
0276:
0277: /**
0278: * Creates a context from a template
0279: *
0280: * @param context
0281: * The template
0282: * @since 1.0beta9
0283: */
0284: public Context(Context context) {
0285: this .outputWriter = context.outputWriter;
0286: this .errorWriter = context.errorWriter;
0287: this .terminalWriter = context.terminalWriter;
0288: this .currentPackage = context.currentPackage;
0289: this .pnutsImpl = context.pnutsImpl;
0290: this .encoding = context.encoding;
0291: this .setConfiguration(context.config);
0292:
0293: this .classLoader = new ClassLoader[] { context.classLoader[0] };
0294: this .namespaceRefreshed = new boolean[] { context.namespaceRefreshed[0] };
0295:
0296: this .importEnv = (ImportEnv) context.importEnv.clone();
0297:
0298: if (context.moduleList != null) {
0299: this .moduleList = (ModuleList) context.moduleList.clone();
0300: }
0301: if (context.localModuleList != null) {
0302: this .localModuleList = (ModuleList) context.localModuleList
0303: .clone();
0304: }
0305:
0306: if (context.unitTable != null) {
0307: this .unitTable = (Hashtable) context.unitTable.clone();
0308: }
0309: if (context.environment != null) {
0310: this .environment = (SymbolTable) context.environment
0311: .clone();
0312: }
0313: this .provideTable = (SymbolTable) context.provideTable.clone();
0314: }
0315:
0316: public Context(Properties properties) {
0317: setConfiguration(Configuration.getDefault(properties));
0318: setImplementation(PnutsImpl.getDefault(properties));
0319: }
0320:
0321: /**
0322: * Make a clone of the context
0323: */
0324: public Object clone() {
0325: return clone(true, true);
0326: }
0327:
0328: /**
0329: * Make a clone of the context
0330: *
0331: * @param clear_attributes
0332: * If true, import() state and current package are reset to the
0333: * default values.
0334: * @param clear_locals
0335: * If true, local stack is reset.
0336: */
0337: public Object clone(boolean clear_attributes, boolean clear_locals) {
0338: synchronized (this ) {
0339: if (moduleList == null) {
0340: moduleList = new ModuleList(currentPackage);
0341: }
0342: }
0343: try {
0344: Context ret = (Context) super .clone();
0345: if (clear_locals) {
0346: if (stackFrame != null) {
0347: ret.stackFrame = new StackFrame();
0348: }
0349: ret.parent = this ;
0350: if (environment != null) {
0351: ret.environment = new SymbolTable(environment);
0352: }
0353: }
0354: if (clear_attributes) {
0355: ret.eval = false;
0356: ret.frame = null;
0357: ret.codeLoader = null;
0358: ret.importEnv = defaultImports;
0359: ret.currentPackage = rootPackage;
0360: ret.beginLine = -1;
0361: ret.beginColumn = -1;
0362: }
0363: return ret;
0364: } catch (CloneNotSupportedException e) {
0365: throw new PnutsException(e, this );
0366: }
0367: }
0368:
0369: /**
0370: * Sets the name of the context
0371: *
0372: * @param name
0373: * The name of the context.
0374: */
0375: public void setName(String name) {
0376: this .name = name;
0377: }
0378:
0379: /**
0380: * Gets the name of the context
0381: *
0382: * @return The name of the context.
0383: */
0384: public String getName() {
0385: return name;
0386: }
0387:
0388: /**
0389: * Changes the PnutsImpl object associated with this context
0390: *
0391: * @param impl
0392: * The PnutsImpl object, which defines the implementation of the
0393: * interpreter. eval(), load(), and loadFile() of
0394: * pnuts.lang.Pnuts select an implementation (pure interpreter,
0395: * on-the-fly compiler, etc.), according to the context passed to
0396: * the methods.
0397: *
0398: * @deprecated replaced by setImplementation()
0399: */
0400: public void setPnutsImpl(PnutsImpl impl) {
0401: setImplementation(impl);
0402: }
0403:
0404: /**
0405: * Gets the PnutsImpl object associated with this context
0406: *
0407: * @deprecated replaced by getImplementation()
0408: */
0409: public PnutsImpl getPnutsImpl() {
0410: return (PnutsImpl) getImplementation();
0411: }
0412:
0413: /**
0414: * Changes the Implementation object associated with this context
0415: *
0416: * @param impl
0417: * The Implementation object, which defines the implementation of
0418: * the interpreter. eval(), load(), and loadFile() of
0419: * pnuts.lang.Pnuts select an implementation (pure interpreter,
0420: * on-the-fly compiler, etc.), according to the context passed to
0421: * the methods.
0422: */
0423: public void setImplementation(Implementation impl) {
0424: if (impl != null) {
0425: pnutsImpl = impl;
0426: }
0427: }
0428:
0429: /**
0430: * Gets the Implementation object associated with this context
0431: */
0432: public Implementation getImplementation() {
0433: return pnutsImpl;
0434: }
0435:
0436: /**
0437: * Gets an environemnt variable associated with this context.
0438: *
0439: * @param symbol
0440: * the name of the variable, which must be intern'ed.
0441: * @return the value of the variable
0442: */
0443: public Object get(String symbol) {
0444: if (environment != null) {
0445: Value v = environment.lookup(symbol);
0446: if (v != null) {
0447: return v.get();
0448: }
0449: }
0450: return null;
0451: }
0452:
0453: /**
0454: * Defines an environemnt variable associated with this context
0455: *
0456: * To access those environment variables, Context.get(String) should be
0457: * called. Note that those variables can not be accessed just by specifying
0458: * their names in Pnuts interpreter.
0459: *
0460: * Since the environment varariables are bound to the executing context,
0461: * they are accessible from various modules that the script uses. Therefore,
0462: * the name of environment variables should have prefixes so that name
0463: * conflict is unlikely to occur. The name that starts with "pnuts." is
0464: * reserved.
0465: *
0466: * @param symbol
0467: * the name of the variable, which must be intern'ed.
0468: * @param value
0469: * the value of the variable
0470: * @since 1.0beta8
0471: */
0472: public void set(String symbol, Object value) {
0473: synchronized (this ) {
0474: if (environment == null) {
0475: environment = new SymbolTable();
0476: }
0477: }
0478: environment.set(symbol, value);
0479: }
0480:
0481: /**
0482: * Returns an enumeration of the keys in the environment of this context.
0483: */
0484: public Enumeration keys() {
0485: synchronized (this ) {
0486: if (environment == null) {
0487: environment = new SymbolTable();
0488: }
0489: }
0490: return environment.keys();
0491: }
0492:
0493: /**
0494: * set output stream of the context
0495: *
0496: * @deprecated replaced by setTerminalWriter(Writer, boolean)
0497: */
0498: public void setOutputStream(Object out, boolean autoFlush) {
0499: if (out instanceof PrintWriter || out == null) {
0500: this .outputWriter = (PrintWriter) out;
0501: this .outputStream = null;
0502: } else if (out instanceof OutputStream) {
0503: this .outputStream = (OutputStream) out;
0504: this .outputWriter = new PrintWriter((OutputStream) out,
0505: autoFlush);
0506: } else if (out instanceof Writer) {
0507: this .outputStream = null;
0508: this .outputWriter = new PrintWriter((Writer) out, autoFlush);
0509: } else {
0510: throw new IllegalArgumentException(Runtime.getMessage(
0511: "pnuts.lang.pnuts", "illegal.streamType",
0512: Runtime.NO_PARAM));
0513: }
0514: }
0515:
0516: /**
0517: * set output stream of the context
0518: *
0519: * @deprecated replaced by setTerminalWriter(Writer)
0520: */
0521: public void setOutputStream(Object outputStream) {
0522: setOutputStream(outputStream, false);
0523: }
0524:
0525: /**
0526: * Set the specified OutputStream as the standard output stream of the
0527: * context, to which write() writes data. A PrintWriter is created from the
0528: * specified OutputStream, which is returned by getWriter(). If null is
0529: * specified, both getOutputStream() and getWriter() return null.
0530: *
0531: * @param out
0532: * the OutputStream
0533: */
0534: public void setOutputStream(OutputStream out) {
0535: this .outputStream = out;
0536: if (out == null) {
0537: this .outputWriter = null;
0538: } else {
0539: this .outputWriter = new PrintWriter(out, false);
0540: }
0541: }
0542:
0543: /**
0544: * Set the specified Writer as the standard writer of the context.
0545: * PrintWriter is created from the specified Writer if the Writer is not an
0546: * instance of PrintWriter. If this method has been called,
0547: * getOutputStream() returns null. If null is specifed to this method, both
0548: * getWriter() and getOutputStream() return null.
0549: *
0550: * @param out
0551: * the Writer
0552: */
0553: public void setWriter(Writer out) {
0554: setWriter(out, false);
0555: }
0556:
0557: /**
0558: * Set the specified Writer as the standard writer of the context.
0559: * PrintWriter is created from the specified Writer if the Writer is not an
0560: * instance of PrintWriter. If this method has been called,
0561: * getOutputStream() returns null. If null is specifed to this method, both
0562: * getWriter() and getOutputStream() return null.
0563: *
0564: * @param out
0565: * the Writer
0566: * @param autoFlush
0567: * A boolean; if true, the PrintWriter.println() methods will
0568: * flush the output buffer
0569: */
0570: public void setWriter(Writer out, boolean autoFlush) {
0571: this .outputStream = null;
0572: if (out instanceof PrintWriter || out == null) {
0573: this .outputWriter = (PrintWriter) out;
0574: } else {
0575: this .outputWriter = new PrintWriter(out, autoFlush);
0576: }
0577: }
0578:
0579: /**
0580: * Get the standard output stream of the context, to which write() writes
0581: * data. This method returns the OutputStream previously set by
0582: * setOutputStream(). If setWriter() has been called, getOutputStream()
0583: * returns null.
0584: *
0585: * @return the standard output stream of the context
0586: */
0587: public OutputStream getOutputStream() {
0588: return outputStream;
0589: }
0590:
0591: /**
0592: * Get the standard writer of the context, to which print()/println() write
0593: * messages.
0594: *
0595: * @return the standard writer of the context
0596: */
0597: public PrintWriter getWriter() {
0598: return outputWriter;
0599: }
0600:
0601: /**
0602: * Set the terminal stream of the context, in which the prompt is shown.
0603: *
0604: * @deprecated replaced by setTerminalWriter(Writer, boolean)
0605: */
0606: public void setTerminalStream(Object str, boolean autoFlush) {
0607: if (str == null) {
0608: this .terminalWriter = null;
0609: } else if (str instanceof OutputStream) {
0610: this .terminalWriter = new PrintWriter((OutputStream) str,
0611: autoFlush);
0612: } else if (str instanceof Writer) {
0613: this .terminalWriter = new PrintWriter((Writer) str,
0614: autoFlush);
0615: } else {
0616: throw new IllegalArgumentException(Runtime.getMessage(
0617: "pnuts.lang.pnuts", "illegal.streamType",
0618: Runtime.NO_PARAM));
0619: }
0620: }
0621:
0622: /**
0623: * Set the terminal stream of the context
0624: *
0625: * @deprecated replaced by setTerminalWriter(Writer)
0626: */
0627: public void setTerminalStream(Object stream) {
0628: if (stream == null) {
0629: this .terminalWriter = null;
0630: } else if (stream instanceof PrintWriter) {
0631: this .terminalWriter = (PrintWriter) stream;
0632: } else if (stream instanceof Writer) {
0633: this .terminalWriter = new PrintWriter((Writer) stream, true);
0634: } else if (stream instanceof OutputStream) {
0635: this .terminalWriter = new PrintWriter(
0636: (OutputStream) stream, true);
0637: } else {
0638: throw new IllegalArgumentException(String.valueOf(stream));
0639: }
0640: }
0641:
0642: /**
0643: * Set the terminal writer of the context
0644: *
0645: * @param w
0646: * the Writer
0647: */
0648: public void setTerminalWriter(Writer w) {
0649: if (w instanceof PrintWriter || w == null) {
0650: this .terminalWriter = (PrintWriter) w;
0651: } else {
0652: this .terminalWriter = new PrintWriter(w);
0653: }
0654: }
0655:
0656: /**
0657: * Set the terminal writer of the context
0658: *
0659: * @param w
0660: * the Writer
0661: */
0662: public void setTerminalWriter(Writer w, boolean autoFlush) {
0663: if (w == null) {
0664: this .terminalWriter = null;
0665: } else {
0666: this .terminalWriter = new PrintWriter(w, autoFlush);
0667: }
0668: }
0669:
0670: /**
0671: * get terminal-output-stream of the context
0672: *
0673: * @deprecated replaced by getTerminalWriter(Writer)
0674: */
0675: public PrintWriter getTerminalStream() {
0676: return terminalWriter;
0677: }
0678:
0679: /**
0680: * get terminal-output-stream of the context
0681: */
0682: public PrintWriter getTerminalWriter() {
0683: return terminalWriter;
0684: }
0685:
0686: /**
0687: * Set an OutputStream or a Writer to which error() write messages If
0688: * errorStream is null, exception is thrown out of eval loop.
0689: *
0690: * @deprecated replaced by setErrorWriter(Writer, boolean)
0691: */
0692: public void setErrorStream(Object errorStream, boolean autoFlush) {
0693: if (errorStream == null) {
0694: this .errorWriter = null;
0695: } else if (errorStream instanceof OutputStream) {
0696: this .errorWriter = new PrintWriter(
0697: (OutputStream) errorStream, autoFlush);
0698: } else if (errorStream instanceof Writer) {
0699: this .errorWriter = new PrintWriter((Writer) errorStream,
0700: autoFlush);
0701: } else {
0702: throw new IllegalArgumentException(Runtime.getMessage(
0703: "pnuts.lang.pnuts", "illegal.streamType",
0704: Runtime.NO_PARAM));
0705: }
0706: }
0707:
0708: /**
0709: * Set ar PrintWriter to which error() write messages
0710: *
0711: * @deprecated replaced by setErrorWriter(Writer)
0712: */
0713: public void setErrorStream(Object errorStream) {
0714: if (errorStream == null) {
0715: this .errorWriter = null;
0716: } else if (errorStream instanceof OutputStream) {
0717: this .errorWriter = new PrintWriter(
0718: (OutputStream) errorStream, false);
0719: } else if (errorStream instanceof PrintWriter) {
0720: this .errorWriter = (PrintWriter) errorStream;
0721: } else if (errorStream instanceof Writer) {
0722: this .errorWriter = new PrintWriter((Writer) errorStream,
0723: false);
0724: } else {
0725: throw new IllegalArgumentException(String
0726: .valueOf(errorStream));
0727: }
0728: }
0729:
0730: /**
0731: *
0732: */
0733: public void setErrorWriter(Writer w, boolean autoFlush) {
0734: if (w instanceof PrintWriter || w == null) {
0735: this .errorWriter = (PrintWriter) w;
0736: } else {
0737: this .errorWriter = new PrintWriter(w, autoFlush);
0738: }
0739: }
0740:
0741: public void setErrorWriter(Writer w) {
0742: if (w instanceof PrintWriter || w == null) {
0743: this .errorWriter = (PrintWriter) w;
0744: } else {
0745: setErrorWriter(w, false);
0746: }
0747: }
0748:
0749: /**
0750: * Get an OutputStream or a Writer to which error() write messages
0751: *
0752: * @deprecated replaced by getErrorWriter
0753: */
0754: public PrintWriter getErrorStream() {
0755: return errorWriter;
0756: }
0757:
0758: /**
0759: * Get an PrintWriter to which error() write messages
0760: */
0761: public PrintWriter getErrorWriter() {
0762: return errorWriter;
0763: }
0764:
0765: /**
0766: * get the current package
0767: */
0768: public Package getCurrentPackage() {
0769: return currentPackage;
0770: }
0771:
0772: /**
0773: * set the current package
0774: */
0775: public void setCurrentPackage(Package pkg) {
0776: pkg.init(this );
0777: this .currentPackage = pkg;
0778: Package root = pkg.root;
0779: if (root != null) {
0780: this .rootPackage = root;
0781: }
0782: }
0783:
0784: /**
0785: * Changes the current class loader for this context.
0786: *
0787: * The initial value is set to
0788: * Thread.currentThread().getContextClassLoader() when the instance is
0789: * created.
0790: *
0791: * @param loader
0792: * the class loader
0793: */
0794: public void setClassLoader(ClassLoader loader) {
0795: classLoader[0] = loader;
0796: synchronized (namespaceRefreshed) {
0797: namespaceRefreshed[0] = true;
0798: }
0799: }
0800:
0801: /**
0802: * Gets the current class loader.
0803: *
0804: * The initial value is set to
0805: * Thread.currentThread().getContextClassLoader() when the instance is
0806: * created.
0807: *
0808: * @return the class loader
0809: */
0810: public ClassLoader getClassLoader() {
0811: return classLoader[0];
0812: }
0813:
0814: /**
0815: * Gets the current class loader for class geneartion
0816: *
0817: * The initial value is null. Expressions that generate class
0818: * create a classloader to load generated classes based on the current
0819: * class loader.
0820: *
0821: * @return the class loader
0822: */
0823: public ClassLoader getCodeLoader() {
0824: return this .codeLoader;
0825: }
0826:
0827: /**
0828: * Sets the current class loader for class geneartion
0829: *
0830: * Expressions that generate class create a classloader to load generated
0831: * classes based on the current class loader.
0832: *
0833: * @param loader the class loader
0834: */
0835: public void setCodeLoader(ClassLoader loader) {
0836: this .codeLoader = loader;
0837: }
0838:
0839: /**
0840: * Changes the configuration for this context.
0841: *
0842: * @param config
0843: * the configuration
0844: */
0845: public void setConfiguration(Configuration config) {
0846: if (config == null) {
0847: return;
0848: }
0849: this .config = config;
0850: this .defaultImports = Runtime.getDefaultImports(this );
0851: if (this .importEnv == null) {
0852: this .importEnv = this .defaultImports;
0853: }
0854:
0855: // copy operators
0856: this ._add = config._add;
0857: this ._add1 = config._add1;
0858: this ._subtract = config._subtract;
0859: this ._subtract1 = config._subtract1;
0860: this ._multiply = config._multiply;
0861: this ._mod = config._mod;
0862: this ._divide = config._divide;
0863: this ._shiftArithmetic = config._shiftArithmetic;
0864: this ._shiftLeft = config._shiftLeft;
0865: this ._shiftRight = config._shiftRight;
0866: this ._and = config._and;
0867: this ._or = config._or;
0868: this ._xor = config._xor;
0869: this ._not = config._not;
0870: this ._negate = config._negate;
0871: this ._eq = config._eq;
0872: this ._lt = config._lt;
0873: this ._le = config._le;
0874: this ._gt = config._gt;
0875: this ._ge = config._ge;
0876: }
0877:
0878: /*
0879: * Gets the current configuration
0880: */
0881: public Configuration getConfiguration() {
0882: return config;
0883: }
0884:
0885: /**
0886: * Sets a hook to be executed at the end of a script. The default value is
0887: * null.
0888: *
0889: * @param hook
0890: * the hook
0891: */
0892: public void setExitHook(Executable hook) {
0893: exitHook = hook;
0894: }
0895:
0896: /**
0897: * Gets the hook to be executed at the end of a script
0898: *
0899: */
0900: public Executable getExitHook() {
0901: return exitHook;
0902: }
0903:
0904: void addClassToImport(String className) {
0905: synchronized (this ) {
0906: if (importEnv == defaultImports) {
0907: importEnv = (ImportEnv) importEnv.clone();
0908: }
0909: }
0910: importEnv.addClass(className);
0911: }
0912:
0913: void addPackageToImport(String pkgName) {
0914: synchronized (this ) {
0915: if (importEnv == defaultImports) {
0916: importEnv = (ImportEnv) importEnv.clone();
0917: }
0918: }
0919: importEnv.addPackage(pkgName);
0920: }
0921:
0922: void addStaticMembers(String name, boolean wildcard) {
0923: synchronized (this ) {
0924: if (importEnv == defaultImports) {
0925: importEnv = (ImportEnv) importEnv.clone();
0926: }
0927: }
0928: importEnv.addStaticMembers(name, wildcard, this );
0929: }
0930:
0931: /*
0932: * Declares explicitly that the context is reading from a script source
0933: * (usually a URL). Must be followed by popFile() call.
0934: */
0935: synchronized void pushFile(Object file) {
0936: Cell cell = new Cell();
0937: cell.object = file;
0938: cell.next = loadingResource;
0939: loadingResource = cell;
0940: }
0941:
0942: /*
0943: * Declares that the current script source is no longer used.
0944: */
0945: synchronized void popFile() {
0946: if (exitHook != null) {
0947: exitHook.run(this );
0948: }
0949: if (loadingResource != null) {
0950: loadingResource = loadingResource.next;
0951: }
0952: }
0953:
0954: public synchronized boolean unusePackage(Package pkg) {
0955: if (moduleList != null) {
0956: if (moduleList.remove(pkg)) {
0957: localModuleList = null;
0958: return true;
0959: }
0960: }
0961: return false;
0962: }
0963:
0964: public boolean usePackage(Package pkg, boolean checkException) {
0965: synchronized (this ) {
0966: if (moduleList == null) {
0967: moduleList = new ModuleList(currentPackage);
0968: }
0969: if (pendingModules == null) {
0970: pendingModules = new HashSet();
0971: }
0972: }
0973: boolean pending = pendingModules.contains(pkg);
0974: try {
0975: if (!pending && !moduleList.contains(pkg)) {
0976: if (pkg.usedAsModule) {
0977: Context ctx = (Context) clone();
0978: ctx.setCurrentPackage(pkg);
0979: for (Enumeration e = pkg.providedModuleNames
0980: .elements(); e.hasMoreElements();) {
0981: String m = (String) e.nextElement();
0982: if (DEBUG) {
0983: System.out.println("! " + pkg.getName()
0984: + " provides " + m);
0985: }
0986: ctx.usePackage(m);
0987: }
0988:
0989: if (pkg.requiredModuleNames.size() > 0) {
0990: ctx = (Context) ctx.clone();
0991: ctx.clearPackages();
0992: }
0993:
0994: for (Enumeration e = pkg.requiredModuleNames
0995: .elements(); e.hasMoreElements();) {
0996: String m = (String) e.nextElement();
0997: if (DEBUG) {
0998: System.out.println(pkg.getName()
0999: + " requires " + m);
1000: }
1001: ctx.usePackage(m);
1002: }
1003: } else {
1004: pkg.initializeModule();
1005: }
1006: String name = pkg.getName();
1007: synchronized (pkg.moduleIntializationLock) {
1008: pendingModules.add(pkg);
1009: try {
1010: if (!pkg.initialized) {
1011: if (name != null) {
1012: loadModule(name, pkg);
1013: }
1014: pkg.initialized = true;
1015: }
1016: } finally {
1017: pendingModules.remove(pkg);
1018: }
1019: if (name != null && moduleList != null) {
1020: if (currentPackage.usedAsModule) {
1021: if (moduleList.basePackage != currentPackage) {
1022: if (DEBUG) {
1023: System.out.println(currentPackage
1024: .getName()
1025: + " provides... " + name);
1026: }
1027: currentPackage.providedModuleNames
1028: .addElement(name);
1029: } else {
1030: currentPackage.requiredModuleNames
1031: .addElement(name);
1032: }
1033: }
1034: }
1035: }
1036: }
1037: if (!pending) {
1038: moduleList.add(pkg);
1039: }
1040: localModuleList = null;
1041: return true;
1042: } catch (Throwable t) {
1043: if (checkException) {
1044: Runtime.checkException(this , t);
1045: } else {
1046: if (verbose && terminalWriter != null) {
1047: t.printStackTrace(terminalWriter);
1048: }
1049: }
1050: }
1051: return false;
1052: }
1053:
1054: /**
1055: * Add a package to the use()'d package list.
1056: *
1057: * @param name
1058: * the package name
1059: * @return true if successfully use()'d.
1060: */
1061: public boolean usePackage(String name) {
1062: return usePackage(name, false);
1063: }
1064:
1065: /**
1066: * Add a package to the use()'d package list.
1067: *
1068: * @param name
1069: * the package name
1070: * @param checkException
1071: * if false exceptions are ignored
1072: * @return true if successfully use()'d.
1073: */
1074: public boolean usePackage(String name, boolean checkException) {
1075: return usePackage(Package.getPackage(name, this ),
1076: checkException);
1077: }
1078:
1079: /**
1080: * Loads a module is it has not been loaded yet.
1081: *
1082: * The initialization script is: 1) Replace :: and . with / then append
1083: * "/init", e.g. pnuts.lib => pnuts/lib/init 2) The 1st line in
1084: * META-INF/pnuts/module/ <module_name>
1085: *
1086: * @param name
1087: * the name of the module
1088: * @param pkg
1089: * the associated package (name space)
1090: * @exception FileNotFoundException
1091: * thrown when the initialization script is not found.
1092: */
1093: protected void loadModule(String name, Package pkg)
1094: throws IOException {
1095: if (DEBUG) {
1096: System.out.println("loading " + name);
1097: }
1098: Context ctx = (Context) clone();
1099:
1100: ctx.setCurrentPackage(pkg);
1101: ctx.config = defaultConfig;
1102:
1103: FileNotFoundException notfound = null;
1104: try {
1105: Pnuts.load(pkg.getInitScript(), ctx);
1106: } catch (FileNotFoundException e) {
1107: notfound = e;
1108: }
1109: if (notfound != null) {
1110: URL url = Pnuts.getResource(
1111: "META-INF/pnuts/module/" + name, this );
1112: if (url != null) {
1113: BufferedReader br = new BufferedReader(
1114: new InputStreamReader(url.openStream(), "UTF-8"));
1115: try {
1116: String line = br.readLine();
1117: if (line != null) {
1118: try {
1119: Pnuts.load(line.trim(), ctx);
1120: } catch (FileNotFoundException e2) {
1121: throw e2;
1122: }
1123: notfound = null;
1124: }
1125: } finally {
1126: br.close();
1127: }
1128: }
1129: }
1130: if (notfound != null) {
1131: throw notfound;
1132: }
1133:
1134: if (!pkg.exports) {
1135: pkg.exportFunctions();
1136: }
1137: }
1138:
1139: /**
1140: * Unregisteres all use()'d packages
1141: */
1142: public synchronized void clearPackages() {
1143: moduleList = new ModuleList(currentPackage);
1144: localModuleList = null;
1145: }
1146:
1147: synchronized ModuleList localModuleList() {
1148: ModuleList list = this .localModuleList;
1149: if (list == null && this .moduleList != null) {
1150: list = this .localModuleList = (ModuleList) this .moduleList
1151: .clone();
1152: }
1153: return list;
1154: }
1155:
1156: /**
1157: * Returns the list of use()'d packages
1158: */
1159: public String[] usedPackages() {
1160: if (moduleList == null) {
1161: return new String[] {};
1162: } else {
1163: return moduleList.getPackageNames();
1164: }
1165: }
1166:
1167: /**
1168: * Loads a script file only if it has not been loaded. It is guaranteed that
1169: * the script runs at most once in this context.
1170: *
1171: * @param file
1172: * the script file, which must be an intern'ed String.
1173: */
1174: void require(String file, boolean checkForUpdate)
1175: throws FileNotFoundException {
1176: SymbolTable table = provideTable;
1177: Binding b;
1178: synchronized (table) {
1179: b = table.lookup0(file);
1180: if (b == null) {
1181: table.set(file, null);
1182: b = table.lookup0(file);
1183: }
1184: }
1185: synchronized (b) {
1186: Long timestamp = (Long) b.value;
1187: if (timestamp == null) {
1188: Pnuts.load(file, this );
1189: } else if (checkForUpdate) {
1190: long m = lastModified(file);
1191: if (m > timestamp.longValue()) {
1192: Pnuts.load(file, this );
1193: }
1194: }
1195: }
1196: }
1197:
1198: long lastModified(String file) {
1199: try {
1200: URL url = Runtime.getScriptURL(file + ".pnut", this );
1201: if (url == null) {
1202: return -1;
1203: }
1204: URLConnection c = url.openConnection();
1205: return c.getLastModified();
1206: } catch (IOException e) {
1207: return -1;
1208: }
1209: }
1210:
1211: void provide(String file) {
1212: file = file.intern();
1213: Binding b;
1214: SymbolTable table = provideTable;
1215: synchronized (table) {
1216: b = table.lookup0(file);
1217: if (b == null) {
1218: table.set(file, null);
1219: b = table.lookup0(file);
1220: }
1221: }
1222: synchronized (b) {
1223: b.set(new Long(System.currentTimeMillis()));
1224: }
1225: }
1226:
1227: void revoke(String file) {
1228: file = file.intern();
1229: provideTable.removeBinding(file);
1230: }
1231:
1232: /**
1233: * Get the source of the script.
1234: *
1235: * @return
1236: *
1237: * <pre>
1238: *
1239: * java.net.URL object, when the script is not precompiled
1240: * pnuts.lang.Runtime object, when the script is precompiled
1241: *
1242: * </pre>
1243: */
1244: protected Object getScriptSource() {
1245: if (frame != null) {
1246: return frame.file;
1247: } else {
1248: Cell c = loadingResource;
1249: if (c != null) {
1250: return c.object;
1251: } else {
1252: return null;
1253: }
1254: }
1255: }
1256:
1257: void updateLine(SimpleNode node) {
1258: updateLine(node, node.beginLine, node.beginColumn);
1259: }
1260:
1261: /**
1262: * AST interpreter calls this method when line number changes, giving AST nodes
1263: * and line information
1264: *
1265: * Not that compiler does not call this method.
1266: *
1267: * @param node the current AST node
1268: * @param beginLine the line number at which the current expression starts.
1269: * @param beginColumn the column number at which theh current expression ends.
1270: */
1271: protected void updateLine(SimpleNode node, int beginLine,
1272: int beginColumn) {
1273: updateLine(beginLine);
1274: updateColumn(beginColumn);
1275: }
1276:
1277: /**
1278: * Both AST interpreter and compiler call this method when line number changes.
1279: *
1280: * Subclasses may override this method to interact with
1281: * running script. For example, a subclass may redefine this
1282: * method so that it can stop the execution if Thread.interrupt()
1283: * has been called.
1284: *
1285: * @param line the line number
1286: */
1287: protected void updateLine(int line) {
1288: if (line > 0) {
1289: this .beginLine = line;
1290: this .endLine = line;
1291: this .beginColumn = -1;
1292: }
1293: }
1294:
1295: protected void updateColumn(int column) {
1296: if (column > 0) {
1297: this .beginColumn = column;
1298: }
1299: }
1300:
1301: /**
1302: * This method is called when the excecution is terminated normally.
1303: */
1304: protected void onExit(Object arg) {
1305: }
1306:
1307: /**
1308: * This method is called when an exception is thrown.
1309: */
1310: protected void onError(Throwable t) {
1311: }
1312:
1313: /**
1314: * Gets the value of a symbol.
1315: *
1316: * @param interned
1317: * a symbol (interned string)
1318: * @return the value of the symbol
1319: * @exception PnutsException
1320: * if the specified symbol is not defined
1321: */
1322: public Object getId(String interned) {
1323: Object v = _getId(interned);
1324: if (v == UNDEF) {
1325: return undefined(interned);
1326: } else {
1327: return v;
1328: }
1329: }
1330:
1331: /**
1332: * Resolves the value of a symbol in the following order:
1333: *
1334: * (1) current package (2) builtin functions, primitive types, pnuts_version
1335: * (3) module exports (4) imported classes (5) parent packages
1336: *
1337: * @param interned
1338: * a symbol (interned string)
1339: * @return the value of the symbol, or null if it is not defined.
1340: */
1341: public Object resolveSymbol(String interned) {
1342: Object v = _getId(interned);
1343: if (v == UNDEF) {
1344: return null;
1345: } else {
1346: return v;
1347: }
1348: }
1349:
1350: static Map primitiveTypes = new HashMap();
1351: static {
1352: primitiveTypes.put("int", int.class);
1353: primitiveTypes.put("short", short.class);
1354: primitiveTypes.put("long", long.class);
1355: primitiveTypes.put("byte", byte.class);
1356: primitiveTypes.put("char", char.class);
1357: primitiveTypes.put("long", long.class);
1358: primitiveTypes.put("boolean", boolean.class);
1359: primitiveTypes.put("float", float.class);
1360: primitiveTypes.put("double", double.class);
1361: }
1362:
1363: /*
1364: * Resolves a class using import()'ed names
1365: *
1366: * @param symbol an interned String
1367: * @return a Class object that represents
1368: * the class, or null if not found.
1369: */
1370: public Class resolveClass(String symbol) {
1371: Class type = (Class) primitiveTypes.get(symbol);
1372: if (type != null) {
1373: return type;
1374: }
1375:
1376: if (symbol.indexOf('.') > 0) {
1377: try {
1378: return Pnuts.loadClass(symbol, this );
1379: } catch (ClassNotFoundException e) {
1380: return null;
1381: }
1382: } else {
1383: NamedValue binding = currentPackage.lookup(symbol);
1384: if (binding != null) {
1385: Object value = binding.get();
1386: if (value instanceof Class) {
1387: return (Class) value;
1388: }
1389: }
1390: Object obj = importEnv.get(symbol, this );
1391: if (obj instanceof Class) {
1392: return (Class) obj;
1393: } else {
1394: obj = _getId(symbol);
1395: if (obj instanceof Class) {
1396: return (Class) obj;
1397: }
1398: return null;
1399: }
1400: }
1401: }
1402:
1403: Object _getId(String symbol) {
1404: Value v = currentPackage.lookup(symbol, this );
1405: if (v != null) {
1406: return v.get();
1407: }
1408: v = globals.lookup0(symbol);
1409: if (v != null) {
1410: return v.get();
1411: }
1412: if (moduleList != null) {
1413: v = moduleList.resolve(symbol, this );
1414: if (v != null) {
1415: return v.get();
1416: }
1417: }
1418:
1419: if (symbol.charAt(0) == '!') {
1420: return null;
1421: }
1422:
1423: synchronized (namespaceRefreshed) {
1424: if (namespaceRefreshed[0]) {
1425: resetImportEnv();
1426: }
1427: namespaceRefreshed[0] = false;
1428: }
1429:
1430: Object c = importEnv.get(symbol, this );
1431: if (c != null) {
1432: return c;
1433: }
1434:
1435: Package parent = currentPackage.getParent();
1436: if (parent != null) {
1437: v = parent.lookupRecursively(symbol, this );
1438: }
1439: if (v != null) {
1440: return v.get();
1441: }
1442:
1443: return UNDEF;
1444: }
1445:
1446: void resetImportEnv() {
1447: importEnv.reset();
1448: if (parent != null) {
1449: parent.resetImportEnv();
1450: }
1451: }
1452:
1453: /**
1454: * Registers an autoload script for the <em>name</em>. If <em>name</em>
1455: * is not defined when accessed, the registerred <em>file</em> is loaded.
1456: *
1457: * @param name
1458: * variable name
1459: * @param file
1460: * the file
1461: */
1462: public void autoload(String name, String file) {
1463: currentPackage.autoload(name, file, this );
1464: }
1465:
1466: /**
1467: * Registers an AutoloadHook for the <em>name</em> in the current package.
1468: *
1469: * @param name
1470: * variable name
1471: * @param hook
1472: * the AutoloadHook
1473: */
1474: public void autoload(String name, AutoloadHook hook) {
1475: currentPackage.autoload(name, hook);
1476: }
1477:
1478: Object undefined(String sym) {
1479: return config.handleUndefinedSymbol(sym, this );
1480: }
1481:
1482: /**
1483: * Checks if the name is defined in the context.
1484: */
1485: public boolean defined(String name) {
1486: return _getId(name.intern()) != UNDEF;
1487: }
1488:
1489: /**
1490: * Defines a unit.
1491: *
1492: * @param unit
1493: * The unit symbol
1494: * @param fac
1495: * A QuantityFactory object which defines what kind of object is
1496: * created when a decimal number with this unit symbol is
1497: * evaluated.
1498: */
1499: public void registerQuantityFactory(String unit, QuantityFactory fac) {
1500: synchronized (this ) {
1501: if (unitTable == null) {
1502: unitTable = new Hashtable(8);
1503: }
1504: }
1505: if (fac != null) {
1506: unitTable.put(unit, fac);
1507: } else {
1508: unitTable.remove(unit);
1509: }
1510: }
1511:
1512: ////// Methods below are used by the pure interpreter
1513:
1514: /*
1515: * open new scope with the pure interpreter
1516: */
1517: protected void open(Function f, Object args[]) {
1518: String[] locals = f.locals;
1519: stackFrame = new StackFrame(locals, stackFrame);
1520: StackFrame sf = stackFrame;
1521: for (int i = 0; i < args.length; i++) {
1522: sf.bind(locals[i], args[i]);
1523: }
1524: if (f.outer != null && f.name != null) {
1525: sf.bind(f.name, f.function);
1526: }
1527: }
1528:
1529: /*
1530: * close the scope with the pure interpreter
1531: */
1532: protected void close(Function func, Object args[]) {
1533: stackFrame = stackFrame.parent;
1534: }
1535:
1536: /*
1537: * open for/foreach scope with the pure interpreter
1538: */
1539: void openLocal(String locals[]) {
1540: stackFrame.openLocal(locals);
1541: }
1542:
1543: /*
1544: * close for/foreach scope with the pure interpreter
1545: */
1546: void closeLocal() {
1547: stackFrame.closeLocal();
1548: }
1549:
1550: final void bind(String symbol, Object obj) {
1551: stackFrame.bind(symbol, obj);
1552: }
1553:
1554: void resetStackFrame() {
1555: if (stackFrame != null) {
1556: stackFrame = new StackFrame();
1557: }
1558: }
1559:
1560: protected Object getValue(String symbol) {
1561: Object val;
1562:
1563: if (stackFrame != null) {
1564: Binding b = (Binding) stackFrame.lookup(symbol);
1565: if (b != null) {
1566: return b.value;
1567: }
1568: Function ff = frame;
1569: while (ff != null) {
1570: SymbolTable ls = ff.lexicalScope;
1571: if (ls != null) {
1572: Binding bb = ls.lookup0(symbol);
1573: if (bb != null) {
1574: return ((Binding) bb.value).value;
1575: }
1576: }
1577: ff = ff.outer;
1578: }
1579: }
1580: val = _getId(symbol);
1581: if (val == UNDEF) {
1582: return undefined(symbol);
1583: } else {
1584: return val;
1585: }
1586: }
1587:
1588: protected void setValue(String symbol, Object obj) {
1589: Binding b = (Binding) stackFrame.lookup(symbol);
1590: if (b != null) {
1591: b.value = obj;
1592: return;
1593: }
1594:
1595: Function ff = frame;
1596:
1597: while (ff != null) {
1598: SymbolTable ls = ff.lexicalScope;
1599: if (ls != null) {
1600: Binding bb = ls.lookup0(symbol);
1601: if (bb != null) {
1602: ((Binding) bb.value).value = obj;
1603: return;
1604: }
1605: }
1606: ff = ff.outer;
1607: }
1608:
1609: if (stackFrame.parent != null) {
1610: stackFrame.declare(symbol, obj);
1611: } else {
1612: currentPackage.set(symbol, obj, this );
1613: }
1614: }
1615:
1616: void catchException(Class t, PnutsFunction f) {
1617: if (stackFrame.parent != null) {
1618: Runtime.TypeMap tmap = null;
1619: Value b = stackFrame.lookup(exceptionHandlerTableSymbol);
1620: if (b != null) {
1621: tmap = (Runtime.TypeMap) b.get();
1622: }
1623: Runtime.TypeMap newtmap = new Runtime.TypeMap(t, f, tmap);
1624: stackFrame.declare(exceptionHandlerTableSymbol, newtmap);
1625: } else {
1626: Runtime.catchException(t, f, this );
1627: }
1628: }
1629:
1630: void setFinallyFunction(final PnutsFunction func) {
1631: if (stackFrame.parent != null) {
1632: stackFrame.declare(finallyFunctionSymbol, func);
1633: if (frame != null) {
1634: frame.finallySet = true;
1635: }
1636: } else {
1637: Runtime.setExitHook(this , func);
1638: }
1639: }
1640:
1641: /**
1642: * Sets the verbose mode
1643: */
1644: public void setVerbose(boolean b) {
1645: verbose = b;
1646: }
1647:
1648: /**
1649: * Check the current verbose mode
1650: *
1651: * @return the current verbose mode
1652: */
1653: public boolean isVerbose() {
1654: return verbose;
1655: }
1656:
1657: /**
1658: * Changes the script encoding for the context
1659: *
1660: * @param encoding
1661: * the encoding
1662: */
1663: public void setScriptEncoding(String encoding) {
1664: this .encoding = encoding;
1665: }
1666:
1667: /**
1668: * Gets the current script encoding
1669: *
1670: * @return the current script encoding
1671: */
1672: public String getScriptEncoding() {
1673: return this.encoding;
1674: }
1675: }
|