예제 #1
0
  /**
   * Generates a method called "super$methodName()" which can be called from JavaScript that is
   * equivalent to calling "super.methodName()" from Java. Eventually, this may be supported
   * directly in JavaScript.
   */
  private static void generateSuper(
      ClassFileWriter cfw,
      String genName,
      String superName,
      String methodName,
      String methodSignature,
      Class[] parms,
      Class returnType) {
    cfw.startMethod("super$" + methodName, methodSignature, ClassFileWriter.ACC_PUBLIC);

    // push "this"
    cfw.add(ByteCode.ALOAD, 0);

    // push the rest of the parameters.
    int paramOffset = 1;
    for (int i = 0; i < parms.length; i++) {
      paramOffset += generatePushParam(cfw, paramOffset, parms[i]);
    }

    // call the superclass implementation of the method.
    cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, methodName, methodSignature);

    // now, handle the return type appropriately.
    Class retType = returnType;
    if (!retType.equals(Void.TYPE)) {
      generatePopResult(cfw, retType);
    } else {
      cfw.add(ByteCode.RETURN);
    }
    cfw.stopMethod((short) (paramOffset + 1));
  }
예제 #2
0
 /**
  * Generates code to return a Java type, after calling a Java method that returns the same type.
  * Generates the appropriate RETURN bytecode.
  */
 private static void generatePopResult(ClassFileWriter cfw, Class retType) {
   if (retType.isPrimitive()) {
     String typeName = retType.getName();
     switch (typeName.charAt(0)) {
       case 'b':
       case 'c':
       case 's':
       case 'i':
       case 'z':
         cfw.add(ByteCode.IRETURN);
         break;
       case 'l':
         cfw.add(ByteCode.LRETURN);
         break;
       case 'f':
         cfw.add(ByteCode.FRETURN);
         break;
       case 'd':
         cfw.add(ByteCode.DRETURN);
         break;
     }
   } else {
     cfw.add(ByteCode.ARETURN);
   }
 }
예제 #3
0
 /**
  * Generates code to push typed parameters onto the operand stack prior to a direct Java method
  * call.
  */
 private static int generatePushParam(ClassFileWriter cfw, int paramOffset, Class paramType) {
   if (!paramType.isPrimitive()) {
     cfw.addALoad(paramOffset);
     return 1;
   }
   String typeName = paramType.getName();
   switch (typeName.charAt(0)) {
     case 'z':
     case 'b':
     case 'c':
     case 's':
     case 'i':
       // load an int value, convert to double.
       cfw.addILoad(paramOffset);
       return 1;
     case 'l':
       // load a long, convert to double.
       cfw.addLLoad(paramOffset);
       return 2;
     case 'f':
       // load a float, convert to double.
       cfw.addFLoad(paramOffset);
       return 1;
     case 'd':
       cfw.addDLoad(paramOffset);
       return 2;
   }
   throw Kit.codeBug();
 }
예제 #4
0
 static Method[] getOverridableMethods(Class c) {
   ArrayList list = new ArrayList();
   while (c != null) {
     Method[] methods = c.getDeclaredMethods();
     for (int i = 0; i < methods.length; i++) {
       int mods = methods[i].getModifiers();
       if (Modifier.isStatic(mods) || Modifier.isFinal(mods)) continue;
       if (Modifier.isPublic(mods) || Modifier.isProtected(mods)) list.add(methods[i]);
     }
     c = c.getSuperclass();
   }
   return (Method[]) list.toArray(new Method[list.size()]);
 }
