From: <pj...@us...> - 2009-10-03 20:35:17
|
Revision: 6829 http://jython.svn.sourceforge.net/jython/?rev=6829&view=rev Author: pjenvey Date: 2009-10-03 20:34:59 +0000 (Sat, 03 Oct 2009) Log Message: ----------- add the ability for the method exposer to optionally pass in the current ThreadState when a method signature requests it @ arg 0 Modified Paths: -------------- trunk/jython/src/org/python/expose/generate/ClassMethodExposer.java trunk/jython/src/org/python/expose/generate/InstanceMethodExposer.java trunk/jython/src/org/python/expose/generate/MethodExposer.java trunk/jython/src/org/python/expose/generate/PyTypes.java trunk/jython/tests/java/org/python/expose/generate/ExposedTypeProcessorTest.java trunk/jython/tests/java/org/python/expose/generate/MethodExposerTest.java trunk/jython/tests/java/org/python/expose/generate/SimpleExposed.java Modified: trunk/jython/src/org/python/expose/generate/ClassMethodExposer.java =================================================================== --- trunk/jython/src/org/python/expose/generate/ClassMethodExposer.java 2009-10-03 00:48:54 UTC (rev 6828) +++ trunk/jython/src/org/python/expose/generate/ClassMethodExposer.java 2009-10-03 20:34:59 UTC (rev 6829) @@ -29,12 +29,26 @@ private static Type[] getArgs(Type onType, String methodName, String desc) { Type[] args = Type.getArgumentTypes(desc); - if (args.length == 0 || !args[0].equals(PYTYPE)) { - throw new InvalidExposingException("The first argument to an ExposedClassMethod must be PyType[method=" - + onType.getClassName() + "." + methodName + "]"); + boolean needsThreadState = needsThreadState(args); + int offset = needsThreadState ? 1 : 0; + + if (args.length == offset || !args[offset].equals(PYTYPE)) { + String msg = String.format("ExposedClassMethod's first argument %smust be " + + "PyType[method=%s.%s]", + needsThreadState ? "(following ThreadState) " : "", + onType.getClassName(), methodName); + throw new InvalidExposingException(msg); } + + // Remove PyType from the exposed __call__'s args, it'll be already bound as self Type[] filledInArgs = new Type[args.length - 1]; - System.arraycopy(args, 1, filledInArgs, 0, filledInArgs.length); + if (needsThreadState) { + // ThreadState precedes PyType + filledInArgs[0] = args[0]; + System.arraycopy(args, 2, filledInArgs, 1, filledInArgs.length - 1); + } else { + System.arraycopy(args, 1, filledInArgs, 0, filledInArgs.length); + } return filledInArgs; } @@ -47,4 +61,13 @@ protected void checkSelf() { mv.visitTypeInsn(CHECKCAST, PYTYPE.getInternalName()); } + + @Override + protected void loadSelfAndThreadState() { + // ThreadState precedes self for ClassMethods, load it first if necessary + loadThreadState(); + // Push self on the stack so we can call it + get("self", PYOBJ); + checkSelf(); + } } Modified: trunk/jython/src/org/python/expose/generate/InstanceMethodExposer.java =================================================================== --- trunk/jython/src/org/python/expose/generate/InstanceMethodExposer.java 2009-10-03 00:48:54 UTC (rev 6828) +++ trunk/jython/src/org/python/expose/generate/InstanceMethodExposer.java 2009-10-03 20:34:59 UTC (rev 6829) @@ -99,7 +99,7 @@ + "', not a '"); } }); - mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, !needsThreadState(args) ? 1 : 2); call(PYOBJ, "getType", PYTYPE); call(PYTYPE, "fastGetName", STRING); call(STRING_BUILDER, "append", STRING_BUILDER, STRING); Modified: trunk/jython/src/org/python/expose/generate/MethodExposer.java =================================================================== --- trunk/jython/src/org/python/expose/generate/MethodExposer.java 2009-10-03 00:48:54 UTC (rev 6828) +++ trunk/jython/src/org/python/expose/generate/MethodExposer.java 2009-10-03 20:34:59 UTC (rev 6829) @@ -132,14 +132,27 @@ } private void generateWideCall() { - startMethod("__call__", PYOBJ, APYOBJ, ASTRING); - get("self", PYOBJ); - checkSelf(); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); + boolean needsThreadState = needsThreadState(args); + int offset = needsThreadState ? 1 : 0; + Type[] callArgs; + + if (needsThreadState) { + callArgs = new Type[] {THREAD_STATE, APYOBJ, ASTRING}; + } else { + callArgs = new Type[] {APYOBJ, ASTRING}; + } + startMethod("__call__", PYOBJ, callArgs); + + loadSelfAndThreadState(); + mv.visitVarInsn(ALOAD, offset + 1); + mv.visitVarInsn(ALOAD, offset + 2); makeCall(); toPy(returnType); endMethod(ARETURN); + + if (needsThreadState) { + generateCallNoThreadState(callArgs); + } } private boolean hasDefault(int argIndex) { @@ -151,17 +164,25 @@ } private void generateCall(int numDefaults) { - int usedLocals = 1;// We always have one used local for self - Type[] callArgs = new Type[args.length - numDefaults]; - for(int i = 0; i < callArgs.length; i++) { + boolean needsThreadState = needsThreadState(args); + int requiredLength = args.length - numDefaults; + int offset = needsThreadState ? 1 : 0; + // We always have one used local for self, and possibly one for ThreadState + int usedLocals = 1 + offset; + Type[] callArgs = new Type[requiredLength]; + + if (needsThreadState) { + callArgs[0] = THREAD_STATE; + } + for(int i = offset; i < callArgs.length; i++) { callArgs[i] = PYOBJ; } startMethod("__call__", PYOBJ, callArgs); - // Push self on the stack so we can call it - get("self", PYOBJ); - checkSelf(); + + loadSelfAndThreadState(); // Push the passed in callArgs onto the stack, and convert them if necessary - for(int i = 0; i < callArgs.length; i++) { + int i; + for(i = offset; i < requiredLength; i++) { mv.visitVarInsn(ALOAD, usedLocals++); if(PRIMITIVES.containsKey(args[i])) { callStatic(PY, "py2" + args[i].getClassName(), args[i], PYOBJ); @@ -174,14 +195,47 @@ } } // Push the defaults onto the stack - for(int i = callArgs.length; i < args.length; i++) { + for(; i < args.length; i++) { pushDefault(getDefault(i), args[i]); } makeCall(); toPy(returnType); endMethod(ARETURN); + + if (needsThreadState) { + generateCallNoThreadState(callArgs); + } } + private void generateCallNoThreadState(Type[] callArgs) { + Type[] noThreadStateArgs = new Type[callArgs.length - 1]; + System.arraycopy(callArgs, 1, noThreadStateArgs, 0, noThreadStateArgs.length); + startMethod("__call__", PYOBJ, noThreadStateArgs); + + mv.visitVarInsn(ALOAD, 0); + callStatic(PY, "getThreadState", THREAD_STATE); + for (int i = 0; i < noThreadStateArgs.length; i++) { + mv.visitVarInsn(ALOAD, i + 1); + } + + call(thisType, "__call__", PYOBJ, callArgs); + endMethod(ARETURN); + } + + protected void loadSelfAndThreadState() { + // Push self on the stack so we can call it + get("self", PYOBJ); + checkSelf(); + // Load ThreadState if necessary + loadThreadState(); + } + + protected void loadThreadState() { + if (needsThreadState(args)) { + mv.visitVarInsn(ALOAD, 1); + } + } + protected abstract void checkSelf(); protected abstract void makeCall(); @@ -218,8 +272,14 @@ } } + protected static boolean needsThreadState(Type[] args) { + return args.length > 0 && args[0].equals(THREAD_STATE); + } + protected static boolean isWide(Type[] args) { - return args.length == 2 && args[0].equals(APYOBJ) && args[1].equals(ASTRING); + int offset = needsThreadState(args) ? 1 : 0; + return args.length == 2 + offset + && args[offset].equals(APYOBJ) && args[offset + 1].equals(ASTRING); } } Modified: trunk/jython/src/org/python/expose/generate/PyTypes.java =================================================================== --- trunk/jython/src/org/python/expose/generate/PyTypes.java 2009-10-03 00:48:54 UTC (rev 6828) +++ trunk/jython/src/org/python/expose/generate/PyTypes.java 2009-10-03 20:34:59 UTC (rev 6829) @@ -15,6 +15,7 @@ import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyType; +import org.python.core.ThreadState; import org.python.expose.ExposeAsSuperclass; import org.python.expose.ExposedClassMethod; import org.python.expose.ExposedDelete; @@ -71,6 +72,8 @@ public static final Type BUILTIN_INFO = Type.getType(PyBuiltinCallable.Info.class); + public static final Type THREAD_STATE = Type.getType(ThreadState.class); + // Exposer Jython types public static final Type EXPOSED_TYPE = Type.getType(ExposedType.class); Modified: trunk/jython/tests/java/org/python/expose/generate/ExposedTypeProcessorTest.java =================================================================== --- trunk/jython/tests/java/org/python/expose/generate/ExposedTypeProcessorTest.java 2009-10-03 00:48:54 UTC (rev 6828) +++ trunk/jython/tests/java/org/python/expose/generate/ExposedTypeProcessorTest.java 2009-10-03 20:34:59 UTC (rev 6829) @@ -16,7 +16,7 @@ .getResourceAsStream("org/python/expose/generate/SimpleExposed.class"); ExposedTypeProcessor ice = new ExposedTypeProcessor(in); assertEquals("simpleexposed", ice.getName()); - assertEquals(19, ice.getMethodExposers().size()); + assertEquals(22, ice.getMethodExposers().size()); assertNotNull(ice.getNewExposer()); assertEquals(1, ice.getDescriptorExposers().size()); assertEquals("simpleexposed", ice.getTypeExposer().getName()); Modified: trunk/jython/tests/java/org/python/expose/generate/MethodExposerTest.java =================================================================== --- trunk/jython/tests/java/org/python/expose/generate/MethodExposerTest.java 2009-10-03 00:48:54 UTC (rev 6828) +++ trunk/jython/tests/java/org/python/expose/generate/MethodExposerTest.java 2009-10-03 20:34:59 UTC (rev 6829) @@ -6,6 +6,8 @@ import org.python.core.Py; import org.python.core.PyBuiltinCallable; import org.python.core.PyException; +import org.python.core.PyObject; +import org.python.core.ThreadState; import org.python.expose.MethodType; public class MethodExposerTest extends InterpTestCase implements Opcodes, PyTypes { @@ -226,6 +228,55 @@ assertEquals(3, bound.__call__(Py.newString("nothello"), Py.One).asInt()); } + public void testThreadState() throws Exception { + PyBuiltinCallable bound = createBound("needsThreadState", STRING, THREAD_STATE, STRING); + ThreadState ts = Py.getThreadState(); + PyObject expected = Py.newString("foo got state " + ts.hashCode()); + assertEquals(expected, bound.__call__(Py.getThreadState(), Py.newString("foo"))); + assertEquals(expected, bound.__call__(Py.newString("foo"))); + ts = new ThreadState(new Thread(), ts.systemState); + assertEquals(Py.newString("foo got state " + ts.hashCode()), + bound.__call__(ts, Py.newString("foo"))); + } + + public void testThreadStateFullArguments() throws Exception { + InstanceMethodExposer exp = new InstanceMethodExposer(Type.getType(SimpleExposed.class), + Opcodes.ACC_PUBLIC, + "needsThreadStateWide", + Type.getMethodDescriptor(Type.INT_TYPE, + new Type[] {THREAD_STATE, + APYOBJ, + ASTRING}), + "simpleexposed"); + PyBuiltinCallable bound = createBound(exp); + assertEquals(Py.Zero, bound.__call__(Py.getThreadState())); + assertEquals(Py.Zero, bound.__call__()); + assertEquals(Py.One, bound.__call__(Py.getThreadState(), Py.One)); + assertEquals(Py.One, bound.__call__(Py.One)); + } + + public void testThreadStateClassMethod() throws Exception { + ClassMethodExposer exp = new ClassMethodExposer(Type.getType(SimpleExposed.class), + Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, + "needsThreadStateClass", + Type.getMethodDescriptor(STRING, + new Type[] {THREAD_STATE, + PYTYPE, + STRING, + STRING}), + "simpleexposed", + new String[0], + new String[] {"null"}, + ""); + PyBuiltinCallable bound = createBound(exp); + ThreadState ts = Py.getThreadState(); + PyObject arg0 = Py.newString("bar"); + PyObject arg1 = Py.newString(" and extra"); + PyObject expected = Py.newString("bar got state " + ts.hashCode() + " got type and extra"); + assertEquals(expected, bound.__call__(Py.getThreadState(), arg0, arg1)); + assertEquals(expected, bound.__call__(arg0, arg1)); + } + public void test__new__() throws Exception { try { createExposer("__new__", VOID); Modified: trunk/jython/tests/java/org/python/expose/generate/SimpleExposed.java =================================================================== --- trunk/jython/tests/java/org/python/expose/generate/SimpleExposed.java 2009-10-03 00:48:54 UTC (rev 6828) +++ trunk/jython/tests/java/org/python/expose/generate/SimpleExposed.java 2009-10-03 20:34:59 UTC (rev 6829) @@ -6,6 +6,7 @@ import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyType; +import org.python.core.ThreadState; import org.python.expose.ExposedClassMethod; import org.python.expose.ExposedDelete; import org.python.expose.ExposedGet; @@ -150,4 +151,32 @@ public String toStringVal = TO_STRING_RETURN; public static final String TO_STRING_RETURN = "A simple test class"; + + @ExposedMethod + public String needsThreadState(ThreadState state, String s) { + return needsThreadStateClass(state, null, s, null); + } + + @ExposedMethod + public int needsThreadStateWide(ThreadState state, PyObject[] args, String[] kws) { + if (state == null) { + return -1; + } + return args.length + kws.length; + } + + @ExposedClassMethod(defaults = {"null"}) + public static String needsThreadStateClass(ThreadState state, PyType onType, String s, + String possiblyNull) { + if (state != null) { + s += " got state " + state.hashCode(); + } + if (onType != null) { + s += " got type"; + } + if (possiblyNull != null) { + s += possiblyNull; + } + return s; + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |