static Method[] getOverridableMethods(Class c) { ArrayList<Method> list = new ArrayList<Method>(); HashSet<String> skip = new HashSet<String>(); while (c != null) { Method[] methods = c.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { String methodKey = methods[i].getName() + getMethodSignature(methods[i], methods[i].getParameterTypes()); if (skip.contains(methodKey)) continue; // skip this method int mods = methods[i].getModifiers(); if (Modifier.isStatic(mods)) continue; if (Modifier.isFinal(mods)) { // Make sure we don't add a final method to the list // of overridable methods. skip.add(methodKey); continue; } if (Modifier.isPublic(mods) || Modifier.isProtected(mods)) { list.add(methods[i]); skip.add(methodKey); } } c = c.getSuperclass(); } return list.toArray(new Method[list.size()]); }
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 superclass's 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(); }