예제 #5
0
  /**
   * Generates code to wrap Java argument into Object. Non-primitive Java types are left unconverted
   * pending convertion in the helper method. Leaves the wrapper object on the top of the stack.
   */
  private static int generateWrapArg(ClassFileWriter cfw, int paramOffset, Class argType) {
    int size = 1;
    if (!argType.isPrimitive()) {
      cfw.add(ByteCode.ALOAD, paramOffset);

    } else if (argType == Boolean.TYPE) {
      // wrap boolean values with java.lang.Boolean.
      cfw.add(ByteCode.NEW, "java/lang/Boolean");
      cfw.add(ByteCode.DUP);
      cfw.add(ByteCode.ILOAD, paramOffset);
      cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Boolean", "<init>", "(Z)V");

    } else if (argType == Character.TYPE) {
      // Create a string of length 1 using the character parameter.
      cfw.add(ByteCode.ILOAD, paramOffset);
      cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;");

    } else {
      // convert all numeric values to java.lang.Double.
      cfw.add(ByteCode.NEW, "java/lang/Double");
      cfw.add(ByteCode.DUP);
      String typeName = argType.getName();
      switch (typeName.charAt(0)) {
        case 'b':
        case 's':
        case 'i':
          // load an int value, convert to double.
          cfw.add(ByteCode.ILOAD, paramOffset);
          cfw.add(ByteCode.I2D);
          break;
        case 'l':
          // load a long, convert to double.
          cfw.add(ByteCode.LLOAD, paramOffset);
          cfw.add(ByteCode.L2D);
          size = 2;
          break;
        case 'f':
          // load a float, convert to double.
          cfw.add(ByteCode.FLOAD, paramOffset);
          cfw.add(ByteCode.F2D);
          break;
        case 'd':
          cfw.add(ByteCode.DLOAD, paramOffset);
          size = 2;
          break;
      }
      cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Double", "<init>", "(D)V");
    }
    return size;
  }
예제 #6
0
  // Needed by NativeJavaObject serializer
  public static void writeAdapterObject(Object javaObject, ObjectOutputStream out)
      throws IOException {
    Class cl = javaObject.getClass();
    out.writeObject(cl.getSuperclass().getName());

    Class[] interfaces = cl.getInterfaces();
    String[] interfaceNames = new String[interfaces.length];

    for (int i = 0; i < interfaces.length; i++) interfaceNames[i] = interfaces[i].getName();

    out.writeObject(interfaceNames);

    try {
      Object delegee = cl.getField("delegee").get(javaObject);
      out.writeObject(delegee);
      return;
    } catch (IllegalAccessException e) {
    } catch (NoSuchFieldException e) {
    }
    throw new IOException();
  }
예제 #7
0
  // Needed by NativeJavaObject de-serializer
  public static Object readAdapterObject(Scriptable self, ObjectInputStream in)
      throws IOException, ClassNotFoundException {
    ContextFactory factory;
    Context cx = Context.getCurrentContext();
    if (cx != null) {
      factory = cx.getFactory();
    } else {
      factory = null;
    }

    Class superClass = Class.forName((String) in.readObject());

    String[] interfaceNames = (String[]) in.readObject();
    Class[] interfaces = new Class[interfaceNames.length];

    for (int i = 0; i < interfaceNames.length; i++)
      interfaces[i] = Class.forName(interfaceNames[i]);

    Scriptable delegee = (Scriptable) in.readObject();

    Class adapterClass = getAdapterClass(self, superClass, interfaces, delegee);

    Class[] ctorParms = {
      ScriptRuntime.ContextFactoryClass,
      ScriptRuntime.ScriptableClass,
      ScriptRuntime.ScriptableClass
    };
    Object[] ctorArgs = {factory, delegee, self};
    try {
      return adapterClass.getConstructor(ctorParms).newInstance(ctorArgs);
    } catch (InstantiationException e) {
    } catch (IllegalAccessException e) {
    } catch (InvocationTargetException e) {
    } catch (NoSuchMethodException e) {
    }

    throw new ClassNotFoundException("adapter");
  }
예제 #8
0
 private static StringBuffer appendTypeString(StringBuffer sb, Class type) {
   while (type.isArray()) {
     sb.append('[');
     type = type.getComponentType();
   }
   if (type.isPrimitive()) {
     char typeLetter;
     if (type == Boolean.TYPE) {
       typeLetter = 'Z';
     } else if (type == Long.TYPE) {
       typeLetter = 'J';
     } else {
       String typeName = type.getName();
       typeLetter = Character.toUpperCase(typeName.charAt(0));
     }
     sb.append(typeLetter);
   } else {
     sb.append('L');
     sb.append(type.getName().replace('.', '/'));
     sb.append(';');
   }
   return sb;
 }
