EXCLUDE_METHODS.add("__jsend!");
}
@JRubyMethod(meta = true)
public static RubyObject get_with_class(IRubyObject recv, IRubyObject obj) {
Ruby runtime = recv.getRuntime();
if (!(obj instanceof RubyClass)) {
throw runtime.newTypeError(obj, runtime.getClassClass());
}
RubyClass clazz = (RubyClass)obj;
// Let's only generate methods for those the user may actually
// intend to override. That includes any defined in the current
// class, and any ancestors that are also JavaProxyClasses (but none
// from any other ancestor classes). Methods defined in mixins will
// be considered intentionally overridden, except those from Kernel,
// Java, and JavaProxyMethods, as well as Enumerable.
// TODO: may want to exclude other common mixins?
JavaClass javaClass = null;
Set<String> names = new HashSet<String>(); // need names ordered for key generation later
List<Class<?>> interfaceList = new ArrayList<Class<?>>();
List<IRubyObject> ancestors = clazz.getAncestorList();
boolean skipRemainingClasses = false;
for (IRubyObject ancestorObject: ancestors) {
RubyModule ancestor = (RubyModule) ancestorObject;
if (ancestor instanceof RubyClass) {
if (skipRemainingClasses) continue;
// we only collect methods and interfaces for
// user-defined proxy classes.
if (!ancestor.getInstanceVariables().fastHasInstanceVariable("@java_proxy_class")) {
skipRemainingClasses = true;
continue;
}
// get JavaClass if this is the new proxy class; verify it
// matches if this is a superclass proxy.
IRubyObject var = ancestor.getInstanceVariables().fastGetInstanceVariable("@java_class");
if (var == null) {
throw runtime.newTypeError(
"no java_class defined for proxy (or ancestor): " + ancestor);
} else if (!(var instanceof JavaClass)) {
throw runtime.newTypeError(
"invalid java_class defined for proxy (or ancestor): " +
ancestor + ": " + var);
}
if (javaClass == null) {
javaClass = (JavaClass)var;
} else if (javaClass != var) {
throw runtime.newTypeError(
"java_class defined for " + clazz + " (" + javaClass +
") does not match java_class for ancestor " + ancestor +
" (" + var + ")");
}
// get any included interfaces
var = ancestor.getInstanceVariables().fastGetInstanceVariable("@java_interfaces");
if (var != null && !(var instanceof RubyNil)) {
if (!(var instanceof RubyArray)) {
throw runtime.newTypeError(
"invalid java_interfaces defined for proxy (or ancestor): " +
ancestor + ": " + var);
}
RubyArray ifcArray = (RubyArray)var;
int size = ifcArray.size();
for (int i = size; --i >= 0; ) {
IRubyObject ifc = ifcArray.eltInternal(i);
if (!(ifc instanceof JavaClass)) {
throw runtime.newTypeError(
"invalid java interface defined for proxy (or ancestor): " +
ancestor + ": " + ifc);
}
Class interfaceClass = ((JavaClass)ifc).javaClass();
if (!interfaceClass.isInterface()) {
throw runtime.newTypeError(
"invalid java interface defined for proxy (or ancestor): " +
ancestor + ": " + ifc + " (not an interface)");
}
if (!interfaceList.contains(interfaceClass)) {
interfaceList.add(interfaceClass);
}
}
}
// set this class's method names in var @__java_ovrd_methods if this
// is the new class; otherwise, get method names from there if this is
// a proxy superclass.
// FIXME: shouldn't need @__java_ovrd_methods, just query locally defined methods.
var = ancestor.getInstanceVariables().fastGetInstanceVariable("@__java_ovrd_methods");
if (var == null) {
// lock in the overridden methods for the new class, and any as-yet
// uninstantiated ancestor class.
Map<String, DynamicMethod> methods;
RubyArray methodNames;
synchronized(methods = ancestor.getMethods()) {
methodNames = RubyArray.newArrayLight(runtime,methods.size());
for (String methodName: methods.keySet()) {
if (!EXCLUDE_METHODS.contains(methodName)) {
names.add(methodName);
methodNames.append(runtime.newString(methodName));
}
}
}
ancestor.fastSetInstanceVariable("@__java_ovrd_methods",methodNames);
} else {
if (!(var instanceof RubyArray)) {
throw runtime.newTypeError(
"invalid @__java_ovrd_methods defined for proxy: " +
ancestor + ": " + var);
}
RubyArray methodNames = (RubyArray)var;
int size = methodNames.size();
for (int i = size; --i >= 0; ) {
IRubyObject methodName = methodNames.eltInternal(i);
if (!(methodName instanceof RubyString)) {
throw runtime.newTypeError(
"invalid method name defined for proxy (or ancestor): " +
ancestor + ": " + methodName);
}
names.add(methodName.asJavaString());
}
}
} else if (!EXCLUDE_MODULES.contains(ancestor.getName())) {
Map<String, DynamicMethod> methods;
synchronized(methods = ancestor.getMethods()) {
for (String methodName: methods.keySet()) {
if (!EXCLUDE_METHODS.contains(methodName)) {
names.add(methodName);
}
}
}
}
}
if (javaClass == null) {
throw runtime.newArgumentError("unable to create proxy class: no java_class defined for " + clazz);
}
int interfaceCount = interfaceList.size();
Class<?>[] interfaces = new Class<?>[interfaceCount];
for (int i = interfaceCount; --i >= 0; ) {