001: /*
002: * PnutsFunction.java
003: *
004: * Copyright (c) 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package pnuts.lang;
010:
011: import java.io.IOException;
012: import java.io.ObjectInputStream;
013: import java.io.ObjectOutputStream;
014: import java.io.Serializable;
015: import java.util.Enumeration;
016: import java.util.NoSuchElementException;
017:
018: /**
019: * A PnutsFunction represents a group of Pnuts functions with a same name.
020: * <p>
021: * This class is serializable, whether the function is compiled or not.
022: * When an PnutsFunction object is serialized, the function definitions
023: * are written to the object stream, along with its attributes such as
024: * configuration, import environment, current package, and module list.
025: * Note that the current package is deeply copied, but the module list are
026: * written as an array of module names.</p>
027: * <p>
028: * When the function is deserialized, the function definition is restored
029: * from the function definition read from the object stream. If the function
030: * had been compiled, the script is compiled. If modules referenced by the
031: * function are not used in the current context, the module is initialized for
032: * the function in such a way that it does not affect the current context.</p>
033: * <p>
034: * On AST interpreter, nested functions with lexical scope can be
035: * serialized/deserialized. But with bytecode compiler, only top-level
036: * functions can be serialized/deserialized.</p>
037: * <p>
038: * Serialized objects of this class will not be compatible with
039: * future releases. The current serialization support is
040: * appropriate for short term storage or RMI between applications running
041: * the same version of Swing. </p>
042: */
043: public class PnutsFunction extends Runtime implements Callable,
044: Cloneable, Serializable {
045:
046: static final long serialVersionUID = -8114380088747362228L;
047:
048: transient Function[] functions = new Function[4];
049:
050: int count = 0; // The number of Functions
051:
052: /**
053: * the name
054: *
055: * @serial
056: */
057: protected String name;
058:
059: final static String _getContext = "getContext".intern();
060:
061: final static String _package = "package".intern();
062:
063: final static String _import = "import".intern();
064:
065: final static String _catch = "catch".intern();
066:
067: final static String _throw = "throw".intern();
068:
069: final static String _eval = "eval".intern();
070:
071: final static String _loadFile = "loadFile".intern();
072:
073: final static String _load = "load".intern();
074:
075: final static String _autoload = "autoload".intern();
076:
077: final static String _quit = "quit".intern();
078:
079: final static String _defined = "defined".intern();
080:
081: final static String _use = "use".intern();
082:
083: final static String _unuse = "unuse".intern();
084:
085: final static String _class = "class".intern();
086:
087: final static String _require = "require".intern();
088:
089: public final static PnutsFunction GET_CONTEXT = new Builtin(
090: _getContext);
091:
092: public final static PnutsFunction PACKAGE = new Builtin(_package);
093:
094: public final static PnutsFunction IMPORT = new Builtin(_import);
095:
096: public final static PnutsFunction CATCH = new Builtin(_catch);
097:
098: public final static PnutsFunction THROW = new Builtin(_throw);
099:
100: public final static PnutsFunction EVAL = new Builtin(_eval);
101:
102: public final static PnutsFunction LOAD_FILE = new Builtin(_loadFile);
103:
104: public final static PnutsFunction LOAD = new Builtin(_load);
105:
106: public final static PnutsFunction AUTOLOAD = new Builtin(_autoload);
107:
108: public final static PnutsFunction QUIT = new Builtin(_quit);
109:
110: public final static PnutsFunction DEFINED = new Builtin(_defined);
111:
112: public final static PnutsFunction USE = new Builtin(_use);
113:
114: public final static PnutsFunction UNUSE = new Builtin(_unuse);
115:
116: public final static PnutsFunction CLASS = new Builtin(_class);
117:
118: public final static PnutsFunction REQUIRE = new Builtin(_require);
119:
120: final static PnutsFunction primitives[] = { GET_CONTEXT, PACKAGE,
121: IMPORT, THROW, EVAL, LOAD_FILE, LOAD, AUTOLOAD, QUIT,
122: DEFINED, USE, UNUSE, CLASS, REQUIRE };
123:
124: protected transient Package pkg = Package.globalPackage;
125:
126: private PnutsFunction parent = null;
127:
128: protected PnutsFunction() {
129: this (null);
130: }
131:
132: /**
133: * Constructor
134: *
135: * @param name
136: * the name of the function
137: */
138: protected PnutsFunction(String name) {
139: this .name = name;
140: }
141:
142: /**
143: * Constructor
144: *
145: * @param name
146: * the name of the function
147: * @param parent
148: * the parent function
149: */
150: protected PnutsFunction(String name, PnutsFunction parent) {
151: this .name = name;
152: this .parent = parent;
153: }
154:
155: synchronized void put(int narg, Function f) {
156: int n = narg + 1;
157: if (functions.length < n + 1) {
158: Function func[] = new Function[(n + 1) * 2];
159: System.arraycopy(functions, 0, func, 0, functions.length);
160: functions = func;
161: }
162: functions[n] = f;
163: f.function = this ;
164: count++;
165: added(narg);
166: }
167:
168: /**
169: * This method is called when a Function object is registered to this
170: * object. This method just returns. Subclass can override this as a hook
171: * method.
172: *
173: * @param narg
174: * the number of parameters
175: */
176: protected void added(int narg) {
177: }
178:
179: public final Function get(int narg) {
180: int n = narg + 1;
181: if (n >= functions.length) {
182: if (parent != null) {
183: return parent.get(narg);
184: } else {
185: return null;
186: }
187: }
188: Function f = functions[n];
189: if (f != null) {
190: return f;
191: }
192: if (parent != null) {
193: return parent.get(narg);
194: } else {
195: return null;
196: }
197: }
198:
199: /**
200: * Check if the function with narg parameter is defined
201: *
202: * @param narg the number of paramters.
203: * @return true if a function with narg, otherwise false
204: */
205: public boolean defined(int narg) {
206: Function f = get(narg);
207: if (f != null) {
208: return true;
209: }
210: f = functions[0];
211: if (f != null) {
212: if (narg >= f.nargs - 1) {
213: return true;
214: }
215: }
216: return parent != null && parent.defined(narg);
217: }
218:
219: /**
220: * @return the name of functions
221: */
222: public String getName() {
223: return name;
224: }
225:
226: /**
227: * Call a function in "context" with arguments "args". Increments the
228: * counter for Pnuts.evalDepth() during the execution.
229: *
230: * @return the result of the call
231: */
232: public final Object call(Object[] args, Context context) {
233: return exec(args, context);
234: }
235:
236: /**
237: * Call a function in "context" with arguments "args". Subclasses of this
238: * class should override this method.
239: *
240: * @return the result of the call
241: */
242: protected Object exec(Object[] args, Context context) {
243: Function f = null;
244: try {
245: f = functions[args.length + 1];
246: } catch (IndexOutOfBoundsException e) {
247: }
248: if (f == null) {
249: f = functions[0];
250: if (f == null) {
251: if (parent != null) {
252: return parent.exec(args, context);
253: } else {
254: undefined(args, context);
255: return null;
256: }
257: } else {
258: int n = f.nargs;
259: int len = args.length;
260: if (len < n - 1) {
261: undefined(args, context);
262: }
263: if (n == 1) {
264: args = new Object[] { args };
265: } else {
266: Object[] newargs = new Object[n];
267: for (int i = 0; i < n - 1; i++) {
268: newargs[i] = args[i];
269: }
270: Object[] rests = new Object[len - n + 1];
271: for (int i = 0; i < len - n + 1; i++) {
272: rests[i] = args[i + n - 1];
273: }
274: newargs[n - 1] = rests;
275: args = newargs;
276: }
277: }
278: }
279: Function caller = context.frame;
280: int line = context.beginLine;
281: ImportEnv saved = context.importEnv;
282: Package outerPackage = context.currentPackage;
283: // boolean eval = context.eval;
284: Configuration config = context.config;
285: ModuleList moduleList = context.moduleList;
286: ModuleList localModuleList = context.localModuleList;
287: context.importEnv = f.importEnv;
288: context.currentPackage = f.pkg;
289: // context.eval = false;
290: context.frame = f;
291: context.config = f.config;
292: context.moduleList = f.moduleList;
293: context.localModuleList = null;
294: try {
295: return f.exec(args, context);
296: } catch (Jump jump) {
297: return jump.getValue();
298: } catch (Escape esc) {
299: throw esc;
300: } catch (ThreadDeath td) {
301: throw td;
302: } catch (Throwable t) {
303: PnutsException p = null;
304: if (t instanceof PnutsException) {
305: p = (PnutsException) t;
306: } else {
307: p = new PnutsException(t, context);
308: }
309: throw p;
310: } finally {
311: context.frame = caller;
312: context.importEnv = saved;
313: // context.eval = eval;
314: context.currentPackage = outerPackage;
315: context.config = config;
316: context.moduleList = moduleList;
317: context.localModuleList = localModuleList;
318: }
319: }
320:
321: protected void undefined(Object args[], Context context) {
322: throw new PnutsException("function.notDefined", new Object[] {
323: name, new Integer(args.length) }, context);
324: }
325:
326: public String toString() {
327: StringBuffer buf = new StringBuffer();
328: int i = 0;
329: while (i < functions.length) {
330: Function f = functions[i++];
331: if (f != null) {
332: buf.append(f.toString());
333: break;
334: }
335: }
336: while (i < functions.length) {
337: Function f = functions[i++];
338: if (f != null) {
339: buf.append(",");
340: buf.append(f.paramString());
341: }
342: }
343: if (buf.length() < 1) {
344: if (name != null) {
345: buf.append("function " + name);
346: } else {
347: buf.append(getClass().getName() + "@"
348: + Integer.toHexString(hashCode()));
349: }
350: }
351: if (parent == null) {
352: return buf.toString();
353: } else {
354: return buf + ", ...";
355: }
356: }
357:
358: /**
359: * call a function "name" in "context" with arguments "args"
360: */
361: public static Object call(String name, Object args[],
362: Context context) {
363: Object o = context.resolveSymbol(name.intern());
364: if (o instanceof PnutsFunction) {
365: return exec((PnutsFunction) o, args, context);
366: } else {
367: throw new PnutsException("function.notDefined",
368: new Object[] { name, new Integer(args.length) },
369: context);
370: }
371: }
372:
373: protected static Object exec(PnutsFunction func, Object args[],
374: Context context) {
375: return func.call(args, context);
376: }
377:
378: /**
379: * Retrieve the symbolic definition of the function.
380: *
381: * @param narg
382: * the number of paramters. -1 means a arbitrary length
383: * parameter.
384: * @return the function definition
385: */
386: public String unparse(int narg) {
387: Function f = get(narg);
388: if (f != null) {
389: return f.unparse(null);
390: }
391: return null;
392: }
393:
394: /**
395: * @return Package in which the function is defined
396: *
397: * @deprecated
398: */
399: public Package getPackage() {
400: return pkg;
401: }
402:
403: /*
404: * experimental
405: */
406: public void setPackage(Package pkg) {
407: this .pkg = pkg;
408: for (Enumeration e = elements(); e != null
409: && e.hasMoreElements();) {
410: Function f = (Function) e.nextElement();
411: f.setPackage(pkg);
412: }
413: }
414:
415: /**
416: * @param narg
417: * the number of paramters. -1 means a arbitrary length
418: * parameter.
419: * @return imports of the function (array of Class or String)
420: */
421: public String[] getImportEnv(int narg) {
422: Function f = get(narg);
423: if (f != null) {
424: return f.importEnv.list();
425: }
426: return null;
427: }
428:
429: public boolean isBuiltin() {
430: return false;
431: }
432:
433: /**
434: * @param narg
435: * the number of paramters. -1 means a arbitrary length
436: * parameter.
437: */
438: public Object accept(int narg, Visitor visitor, Context context) {
439: Function f = get(narg);
440: if (f != null) {
441: return f.accept(visitor, context);
442: }
443: return null;
444: }
445:
446: public Object clone() {
447: try {
448: PnutsFunction f = (PnutsFunction) super .clone();
449: f.functions = (Function[]) functions.clone();
450: return f;
451: } catch (CloneNotSupportedException e) {
452: throw new InternalError();
453: }
454: }
455:
456: class Enum implements Enumeration {
457: int idx = 0;
458:
459: int size = functions.length;
460:
461: public boolean hasMoreElements() {
462: while (!defined(idx - 1) && idx < size) {
463: idx++;
464: }
465: return idx < size;
466: }
467:
468: public Object nextElement() {
469: if (idx < size) {
470: while (!defined(idx - 1)) {
471: idx++;
472: }
473: return get(idx++ - 1);
474: } else {
475: throw new NoSuchElementException(PnutsFunction.this
476: .toString());
477: }
478: }
479: }
480:
481: protected Enumeration elements() {
482: if (count > 0) {
483: return new Enum();
484: } else {
485: return null;
486: }
487: }
488:
489: private void writeObject(ObjectOutputStream s) throws IOException {
490: s.defaultWriteObject();
491: Runtime.serializePnutsFunction(this , s);
492: }
493:
494: private void readObject(ObjectInputStream s) throws IOException,
495: ClassNotFoundException {
496: s.defaultReadObject();
497: Runtime.deserializePnutsFunction(this, s);
498: }
499: }
|