2003/05/12 05:10:30
[org.ibex.core.git] / src / org / mozilla / javascript / FunctionObject.java
1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
2  *\r
3  * The contents of this file are subject to the Netscape Public\r
4  * License Version 1.1 (the "License"); you may not use this file\r
5  * except in compliance with the License. You may obtain a copy of\r
6  * the License at http://www.mozilla.org/NPL/\r
7  *\r
8  * Software distributed under the License is distributed on an "AS\r
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
10  * implied. See the License for the specific language governing\r
11  * rights and limitations under the License.\r
12  *\r
13  * The Original Code is Rhino code, released\r
14  * May 6, 1999.\r
15  *\r
16  * The Initial Developer of the Original Code is Netscape\r
17  * Communications Corporation.  Portions created by Netscape are\r
18  * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
19  * Rights Reserved.\r
20  *\r
21  * Contributor(s): \r
22  * Norris Boyd\r
23  * Igor Bukanov\r
24  * David C. Navas\r
25  * Ted Neward\r
26  *\r
27  * Alternatively, the contents of this file may be used under the\r
28  * terms of the GNU Public License (the "GPL"), in which case the\r
29  * provisions of the GPL are applicable instead of those above.\r
30  * If you wish to allow use of your version of this file only\r
31  * under the terms of the GPL and not to allow others to use your\r
32  * version of this file under the NPL, indicate your decision by\r
33  * deleting the provisions above and replace them with the notice\r
34  * and other provisions required by the GPL.  If you do not delete\r
35  * the provisions above, a recipient may use your version of this\r
36  * file under either the NPL or the GPL.\r
37  */\r
38 \r
39 // API class\r
40 \r
41 package org.mozilla.javascript;\r
42 \r
43 import java.util.Vector;\r
44 import java.lang.reflect.Constructor;\r
45 import java.lang.reflect.Member;\r
46 import java.lang.reflect.Method;\r
47 import java.lang.reflect.Modifier;\r
48 import java.lang.reflect.InvocationTargetException;\r
49 \r
50 public class FunctionObject extends NativeFunction {\r
51 \r
52     /**\r
53      * Create a JavaScript function object from a Java method.\r
54      *\r
55      * <p>The <code>member</code> argument must be either a java.lang.reflect.Method\r
56      * or a java.lang.reflect.Constructor and must match one of two forms.<p>\r
57      *\r
58      * The first form is a member with zero or more parameters\r
59      * of the following types: Object, String, boolean, Scriptable,\r
60      * byte, short, int, float, or double. The Long type is not supported\r
61      * because the double representation of a long (which is the \r
62      * EMCA-mandated storage type for Numbers) may lose precision.\r
63      * If the member is a Method, the return value must be void or one \r
64      * of the types allowed for parameters.<p>\r
65      *\r
66      * The runtime will perform appropriate conversions based\r
67      * upon the type of the parameter. A parameter type of\r
68      * Object specifies that no conversions are to be done. A parameter\r
69      * of type String will use Context.toString to convert arguments.\r
70      * Similarly, parameters of type double, boolean, and Scriptable\r
71      * will cause Context.toNumber, Context.toBoolean, and\r
72      * Context.toObject, respectively, to be called.<p>\r
73      *\r
74      * If the method is not static, the Java 'this' value will\r
75      * correspond to the JavaScript 'this' value. Any attempt\r
76      * to call the function with a 'this' value that is not\r
77      * of the right Java type will result in an error.<p>\r
78      *\r
79      * The second form is the variable arguments (or "varargs")\r
80      * form. If the FunctionObject will be used as a constructor,\r
81      * the member must have the following parameters\r
82      * <pre>\r
83      *      (Context cx, Object[] args, Function ctorObj,\r
84      *       boolean inNewExpr)</pre>\r
85      * and if it is a Method, be static and return an Object result.<p>\r
86      *\r
87      * Otherwise, if the FunctionObject will <i>not</i> be used to define a\r
88      * constructor, the member must be a static Method with parameters\r
89      *      (Context cx, Scriptable thisObj, Object[] args,\r
90      *       Function funObj) </pre>\r
91      * <pre>\r
92      * and an Object result.<p>\r
93      *\r
94      * When the function varargs form is called as part of a function call,\r
95      * the <code>args</code> parameter contains the\r
96      * arguments, with <code>thisObj</code>\r
97      * set to the JavaScript 'this' value. <code>funObj</code>\r
98      * is the function object for the invoked function.<p>\r
99      *\r
100      * When the constructor varargs form is called or invoked while evaluating\r
101      * a <code>new</code> expression, <code>args</code> contains the\r
102      * arguments, <code>ctorObj</code> refers to this FunctionObject, and\r
103      * <code>inNewExpr</code> is true if and only if  a <code>new</code>\r
104      * expression caused the call. This supports defining a function that\r
105      * has different behavior when called as a constructor than when\r
106      * invoked as a normal function call. (For example, the Boolean\r
107      * constructor, when called as a function,\r
108      * will convert to boolean rather than creating a new object.)<p>\r
109      *\r
110      * @param name the name of the function\r
111      * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor\r
112      *                            that defines the object\r
113      * @param scope enclosing scope of function\r
114      * @see org.mozilla.javascript.Scriptable\r
115      */\r
116     public FunctionObject(String name, Member methodOrConstructor,\r
117                           Scriptable scope)\r
118     {\r
119         String methodName;\r
120         if (methodOrConstructor instanceof Constructor) {\r
121             ctor = (Constructor) methodOrConstructor;\r
122             isStatic = true; // well, doesn't take a 'this'\r
123             types = ctor.getParameterTypes();\r
124             methodName = ctor.getName();\r
125         } else {\r
126             method = (Method) methodOrConstructor;\r
127             isStatic = Modifier.isStatic(method.getModifiers());\r
128             types = method.getParameterTypes();\r
129             methodName = method.getName();\r
130         }\r
131         this.functionName = name;\r
132         int length;\r
133         if (types.length == 4 && (types[1].isArray() || types[2].isArray())) {\r
134             // Either variable args or an error.\r
135             if (types[1].isArray()) {\r
136                 if (!isStatic ||\r
137                     types[0] != Context.class ||\r
138                     types[1].getComponentType() != ScriptRuntime.ObjectClass ||\r
139                     types[2] != ScriptRuntime.FunctionClass ||\r
140                     types[3] != Boolean.TYPE)\r
141                 {\r
142                     throw Context.reportRuntimeError1(\r
143                         "msg.varargs.ctor", methodName);\r
144                 }\r
145                 parmsLength = VARARGS_CTOR;\r
146             } else {\r
147                 if (!isStatic ||\r
148                     types[0] != Context.class ||\r
149                     types[1] != ScriptRuntime.ScriptableClass ||\r
150                     types[2].getComponentType() != ScriptRuntime.ObjectClass ||\r
151                     types[3] != ScriptRuntime.FunctionClass)\r
152                 {\r
153                     throw Context.reportRuntimeError1(\r
154                         "msg.varargs.fun", methodName);\r
155                 }\r
156                 parmsLength = VARARGS_METHOD;\r
157             }\r
158             // XXX check return type\r
159             length = 1;\r
160         } else {\r
161             parmsLength = (short) types.length;\r
162             for (int i=0; i < parmsLength; i++) {\r
163                 Class type = types[i];\r
164                 if (type != ScriptRuntime.ObjectClass &&\r
165                     type != ScriptRuntime.StringClass &&\r
166                     type != ScriptRuntime.BooleanClass &&\r
167                     !ScriptRuntime.NumberClass.isAssignableFrom(type) &&\r
168                     !Scriptable.class.isAssignableFrom(type) &&\r
169                     type != Boolean.TYPE &&\r
170                     type != Byte.TYPE &&\r
171                     type != Short.TYPE &&\r
172                     type != Integer.TYPE &&\r
173                     type != Float.TYPE && \r
174                     type != Double.TYPE)\r
175                 {\r
176                     // Note that long is not supported.\r
177                     throw Context.reportRuntimeError1("msg.bad.parms", \r
178                                                       methodName);\r
179                 }\r
180             }\r
181             length = parmsLength;\r
182         }\r
183 \r
184         // Initialize length property\r
185         lengthPropertyValue = (short) length;\r
186 \r
187         hasVoidReturn = method != null && method.getReturnType() == Void.TYPE;\r
188         this.argCount = (short) length;\r
189 \r
190         setParentScope(scope);\r
191         setPrototype(getFunctionPrototype(scope));\r
192         Context cx = Context.getCurrentContext();\r
193         useDynamicScope = cx != null && \r
194                           cx.hasCompileFunctionsWithDynamicScope();\r
195     }\r
196 \r
197     /**\r
198      * Return the value defined by  the method used to construct the object\r
199      * (number of parameters of the method, or 1 if the method is a "varargs"\r
200      * form), unless setLength has been called with a new value.\r
201      * Overrides getLength in BaseFunction.\r
202      *\r
203      * @see org.mozilla.javascript.FunctionObject#setLength\r
204      * @see org.mozilla.javascript.BaseFunction#getLength\r
205      */\r
206     public int getLength() {\r
207         return lengthPropertyValue;\r
208     }\r
209 \r
210     /**\r
211      * Set the value of the "length" property.\r
212      *\r
213      * <p>Changing the value of the "length" property of a FunctionObject only\r
214      * affects the value retrieved from get() and does not affect the way\r
215      * the method itself is called. <p>\r
216      *\r
217      * The "length" property will be defined by default as the number\r
218      * of parameters of the method used to construct the FunctionObject,\r
219      * unless the method is a "varargs" form, in which case the "length"\r
220      * property will be defined to 1.\r
221      *\r
222      * @param length the new length\r
223      */\r
224     public void setLength(short length) {\r
225         lengthPropertyValue = length;\r
226     }\r
227 \r
228     // TODO: Make not public\r
229     /**\r
230      * Finds methods of a given name in a given class.\r
231      *\r
232      * <p>Searches <code>clazz</code> for methods with name\r
233      * <code>name</code>. Maintains a cache so that multiple\r
234      * lookups on the same class are cheap.\r
235      *\r
236      * @param clazz the class to search\r
237      * @param name the name of the methods to find\r
238      * @return an array of the found methods, or null if no methods\r
239      *         by that name were found.\r
240      * @see java.lang.Class#getMethods\r
241      */\r
242     public static Method[] findMethods(Class clazz, String name) {\r
243         return findMethods(getMethodList(clazz), name);\r
244     }\r
245     \r
246     static Method[] findMethods(Method[] methods, String name) {\r
247         // Usually we're just looking for a single method, so optimize\r
248         // for that case.\r
249         Vector v = null;\r
250         Method first = null;\r
251         for (int i=0; i < methods.length; i++) {\r
252             if (methods[i] == null)\r
253                 continue;\r
254             if (methods[i].getName().equals(name)) {\r
255                 if (first == null) {\r
256                     first = methods[i];\r
257                 } else {\r
258                     if (v == null) {\r
259                         v = new Vector(5);\r
260                         v.addElement(first);\r
261                     }\r
262                     v.addElement(methods[i]);\r
263                 }\r
264             }\r
265         }\r
266         if (v == null) {\r
267             if (first == null)\r
268                 return null;\r
269             Method[] single = { first };\r
270             return single;\r
271         }\r
272         Method[] result = new Method[v.size()];\r
273         v.copyInto(result);\r
274         return result;\r
275     }\r
276 \r
277     static Method[] getMethodList(Class clazz) {\r
278         Method[] cached = methodsCache; // get once to avoid synchronization\r
279         if (cached != null && cached[0].getDeclaringClass() == clazz)\r
280             return cached;\r
281         Method[] methods = null;\r
282         try {\r
283             // getDeclaredMethods may be rejected by the security manager\r
284             // but getMethods is more expensive\r
285             if (!sawSecurityException) \r
286                 methods = clazz.getDeclaredMethods();\r
287         } catch (SecurityException e) {\r
288             // If we get an exception once, give up on getDeclaredMethods\r
289             sawSecurityException = true;\r
290         }\r
291         if (methods == null) {\r
292             methods = clazz.getMethods();\r
293         }\r
294         int count = 0;\r
295         for (int i=0; i < methods.length; i++) {\r
296             if (sawSecurityException \r
297                 ? methods[i].getDeclaringClass() != clazz\r
298                 : !Modifier.isPublic(methods[i].getModifiers()))\r
299             {\r
300                 methods[i] = null;\r
301             } else {\r
302                 count++;\r
303             }\r
304         }\r
305         Method[] result = new Method[count];\r
306         int j=0;\r
307         for (int i=0; i < methods.length; i++) {\r
308             if (methods[i] != null)\r
309                 result[j++] = methods[i];\r
310         }\r
311         if (result.length > 0 && Context.isCachingEnabled)\r
312             methodsCache = result;\r
313         return result;\r
314     }\r
315 \r
316     /**\r
317      * Define this function as a JavaScript constructor.\r
318      * <p>\r
319      * Sets up the "prototype" and "constructor" properties. Also\r
320      * calls setParent and setPrototype with appropriate values.\r
321      * Then adds the function object as a property of the given scope, using\r
322      *      <code>prototype.getClassName()</code>\r
323      * as the name of the property.\r
324      *\r
325      * @param scope the scope in which to define the constructor (typically\r
326      *              the global object)\r
327      * @param prototype the prototype object\r
328      * @see org.mozilla.javascript.Scriptable#setParentScope\r
329      * @see org.mozilla.javascript.Scriptable#setPrototype\r
330      * @see org.mozilla.javascript.Scriptable#getClassName\r
331      */\r
332     public void addAsConstructor(Scriptable scope, Scriptable prototype) {\r
333         setParentScope(scope);\r
334         setPrototype(getFunctionPrototype(scope));\r
335         setImmunePrototypeProperty(prototype);\r
336 \r
337         prototype.setParentScope(this);\r
338         \r
339         final int attr = ScriptableObject.DONTENUM  |\r
340                          ScriptableObject.PERMANENT |\r
341                          ScriptableObject.READONLY;\r
342         defineProperty(prototype, "constructor", this, attr);\r
343 \r
344         String name = prototype.getClassName();\r
345         defineProperty(scope, name, this, ScriptableObject.DONTENUM);\r
346 \r
347         setParentScope(scope);\r
348     }\r
349 \r
350     static public Object convertArg(Scriptable scope,\r
351                                     Object arg, Class desired)\r
352     {\r
353         if (desired == ScriptRuntime.StringClass) \r
354             return ScriptRuntime.toString(arg);\r
355         if (desired == ScriptRuntime.IntegerClass || \r
356             desired == Integer.TYPE)\r
357         {\r
358             return new Integer(ScriptRuntime.toInt32(arg));\r
359         }\r
360         if (desired == ScriptRuntime.BooleanClass || \r
361             desired == Boolean.TYPE)\r
362         {\r
363             return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE \r
364                                                 : Boolean.FALSE;\r
365         }\r
366         if (desired == ScriptRuntime.DoubleClass || \r
367             desired == Double.TYPE)\r
368         {\r
369             return new Double(ScriptRuntime.toNumber(arg));\r
370         }\r
371         if (desired == ScriptRuntime.ScriptableClass)\r
372             return ScriptRuntime.toObject(scope, arg);\r
373         if (desired == ScriptRuntime.ObjectClass)\r
374             return arg;\r
375         \r
376         // Note that the long type is not supported; see the javadoc for\r
377         // the constructor for this class\r
378         throw Context.reportRuntimeError1\r
379             ("msg.cant.convert", desired.getName());\r
380     }\r
381 \r
382     /**\r
383      * Performs conversions on argument types if needed and\r
384      * invokes the underlying Java method or constructor.\r
385      * <p>\r
386      * Implements Function.call.\r
387      *\r
388      * @see org.mozilla.javascript.Function#call\r
389      * @exception JavaScriptException if the underlying Java method or \r
390      *            constructor threw an exception\r
391      */\r
392     public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
393                        Object[] args)\r
394         throws JavaScriptException\r
395     {\r
396         if (parmsLength < 0) {\r
397             return callVarargs(cx, thisObj, args, false);\r
398         }\r
399         if (!isStatic) {\r
400             // OPT: cache "clazz"?\r
401             Class clazz = method != null ? method.getDeclaringClass()\r
402                                          : ctor.getDeclaringClass();\r
403             while (!clazz.isInstance(thisObj)) {\r
404                 thisObj = thisObj.getPrototype();\r
405                 if (thisObj == null || !useDynamicScope) {\r
406                     // Couldn't find an object to call this on.\r
407                     throw NativeGlobal.typeError1\r
408                         ("msg.incompat.call", functionName, scope);\r
409                 }\r
410             }\r
411         }\r
412         Object[] invokeArgs;\r
413         int i;\r
414         if (parmsLength == args.length) {\r
415             invokeArgs = args;\r
416             // avoid copy loop if no conversions needed\r
417             i = (types == null) ? parmsLength : 0;\r
418         } else {\r
419             invokeArgs = new Object[parmsLength];\r
420             i = 0;\r
421         }\r
422         for (; i < parmsLength; i++) {\r
423             Object arg = (i < args.length)\r
424                          ? args[i]\r
425                          : Undefined.instance;\r
426             if (types != null) {\r
427                 arg = convertArg(this, arg, types[i]);\r
428             }\r
429             invokeArgs[i] = arg;\r
430         }\r
431         try {\r
432             Object result = method == null ? ctor.newInstance(invokeArgs)\r
433                                            : doInvoke(thisObj, invokeArgs);\r
434             return hasVoidReturn ? Undefined.instance : result;\r
435         }\r
436         catch (InvocationTargetException e) {\r
437             throw JavaScriptException.wrapException(scope, e);\r
438         }\r
439         catch (IllegalAccessException e) {\r
440             throw WrappedException.wrapException(e);\r
441         }\r
442         catch (InstantiationException e) {\r
443             throw WrappedException.wrapException(e);\r
444         }\r
445     }\r
446 \r
447     /**\r
448      * Performs conversions on argument types if needed and\r
449      * invokes the underlying Java method or constructor\r
450      * to create a new Scriptable object.\r
451      * <p>\r
452      * Implements Function.construct.\r
453      *\r
454      * @param cx the current Context for this thread\r
455      * @param scope the scope to execute the function relative to. This\r
456      *              set to the value returned by getParentScope() except\r
457      *              when the function is called from a closure.\r
458      * @param args arguments to the constructor\r
459      * @see org.mozilla.javascript.Function#construct\r
460      * @exception JavaScriptException if the underlying Java method or constructor\r
461      *            threw an exception\r
462      */\r
463     public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
464         throws JavaScriptException\r
465     {\r
466         if (method == null || parmsLength == VARARGS_CTOR) {\r
467             Scriptable result;\r
468             if (method != null) {\r
469                 result = (Scriptable) callVarargs(cx, null, args, true);\r
470             } else {\r
471                 result = (Scriptable) call(cx, scope, null, args);\r
472             }\r
473 \r
474             if (result.getPrototype() == null)\r
475                 result.setPrototype(getClassPrototype());\r
476             if (result.getParentScope() == null) {\r
477                 Scriptable parent = getParentScope();\r
478                 if (result != parent)\r
479                     result.setParentScope(parent);\r
480             }\r
481 \r
482             return result;\r
483         } else if (method != null && !isStatic) {\r
484             Scriptable result;\r
485             try {\r
486                 result = (Scriptable) method.getDeclaringClass().newInstance();\r
487             } catch (IllegalAccessException e) {\r
488                 throw WrappedException.wrapException(e);\r
489             } catch (InstantiationException e) {\r
490                 throw WrappedException.wrapException(e);\r
491             }\r
492 \r
493             result.setPrototype(getClassPrototype());\r
494             result.setParentScope(getParentScope());\r
495 \r
496             Object val = call(cx, scope, result, args);\r
497             if (val != null && val != Undefined.instance &&\r
498                 val instanceof Scriptable)\r
499             {\r
500                 return (Scriptable) val;\r
501             }\r
502             return result;\r
503         }\r
504 \r
505         return super.construct(cx, scope, args);\r
506     }\r
507         \r
508     private final Object doInvoke(Object thisObj, Object[] args) \r
509         throws IllegalAccessException, InvocationTargetException\r
510     {\r
511         Invoker master = invokerMaster;\r
512         if (master != null) {\r
513             if (invoker == null) {\r
514                 invoker = master.createInvoker(method, types);\r
515             }\r
516             try {\r
517                 return invoker.invoke(thisObj, args);\r
518             } catch (RuntimeException e) {\r
519                 throw new InvocationTargetException(e);\r
520             }\r
521         } \r
522         return method.invoke(thisObj, args);\r
523     }\r
524 \r
525     private Object callVarargs(Context cx, Scriptable thisObj, Object[] args,\r
526                                boolean inNewExpr)\r
527         throws JavaScriptException\r
528     {\r
529         try {\r
530             Object[] invokeArgs;\r
531             Object ret;\r
532             if (cx.arrayCache.size() > 0) invokeArgs = (Object[])cx.arrayCache.lastElement();\r
533             else invokeArgs = new Object[4];\r
534             \r
535             if (parmsLength == VARARGS_METHOD) {\r
536                 invokeArgs[0] = cx;\r
537                 invokeArgs[1] = thisObj;\r
538                 invokeArgs[2] = args;\r
539                 invokeArgs[3] = this;\r
540                 Object result = doInvoke(null, invokeArgs);\r
541                 ret = hasVoidReturn ? Undefined.instance : result;\r
542             } else {\r
543                 Boolean b = inNewExpr ? Boolean.TRUE : Boolean.FALSE;\r
544                 invokeArgs[0] = cx;\r
545                 invokeArgs[1] = args;\r
546                 invokeArgs[2] = this;\r
547                 invokeArgs[3] = b;\r
548                 ret = (method == null)\r
549                     ? ctor.newInstance(invokeArgs)\r
550                     : doInvoke(null, invokeArgs);\r
551             }\r
552             \r
553             cx.arrayCache.addElement(invokeArgs);\r
554             return ret;\r
555         }\r
556         catch (InvocationTargetException e) {\r
557             Throwable target = e.getTargetException();\r
558             if (target instanceof EvaluatorException)\r
559                 throw (EvaluatorException) target;\r
560             if (target instanceof EcmaError)\r
561                 throw (EcmaError) target;\r
562             Scriptable scope = thisObj == null ? this : thisObj;\r
563             throw JavaScriptException.wrapException(scope, target);\r
564         }\r
565         catch (IllegalAccessException e) {\r
566             throw WrappedException.wrapException(e);\r
567         }\r
568         catch (InstantiationException e) {\r
569             throw WrappedException.wrapException(e);\r
570         }\r
571     }\r
572     \r
573     boolean isVarArgsMethod() { \r
574         return parmsLength == VARARGS_METHOD;\r
575     }\r
576 \r
577     boolean isVarArgsConstructor() { \r
578         return parmsLength == VARARGS_CTOR;\r
579     }\r
580     \r
581     static void setCachingEnabled(boolean enabled) {\r
582         if (!enabled) {\r
583             methodsCache = null;\r
584             invokerMaster = null;\r
585         } else if (invokerMaster == null) {\r
586             invokerMaster = newInvokerMaster();\r
587         }\r
588     }\r
589 \r
590     /** Get default master implementation or null if not available */\r
591     private static Invoker newInvokerMaster() {\r
592         /*\r
593         try {\r
594             Class cl = ScriptRuntime.loadClassName(INVOKER_MASTER_CLASS);\r
595             return (Invoker)cl.newInstance();\r
596         }\r
597         catch (ClassNotFoundException ex) {}\r
598         catch (IllegalAccessException ex) {}\r
599         catch (InstantiationException ex) {}\r
600         catch (SecurityException ex) {}\r
601         */\r
602         return null;\r
603     }\r
604 \r
605     private static final String \r
606         INVOKER_MASTER_CLASS = "org.mozilla.javascript.optimizer.InvokerImpl";\r
607 \r
608     static Invoker invokerMaster = newInvokerMaster();\r
609     \r
610     private static final short VARARGS_METHOD = -1;\r
611     private static final short VARARGS_CTOR =   -2;\r
612     \r
613     private static boolean sawSecurityException;\r
614 \r
615     static Method[] methodsCache;\r
616 \r
617     Method method;\r
618     Constructor ctor;\r
619     private Class[] types;\r
620     Invoker invoker;\r
621     private short parmsLength;\r
622     private short lengthPropertyValue;\r
623     private boolean hasVoidReturn;\r
624     private boolean isStatic;\r
625     private boolean useDynamicScope;\r
626 }\r