001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Norris Boyd
026: * Igor Bukanov
027: * Roger Lawrence
028: * Mike McCabe
029: *
030: * Alternatively, the contents of this file may be used under the terms of
031: * the GNU General Public License Version 2 or later (the "GPL"), in which
032: * case the provisions of the GPL are applicable instead of those above. If
033: * you wish to allow use of your version of this file only under the terms of
034: * the GPL and not to allow others to use your version of this file under the
035: * MPL, indicate your decision by deleting the provisions above and replacing
036: * them with the notice and other provisions required by the GPL. If you do
037: * not delete the provisions above, a recipient may use your version of this
038: * file under either the MPL or the GPL.
039: *
040: * ***** END LICENSE BLOCK ***** */
041:
042: package org.mozilla.javascript;
043:
044: /**
045: * The base class for Function objects
046: * See ECMA 15.3.
047: * @author Norris Boyd
048: */
049: public class BaseFunction extends IdScriptableObject implements
050: Function {
051:
052: static final long serialVersionUID = 5311394446546053859L;
053:
054: private static final Object FUNCTION_TAG = new Object();
055:
056: static void init(Scriptable scope, boolean sealed) {
057: BaseFunction obj = new BaseFunction();
058: // Function.prototype attributes: see ECMA 15.3.3.1
059: obj.prototypePropertyAttributes = DONTENUM | READONLY
060: | PERMANENT;
061: obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
062: }
063:
064: public BaseFunction() {
065: }
066:
067: public BaseFunction(Scriptable scope, Scriptable prototype) {
068: super (scope, prototype);
069: }
070:
071: public String getClassName() {
072: return "Function";
073: }
074:
075: /**
076: * Implements the instanceof operator for JavaScript Function objects.
077: * <p>
078: * <code>
079: * foo = new Foo();<br>
080: * foo instanceof Foo; // true<br>
081: * </code>
082: *
083: * @param instance The value that appeared on the LHS of the instanceof
084: * operator
085: * @return true if the "prototype" property of "this" appears in
086: * value's prototype chain
087: *
088: */
089: public boolean hasInstance(Scriptable instance) {
090: Object protoProp = ScriptableObject.getProperty(this ,
091: "prototype");
092: if (protoProp instanceof Scriptable) {
093: return ScriptRuntime.jsDelegatesTo(instance,
094: (Scriptable) protoProp);
095: }
096: throw ScriptRuntime.typeError1("msg.instanceof.bad.prototype",
097: getFunctionName());
098: }
099:
100: // #string_id_map#
101:
102: private static final int Id_length = 1, Id_arity = 2, Id_name = 3,
103: Id_prototype = 4, Id_arguments = 5,
104:
105: MAX_INSTANCE_ID = 5;
106:
107: protected int getMaxInstanceId() {
108: return MAX_INSTANCE_ID;
109: }
110:
111: protected int findInstanceIdInfo(String s) {
112: int id;
113: // #generated# Last update: 2007-05-09 08:15:15 EDT
114: L0: {
115: id = 0;
116: String X = null;
117: int c;
118: L: switch (s.length()) {
119: case 4:
120: X = "name";
121: id = Id_name;
122: break L;
123: case 5:
124: X = "arity";
125: id = Id_arity;
126: break L;
127: case 6:
128: X = "length";
129: id = Id_length;
130: break L;
131: case 9:
132: c = s.charAt(0);
133: if (c == 'a') {
134: X = "arguments";
135: id = Id_arguments;
136: } else if (c == 'p') {
137: X = "prototype";
138: id = Id_prototype;
139: }
140: break L;
141: }
142: if (X != null && X != s && !X.equals(s))
143: id = 0;
144: break L0;
145: }
146: // #/generated#
147: // #/string_id_map#
148:
149: if (id == 0)
150: return super .findInstanceIdInfo(s);
151:
152: int attr;
153: switch (id) {
154: case Id_length:
155: case Id_arity:
156: case Id_name:
157: attr = DONTENUM | READONLY | PERMANENT;
158: break;
159: case Id_prototype:
160: attr = prototypePropertyAttributes;
161: break;
162: case Id_arguments:
163: attr = DONTENUM | PERMANENT;
164: break;
165: default:
166: throw new IllegalStateException();
167: }
168: return instanceIdInfo(attr, id);
169: }
170:
171: protected String getInstanceIdName(int id) {
172: switch (id) {
173: case Id_length:
174: return "length";
175: case Id_arity:
176: return "arity";
177: case Id_name:
178: return "name";
179: case Id_prototype:
180: return "prototype";
181: case Id_arguments:
182: return "arguments";
183: }
184: return super .getInstanceIdName(id);
185: }
186:
187: protected Object getInstanceIdValue(int id) {
188: switch (id) {
189: case Id_length:
190: return ScriptRuntime.wrapInt(getLength());
191: case Id_arity:
192: return ScriptRuntime.wrapInt(getArity());
193: case Id_name:
194: return getFunctionName();
195: case Id_prototype:
196: return getPrototypeProperty();
197: case Id_arguments:
198: return getArguments();
199: }
200: return super .getInstanceIdValue(id);
201: }
202:
203: protected void setInstanceIdValue(int id, Object value) {
204: if (id == Id_prototype) {
205: if ((prototypePropertyAttributes & READONLY) == 0) {
206: prototypeProperty = (value != null) ? value
207: : UniqueTag.NULL_VALUE;
208: }
209: return;
210: } else if (id == Id_arguments) {
211: if (value == NOT_FOUND) {
212: // This should not be called since "arguments" is PERMANENT
213: Kit.codeBug();
214: }
215: defaultPut("arguments", value);
216: }
217: super .setInstanceIdValue(id, value);
218: }
219:
220: protected void fillConstructorProperties(IdFunctionObject ctor) {
221: // Fix up bootstrapping problem: getPrototype of the IdFunctionObject
222: // can not return Function.prototype because Function object is not
223: // yet defined.
224: ctor.setPrototype(this );
225: super .fillConstructorProperties(ctor);
226: }
227:
228: protected void initPrototypeId(int id) {
229: String s;
230: int arity;
231: switch (id) {
232: case Id_constructor:
233: arity = 1;
234: s = "constructor";
235: break;
236: case Id_toString:
237: arity = 1;
238: s = "toString";
239: break;
240: case Id_toSource:
241: arity = 1;
242: s = "toSource";
243: break;
244: case Id_apply:
245: arity = 2;
246: s = "apply";
247: break;
248: case Id_call:
249: arity = 1;
250: s = "call";
251: break;
252: default:
253: throw new IllegalArgumentException(String.valueOf(id));
254: }
255: initPrototypeMethod(FUNCTION_TAG, id, s, arity);
256: }
257:
258: static boolean isApply(IdFunctionObject f) {
259: return f.hasTag(FUNCTION_TAG) && f.methodId() == Id_apply;
260: }
261:
262: static boolean isApplyOrCall(IdFunctionObject f) {
263: if (f.hasTag(FUNCTION_TAG)) {
264: switch (f.methodId()) {
265: case Id_apply:
266: case Id_call:
267: return true;
268: }
269: }
270: return false;
271: }
272:
273: public Object execIdCall(IdFunctionObject f, Context cx,
274: Scriptable scope, Scriptable this Obj, Object[] args) {
275: if (!f.hasTag(FUNCTION_TAG)) {
276: return super .execIdCall(f, cx, scope, this Obj, args);
277: }
278: int id = f.methodId();
279: switch (id) {
280: case Id_constructor:
281: return jsConstructor(cx, scope, args);
282:
283: case Id_toString: {
284: BaseFunction realf = realFunction(this Obj, f);
285: int indent = ScriptRuntime.toInt32(args, 0);
286: return realf.decompile(indent, 0);
287: }
288:
289: case Id_toSource: {
290: BaseFunction realf = realFunction(this Obj, f);
291: int indent = 0;
292: int flags = Decompiler.TO_SOURCE_FLAG;
293: if (args.length != 0) {
294: indent = ScriptRuntime.toInt32(args[0]);
295: if (indent >= 0) {
296: flags = 0;
297: } else {
298: indent = 0;
299: }
300: }
301: return realf.decompile(indent, flags);
302: }
303:
304: case Id_apply:
305: case Id_call:
306: return ScriptRuntime.applyOrCall(id == Id_apply, cx, scope,
307: this Obj, args);
308: }
309: throw new IllegalArgumentException(String.valueOf(id));
310: }
311:
312: private BaseFunction realFunction(Scriptable this Obj,
313: IdFunctionObject f) {
314: Object x = this Obj.getDefaultValue(ScriptRuntime.FunctionClass);
315: if (x instanceof BaseFunction) {
316: return (BaseFunction) x;
317: }
318: throw ScriptRuntime.typeError1("msg.incompat.call", f
319: .getFunctionName());
320: }
321:
322: /**
323: * Make value as DontEnum, DontDelete, ReadOnly
324: * prototype property of this Function object
325: */
326: public void setImmunePrototypeProperty(Object value) {
327: if ((prototypePropertyAttributes & READONLY) != 0) {
328: throw new IllegalStateException();
329: }
330: prototypeProperty = (value != null) ? value
331: : UniqueTag.NULL_VALUE;
332: prototypePropertyAttributes = DONTENUM | PERMANENT | READONLY;
333: }
334:
335: protected Scriptable getClassPrototype() {
336: Object protoVal = getPrototypeProperty();
337: if (protoVal instanceof Scriptable) {
338: return (Scriptable) protoVal;
339: }
340: return getClassPrototype(this , "Object");
341: }
342:
343: /**
344: * Should be overridden.
345: */
346: public Object call(Context cx, Scriptable scope,
347: Scriptable this Obj, Object[] args) {
348: return Undefined.instance;
349: }
350:
351: public Scriptable construct(Context cx, Scriptable scope,
352: Object[] args) {
353: Scriptable result = createObject(cx, scope);
354: if (result != null) {
355: Object val = call(cx, scope, result, args);
356: if (val instanceof Scriptable) {
357: result = (Scriptable) val;
358: }
359: } else {
360: Object val = call(cx, scope, null, args);
361: if (!(val instanceof Scriptable)) {
362: // It is program error not to return Scriptable from
363: // the call method if createObject returns null.
364: throw new IllegalStateException(
365: "Bad implementaion of call as constructor, name="
366: + getFunctionName() + " in "
367: + getClass().getName());
368: }
369: result = (Scriptable) val;
370: if (result.getPrototype() == null) {
371: result.setPrototype(getClassPrototype());
372: }
373: if (result.getParentScope() == null) {
374: Scriptable parent = getParentScope();
375: if (result != parent) {
376: result.setParentScope(parent);
377: }
378: }
379: }
380: return result;
381: }
382:
383: /**
384: * Creates new script object.
385: * The default implementation of {@link #construct} uses the method to
386: * to get the value for <tt>thisObj</tt> argument when invoking
387: * {@link #call}.
388: * The methos is allowed to return <tt>null</tt> to indicate that
389: * {@link #call} will create a new object itself. In this case
390: * {@link #construct} will set scope and prototype on the result
391: * {@link #call} unless they are already set.
392: */
393: public Scriptable createObject(Context cx, Scriptable scope) {
394: Scriptable newInstance = new NativeObject();
395: newInstance.setPrototype(getClassPrototype());
396: newInstance.setParentScope(getParentScope());
397: return newInstance;
398: }
399:
400: /**
401: * Decompile the source information associated with this js
402: * function/script back into a string.
403: *
404: * @param indent How much to indent the decompiled result.
405: *
406: * @param flags Flags specifying format of decompilation output.
407: */
408: String decompile(int indent, int flags) {
409: StringBuffer sb = new StringBuffer();
410: boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
411: if (!justbody) {
412: sb.append("function ");
413: sb.append(getFunctionName());
414: sb.append("() {\n\t");
415: }
416: sb.append("[native code, arity=");
417: sb.append(getArity());
418: sb.append("]\n");
419: if (!justbody) {
420: sb.append("}\n");
421: }
422: return sb.toString();
423: }
424:
425: public int getArity() {
426: return 0;
427: }
428:
429: public int getLength() {
430: return 0;
431: }
432:
433: public String getFunctionName() {
434: return "";
435: }
436:
437: final Object getPrototypeProperty() {
438: Object result = prototypeProperty;
439: if (result == null) {
440: synchronized (this ) {
441: result = prototypeProperty;
442: if (result == null) {
443: setupDefaultPrototype();
444: result = prototypeProperty;
445: }
446: }
447: } else if (result == UniqueTag.NULL_VALUE) {
448: result = null;
449: }
450: return result;
451: }
452:
453: private void setupDefaultPrototype() {
454: NativeObject obj = new NativeObject();
455: final int attr = ScriptableObject.DONTENUM;
456: obj.defineProperty("constructor", this , attr);
457: // put the prototype property into the object now, then in the
458: // wacky case of a user defining a function Object(), we don't
459: // get an infinite loop trying to find the prototype.
460: prototypeProperty = obj;
461: Scriptable proto = getObjectPrototype(this );
462: if (proto != obj) {
463: // not the one we just made, it must remain grounded
464: obj.setPrototype(proto);
465: }
466: }
467:
468: private Object getArguments() {
469: // <Function name>.arguments is deprecated, so we use a slow
470: // way of getting it that doesn't add to the invocation cost.
471: // TODO: add warning, error based on version
472: Object value = defaultGet("arguments");
473: if (value != NOT_FOUND) {
474: // Should after changing <Function name>.arguments its
475: // activation still be available during Function call?
476: // This code assumes it should not:
477: // defaultGet("arguments") != NOT_FOUND
478: // means assigned arguments
479: return value;
480: }
481: Context cx = Context.getContext();
482: NativeCall activation = ScriptRuntime.findFunctionActivation(
483: cx, this );
484: return (activation == null) ? null : activation.get(
485: "arguments", activation);
486: }
487:
488: private static Object jsConstructor(Context cx, Scriptable scope,
489: Object[] args) {
490: int arglen = args.length;
491: StringBuffer sourceBuf = new StringBuffer();
492:
493: sourceBuf.append("function ");
494: /* version != 1.2 Function constructor behavior -
495: * print 'anonymous' as the function name if the
496: * version (under which the function was compiled) is
497: * less than 1.2... or if it's greater than 1.2, because
498: * we need to be closer to ECMA.
499: */
500: if (cx.getLanguageVersion() != Context.VERSION_1_2) {
501: sourceBuf.append("anonymous");
502: }
503: sourceBuf.append('(');
504:
505: // Append arguments as coma separated strings
506: for (int i = 0; i < arglen - 1; i++) {
507: if (i > 0) {
508: sourceBuf.append(',');
509: }
510: sourceBuf.append(ScriptRuntime.toString(args[i]));
511: }
512: sourceBuf.append(") {");
513: if (arglen != 0) {
514: // append function body
515: String funBody = ScriptRuntime.toString(args[arglen - 1]);
516: sourceBuf.append(funBody);
517: }
518: sourceBuf.append('}');
519: String source = sourceBuf.toString();
520:
521: int[] linep = new int[1];
522: String filename = Context.getSourcePositionFromStack(linep);
523: if (filename == null) {
524: filename = "<eval'ed string>";
525: linep[0] = 1;
526: }
527:
528: String sourceURI = ScriptRuntime.makeUrlForGeneratedScript(
529: false, filename, linep[0]);
530:
531: Scriptable global = ScriptableObject.getTopLevelScope(scope);
532:
533: ErrorReporter reporter;
534: reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
535:
536: Evaluator evaluator = Context.createInterpreter();
537: if (evaluator == null) {
538: throw new JavaScriptException("Interpreter not present",
539: filename, linep[0]);
540: }
541:
542: // Compile with explicit interpreter instance to force interpreter
543: // mode.
544: return cx.compileFunction(global, source, evaluator, reporter,
545: sourceURI, 1, null);
546: }
547:
548: protected int findPrototypeId(String s) {
549: int id;
550: // #string_id_map#
551: // #generated# Last update: 2007-05-09 08:15:15 EDT
552: L0: {
553: id = 0;
554: String X = null;
555: int c;
556: L: switch (s.length()) {
557: case 4:
558: X = "call";
559: id = Id_call;
560: break L;
561: case 5:
562: X = "apply";
563: id = Id_apply;
564: break L;
565: case 8:
566: c = s.charAt(3);
567: if (c == 'o') {
568: X = "toSource";
569: id = Id_toSource;
570: } else if (c == 't') {
571: X = "toString";
572: id = Id_toString;
573: }
574: break L;
575: case 11:
576: X = "constructor";
577: id = Id_constructor;
578: break L;
579: }
580: if (X != null && X != s && !X.equals(s))
581: id = 0;
582: break L0;
583: }
584: // #/generated#
585: return id;
586: }
587:
588: private static final int Id_constructor = 1, Id_toString = 2,
589: Id_toSource = 3, Id_apply = 4, Id_call = 5,
590:
591: MAX_PROTOTYPE_ID = 5;
592:
593: // #/string_id_map#
594:
595: private Object prototypeProperty;
596: // For function object instances, attribute is PERMANENT; see ECMA 15.3.5.2
597: private int prototypePropertyAttributes = PERMANENT;
598: }
|