001: /*
002: * Copyright 2004,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.bsf.engines.javascript;
018:
019: import java.util.Iterator;
020: import java.util.Vector;
021:
022: import org.apache.bsf.BSFDeclaredBean;
023: import org.apache.bsf.BSFException;
024: import org.apache.bsf.BSFManager;
025: import org.apache.bsf.util.BSFEngineImpl;
026: import org.apache.bsf.util.BSFFunctions;
027: import org.mozilla.javascript.Context;
028: import org.mozilla.javascript.EvaluatorException;
029: import org.mozilla.javascript.Function;
030: import org.mozilla.javascript.ImporterTopLevel;
031: import org.mozilla.javascript.JavaScriptException;
032: import org.mozilla.javascript.NativeJavaObject;
033: import org.mozilla.javascript.Scriptable;
034: import org.mozilla.javascript.WrappedException;
035: import org.mozilla.javascript.Wrapper;
036:
037: /**
038: * This is the interface to Netscape's Rhino (JavaScript) from the
039: * Bean Scripting Framework.
040: * <p>
041: * The original version of this code was first written by Adam Peller
042: * for use in LotusXSL. Sanjiva took his code and adapted it for BSF.
043: *
044: * @author Adam Peller <peller@lotus.com>
045: * @author Sanjiva Weerawarana
046: * @author Matthew J. Duftler
047: * @author Norris Boyd
048: */
049: public class JavaScriptEngine extends BSFEngineImpl {
050: /**
051: * The global script object, where all embedded functions are defined,
052: * as well as the standard ECMA "core" objects.
053: */
054: private Scriptable global;
055:
056: /**
057: * Return an object from an extension.
058: * @param object Object on which to make the call (ignored).
059: * @param method The name of the method to call.
060: * @param args an array of arguments to be
061: * passed to the extension, which may be either
062: * Vectors of Nodes, or Strings.
063: */
064: public Object call(Object object, String method, Object[] args)
065: throws BSFException {
066:
067: Object retval = null;
068: Context cx;
069:
070: try {
071: cx = Context.enter();
072:
073: // REMIND: convert arg list Vectors here?
074:
075: Object fun = global.get(method, global);
076: // NOTE: Source and line arguments are nonsense in a call().
077: // Any way to make these arguments *sensible?
078: if (fun == Scriptable.NOT_FOUND)
079: throw new EvaluatorException("function " + method
080: + " not found.", "none", 0);
081:
082: cx.setOptimizationLevel(-1);
083: cx.setGeneratingDebug(false);
084: cx.setGeneratingSource(false);
085: cx.setOptimizationLevel(0);
086: cx.setDebugger(null, null);
087:
088: retval = ((Function) fun).call(cx, global, global, args);
089:
090: // ScriptRuntime.call(cx, fun, global, args, global);
091:
092: if (retval instanceof Wrapper)
093: retval = ((Wrapper) retval).unwrap();
094: } catch (Throwable t) {
095: handleError(t);
096: } finally {
097: Context.exit();
098: }
099: return retval;
100: }
101:
102: public void declareBean(BSFDeclaredBean bean) throws BSFException {
103: if ((bean.bean instanceof Number)
104: || (bean.bean instanceof String)
105: || (bean.bean instanceof Boolean)) {
106: global.put(bean.name, global, bean.bean);
107: } else {
108: // Must wrap non-scriptable objects before presenting to Rhino
109: Scriptable wrapped = Context.toObject(bean.bean, global);
110: global.put(bean.name, global, wrapped);
111: }
112: }
113:
114: /**
115: * This is used by an application to evaluate a string containing
116: * some expression.
117: */
118: public Object eval(String source, int lineNo, int columnNo,
119: Object oscript) throws BSFException {
120:
121: String scriptText = oscript.toString();
122: Object retval = null;
123: Context cx;
124:
125: try {
126: cx = Context.enter();
127:
128: cx.setOptimizationLevel(-1);
129: cx.setGeneratingDebug(false);
130: cx.setGeneratingSource(false);
131: cx.setOptimizationLevel(0);
132: cx.setDebugger(null, null);
133:
134: retval = cx.evaluateString(global, scriptText, source,
135: lineNo, null);
136:
137: if (retval instanceof NativeJavaObject)
138: retval = ((NativeJavaObject) retval).unwrap();
139:
140: } catch (Throwable t) { // includes JavaScriptException, rethrows Errors
141: handleError(t);
142: } finally {
143: Context.exit();
144: }
145: return retval;
146: }
147:
148: private void handleError(Throwable t) throws BSFException {
149: if (t instanceof WrappedException)
150: t = ((WrappedException) t).getWrappedException();
151:
152: String message = null;
153: Throwable target = t;
154:
155: if (t instanceof JavaScriptException) {
156: message = t.getLocalizedMessage();
157:
158: // Is it an exception wrapped in a JavaScriptException?
159: Object value = ((JavaScriptException) t).getValue();
160: if (value instanceof Throwable) {
161: // likely a wrapped exception from a LiveConnect call.
162: // Display its stack trace as a diagnostic
163: target = (Throwable) value;
164: }
165: } else if (t instanceof EvaluatorException
166: || t instanceof SecurityException) {
167: message = t.getLocalizedMessage();
168: } else if (t instanceof RuntimeException) {
169: message = "Internal Error: " + t.toString();
170: } else if (t instanceof StackOverflowError) {
171: message = "Stack Overflow";
172: }
173:
174: if (message == null)
175: message = t.toString();
176:
177: if (t instanceof Error && !(t instanceof StackOverflowError)) {
178: // Re-throw Errors because we're supposed to let the JVM see it
179: // Don't re-throw StackOverflows, because we know we've
180: // corrected the situation by aborting the loop and
181: // a long stacktrace would end up on the user's console
182: throw (Error) t;
183: } else {
184: throw new BSFException(BSFException.REASON_OTHER_ERROR,
185: "JavaScript Error: " + message, target);
186: }
187: }
188:
189: /**
190: * Initialize the engine.
191: * Put the manager into the context-manager
192: * map hashtable too.
193: */
194: public void initialize(BSFManager mgr, String lang,
195: Vector declaredBeans) throws BSFException {
196:
197: super .initialize(mgr, lang, declaredBeans);
198:
199: // Initialize context and global scope object
200: try {
201: Context cx = Context.enter();
202: global = new ImporterTopLevel(cx);
203: Scriptable bsf = Context.toObject(new BSFFunctions(mgr,
204: this ), global);
205: global.put("bsf", global, bsf);
206:
207: for (Iterator it = declaredBeans.iterator(); it.hasNext();) {
208: declareBean((BSFDeclaredBean) it.next());
209: }
210: } catch (Throwable t) {
211:
212: } finally {
213: Context.exit();
214: }
215: }
216:
217: public void undeclareBean(BSFDeclaredBean bean) throws BSFException {
218: global.delete(bean.name);
219: }
220: }
|