1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
\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
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
13 * The Original Code is Rhino code, released
\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-1999 Netscape Communications Corporation. All
\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
39 package org.mozilla.javascript;
\r
42 * The base class for Function objects
\r
44 * @author Norris Boyd
\r
46 public class BaseFunction extends IdScriptable implements Function {
\r
48 static void init(Context cx, Scriptable scope, boolean sealed) {
\r
49 BaseFunction obj = new BaseFunction();
\r
50 obj.prototypeFlag = true;
\r
51 obj.functionName = "";
\r
52 obj.prototypePropertyAttrs = DONTENUM | READONLY | PERMANENT;
\r
53 obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);
\r
56 protected void fillConstructorProperties
\r
57 (Context cx, IdFunction ctor, boolean sealed)
\r
59 // Fix up bootstrapping problem: getPrototype of the IdFunction
\r
60 // can not return Function.prototype because Function object is not
\r
62 ctor.setPrototype(this);
\r
65 public String getClassName() {
\r
70 * Implements the instanceof operator for JavaScript Function objects.
\r
73 * foo = new Foo();<br>
\r
74 * foo instanceof Foo; // true<br>
\r
77 * @param instance The value that appeared on the LHS of the instanceof
\r
79 * @return true if the "prototype" property of "this" appears in
\r
80 * value's prototype chain
\r
83 public boolean hasInstance(Scriptable instance) {
\r
84 Object protoProp = ScriptableObject.getProperty(this, "prototype");
\r
85 if (protoProp instanceof Scriptable && protoProp != Undefined.instance)
\r
87 return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);
\r
89 throw NativeGlobal.typeError1
\r
90 ("msg.instanceof.bad.prototype", functionName, instance);
\r
93 protected int getIdDefaultAttributes(int id) {
\r
98 return DONTENUM | READONLY | PERMANENT;
\r
100 return prototypePropertyAttrs;
\r
104 return super.getIdDefaultAttributes(id);
\r
107 protected boolean hasIdValue(int id) {
\r
108 if (id == Id_prototype) {
\r
109 return prototypeProperty != NOT_FOUND;
\r
111 else if (id == Id_arguments) {
\r
112 // Should after delete Function.arguments its activation still
\r
113 // be available during Function call?
\r
114 // This code assumes it should not: after default set/deleteIdValue
\r
115 // hasIdValue/getIdValue would not be called again
\r
116 // To handle the opposite case, set/deleteIdValue should be
\r
117 // overwritten as well
\r
118 return null != getActivation(Context.getContext());
\r
120 return super.hasIdValue(id);
\r
123 protected Object getIdValue(int id) {
\r
125 case Id_length: return wrap_int(getLength());
\r
126 case Id_arity: return wrap_int(getArity());
\r
127 case Id_name: return getFunctionName();
\r
128 case Id_prototype: return getPrototypeProperty();
\r
129 case Id_arguments: return getArguments();
\r
131 return super.getIdValue(id);
\r
134 protected void setIdValue(int id, Object value) {
\r
135 if (id == Id_prototype) {
\r
136 prototypeProperty = (value != null) ? value : NULL_TAG;
\r
139 super.setIdValue(id, value);
\r
142 protected void deleteIdValue(int id) {
\r
143 if (id == Id_prototype) {
\r
144 prototypeProperty = NOT_FOUND;
\r
147 super.deleteIdValue(id);
\r
150 public int methodArity(int methodId) {
\r
151 if (prototypeFlag) {
\r
152 switch (methodId) {
\r
153 case Id_constructor:
\r
160 return super.methodArity(methodId);
\r
163 public Object execMethod(int methodId, IdFunction f, Context cx,
\r
164 Scriptable scope, Scriptable thisObj,
\r
166 throws JavaScriptException
\r
168 if (prototypeFlag) {
\r
169 switch (methodId) {
\r
170 case Id_constructor:
\r
171 return jsConstructor(cx, scope, args);
\r
174 return jsFunction_toString(cx, thisObj, args);
\r
177 return jsFunction_apply(cx, scope, thisObj, args);
\r
180 return jsFunction_call(cx, scope, thisObj, args);
\r
183 return super.execMethod(methodId, f, cx, scope, thisObj, args);
\r
187 * Make value as DontEnum, DontDelete, ReadOnly
\r
188 * prototype property of this Function object
\r
190 public void setImmunePrototypeProperty(Object value) {
\r
191 prototypeProperty = (value != null) ? value : NULL_TAG;
\r
192 prototypePropertyAttrs = DONTENUM | READONLY | PERMANENT;
\r
195 protected Scriptable getClassPrototype() {
\r
196 Object protoVal = getPrototypeProperty();
\r
197 if (protoVal == null
\r
198 || !(protoVal instanceof Scriptable)
\r
199 || (protoVal == Undefined.instance))
\r
200 protoVal = getClassPrototype(this, "Object");
\r
201 return (Scriptable) protoVal;
\r
205 * Should be overridden.
\r
207 public Object call(Context cx, Scriptable scope, Scriptable thisObj,
\r
209 throws JavaScriptException
\r
211 return Undefined.instance;
\r
214 public Scriptable construct(Context cx, Scriptable scope, Object[] args)
\r
215 throws JavaScriptException
\r
217 Scriptable newInstance = new NativeObject();
\r
219 newInstance.setPrototype(getClassPrototype());
\r
220 newInstance.setParentScope(getParentScope());
\r
222 Object val = call(cx, scope, newInstance, args);
\r
223 if (val instanceof Scriptable && val != Undefined.instance) {
\r
224 return (Scriptable) val;
\r
226 return newInstance;
\r
230 * Decompile the source information associated with this js
\r
231 * function/script back into a string.
\r
233 * @param cx Current context
\r
235 * @param indent How much to indent the decompiled result
\r
237 * @param justbody Whether the decompilation should omit the
\r
238 * function header and trailing brace.
\r
241 public String decompile(Context cx, int indent, boolean justbody) {
\r
242 StringBuffer sb = new StringBuffer();
\r
244 sb.append("function ");
\r
245 sb.append(getFunctionName());
\r
246 sb.append("() {\n\t");
\r
248 sb.append("[native code, arity=");
\r
249 sb.append(getArity());
\r
254 return sb.toString();
\r
257 public int getArity() { return 0; }
\r
259 public int getLength() { return 0; }
\r
261 public String getFunctionName() {
\r
262 if (functionName == null)
\r
264 if (functionName.equals("anonymous")) {
\r
265 Context cx = Context.getCurrentContext();
\r
266 if (cx != null && cx.getLanguageVersion() == Context.VERSION_1_2)
\r
269 return functionName;
\r
272 private Object getPrototypeProperty() {
\r
273 Object result = prototypeProperty;
\r
274 if (result == null) {
\r
275 synchronized (this) {
\r
276 result = prototypeProperty;
\r
277 if (result == null) {
\r
278 setupDefaultPrototype();
\r
279 result = prototypeProperty;
\r
283 else if (result == NULL_TAG) { result = null; }
\r
287 private void setupDefaultPrototype() {
\r
288 NativeObject obj = new NativeObject();
\r
289 final int attr = ScriptableObject.DONTENUM |
\r
290 ScriptableObject.READONLY |
\r
291 ScriptableObject.PERMANENT;
\r
292 obj.defineProperty("constructor", this, attr);
\r
293 // put the prototype property into the object now, then in the
\r
294 // wacky case of a user defining a function Object(), we don't
\r
295 // get an infinite loop trying to find the prototype.
\r
296 prototypeProperty = obj;
\r
297 Scriptable proto = getObjectPrototype(this);
\r
298 if (proto != obj) {
\r
299 // not the one we just made, it must remain grounded
\r
300 obj.setPrototype(proto);
\r
304 private Object getArguments() {
\r
305 // <Function name>.arguments is deprecated, so we use a slow
\r
306 // way of getting it that doesn't add to the invocation cost.
\r
307 // TODO: add warning, error based on version
\r
308 NativeCall activation = getActivation(Context.getContext());
\r
309 return activation == null
\r
311 : activation.get("arguments", activation);
\r
314 NativeCall getActivation(Context cx) {
\r
315 NativeCall activation = cx.currentActivation;
\r
316 while (activation != null) {
\r
317 if (activation.getFunctionObject() == this)
\r
319 activation = activation.caller;
\r
324 private static Object jsConstructor(Context cx, Scriptable scope,
\r
327 int arglen = args.length;
\r
328 StringBuffer funArgs = new StringBuffer();
\r
330 /* Collect the arguments into a string. */
\r
333 for (i = 0; i < arglen - 1; i++) {
\r
335 funArgs.append(',');
\r
336 funArgs.append(ScriptRuntime.toString(args[i]));
\r
338 String funBody = arglen == 0 ? "" : ScriptRuntime.toString(args[i]);
\r
340 String source = "function (" + funArgs.toString() + ") {" +
\r
342 int[] linep = { 0 };
\r
343 String filename = Context.getSourcePositionFromStack(linep);
\r
344 if (filename == null) {
\r
345 filename = "<eval'ed string>";
\r
348 Object securityDomain = cx.getSecurityDomainForStackDepth(4);
\r
349 Scriptable global = ScriptableObject.getTopLevelScope(scope);
\r
351 // Compile the function with opt level of -1 to force interpreter
\r
353 int oldOptLevel = cx.getOptimizationLevel();
\r
354 cx.setOptimizationLevel(-1);
\r
357 fn = (NativeFunction) cx.compileFunction(global, source,
\r
358 filename, linep[0],
\r
361 finally { cx.setOptimizationLevel(oldOptLevel); }
\r
363 fn.functionName = "anonymous";
\r
364 fn.setPrototype(getFunctionPrototype(global));
\r
365 fn.setParentScope(global);
\r
370 private static Object jsFunction_toString(Context cx, Scriptable thisObj,
\r
373 int indent = ScriptRuntime.toInt32(args, 0);
\r
374 Object val = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
\r
375 if (val instanceof BaseFunction) {
\r
376 return ((BaseFunction)val).decompile(cx, indent, false);
\r
378 throw NativeGlobal.typeError1("msg.incompat.call", "toString", thisObj);
\r
382 * Function.prototype.apply
\r
384 * A proposed ECMA extension for round 2.
\r
386 private static Object jsFunction_apply(Context cx, Scriptable scope,
\r
387 Scriptable thisObj, Object[] args)
\r
388 throws JavaScriptException
\r
390 if (args.length != 2)
\r
391 return jsFunction_call(cx, scope, thisObj, args);
\r
392 Object val = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
\r
393 Scriptable newThis = args[0] == null
\r
394 ? ScriptableObject.getTopLevelScope(thisObj)
\r
395 : ScriptRuntime.toObject(scope, args[0]);
\r
397 if (args.length > 1) {
\r
398 if ((args[1] instanceof NativeArray)
\r
399 || (args[1] instanceof Arguments))
\r
400 newArgs = cx.getElements((Scriptable) args[1]);
\r
402 throw NativeGlobal.typeError0("msg.arg.isnt.array", thisObj);
\r
405 newArgs = ScriptRuntime.emptyArgs;
\r
406 return ScriptRuntime.call(cx, val, newThis, newArgs, newThis);
\r
410 * Function.prototype.call
\r
412 * A proposed ECMA extension for round 2.
\r
414 private static Object jsFunction_call(Context cx, Scriptable scope,
\r
415 Scriptable thisObj, Object[] args)
\r
416 throws JavaScriptException
\r
418 Object val = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
\r
419 if (args.length == 0) {
\r
420 Scriptable s = ScriptRuntime.toObject(scope, val);
\r
421 Scriptable topScope = s.getParentScope();
\r
422 return ScriptRuntime.call(cx, val,
\r
423 topScope, ScriptRuntime.emptyArgs,
\r
426 Scriptable newThis = args[0] == null
\r
427 ? ScriptableObject.getTopLevelScope(thisObj)
\r
428 : ScriptRuntime.toObject(scope, args[0]);
\r
430 Object[] newArgs = new Object[args.length - 1];
\r
431 System.arraycopy(args, 1, newArgs, 0, newArgs.length);
\r
432 return ScriptRuntime.call(cx, val, newThis, newArgs, newThis);
\r
436 protected int maxInstanceId() { return MAX_INSTANCE_ID; }
\r
438 protected String getIdName(int id) {
\r
440 case Id_length: return "length";
\r
441 case Id_arity: return "arity";
\r
442 case Id_name: return "name";
\r
443 case Id_prototype: return "prototype";
\r
444 case Id_arguments: return "arguments";
\r
447 if (prototypeFlag) {
\r
449 case Id_constructor: return "constructor";
\r
450 case Id_toString: return "toString";
\r
451 case Id_apply: return "apply";
\r
452 case Id_call: return "call";
\r
460 private static final int
\r
467 MAX_INSTANCE_ID = 5;
\r
469 protected int mapNameToId(String s) {
\r
471 // #generated# Last update: 2001-05-20 00:12:12 GMT+02:00
\r
472 L0: { id = 0; String X = null; int c;
\r
473 L: switch (s.length()) {
\r
474 case 4: X="name";id=Id_name; break L;
\r
475 case 5: X="arity";id=Id_arity; break L;
\r
476 case 6: X="length";id=Id_length; break L;
\r
477 case 9: c=s.charAt(0);
\r
478 if (c=='a') { X="arguments";id=Id_arguments; }
\r
479 else if (c=='p') { X="prototype";id=Id_prototype; }
\r
482 if (X!=null && X!=s && !X.equals(s)) id = 0;
\r
485 // #/string_id_map#
\r
487 if (id != 0 || !prototypeFlag) { return id; }
\r
490 // #generated# Last update: 2001-05-20 00:12:12 GMT+02:00
\r
491 L0: { id = 0; String X = null;
\r
492 L: switch (s.length()) {
\r
493 case 4: X="call";id=Id_call; break L;
\r
494 case 5: X="apply";id=Id_apply; break L;
\r
495 case 8: X="toString";id=Id_toString; break L;
\r
496 case 11: X="constructor";id=Id_constructor; break L;
\r
498 if (X!=null && X!=s && !X.equals(s)) id = 0;
\r
504 private static final int
\r
505 Id_constructor = MAX_INSTANCE_ID + 1,
\r
506 Id_toString = MAX_INSTANCE_ID + 2,
\r
507 Id_apply = MAX_INSTANCE_ID + 3,
\r
508 Id_call = MAX_INSTANCE_ID + 4,
\r
510 MAX_PROTOTYPE_ID = MAX_INSTANCE_ID + 4;
\r
512 // #/string_id_map#
\r
514 protected String functionName;
\r
516 private Object prototypeProperty;
\r
517 private int prototypePropertyAttrs = DONTENUM;
\r
519 private boolean prototypeFlag;
\r