private static double toDouble(Object value) {
   if (value instanceof Number) {
     return ((Number) value).doubleValue();
   } else if (value instanceof String) {
     return ScriptRuntime.toNumber((String) value);
   } else if (value instanceof Scriptable) {
     if (value instanceof Wrapper) {
       // XXX: optimize tail-recursion?
       return toDouble(((Wrapper) value).unwrap());
     } else {
       return ScriptRuntime.toNumber(value);
     }
   } else {
     Method meth;
     try {
       meth = value.getClass().getMethod("doubleValue", (Class[]) null);
     } catch (NoSuchMethodException e) {
       meth = null;
     } catch (SecurityException e) {
       meth = null;
     }
     if (meth != null) {
       try {
         return ((Number) meth.invoke(value, (Object[]) null)).doubleValue();
       } catch (IllegalAccessException e) {
         // XXX: ignore, or error message?
         reportConversionError(value, Double.TYPE);
       } catch (InvocationTargetException e) {
         // XXX: ignore, or error message?
         reportConversionError(value, Double.TYPE);
       }
     }
     return ScriptRuntime.toNumber(value.toString());
   }
 }
예제 #2
0
 public static Object convertArg(Context cx, Scriptable scope, Object arg, int typeTag) {
   switch (typeTag) {
     case JAVA_STRING_TYPE:
       if (arg instanceof String) {
         return arg;
       }
       return ScriptRuntime.toString(arg);
     case JAVA_INT_TYPE:
       if (arg instanceof Integer) {
         return arg;
       }
       return new Integer(ScriptRuntime.toInt32(arg));
     case JAVA_BOOLEAN_TYPE:
       if (arg instanceof Boolean) {
         return arg;
       }
       return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE : Boolean.FALSE;
     case JAVA_DOUBLE_TYPE:
       if (arg instanceof Double) {
         return arg;
       }
       return new Double(ScriptRuntime.toNumber(arg));
     case JAVA_SCRIPTABLE_TYPE:
       if (arg instanceof Scriptable) {
         return arg;
       }
       return ScriptRuntime.toObject(cx, scope, arg);
     case JAVA_OBJECT_TYPE:
       return arg;
     default:
       throw new IllegalArgumentException();
   }
 }
 public Object getDefaultValue(Class<?> hint) {
   Object value;
   if (hint == null) {
     if (javaObject instanceof Boolean) {
       hint = ScriptRuntime.BooleanClass;
     }
   }
   if (hint == null || hint == ScriptRuntime.StringClass) {
     value = javaObject.toString();
   } else {
     String converterName;
     if (hint == ScriptRuntime.BooleanClass) {
       converterName = "booleanValue";
     } else if (hint == ScriptRuntime.NumberClass) {
       converterName = "doubleValue";
     } else {
       throw Context.reportRuntimeError0("msg.default.value");
     }
     Object converterObject = get(converterName, this);
     if (converterObject instanceof Function) {
       Function f = (Function) converterObject;
       value = f.call(Context.getContext(), f.getParentScope(), this, ScriptRuntime.emptyArgs);
     } else {
       if (hint == ScriptRuntime.NumberClass && javaObject instanceof Boolean) {
         boolean b = ((Boolean) javaObject).booleanValue();
         value = ScriptRuntime.wrapNumber(b ? 1.0 : 0.0);
       } else {
         value = javaObject.toString();
       }
     }
   }
   return value;
 }
