/** * Define this function as a JavaScript constructor. * * <p>Sets up the "prototype" and "constructor" properties. Also calls setParent and setPrototype * with appropriate values. Then adds the function object as a property of the given scope, using * <code>prototype.getClassName()</code> as the name of the property. * * @param scope the scope in which to define the constructor (typically the global object) * @param prototype the prototype object * @see gov.nbcs.rp.common.js.javascript.Scriptable#setParentScope * @see gov.nbcs.rp.common.js.javascript.Scriptable#setPrototype * @see gov.nbcs.rp.common.js.javascript.Scriptable#getClassName */ public void addAsConstructor(Scriptable scope, Scriptable prototype) { ScriptRuntime.setFunctionProtoAndParent(this, scope); setImmunePrototypeProperty(prototype); prototype.setParentScope(this); final int attr = ScriptableObject.DONTENUM | ScriptableObject.PERMANENT | ScriptableObject.READONLY; defineProperty(prototype, "constructor", this, attr); String name = prototype.getClassName(); defineProperty(scope, name, this, ScriptableObject.DONTENUM); setParentScope(scope); }
/** * Create a JavaScript function object from a Java method. * * <p>The <code>member</code> argument must be either a java.lang.reflect.Method * or a java.lang.reflect.Constructor and must match one of two forms.<p> * * The first form is a member with zero or more parameters * of the following types: Object, String, boolean, Scriptable, * int, or double. The Long type is not supported * because the double representation of a long (which is the * EMCA-mandated storage type for Numbers) may lose precision. * If the member is a Method, the return value must be void or one * of the types allowed for parameters.<p> * * The runtime will perform appropriate conversions based * upon the type of the parameter. A parameter type of * Object specifies that no conversions are to be done. A parameter * of type String will use Context.toString to convert arguments. * Similarly, parameters of type double, boolean, and Scriptable * will cause Context.toNumber, Context.toBoolean, and * Context.toObject, respectively, to be called.<p> * * If the method is not static, the Java 'this' value will * correspond to the JavaScript 'this' value. Any attempt * to call the function with a 'this' value that is not * of the right Java type will result in an error.<p> * * The second form is the variable arguments (or "varargs") * form. If the FunctionObject will be used as a constructor, * the member must have the following parameters * <pre> * (Context cx, Object[] args, Function ctorObj, * boolean inNewExpr)</pre> * and if it is a Method, be static and return an Object result.<p> * * Otherwise, if the FunctionObject will <i>not</i> be used to define a * constructor, the member must be a static Method with parameters * (Context cx, Scriptable thisObj, Object[] args, * Function funObj) </pre> * <pre> * and an Object result.<p> * * When the function varargs form is called as part of a function call, * the <code>args</code> parameter contains the * arguments, with <code>thisObj</code> * set to the JavaScript 'this' value. <code>funObj</code> * is the function object for the invoked function.<p> * * When the constructor varargs form is called or invoked while evaluating * a <code>new</code> expression, <code>args</code> contains the * arguments, <code>ctorObj</code> refers to this FunctionObject, and * <code>inNewExpr</code> is true if and only if a <code>new</code> * expression caused the call. This supports defining a function that * has different behavior when called as a constructor than when * invoked as a normal function call. (For example, the Boolean * constructor, when called as a function, * will convert to boolean rather than creating a new object.)<p> * * @param name the name of the function * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor * that defines the object * @param scope enclosing scope of function * @see gov.nbcs.rp.common.js.javascript.Scriptable */ public FunctionObject(String name, Member methodOrConstructor, Scriptable scope) { if (methodOrConstructor instanceof Constructor) { member = new MemberBox((Constructor) methodOrConstructor); isStatic = true; // well, doesn't take a 'this' } else { member = new MemberBox((Method) methodOrConstructor); isStatic = member.isStatic(); } String methodName = member.getName(); this.functionName = name; Class[] types = member.argTypes; int arity = types.length; if ((arity == 4) && (types[1].isArray() || types[2].isArray())) { // Either variable args or an error. if (types[1].isArray()) { if (!isStatic || (types[0] != ScriptRuntime.ContextClass) || (types[1].getComponentType() != ScriptRuntime.ObjectClass) || (types[2] != ScriptRuntime.FunctionClass) || (types[3] != Boolean.TYPE)) { throw Context.reportRuntimeError1("msg.varargs.ctor", methodName); } parmsLength = VARARGS_CTOR; } else { if (!isStatic || (types[0] != ScriptRuntime.ContextClass) || (types[1] != ScriptRuntime.ScriptableClass) || (types[2].getComponentType() != ScriptRuntime.ObjectClass) || (types[3] != ScriptRuntime.FunctionClass)) { throw Context.reportRuntimeError1("msg.varargs.fun", methodName); } parmsLength = VARARGS_METHOD; } } else { parmsLength = arity; if (arity > 0) { typeTags = new byte[arity]; for (int i = 0; i != arity; ++i) { int tag = getTypeTag(types[i]); if (tag == JAVA_UNSUPPORTED_TYPE) { throw Context.reportRuntimeError2("msg.bad.parms", types[i].getName(), methodName); } typeTags[i] = (byte) tag; } } } if (member.isMethod()) { Method method = member.method(); Class returnType = method.getReturnType(); if (returnType == Void.TYPE) { hasVoidReturn = true; } else { returnTypeTag = getTypeTag(returnType); } } else { Class ctorType = member.getDeclaringClass(); if (!ScriptRuntime.ScriptableClass.isAssignableFrom(ctorType)) { throw Context.reportRuntimeError1("msg.bad.ctor.return", ctorType.getName()); } } ScriptRuntime.setFunctionProtoAndParent(this, scope); }