예제 #9
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);
    }
  }
예제 #10
0
 public int hashCode() {
   return superClass.hashCode() | (0x9e3779b9 * (names.size() | (interfaces.length << 16)));
 }
예제 #11
0
  /**
   * Generates code to convert a wrapped value type to a primitive type. Handles unwrapping
   * java.lang.Boolean, and java.lang.Number types. Generates the appropriate RETURN bytecode.
   */
  static void generateReturnResult(ClassFileWriter cfw, Class retType, boolean callConvertResult) {
    // wrap boolean values with java.lang.Boolean, convert all other
    // primitive values to java.lang.Double.
    if (retType == Void.TYPE) {
      cfw.add(ByteCode.POP);
      cfw.add(ByteCode.RETURN);

    } else if (retType == Boolean.TYPE) {
      cfw.addInvoke(
          ByteCode.INVOKESTATIC,
          "org/mozilla/javascript/Context",
          "toBoolean",
          "(Ljava/lang/Object;)Z");
      cfw.add(ByteCode.IRETURN);

    } else if (retType == Character.TYPE) {
      // characters are represented as strings in JavaScript.
      // return the first character.
      // first convert the value to a string if possible.
      cfw.addInvoke(
          ByteCode.INVOKESTATIC,
          "org/mozilla/javascript/Context",
          "toString",
          "(Ljava/lang/Object;)Ljava/lang/String;");
      cfw.add(ByteCode.ICONST_0);
      cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C");
      cfw.add(ByteCode.IRETURN);

    } else if (retType.isPrimitive()) {
      cfw.addInvoke(
          ByteCode.INVOKESTATIC,
          "org/mozilla/javascript/Context",
          "toNumber",
          "(Ljava/lang/Object;)D");
      String typeName = retType.getName();
      switch (typeName.charAt(0)) {
        case 'b':
        case 's':
        case 'i':
          cfw.add(ByteCode.D2I);
          cfw.add(ByteCode.IRETURN);
          break;
        case 'l':
          cfw.add(ByteCode.D2L);
          cfw.add(ByteCode.LRETURN);
          break;
        case 'f':
          cfw.add(ByteCode.D2F);
          cfw.add(ByteCode.FRETURN);
          break;
        case 'd':
          cfw.add(ByteCode.DRETURN);
          break;
        default:
          throw new RuntimeException("Unexpected return type " + retType.toString());
      }

    } else {
      String retTypeStr = retType.getName();
      if (callConvertResult) {
        cfw.addLoadConstant(retTypeStr);
        cfw.addInvoke(
            ByteCode.INVOKESTATIC,
            "java/lang/Class",
            "forName",
            "(Ljava/lang/String;)Ljava/lang/Class;");

        cfw.addInvoke(
            ByteCode.INVOKESTATIC,
            "org/mozilla/javascript/JavaAdapter",
            "convertResult",
            "(Ljava/lang/Object;" + "Ljava/lang/Class;" + ")Ljava/lang/Object;");
      }
      // Now cast to return type
      cfw.add(ByteCode.CHECKCAST, retTypeStr);
      cfw.add(ByteCode.ARETURN);
    }
  }