예제 #4
0
  static Object js_createAdpter(Context cx, Scriptable scope, Object[] args) {
    int N = args.length;
    if (N == 0) {
      throw ScriptRuntime.typeError0("msg.adapter.zero.args");
    }

    Class superClass = null;
    Class[] intfs = new Class[N - 1];
    int interfaceCount = 0;
    for (int i = 0; i != N - 1; ++i) {
      Object arg = args[i];
      if (!(arg instanceof NativeJavaClass)) {
        throw ScriptRuntime.typeError2(
            "msg.not.java.class.arg", String.valueOf(i), ScriptRuntime.toString(arg));
      }
      Class c = ((NativeJavaClass) arg).getClassObject();
      if (!c.isInterface()) {
        if (superClass != null) {
          throw ScriptRuntime.typeError2("msg.only.one.super", superClass.getName(), c.getName());
        }
        superClass = c;
      } else {
        intfs[interfaceCount++] = c;
      }
    }

    if (superClass == null) superClass = ScriptRuntime.ObjectClass;

    Class[] interfaces = new Class[interfaceCount];
    System.arraycopy(intfs, 0, interfaces, 0, interfaceCount);
    Scriptable obj = ScriptRuntime.toObject(cx, scope, args[N - 1]);

    Class adapterClass = getAdapterClass(scope, superClass, interfaces, obj);

    Class[] ctorParms = {ScriptRuntime.ContextFactoryClass, ScriptRuntime.ScriptableClass};
    Object[] ctorArgs = {cx.getFactory(), obj};
    try {
      Object adapter = adapterClass.getConstructor(ctorParms).newInstance(ctorArgs);
      return getAdapterSelf(adapterClass, adapter);
    } catch (Exception ex) {
      throw Context.throwAsScriptRuntimeEx(ex);
    }
  }
  private static long toInteger(Object value, Class<?> type, double min, double max) {
    double d = toDouble(value);

    if (Double.isInfinite(d) || Double.isNaN(d)) {
      // Convert to string first, for more readable message
      reportConversionError(ScriptRuntime.toString(value), type);
    }

    if (d > 0.0) {
      d = Math.floor(d);
    } else {
      d = Math.ceil(d);
    }

    if (d < min || d > max) {
      // Convert to string first, for more readable message
      reportConversionError(ScriptRuntime.toString(value), type);
    }
    return (long) d;
  }
예제 #6
0
  public static Function getFunction(Scriptable obj, String functionName) {
    Object x = ScriptableObject.getProperty(obj, functionName);
    if (x == Scriptable.NOT_FOUND) {
      // This method used to swallow the exception from calling
      // an undefined method. People have come to depend on this
      // somewhat dubious behavior. It allows people to avoid
      // implementing listener methods that they don't care about,
      // for instance.
      return null;
    }
    if (!(x instanceof Function)) throw ScriptRuntime.notFunctionError(x, functionName);

    return (Function) x;
  }
