Skip to content

Commit 8ef71d4

Browse files
committed
Make marshal a proper module
We now deal with the marshal module as an instance,initialised in two phases. We do not yet have an interpreter object, or import mechanism, so this is all a bit hand-crafted.
1 parent 9528cb7 commit 8ef71d4

File tree

7 files changed

+81
-32
lines changed

7 files changed

+81
-32
lines changed

rt4core/src/main/java/uk/co/farowl/vsj4/core/BuiltinsModule.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ class BuiltinsModule extends JavaModule {
3030
super(DEFINITION);
3131
}
3232

33-
/** Execute the body of the module. */
3433
@Override
35-
void exec() {
34+
public void exec() {
3635
super.exec();
3736

3837
/*

rt4core/src/main/java/uk/co/farowl/vsj4/core/JavaModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ protected JavaModule(ModuleDef definition) {
3636
* of PEP 489 phases, this is the {@code Py_mod_exec} phase.
3737
*/
3838
@Override
39-
void exec() {
39+
public void exec() {
4040
super.exec();
4141
definition.addMembers(this);
4242
}

rt4core/src/main/java/uk/co/farowl/vsj4/core/ModuleDef.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import java.lang.invoke.MethodHandle;
66
import java.lang.invoke.MethodHandles.Lookup;
77

8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
811
import uk.co.farowl.vsj4.types.Exposed;
912

1013
/**
@@ -31,6 +34,9 @@
3134
public class ModuleDef {
3235
// Compare CPython PyModuleDef
3336

37+
/** Logger for the module definition. */
38+
final Logger logger = LoggerFactory.getLogger(ModuleDef.class);
39+
3440
/** Name of the module. */
3541
final String name;
3642

@@ -57,14 +63,16 @@ public class ModuleDef {
5763
* @param name of the module (e.g. "sys" or "math")
5864
* @param lookup authorises access to the defining class.
5965
*/
60-
ModuleDef(String name, Lookup lookup) {
66+
public ModuleDef(String name, Lookup lookup) {
6167
this.name = name;
6268
this.definingClass = lookup.lookupClass();
6369
ModuleExposer exposer = new ModuleExposer(name);
6470
exposer.exposeMethods(definingClass);
6571
// XXX ... and for fields.
6672
// XXX ... and for types defined in the module maybe? :o
6773
this.methods = exposer.getMethodDefs(lookup);
74+
logger.atInfo().setMessage("Module definition '{}'")
75+
.addArgument(name).log();
6876
}
6977

7078
/**
@@ -84,6 +92,8 @@ void addMembers(JavaModule module) {
8492
PyDict d = module.dict;
8593
for (MethodDef md : methods) {
8694
// Create function by binding to the module
95+
logger.atTrace().setMessage("Add {}.{}").addArgument(name)
96+
.addArgument(md.argParser.name).log();
8797
PyJavaFunction func = PyJavaFunction.forModule(md.argParser,
8898
md.handle, module, this.name);
8999
d.put(md.argParser.name, func);

rt4core/src/main/java/uk/co/farowl/vsj4/core/PyModule.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import java.lang.invoke.MethodHandles;
66

7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
710
import uk.co.farowl.vsj4.types.TypeSpec;
811
import uk.co.farowl.vsj4.types.WithDict;
912

@@ -34,6 +37,9 @@
3437
*/
3538
public class PyModule implements WithDict {
3639

40+
/** Logger for the marshal module. */
41+
final Logger logger = LoggerFactory.getLogger(PyModule.class);
42+
3743
/** The type of Python object this class implements. */
3844
public static final PyType TYPE = PyType.fromSpec( //
3945
new TypeSpec("module", MethodHandles.lookup()));
@@ -71,7 +77,10 @@ public class PyModule implements WithDict {
7177
* entries to {@link #dict}. These become the members (globals) of
7278
* the module.
7379
*/
74-
void exec() {}
80+
public void exec() {
81+
logger.atInfo().setMessage("Executing body of module '{}'.")
82+
.addArgument(name).log();
83+
}
7584

7685
@Override
7786
public PyType getType() { return type; }
@@ -103,5 +112,9 @@ public String toString() {
103112
* @param name to use as key
104113
* @param o value for key
105114
*/
106-
void add(String name, Object o) { dict.put(name, o); }
115+
void add(String name, Object o) {
116+
logger.atTrace().setMessage("Add {}.{}").addArgument(this.name)
117+
.addArgument(name).log();
118+
dict.put(name, o);
119+
}
107120
}

rt4core/src/main/java/uk/co/farowl/vsj4/modules/marshal.java

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.io.IOException;
99
import java.io.InputStream;
1010
import java.io.OutputStream;
11+
import java.lang.invoke.MethodHandles;
1112
import java.math.BigInteger;
1213
import java.nio.BufferUnderflowException;
1314
import java.nio.ByteBuffer;
@@ -24,6 +25,8 @@
2425

2526
import uk.co.farowl.vsj4.core.Abstract;
2627
import uk.co.farowl.vsj4.core.CPython311Code;
28+
import uk.co.farowl.vsj4.core.JavaModule;
29+
import uk.co.farowl.vsj4.core.ModuleDef;
2730
import uk.co.farowl.vsj4.core.Py;
2831
import uk.co.farowl.vsj4.core.PyBaseException;
2932
import uk.co.farowl.vsj4.core.PyBool;
@@ -47,7 +50,7 @@
4750
import uk.co.farowl.vsj4.stringlib.IntArrayBuilder;
4851
import uk.co.farowl.vsj4.types.Exposed.Default;
4952
import uk.co.farowl.vsj4.types.Exposed.Member;
50-
import uk.co.farowl.vsj4.types.Exposed.PythonStaticMethod;
53+
import uk.co.farowl.vsj4.types.Exposed.PythonMethod;
5154

5255
/**
5356
* Write Python objects to files and read them back. This is primarily
@@ -56,16 +59,25 @@
5659
* not commonly seen in {@code code} objects, are supported. Version 3
5760
* of this protocol properly supports circular links and sharing.
5861
*/
59-
public class marshal /* extends JavaModule */ {
62+
public class marshal extends JavaModule {
6063

6164
/** Logger for the marshal module. */
62-
static // TODO make marshal a proper module (instance)
6365
final Logger logger = LoggerFactory.getLogger(marshal.class);
6466

6567
/** Version of the marshal protocol in use. */
6668
@Member("version")
6769
final static int VERSION = 4;
6870

71+
/** Definition of the module (completed by the module exposer). */
72+
static final ModuleDef DEFINITION =
73+
new ModuleDef("marshal", MethodHandles.lookup());
74+
75+
/** Construct an instance of the module. */
76+
public marshal() {
77+
super(DEFINITION);
78+
logger.atInfo().setMessage("Instance created").log();
79+
}
80+
6981
/*
7082
* High water mark to determine when the marshalled object is
7183
* dangerously deep and risks coring the interpreter. When the
@@ -275,8 +287,8 @@ private static void register(Codec codec) {
275287
* contains an object that has) an unsupported type
276288
* @throws PyBaseException (OSError) from file operations
277289
*/
278-
@PythonStaticMethod
279-
public static void dump(Object value, Object file,
290+
@PythonMethod
291+
public void dump(Object value, Object file,
280292
@Default("4") int version) throws PyBaseException {
281293
logger.atDebug().setMessage("marshal.dump() to {}")
282294
.addArgument(file).log();
@@ -305,8 +317,8 @@ public static void dump(Object value, Object file,
305317
* @throws PyBaseException (EOFError) when a partial object is read
306318
* @throws PyBaseException (OSError) from file operations generally
307319
*/
308-
@PythonStaticMethod
309-
public static Object load(Object file) {
320+
@PythonMethod
321+
public Object load(Object file) {
310322
logger.atDebug().setMessage("marshal.load() {}")
311323
.addArgument(file).log();
312324
try (InputStream is = StreamReader.adapt(file)) {
@@ -330,8 +342,8 @@ public static Object load(Object file) {
330342
* @throws PyBaseException (ValueError) if the value has (or
331343
* contains an object that has) an unsupported type
332344
*/
333-
@PythonStaticMethod
334-
public static PyBytes dumps(Object value, @Default("4") int version)
345+
@PythonMethod
346+
public PyBytes dumps(Object value, @Default("4") int version)
335347
throws PyBaseException {
336348
logger.atDebug().setMessage("marshal.dumps()").log();
337349
ByteArrayBuilder bb = new ByteArrayBuilder();
@@ -355,8 +367,8 @@ public static PyBytes dumps(Object value, @Default("4") int version)
355367
* null element.
356368
* @throws PyBaseException (EOFError) when a partial object is read
357369
*/
358-
@PythonStaticMethod
359-
public static Object loads(Object bytes) {
370+
@PythonMethod
371+
public Object loads(Object bytes) {
360372
logger.atDebug().setMessage("marshal.loads()").log();
361373
try {
362374
ByteBuffer bb = BytesReader.adapt(bytes);
@@ -646,7 +658,7 @@ void writeInts(OfInt seq) {
646658
* their implementation of decoding methods registered against the
647659
* type codes they support. (See also {@link Codec#decoders()}.
648660
*/
649-
public abstract static class Reader {
661+
public abstract class Reader {
650662

651663
/**
652664
* Objects read from the source may have been marked (by the
@@ -914,7 +926,7 @@ protected static PyBaseException nullObject(String type) {
914926
* {@code java.io.ByteArrayInputStream} needs no additional
915927
* buffering.
916928
*/
917-
public static class StreamReader extends Reader {
929+
public class StreamReader extends Reader {
918930

919931
/**
920932
* The source wrapped in a {@code DataInputStream} on which we
@@ -1003,7 +1015,7 @@ private static InputStream adapt(Object file)
10031015
/**
10041016
* A {@link Reader} that has a {@code ByteBuffer} as its source.
10051017
*/
1006-
public static class BytesReader extends Reader {
1018+
public class BytesReader extends Reader {
10071019

10081020
/**
10091021
* The source as little-endian a {@code ByteBuffer} on which we

rt4core/src/test/java/uk/co/farowl/vsj4/core/CPython311CodeTest.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@
4747
@DisplayName("Given programs compiled by CPython 3.11 ...")
4848
class CPython311CodeTest extends UnitTestSupport {
4949

50+
/** Instance of the {@code marshal} module. */
51+
static final marshal MARSHAL;
52+
static {
53+
MARSHAL = new marshal();
54+
MARSHAL.exec();
55+
}
56+
5057
@SuppressWarnings("static-method")
5158
@DisplayName("marshal can read a code object")
5259
@ParameterizedTest(name = "from {0}")
@@ -472,7 +479,7 @@ static CPython311Code readCode(String progName) {
472479
BufferedInputStream s = new BufferedInputStream(fs);) {
473480

474481
// Wrap a marshal reader around the input stream
475-
marshal.Reader reader = new marshal.StreamReader(s);
482+
marshal.Reader reader = MARSHAL.new StreamReader(s);
476483

477484
// First 4 bytes is a magic header
478485
int magic = reader.readShort();
@@ -516,7 +523,7 @@ static PyDict readResultDict(String progName) {
516523
BufferedInputStream s = new BufferedInputStream(fs);) {
517524

518525
// Wrap a marshal reader around the input stream
519-
marshal.Reader reader = new marshal.StreamReader(s);
526+
marshal.Reader reader = MARSHAL.new StreamReader(s);
520527

521528
// Should be a dict object
522529
Object o = reader.readObject();

rt4core/src/test/java/uk/co/farowl/vsj4/modules/marshalTest.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@
2929
import uk.co.farowl.vsj4.core.PyTuple;
3030
import uk.co.farowl.vsj4.core.PyType;
3131
import uk.co.farowl.vsj4.core.UnitTestSupport;
32-
import uk.co.farowl.vsj4.modules.marshal.BytesReader;
3332
import uk.co.farowl.vsj4.modules.marshal.BytesWriter;
3433
import uk.co.farowl.vsj4.modules.marshal.Reader;
35-
import uk.co.farowl.vsj4.modules.marshal.StreamReader;
3634
import uk.co.farowl.vsj4.modules.marshal.StreamWriter;
3735
import uk.co.farowl.vsj4.modules.marshal.Writer;
3836
import uk.co.farowl.vsj4.stringlib.ByteArrayBuilder;
@@ -54,6 +52,9 @@ class marshalTest extends UnitTestSupport {
5452
*/
5553
abstract static class AbstractElementTest {
5654

55+
/** Instance of the {@code marshal} module. */
56+
marshal marshal = new marshal();
57+
5758
/**
5859
* Test cases for serialising 16-bit ints.
5960
*
@@ -167,31 +168,31 @@ class ReadBytesElementary extends AbstractElementTest {
167168
@ParameterizedTest(name = "r.readShort() = {0}")
168169
@MethodSource("int16")
169170
void int16read(Integer expected, byte[] b) {
170-
Reader r = new BytesReader(b);
171+
Reader r = marshal.new BytesReader(b);
171172
assertEquals(expected, r.readShort());
172173
}
173174

174175
@DisplayName("r.readInt()")
175176
@ParameterizedTest(name = "r.readInt() = {0}")
176177
@MethodSource("int32")
177178
void int32read(Integer expected, byte[] b) {
178-
Reader r = new BytesReader(b);
179+
Reader r = marshal.new BytesReader(b);
179180
assertEquals(expected, r.readInt());
180181
}
181182

182183
@DisplayName("r.readLong()")
183184
@ParameterizedTest(name = "r.readInt() = {0}")
184185
@MethodSource("int64")
185186
void int64read(Long expected, byte[] b) {
186-
Reader r = new BytesReader(b);
187+
Reader r = marshal.new BytesReader(b);
187188
assertEquals(expected, r.readLong());
188189
}
189190

190191
@DisplayName("r.readBigInteger()")
191192
@ParameterizedTest(name = "r.readBigInteger() = {0}")
192193
@MethodSource("bigint")
193194
void bigintread(BigInteger expected, byte[] b) {
194-
Reader r = new BytesReader(b);
195+
Reader r = marshal.new BytesReader(b);
195196
assertEquals(expected, r.readBigInteger());
196197
}
197198
}
@@ -209,31 +210,35 @@ class ReadStreamElementary extends AbstractElementTest {
209210
@ParameterizedTest(name = "r.readShort() = {0}")
210211
@MethodSource("int16")
211212
void int16read(Integer expected, byte[] b) {
212-
Reader r = new StreamReader(new ByteArrayInputStream(b));
213+
Reader r = marshal.new StreamReader(
214+
new ByteArrayInputStream(b));
213215
assertEquals(expected, r.readShort());
214216
}
215217

216218
@DisplayName("r.readInt()")
217219
@ParameterizedTest(name = "r.readInt() = {0}")
218220
@MethodSource("int32")
219221
void int32read(Integer expected, byte[] b) {
220-
Reader r = new StreamReader(new ByteArrayInputStream(b));
222+
Reader r = marshal.new StreamReader(
223+
new ByteArrayInputStream(b));
221224
assertEquals(expected, r.readInt());
222225
}
223226

224227
@DisplayName("r.readLong()")
225228
@ParameterizedTest(name = "r.readInt() = {0}")
226229
@MethodSource("int64")
227230
void int64read(Long expected, byte[] b) {
228-
Reader r = new StreamReader(new ByteArrayInputStream(b));
231+
Reader r = marshal.new StreamReader(
232+
new ByteArrayInputStream(b));
229233
assertEquals(expected, r.readLong());
230234
}
231235

232236
@DisplayName("r.readBigInteger()")
233237
@ParameterizedTest(name = "r.readBigInteger() = {0}")
234238
@MethodSource("bigint")
235239
void bigintread(BigInteger expected, byte[] b) {
236-
Reader r = new StreamReader(new ByteArrayInputStream(b));
240+
Reader r = marshal.new StreamReader(
241+
new ByteArrayInputStream(b));
237242
assertEquals(expected, r.readBigInteger());
238243
}
239244
}
@@ -344,6 +349,9 @@ void bigintwrite(BigInteger v, byte[] expected) {
344349
/** Base of tests that read objects serialised by CPython. */
345350
abstract static class AbstractLoadTest {
346351

352+
/** Instance of the {@code marshal} module. */
353+
marshal marshal = new marshal();
354+
347355
/**
348356
* Provide a stream of examples as parameter sets to the tests.
349357
* In each example, the expression is given (as documentation

0 commit comments

Comments
 (0)