/** * For each member with the given name, find the most derived members for each JSNI reference that * match it. For wildcard JSNI references, there will in general be more than one match. This * method does not ignore synthetic methods. */ private static void findMostDerivedMembers( LinkedHashMap<String, LinkedHashMap<String, HasEnclosingType>> matchesBySig, JDeclaredType targetType, String memberName, boolean addConstructors) { /* * Analyze superclasses and interfaces first. More derived members will thus * be seen later. */ if (targetType instanceof JClassType) { JClassType targetClass = (JClassType) targetType; if (targetClass.getSuperClass() != null) { findMostDerivedMembers(matchesBySig, targetClass.getSuperClass(), memberName, false); } } for (JDeclaredType intf : targetType.getImplements()) { findMostDerivedMembers(matchesBySig, intf, memberName, false); } // Get the methods on this class/interface. for (JMethod method : targetType.getMethods()) { if (method.getName().equals(memberName)) { if (addConstructors || !method.getName().equals(JsniRef.NEW)) { addMember(matchesBySig, method, getJsniSignature(method, false)); addMember(matchesBySig, method, getJsniSignature(method, true)); } } } // Get the fields on this class/interface. for (JField field : targetType.getFields()) { if (field.getName().equals(memberName)) { addMember(matchesBySig, field, field.getName()); } } }
/** Constructed by {@link MemberFactory#get(JFieldType)}. */ public StandardFieldMember(MemberFactory factory, JField field) { this.enclosing = factory.get(field.getEnclosingType()); this.sourceName = field.getEnclosingType().getName() + "::" + field.getName(); }
/** * Look up a JSNI reference. * * @param ref The reference to look up * @param program The program to look up the reference in * @param errorReporter A callback used to indicate the reason for a failed JSNI lookup * @return The item referred to, or <code>null</code> if it could not be found. If the return * value is <code>null</code>, <code>errorReporter</code> will have been invoked. */ public static HasEnclosingType findJsniRefTarget( JsniRef ref, JProgram program, JsniRefLookup.ErrorReporter errorReporter) { String className = ref.className(); JType type = null; if (!className.equals("null")) { type = program.getTypeFromJsniRef(className); if (type == null) { errorReporter.reportError("Unresolvable native reference to type '" + className + "'"); return null; } } if (!ref.isMethod()) { // look for a field String fieldName = ref.memberName(); if (type == null) { if (fieldName.equals("nullField")) { return program.getNullField(); } } else if (fieldName.equals(JsniRef.CLASS)) { JClassLiteral lit = program.getLiteralClass(type); return lit.getField(); } else if (type instanceof JPrimitiveType) { errorReporter.reportError("May not refer to fields on primitive types"); return null; } else if (type instanceof JArrayType) { errorReporter.reportError("May not refer to fields on array types"); return null; } else { for (JField field : ((JDeclaredType) type).getFields()) { if (field.getName().equals(fieldName)) { return field; } } } errorReporter.reportError( "Unresolvable native reference to field '" + fieldName + "' in type '" + className + "'"); return null; } else if (type instanceof JPrimitiveType) { errorReporter.reportError("May not refer to methods on primitive types"); return null; } else { // look for a method LinkedHashMap<String, LinkedHashMap<String, HasEnclosingType>> matchesBySig = new LinkedHashMap<String, LinkedHashMap<String, HasEnclosingType>>(); String methodName = ref.memberName(); String jsniSig = ref.memberSignature(); if (type == null) { if (jsniSig.equals("nullMethod()")) { return program.getNullMethod(); } } else { findMostDerivedMembers(matchesBySig, (JDeclaredType) type, ref.memberName(), true); LinkedHashMap<String, HasEnclosingType> matches = matchesBySig.get(jsniSig); if (matches != null && matches.size() == 1) { /* * Backward compatibility: allow accessing bridge methods with full * qualification */ return matches.values().iterator().next(); } removeSyntheticMembers(matchesBySig); matches = matchesBySig.get(jsniSig); if (matches != null && matches.size() == 1) { return matches.values().iterator().next(); } } // Not found; signal an error if (matchesBySig.isEmpty()) { errorReporter.reportError( "Unresolvable native reference to method '" + methodName + "' in type '" + className + "'"); return null; } else { StringBuilder suggestList = new StringBuilder(); String comma = ""; // use a TreeSet to sort the near matches TreeSet<String> almostMatchSigs = new TreeSet<String>(); for (String sig : matchesBySig.keySet()) { if (matchesBySig.get(sig).size() == 1) { almostMatchSigs.add(sig); } } for (String almost : almostMatchSigs) { suggestList.append(comma + "'" + almost + "'"); comma = ", "; } errorReporter.reportError( "Unresolvable native reference to method '" + methodName + "' in type '" + className + "' (did you mean " + suggestList.toString() + "?)"); return null; } } }