예제 #12
0
  public static byte[] createAdapterCode(
      ObjToIntMap functionNames,
      String adapterName,
      Class superClass,
      Class[] interfaces,
      String scriptClassName) {
    ClassFileWriter cfw = new ClassFileWriter(adapterName, superClass.getName(), "<adapter>");
    cfw.addField(
        "factory",
        "Lorg/mozilla/javascript/ContextFactory;",
        (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
    cfw.addField(
        "delegee",
        "Lorg/mozilla/javascript/Scriptable;",
        (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
    cfw.addField(
        "self",
        "Lorg/mozilla/javascript/Scriptable;",
        (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
    int interfacesCount = interfaces == null ? 0 : interfaces.length;
    for (int i = 0; i < interfacesCount; i++) {
      if (interfaces[i] != null) cfw.addInterface(interfaces[i].getName());
    }

    String superName = superClass.getName().replace('.', '/');
    generateCtor(cfw, adapterName, superName);
    generateSerialCtor(cfw, adapterName, superName);
    if (scriptClassName != null) generateEmptyCtor(cfw, adapterName, superName, scriptClassName);

    ObjToIntMap generatedOverrides = new ObjToIntMap();
    ObjToIntMap generatedMethods = new ObjToIntMap();

    // generate methods to satisfy all specified interfaces.
    for (int i = 0; i < interfacesCount; i++) {
      Method[] methods = interfaces[i].getMethods();
      for (int j = 0; j < methods.length; j++) {
        Method method = methods[j];
        int mods = method.getModifiers();
        if (Modifier.isStatic(mods) || Modifier.isFinal(mods)) {
          continue;
        }
        String methodName = method.getName();
        Class[] argTypes = method.getParameterTypes();
        if (!functionNames.has(methodName)) {
          try {
            superClass.getMethod(methodName, argTypes);
            // The class we're extending implements this method and
            // the JavaScript object doesn't have an override. See
            // bug 61226.
            continue;
          } catch (NoSuchMethodException e) {
            // Not implemented by superclass; fall through
          }
        }
        // make sure to generate only one instance of a particular
        // method/signature.
        String methodSignature = getMethodSignature(method, argTypes);
        String methodKey = methodName + methodSignature;
        if (!generatedOverrides.has(methodKey)) {
          generateMethod(cfw, adapterName, methodName, argTypes, method.getReturnType());
          generatedOverrides.put(methodKey, 0);
          generatedMethods.put(methodName, 0);
        }
      }
    }

    // Now, go through the superclasses methods, checking for abstract
    // methods or additional methods to override.

    // generate any additional overrides that the object might contain.
    Method[] methods = getOverridableMethods(superClass);
    for (int j = 0; j < methods.length; j++) {
      Method method = methods[j];
      int mods = method.getModifiers();
      // if a method is marked abstract, must implement it or the
      // resulting class won't be instantiable. otherwise, if the object
      // has a property of the same name, then an override is intended.
      boolean isAbstractMethod = Modifier.isAbstract(mods);
      String methodName = method.getName();
      if (isAbstractMethod || functionNames.has(methodName)) {
        // make sure to generate only one instance of a particular
        // method/signature.
        Class[] argTypes = method.getParameterTypes();
        String methodSignature = getMethodSignature(method, argTypes);
        String methodKey = methodName + methodSignature;
        if (!generatedOverrides.has(methodKey)) {
          generateMethod(cfw, adapterName, methodName, argTypes, method.getReturnType());
          generatedOverrides.put(methodKey, 0);
          generatedMethods.put(methodName, 0);
        }
        // if a method was overridden, generate a "super$method"
        // which lets the delegate call the superclass' version.
        if (!isAbstractMethod) {
          generateSuper(
              cfw,
              adapterName,
              superName,
              methodName,
              methodSignature,
              argTypes,
              method.getReturnType());
        }
      }
    }

    // Generate Java methods for remaining properties that are not
    // overrides.
    ObjToIntMap.Iterator iter = new ObjToIntMap.Iterator(functionNames);
    for (iter.start(); !iter.done(); iter.next()) {
      String functionName = (String) iter.getKey();
      if (generatedMethods.has(functionName)) continue;
      int length = iter.getValue();
      Class[] parms = new Class[length];
      for (int k = 0; k < length; k++) parms[k] = ScriptRuntime.ObjectClass;
      generateMethod(cfw, adapterName, functionName, parms, ScriptRuntime.ObjectClass);
    }
    return cfw.toByteArray();
  }
예제 #13
0
 public static Object getAdapterSelf(Class adapterClass, Object adapter)
     throws NoSuchFieldException, IllegalAccessException {
   Field self = adapterClass.getDeclaredField("self");
   return self.get(adapter);
 }