예제 #7
0
  /**
   * 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);
  }
예제 #8
0
 private static ObjToIntMap getObjectFunctionNames(Scriptable obj) {
   Object[] ids = ScriptableObject.getPropertyIds(obj);
   ObjToIntMap map = new ObjToIntMap(ids.length);
   for (int i = 0; i != ids.length; ++i) {
     if (!(ids[i] instanceof String)) continue;
     String id = (String) ids[i];
     Object value = ScriptableObject.getProperty(obj, id);
     if (value instanceof Function) {
       Function f = (Function) value;
       int length = ScriptRuntime.toInt32(ScriptableObject.getProperty(f, "length"));
       if (length < 0) {
         length = 0;
       }
       map.put(id, length);
     }
   }
   return map;
 }
예제 #9
0
  @Override
  public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
    // Find a method that matches the types given.
    if (methods.length == 0) {
      throw new RuntimeException("No methods defined for call");
    }

    int index = findFunction(cx, methods, args);
    if (index < 0) {
      Class<?> c = methods[0].method().getDeclaringClass();
      String sig = c.getName() + '.' + getFunctionName() + '(' + scriptSignature(args) + ')';
      throw Context.reportRuntimeError1("msg.java.no_such_method", sig);
    }

    MemberBox meth = methods[index];
    Class<?>[] argTypes = meth.argTypes;

    if (meth.vararg) {
      // marshall the explicit parameters
      Object[] newArgs = new Object[argTypes.length];
      for (int i = 0; i < argTypes.length - 1; i++) {
        newArgs[i] = Context.jsToJava(args[i], argTypes[i]);
      }

      Object varArgs;

      // Handle special situation where a single variable parameter
      // is given and it is a Java or ECMA array or is null.
      if (args.length == argTypes.length
          && (args[args.length - 1] == null
              || args[args.length - 1] instanceof NativeArray
              || args[args.length - 1] instanceof NativeJavaArray)) {
        // convert the ECMA array into a native array
        varArgs = Context.jsToJava(args[args.length - 1], argTypes[argTypes.length - 1]);
      } else {
        // marshall the variable parameters
        Class<?> componentType = argTypes[argTypes.length - 1].getComponentType();
        varArgs = Array.newInstance(componentType, args.length - argTypes.length + 1);
        for (int i = 0; i < Array.getLength(varArgs); i++) {
          Object value = Context.jsToJava(args[argTypes.length - 1 + i], componentType);
          Array.set(varArgs, i, value);
        }
      }

      // add varargs
      newArgs[argTypes.length - 1] = varArgs;
      // replace the original args with the new one
      args = newArgs;
    } else {
      // First, we marshall the args.
      Object[] origArgs = args;
      for (int i = 0; i < args.length; i++) {
        Object arg = args[i];
        Object coerced = Context.jsToJava(arg, argTypes[i]);
        if (coerced != arg) {
          if (origArgs == args) {
            args = args.clone();
          }
          args[i] = coerced;
        }
      }
    }
    Object javaObject;
    if (meth.isStatic()) {
      javaObject = null; // don't need an object
    } else {
      Scriptable o = thisObj;
      Class<?> c = meth.getDeclaringClass();
      for (; ; ) {
        if (o == null) {
          throw Context.reportRuntimeError3(
              "msg.nonjava.method",
              getFunctionName(),
              ScriptRuntime.toString(thisObj),
              c.getName());
        }
        if (o instanceof Wrapper) {
          javaObject = ((Wrapper) o).unwrap();
          if (c.isInstance(javaObject)) {
            break;
          }
        }
        o = o.getPrototype();
      }
    }
    if (debug) {
      printDebug("Calling ", meth, args);
    }

    Object retval = meth.invoke(javaObject, args);
    Class<?> staticType = meth.method().getReturnType();

    if (debug) {
      Class<?> actualType = (retval == null) ? null : retval.getClass();
      System.err.println(
          " ----- Returned " + retval + " actual = " + actualType + " expect = " + staticType);
    }

    Object wrapped =
        cx.getWrapFactory()
            .wrap(
                cx, scope,
                retval, staticType);
    if (debug) {
      Class<?> actualType = (wrapped == null) ? null : wrapped.getClass();
      System.err.println(" ----- Wrapped as " + wrapped + " class = " + actualType);
    }

    if (wrapped == null && staticType == Void.TYPE) {
      wrapped = Undefined.instance;
    }
    return wrapped;
  }
예제 #10
0
  /**
   * Performs conversions on argument types if needed and invokes the underlying Java method or
   * constructor.
   *
   * <p>Implements Function.call.
   *
   * @see gov.nbcs.rp.common.js.javascript.Function#call( Context, Scriptable, Scriptable, Object[])
   */
  public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
    Object result;
    boolean checkMethodResult = false;

    if (parmsLength < 0) {
      if (parmsLength == VARARGS_METHOD) {
        Object[] invokeArgs = {cx, thisObj, args, this};
        result = member.invoke(null, invokeArgs);
        checkMethodResult = true;
      } else {
        boolean inNewExpr = (thisObj == null);
        Boolean b = inNewExpr ? Boolean.TRUE : Boolean.FALSE;
        Object[] invokeArgs = {cx, args, this, b};
        result =
            (member.isCtor()) ? member.newInstance(invokeArgs) : member.invoke(null, invokeArgs);
      }

    } else {
      if (!isStatic) {
        Class clazz = member.getDeclaringClass();
        if (!clazz.isInstance(thisObj)) {
          boolean compatible = false;
          if (thisObj == scope) {
            Scriptable parentScope = getParentScope();
            if (scope != parentScope) {
              // Call with dynamic scope for standalone function,
              // use parentScope as thisObj
              compatible = clazz.isInstance(parentScope);
              if (compatible) {
                thisObj = parentScope;
              }
            }
          }
          if (!compatible) {
            // Couldn't find an object to call this on.
            throw ScriptRuntime.typeError1("msg.incompat.call", functionName);
          }
        }
      }

      Object[] invokeArgs;
      if (parmsLength == args.length) {
        // Do not allocate new argument array if java arguments are
        // the same as the original js ones.
        invokeArgs = args;
        for (int i = 0; i != parmsLength; ++i) {
          Object arg = args[i];
          Object converted = convertArg(cx, scope, arg, typeTags[i]);
          if (arg != converted) {
            if (invokeArgs == args) {
              invokeArgs = (Object[]) args.clone();
            }
            invokeArgs[i] = converted;
          }
        }
      } else if (parmsLength == 0) {
        invokeArgs = ScriptRuntime.emptyArgs;
      } else {
        invokeArgs = new Object[parmsLength];
        for (int i = 0; i != parmsLength; ++i) {
          Object arg = (i < args.length) ? args[i] : Undefined.instance;
          invokeArgs[i] = convertArg(cx, scope, arg, typeTags[i]);
        }
      }

      if (member.isMethod()) {
        result = member.invoke(thisObj, invokeArgs);
        checkMethodResult = true;
      } else {
        result = member.newInstance(invokeArgs);
      }
    }

    if (checkMethodResult) {
      if (hasVoidReturn) {
        result = Undefined.instance;
      } else if (returnTypeTag == JAVA_UNSUPPORTED_TYPE) {
        result = cx.getWrapFactory().wrap(cx, scope, result, null);
      }
      // XXX: the code assumes that if returnTypeTag == JAVA_OBJECT_TYPE
      // then the Java method did a proper job of converting the
      // result to JS primitive or Scriptable to avoid
      // potentially costly Context.javaToJS call.
    }

    return result;
  }
