diff --git a/src/main/java/com/vectron/fcl/Fcl.java b/src/main/java/com/vectron/fcl/Fcl.java index b9723a2..5142745 100644 --- a/src/main/java/com/vectron/fcl/Fcl.java +++ b/src/main/java/com/vectron/fcl/Fcl.java @@ -350,6 +350,7 @@ public class Fcl { addPrimitive(".", () -> show(stack.pop())); addPrimitive("jvm-call-static", interOp::jvmCallStatic); addPrimitive("jvm-call-method", interOp::jvmCallMethod); + addPrimitive("jvm-has-method", interOp::jvmHasMethod); addPrimitive("jvm-static-var", interOp::jvmStaticVar); addPrimitive("jvm-null", () -> stack.push(null)); addPrimitive("asc*", this::sortAsc); diff --git a/src/main/java/com/vectron/fcl/interop/JvmInterOp.java b/src/main/java/com/vectron/fcl/interop/JvmInterOp.java index c9dc318..4eeb81c 100644 --- a/src/main/java/com/vectron/fcl/interop/JvmInterOp.java +++ b/src/main/java/com/vectron/fcl/interop/JvmInterOp.java @@ -2,6 +2,7 @@ package com.vectron.fcl.interop; import com.vectron.fcl.FclStack; import com.vectron.fcl.exceptions.InterOpFailed; +import com.vectron.fcl.types.Bool; import com.vectron.fcl.types.JvmObj; import com.vectron.fcl.types.Obj; @@ -31,6 +32,15 @@ public class JvmInterOp { spec.invoke(stack); } + public void jvmHasMethod() { + String methodName = stack.pop().asStr().value(); + Obj receiver = stack.pop(); + MethodSpec spec = MethodSpec.parseDynamic( + methodName, + receiver instanceof JvmObj ? ((JvmObj) receiver).value() : receiver); + stack.push(spec.exists() ? Bool.TRUE : Bool.FALSE); + } + public void jvmStaticVar() { String spec = stack.pop().asStr().value(); String[] parts = spec.split("/"); diff --git a/src/main/java/com/vectron/fcl/interop/MethodSpec.java b/src/main/java/com/vectron/fcl/interop/MethodSpec.java index 00c8295..321daee 100644 --- a/src/main/java/com/vectron/fcl/interop/MethodSpec.java +++ b/src/main/java/com/vectron/fcl/interop/MethodSpec.java @@ -71,6 +71,20 @@ class MethodSpec { } } + public boolean exists() { + List> types = new ArrayList<>(); + for (int i = 0; i < arity; i++) { + Class clazz = typeOf(typeSpec.charAt(i)); + types.add(clazz); + } + try { + clazz.getMethod(methodName, types.toArray(new Class[0])); + return true; + } catch (ReflectiveOperationException e) { + return false; + } + } + public static void processResult(Object result, FclStack stack) { if (result != null) { if (result instanceof Number) diff --git a/src/main/res/raw/misc.forth b/src/main/res/raw/misc.forth index 8299a98..6b70f86 100644 --- a/src/main/res/raw/misc.forth +++ b/src/main/res/raw/misc.forth @@ -1,4 +1,7 @@ : hist ( c -- m ) + dup 'iterator' jvm-has-method not if + drop #[ ]# exit + then -> tbl { -> elem tbl elem at -> count diff --git a/src/test/java/com/vectron/fcl/FclTest.java b/src/test/java/com/vectron/fcl/FclTest.java index 17a7ec7..e85f287 100644 --- a/src/test/java/com/vectron/fcl/FclTest.java +++ b/src/test/java/com/vectron/fcl/FclTest.java @@ -26,6 +26,7 @@ import static com.vectron.fcl.types.Bool.TRUE; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -391,6 +392,12 @@ public class FclTest { assertEquals(1.5430, evalPop("1 cosh").doubleValue(), 0.001); assertEquals(0.7616, evalPop("1 tanh").doubleValue(), 0.001); assertEquals(1, evalPop("1.3 'intValue' jvm-call-method").doubleValue(), 0.01); + assertFalse(evalPop("1.3 'nosuch' jvm-has-method").boolValue()); + assertTrue(evalPop("1.3 'round' jvm-has-method").boolValue()); + assertTrue(evalPop("[ 1 2 ] 'iterator' jvm-has-method").boolValue()); + assertTrue(evalPop("[ 1 2 ] 'append/O' jvm-has-method").boolValue()); + assertFalse(evalPop("[ 1 2 ] 'append/i' jvm-has-method").boolValue()); + assertFalse(evalPop("[ 1 2 ] 'append/OO' jvm-has-method").boolValue()); } @Test @@ -1122,6 +1129,7 @@ public class FclTest { public void testHist() { assertEquals("#[ 'a' 2 'b' 3 'c' 1 ]#", evalPop("'ababbc' hist").toString()); assertEquals("#[ ]#", evalPop("'' hist").toString()); + assertEquals("#[ ]#", evalPop("12 hist").toString()); } private String transcript() {