Skip to content

Commit

Permalink
(Partially) fixes #140: Allow access to getClass() and instance membe…
Browse files Browse the repository at this point in the history
…rs of java.lang.Class

This enables the code for example:
import(java.util.HashMap)
ageMap <- HashMap$new()
print(ageMap$class$name)
  • Loading branch information
akbertram committed Jan 21, 2016
1 parent 821baa7 commit 3196f6d
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 18 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/renjin/invoke/ClassBindings.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
public class ClassBindings {

public static <T> ClassBinding getClassDefinitionBinding(Class instance) {
return new ClassDefinitionBinding(ClassBindingImpl.get(instance));
return new ClassDefinitionBinding(instance, ClassBindingImpl.get(instance));
}

public static ClassBinding getClassBinding(Class aClass) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ private ClassBindingImpl(Class clazz) {


for(Method method : clazz.getMethods()) {
if((method.getModifiers() & Modifier.PUBLIC) != 0 &&
method.getDeclaringClass() != Object.class) {
if((method.getModifiers() & Modifier.PUBLIC) != 0) {

if((method.getModifiers() & Modifier.STATIC) != 0 ) {
staticMethods.put(Symbol.get(method.getName()), method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@
import org.renjin.invoke.ClassBinding;
import org.renjin.sexp.Symbol;

/**
* Binding for an instance of type {@code java.lang.Class}
*
* <p>Provides bindings <strong>both</strong> for static methods of the class described by
* {@code classInstance}, as well as the methods and properties of the actual {@code java.lang.Class} object
* such as {@link Class#getName()} or {@link Class#hashCode()}</p>
*/
public class ClassDefinitionBinding implements ClassBinding {

private static final Symbol NEW = Symbol.get("new");

private Class classInstance;
private final ClassBindingImpl classBinding;
private final ClassBindingImpl javaLangClassBinding;

public ClassDefinitionBinding(ClassBindingImpl classBinding) {
public ClassDefinitionBinding(Class classInstance, ClassBindingImpl classBinding) {
this.classInstance = classInstance;
this.classBinding = classBinding;
this.javaLangClassBinding = ClassBindingImpl.get(Class.class);
}

@Override
Expand All @@ -21,12 +32,21 @@ public MemberBinding getMemberBinding(Symbol name) {
return classBinding.getConstructorBinding();
}

// First check to see if this symbol matches a static binding
// for the class being described, for example java.util.HashMap
final MemberBinding staticMember = classBinding.getStaticMember(name);
if(staticMember == null) {
throw new EvalException("Class %s has no static member named '%s'",
classBinding.getBoundClass().getName(), name);
if(staticMember != null) {
return staticMember;
}

final MemberBinding instanceMember = javaLangClassBinding.getMemberBinding(name);
if(instanceMember != null) {
return instanceMember;
}

throw new EvalException("Class %s has no static member named '%s', nor does java.lang.Class have an " +
"instance member named '%s'",
classBinding.getBoundClass().getName(), name, name);

return staticMember;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package org.renjin.invoke.reflection;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;

import org.junit.Test;
import org.renjin.EvalTestCase;
import org.renjin.sexp.Symbol;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;


public class ClassBindingTest extends EvalTestCase {

Expand Down Expand Up @@ -38,8 +35,7 @@ public void bindingTest() {

assertThat(binding.getMembers(),
hasItems(Symbol.get("name"), Symbol.get("fooberize")));
assertThat(binding.getMembers(), not(hasItem(Symbol.get("wait"))));
assertThat(binding.getMembers(), not(hasItem(Symbol.get("notifyAll"))));


assertThat(binding.getMembers(), not(hasItem(Symbol.get("foo"))));
assertThat(binding.getMembers(), hasItem(Symbol.get("setFoo")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,5 @@ public void specilConvertTest() {
// assertThat(eval("if(instance$cakeConverted==cake) TRUE else FALSE"),logicalVectorOf(Logical.TRUE));
}


}
10 changes: 8 additions & 2 deletions core/src/test/java/org/renjin/primitives/JvmiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public void sapplyOnLists() throws IOException {
eval("import(org.renjin.primitives.MyBean)");
eval("x <- MyBean$new()");

assertThat( eval("sapply(x$childBeans, function(x) x$count)"), equalTo(c_i(42,42)));
assertThat(eval("sapply(x$childBeans, function(x) x$count)"), equalTo(c_i(42, 42)));
}

@Test
Expand All @@ -139,5 +139,11 @@ public void longIsNotMangled() {

}


@Test
public void classProperty() {
eval("import(java.util.HashMap)");
eval("ageMap <- HashMap$new()");

assertThat(eval("ageMap$class$name"), equalTo(c("java.util.HashMap")));
}
}

1 comment on commit 3196f6d

@hinerm
Copy link

@hinerm hinerm commented on 3196f6d Jan 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Please sign in to comment.