예제 #11
0
  /**
   * 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);
  }
예제 #12
0
  private void reflect(Scriptable scope, boolean includeProtected) {
    // We reflect methods first, because we want overloaded field/method
    // names to be allocated to the NativeJavaMethod before the field
    // gets in the way.

    Method[] methods = discoverAccessibleMethods(cl, includeProtected, includePrivate);
    for (int i = 0; i < methods.length; i++) {
      Method method = methods[i];
      int mods = method.getModifiers();
      boolean isStatic = Modifier.isStatic(mods);
      Map<String, Object> ht = isStatic ? staticMembers : members;
      String name = method.getName();
      Object value = ht.get(name);
      if (value == null) {
        ht.put(name, method);
      } else {
        ObjArray overloadedMethods;
        if (value instanceof ObjArray) {
          overloadedMethods = (ObjArray) value;
        } else {
          if (!(value instanceof Method)) Kit.codeBug();
          // value should be instance of Method as at this stage
          // staticMembers and members can only contain methods
          overloadedMethods = new ObjArray();
          overloadedMethods.add(value);
          ht.put(name, overloadedMethods);
        }
        overloadedMethods.add(method);
      }
    }

    // replace Method instances by wrapped NativeJavaMethod objects
    // first in staticMembers and then in members
    for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
      boolean isStatic = (tableCursor == 0);
      Map<String, Object> ht = isStatic ? staticMembers : members;
      for (String name : ht.keySet()) {
        MemberBox[] methodBoxes;
        Object value = ht.get(name);
        if (value instanceof Method) {
          methodBoxes = new MemberBox[1];
          methodBoxes[0] = new MemberBox((Method) value);
        } else {
          ObjArray overloadedMethods = (ObjArray) value;
          int N = overloadedMethods.size();
          if (N < 2) Kit.codeBug();
          methodBoxes = new MemberBox[N];
          for (int i = 0; i != N; ++i) {
            Method method = (Method) overloadedMethods.get(i);
            methodBoxes[i] = new MemberBox(method);
          }
        }
        NativeJavaMethod fun = new NativeJavaMethod(methodBoxes);
        if (scope != null) {
          ScriptRuntime.setFunctionProtoAndParent(fun, scope);
        }
        ht.put(name, fun);
      }
    }

    // Reflect fields.
    Field[] fields = getAccessibleFields();
    for (int i = 0; i < fields.length; i++) {
      Field field = fields[i];
      String name = field.getName();
      int mods = field.getModifiers();
      if (!includePrivate && !Modifier.isPublic(mods)) {
        continue;
      }
      try {
        boolean isStatic = Modifier.isStatic(mods);
        Map<String, Object> ht = isStatic ? staticMembers : members;
        Object member = ht.get(name);
        if (member == null) {
          ht.put(name, field);
        } else if (member instanceof NativeJavaMethod) {
          NativeJavaMethod method = (NativeJavaMethod) member;
          FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field);
          Map<String, FieldAndMethods> fmht = isStatic ? staticFieldAndMethods : fieldAndMethods;
          if (fmht == null) {
            fmht = new HashMap<String, FieldAndMethods>();
            if (isStatic) {
              staticFieldAndMethods = fmht;
            } else {
              fieldAndMethods = fmht;
            }
          }
          fmht.put(name, fam);
          ht.put(name, fam);
        } else if (member instanceof Field) {
          Field oldField = (Field) member;
          // If this newly reflected field shadows an inherited field,
          // then replace it. Otherwise, since access to the field
          // would be ambiguous from Java, no field should be
          // reflected.
          // For now, the first field found wins, unless another field
          // explicitly shadows it.
          if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) {
            ht.put(name, field);
          }
        } else {
          // "unknown member type"
          Kit.codeBug();
        }
      } catch (SecurityException e) {
        // skip this field
        Context.reportWarning(
            "Could not access field "
                + name
                + " of class "
                + cl.getName()
                + " due to lack of privileges.");
      }
    }

    // Create bean properties from corresponding get/set methods first for
    // static members and then for instance members
    for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
      boolean isStatic = (tableCursor == 0);
      Map<String, Object> ht = isStatic ? staticMembers : members;

      Map<String, BeanProperty> toAdd = new HashMap<String, BeanProperty>();

      // Now, For each member, make "bean" properties.
      for (String name : ht.keySet()) {
        // Is this a getter?
        boolean memberIsGetMethod = name.startsWith("get");
        boolean memberIsSetMethod = name.startsWith("set");
        boolean memberIsIsMethod = name.startsWith("is");
        if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) {
          // Double check name component.
          String nameComponent = name.substring(memberIsIsMethod ? 2 : 3);
          if (nameComponent.length() == 0) continue;

          // Make the bean property name.
          String beanPropertyName = nameComponent;
          char ch0 = nameComponent.charAt(0);
          if (Character.isUpperCase(ch0)) {
            if (nameComponent.length() == 1) {
              beanPropertyName = nameComponent.toLowerCase();
            } else {
              char ch1 = nameComponent.charAt(1);
              if (!Character.isUpperCase(ch1)) {
                beanPropertyName = Character.toLowerCase(ch0) + nameComponent.substring(1);
              }
            }
          }

          // If we already have a member by this name, don't do this
          // property.
          if (ht.containsKey(beanPropertyName) || toAdd.containsKey(beanPropertyName)) {
            continue;
          }

          // Find the getter method, or if there is none, the is-
          // method.
          MemberBox getter = null;
          getter = findGetter(isStatic, ht, "get", nameComponent);
          // If there was no valid getter, check for an is- method.
          if (getter == null) {
            getter = findGetter(isStatic, ht, "is", nameComponent);
          }

          // setter
          MemberBox setter = null;
          NativeJavaMethod setters = null;
          String setterName = "set".concat(nameComponent);

          if (ht.containsKey(setterName)) {
            // Is this value a method?
            Object member = ht.get(setterName);
            if (member instanceof NativeJavaMethod) {
              NativeJavaMethod njmSet = (NativeJavaMethod) member;
              if (getter != null) {
                // We have a getter. Now, do we have a matching
                // setter?
                Class<?> type = getter.method().getReturnType();
                setter = extractSetMethod(type, njmSet.methods, isStatic);
              } else {
                // No getter, find any set method
                setter = extractSetMethod(njmSet.methods, isStatic);
              }
              if (njmSet.methods.length > 1) {
                setters = njmSet;
              }
            }
          }
          // Make the property.
          BeanProperty bp = new BeanProperty(getter, setter, setters);
          toAdd.put(beanPropertyName, bp);
        }
      }

      // Add the new bean properties.
      for (String key : toAdd.keySet()) {
        Object value = toAdd.get(key);
        ht.put(key, value);
      }
    }

    // Reflect constructors
    Constructor<?>[] constructors = getAccessibleConstructors();
    ctors = new MemberBox[constructors.length];
    for (int i = 0; i != constructors.length; ++i) {
      ctors[i] = new MemberBox(constructors[i]);
    }
  }
예제 #13
0
  static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) {
    int N = args.length;
    if (N == 0) {
      throw ScriptRuntime.typeError0("msg.adapter.zero.args");
    }

    // Expected arguments:
    // Any number of NativeJavaClass objects representing the super-class
    // and/or interfaces to implement, followed by one NativeObject providing
    // the implementation, followed by any number of arguments to pass on
    // to the (super-class) constructor.

    int classCount;
    for (classCount = 0; classCount < N - 1; classCount++) {
      Object arg = args[classCount];
      // We explicitly test for NativeObject here since checking for
      // instanceof ScriptableObject or !(instanceof NativeJavaClass)
      // would fail for a Java class that isn't found in the class path
      // as NativeJavaPackage extends ScriptableObject.
      if (arg instanceof NativeObject) {
        break;
      }
      if (!(arg instanceof NativeJavaClass)) {
        throw ScriptRuntime.typeError2(
            "msg.not.java.class.arg", String.valueOf(classCount), ScriptRuntime.toString(arg));
      }
    }
    Class<?> superClass = null;
    Class<?>[] intfs = new Class[classCount];
    int interfaceCount = 0;
    for (int i = 0; i < classCount; ++i) {
      Class<?> c = ((NativeJavaClass) args[i]).getClassObject();
      if (!c.isInterface()) {
        if (superClass != null) {
          throw ScriptRuntime.typeError2("msg.only.one.super", superClass.getName(), c.getName());
        }
        superClass = c;
      } else {
        intfs[interfaceCount++] = c;
      }
    }

    if (superClass == null) {
      superClass = ScriptRuntime.ObjectClass;
    }

    Class<?>[] interfaces = new Class[interfaceCount];
    System.arraycopy(intfs, 0, interfaces, 0, interfaceCount);
    // next argument is implementation, must be scriptable
    Scriptable obj = ScriptableObject.ensureScriptable(args[classCount]);

    Class<?> adapterClass = getAdapterClass(scope, superClass, interfaces, obj);
    Object adapter;

    int argsCount = N - classCount - 1;
    try {
      if (argsCount > 0) {
        // Arguments contain parameters for super-class constructor.
        // We use the generic Java method lookup logic to find and
        // invoke the right constructor.
        Object[] ctorArgs = new Object[argsCount + 2];
        ctorArgs[0] = obj;
        ctorArgs[1] = cx.getFactory();
        System.arraycopy(args, classCount + 1, ctorArgs, 2, argsCount);
        // TODO: cache class wrapper?
        NativeJavaClass classWrapper = new NativeJavaClass(scope, adapterClass, true);
        NativeJavaMethod ctors = classWrapper.members.ctors;
        int index = ctors.findCachedFunction(cx, ctorArgs);
        if (index < 0) {
          String sig = NativeJavaMethod.scriptSignature(args);
          throw Context.reportRuntimeError2("msg.no.java.ctor", adapterClass.getName(), sig);
        }

        // Found the constructor, so try invoking it.
        adapter = NativeJavaClass.constructInternal(ctorArgs, ctors.methods[index]);
      } else {
        Class<?>[] ctorParms = {ScriptRuntime.ScriptableClass, ScriptRuntime.ContextFactoryClass};
        Object[] ctorArgs = {obj, cx.getFactory()};
        adapter = adapterClass.getConstructor(ctorParms).newInstance(ctorArgs);
      }

      Object self = getAdapterSelf(adapterClass, adapter);
      // Return unwrapped JavaAdapter if it implements Scriptable
      if (self instanceof Wrapper) {
        Object unwrapped = ((Wrapper) self).unwrap();
        if (unwrapped instanceof Scriptable) {
          if (unwrapped instanceof ScriptableObject) {
            ScriptRuntime.setObjectProtoAndParent((ScriptableObject) unwrapped, scope);
          }
          return unwrapped;
        }
      }
      return self;
    } catch (Exception ex) {
      throw Context.throwAsScriptRuntimeEx(ex);
    }
  }
  /** Type-munging for field setting and method invocation. Conforms to LC3 specification */
  static Object coerceTypeImpl(Class<?> type, Object value) {
    if (value != null && value.getClass() == type) {
      return value;
    }

    switch (getJSTypeCode(value)) {
      case JSTYPE_NULL:
        // raise error if type.isPrimitive()
        if (type.isPrimitive()) {
          reportConversionError(value, type);
        }
        return null;

      case JSTYPE_UNDEFINED:
        if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) {
          return "undefined";
        } else {
          reportConversionError("undefined", type);
        }
        break;

      case JSTYPE_BOOLEAN:
        // Under LC3, only JS Booleans can be coerced into a Boolean value
        if (type == Boolean.TYPE
            || type == ScriptRuntime.BooleanClass
            || type == ScriptRuntime.ObjectClass) {
          return value;
        } else if (type == ScriptRuntime.StringClass) {
          return value.toString();
        } else {
          reportConversionError(value, type);
        }
        break;

      case JSTYPE_NUMBER:
        if (type == ScriptRuntime.StringClass) {
          return ScriptRuntime.toString(value);
        } else if (type == ScriptRuntime.ObjectClass) {
          return coerceToNumber(Double.TYPE, value);
        } else if ((type.isPrimitive() && type != Boolean.TYPE)
            || ScriptRuntime.NumberClass.isAssignableFrom(type)) {
          return coerceToNumber(type, value);
        } else {
          reportConversionError(value, type);
        }
        break;

      case JSTYPE_STRING:
        if (type == ScriptRuntime.StringClass || type.isInstance(value)) {
          return value;
        } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) {
          // Special case for converting a single char string to a
          // character
          // Placed here because it applies *only* to JS strings,
          // not other JS objects converted to strings
          if (((String) value).length() == 1) {
            return new Character(((String) value).charAt(0));
          } else {
            return coerceToNumber(type, value);
          }
        } else if ((type.isPrimitive() && type != Boolean.TYPE)
            || ScriptRuntime.NumberClass.isAssignableFrom(type)) {
          return coerceToNumber(type, value);
        } else {
          reportConversionError(value, type);
        }
        break;

      case JSTYPE_JAVA_CLASS:
        if (value instanceof Wrapper) {
          value = ((Wrapper) value).unwrap();
        }

        if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) {
          return value;
        } else if (type == ScriptRuntime.StringClass) {
          return value.toString();
        } else {
          reportConversionError(value, type);
        }
        break;

      case JSTYPE_JAVA_OBJECT:
      case JSTYPE_JAVA_ARRAY:
        if (value instanceof Wrapper) {
          value = ((Wrapper) value).unwrap();
        }
        if (type.isPrimitive()) {
          if (type == Boolean.TYPE) {
            reportConversionError(value, type);
          }
          return coerceToNumber(type, value);
        } else {
          if (type == ScriptRuntime.StringClass) {
            return value.toString();
          } else {
            if (type.isInstance(value)) {
              return value;
            } else {
              reportConversionError(value, type);
            }
          }
        }
        break;

      case JSTYPE_OBJECT:
        if (type == ScriptRuntime.StringClass) {
          return ScriptRuntime.toString(value);
        } else if (type.isPrimitive()) {
          if (type == Boolean.TYPE) {
            reportConversionError(value, type);
          }
          return coerceToNumber(type, value);
        } else if (type.isInstance(value)) {
          return value;
        } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) {
          double time = ((NativeDate) value).getJSTimeValue();
          // XXX: This will replace NaN by 0
          return new Date((long) time);
        } else if (type.isArray() && value instanceof NativeArray) {
          // Make a new java array, and coerce the JS array components
          // to the target (component) type.
          NativeArray array = (NativeArray) value;
          long length = array.getLength();
          Class<?> arrayType = type.getComponentType();
          Object Result = Array.newInstance(arrayType, (int) length);
          for (int i = 0; i < length; ++i) {
            try {
              Array.set(Result, i, coerceType(arrayType, array.get(i, array)));
            } catch (EvaluatorException ee) {
              reportConversionError(value, type);
            }
          }

          return Result;
        } else if (value instanceof Wrapper) {
          value = ((Wrapper) value).unwrap();
          if (type.isInstance(value)) return value;
          reportConversionError(value, type);
        } else if (type.isInterface() && value instanceof Callable) {
          // Try to use function as implementation of Java interface.
          //
          // XXX: Currently only instances of ScriptableObject are
          // supported since the resulting interface proxies should
          // be reused next time conversion is made and generic
          // Callable has no storage for it. Weak references can
          // address it but for now use this restriction.
          if (value instanceof ScriptableObject) {
            ScriptableObject so = (ScriptableObject) value;
            Object key = Kit.makeHashKeyFromPair(COERCED_INTERFACE_KEY, type);
            Object old = so.getAssociatedValue(key);
            if (old != null) {
              // Function was already wrapped
              return old;
            }
            Context cx = Context.getContext();
            Object glue = InterfaceAdapter.create(cx, type, (Callable) value);
            // Store for later retrival
            glue = so.associateValue(key, glue);
            return glue;
          }
          reportConversionError(value, type);
        } else {
          reportConversionError(value, type);
        }
        break;
    }

    return value;
  }