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; }
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(); }