Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
544f288
Merge branch 'BF-42' into BF-39
Xmader Apr 3, 2023
ffc4b1c
feat(promise): add the `PromiseType` class inheriting from the PyType…
Xmader Mar 29, 2023
ecaee4a
feat(event-loop): use internal job queues
Xmader Apr 3, 2023
c2ce56e
feat(event-loop): provide our own job queue (not implemented yet)
Xmader Apr 3, 2023
031b29a
fix(event-loop): `JS_DestroyContext` also calls `JS::ShutdownAsyncTas…
Xmader Apr 3, 2023
391677a
feat(event-loop): queue promise jobs to the Python event-loop
Xmader Apr 4, 2023
17facfd
feat(exception-propagation): check if a Python exception has already …
Xmader Apr 4, 2023
38878bc
feat(event-loop): assert Python event-loop running
Xmader Apr 4, 2023
0198ddf
fix(event-loop): more specific error when no Python event-loop running
Xmader Apr 4, 2023
4935aae
refactor(event-loop): use `PyObject_CallMethod` with building values
Xmader Apr 4, 2023
d9895f3
feat(event-loop): support off-thread promises for async WebAssembly APIs
Xmader Apr 4, 2023
7654203
refactor(event-loop): move `asyncio` python calls into its own `enque…
Xmader Apr 4, 2023
6a5edf5
refactor(event-loop): move python calls to the new `PyEventLoop` struct
Xmader Apr 5, 2023
d373206
feat(event-loop): support cancel event-loop jobs
Xmader Apr 5, 2023
65e6626
feat(event-loop): schedule job (with given delay) to the Python event…
Xmader Apr 5, 2023
4fb29b5
feat(event-loop): implement `setTimeout` and `clearTimeout`
Xmader Apr 6, 2023
dae6675
fix(event-loop): `setTimeout` should set `this` to the global object …
Xmader Apr 6, 2023
9f21bf7
fix(event-loop): `setTimeout` should set `delay` to 0 if < 0, as spec-ed
Xmader Apr 6, 2023
d64b3ea
fix(event-loop): `setTimeout` should allow passing additional argumen…
Xmader Apr 6, 2023
24e53c9
refactor(event-loop): use `enqueuePromiseJob`'s `incumbentGlobal` arg…
Xmader Apr 10, 2023
815a111
feat(promise): JS Promise to Python awaitable (`asyncio.Future`) coer…
Xmader Apr 10, 2023
2e17e92
fix(exception-propagation): `errorReport->filename` can be null
Xmader Apr 10, 2023
3ddb084
feat(exception-propagation): JS Error object to Python Exception obje…
Xmader Apr 11, 2023
7484319
feat(promise): handle rejected JS Promise
Xmader Apr 11, 2023
f0615ff
fix(promise): handle Promise rejection with non-Error object thrown
Xmader Apr 12, 2023
d9ec062
feat(promise): python awaitable object check
Xmader Apr 12, 2023
b33af5b
feat(promise): Python awaitable to JS Promise coercion
Xmader Apr 13, 2023
204a068
fix(promise): memory leak for the rooted Promise object in `PromiseTy…
Xmader Apr 13, 2023
42a2dbf
feat(promise): handle cancelled Python awaitable in to JS Promise coe…
Xmader Apr 13, 2023
4fa65f3
Merge branch 'main' into BF-41
Xmader Apr 14, 2023
08a3739
feat(jsTypeFactory): jsTypeFactorySafe to warn instead of setting an …
Xmader Apr 17, 2023
5f9e83a
feat(jsTypeFactory): jsTypeFactorySafe to warn the actual error strin…
Xmader Apr 17, 2023
7e9c393
fix(promise): accessing to arbitrary unsafe memory locations on `onRe…
Xmader Apr 17, 2023
c91ce69
feat(exception-propagation): Python Exception object to JS Error obje…
Xmader Apr 17, 2023
66d32df
fix(jsTypeFactory): for `jsTypeFactorySafe`, the error value may not …
Xmader Apr 17, 2023
5780112
test(exception-propagation): write tests for Python Exception to JS E…
Xmader Apr 18, 2023
186ff7a
test(event-loop): write tests for `setTimeout` and `clearTimeout`
Xmader Apr 18, 2023
a49cec7
feat(promise): handle cancelled Python awaitable
Xmader Apr 18, 2023
432e9bf
fix(promise): the final GC for Python asyncio.Future objects coercion
Xmader Apr 19, 2023
3a60f26
fix(promise): GC for Python asyncio.Future objects coercion
Xmader Apr 19, 2023
fd70e7b
fix(GC): python object would have already been deallocated, `pyObj->o…
Xmader Apr 19, 2023
8a87d21
fix(promise): JS Promise may resolve to a JS function, so we must kee…
Xmader Apr 20, 2023
b3cdb2a
fix(callPyFunc): Python may raise an exception, if the number of argu…
Xmader Apr 20, 2023
a7405d1
test(promise): write tests for JS promise <-> Python awaitable coercion
Xmader Apr 20, 2023
0468e95
Revert "fix(GC): python object would have already been deallocated, `…
Xmader Apr 21, 2023
5a2cbb4
refactor(event-loop): the `_loop` member in `PyEventLoop` struct shou…
Xmader Apr 24, 2023
471ce4b
refactor(event-loop): use a timeoutID pool instead of casting a raw P…
Xmader Apr 24, 2023
b155c8c
fix(event-loop): no exception should be thrown when an invalid timeou…
Xmader Apr 24, 2023
b064d87
fix(event-loop): `std::exchange` compile-time error: ‘exchange’ is no…
Xmader Apr 24, 2023
ee75a56
fix(event-loop): the timeoutID should always be an integer
Xmader Apr 24, 2023
5f0c2bb
feat(event-loop): throw a TypeError when the first parameter to `setT…
Xmader Apr 24, 2023
271a3c4
feat(event-loop): schedule off-thread promises to the event-loop on m…
Xmader Apr 25, 2023
5113e86
refactor(event-loop): rename `PyEventLoop::_mainLoopNotFound` to `_lo…
Xmader Apr 25, 2023
f0f79a0
refactor(event-loop): use only Python C APIs to get the running event…
Xmader Apr 25, 2023
d58f6d5
fix(pyTypeFactory): segfault if `pyObject` is not set yet in construc…
Xmader Apr 25, 2023
5929551
test(promise): a python RuntimeError should be thrown if JS Promise i…
Xmader Apr 25, 2023
9d04387
feat(event-loop): make sure promise jobs are JS functions, using `MOZ…
Xmader Apr 26, 2023
ceab871
feat(buffers): Python bytearray to JS ArrayBuffer coercion
Xmader Apr 28, 2023
0b19619
feat(buffers): Python buffer objects to JS TypedArray coercion, the T…
Xmader May 1, 2023
64bc024
feat(buffers): JS TypedArray/ArrayBuffer to Python `memoryview` coercion
Xmader May 2, 2023
ddd6bb9
fix(buffers): handle empty Python buffers
Xmader May 3, 2023
da40a78
feat(buffers): disallow multidimensional arrays, only 1-D array is su…
Xmader May 3, 2023
bd6ab2f
chore: install numpy for our tests
Xmader May 3, 2023
59ee761
refactor(buffers): `_releasePyBuffer` accepts a single `Py_buffer *` …
Xmader May 4, 2023
5caaabb
fix(buffers): segfault on final GC
Xmader May 4, 2023
7fa4232
test(buffers): write tests for coercing buffers of numpy array to JS …
Xmader May 4, 2023
bce13f2
test(buffers): write tests for JS TypedArray/ArrayBuffer <-> Python m…
Xmader May 4, 2023
afb5fe9
fix(callPyFunc): capture error for the return value as well because `…
Xmader May 4, 2023
c857270
refactor(buffers): suppress compile-time warning `ISO C++ forbids con…
Xmader May 4, 2023
0b59bb0
test(buffers): write tests for JS TypedArray/ArrayBuffer <-> Python m…
Xmader May 8, 2023
dca5d9b
test(buffers): write tests for buffers mutation
Xmader May 8, 2023
45e4b4c
refactor(buffers): use `sizeof(int8_t)` instead of hard-coded byte si…
Xmader May 12, 2023
7944df3
Merge branch 'main' into BF-40
Xmader May 25, 2023
24778fc
Merge branch 'fix/spidermonkey-build' into BF-40
Xmader May 25, 2023
b4e73ad
ci: force GC whenever a test function finishes, to locate GC-related …
Xmader May 26, 2023
fbf3628
fix(GC): in Python 3.11+, python function object would be double-free…
Xmader May 26, 2023
ec1ab5c
Merge branch 'main' into BF-40
Xmader May 30, 2023
0f0ead5
Merge branch 'main' into BF-40
Xmader Jun 16, 2023
298fcb3
fix(event-loop): `PyRunningLoopHolder` is removed in Python 3.12+
Xmader Jun 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions include/BufferType.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @file BufferType.hh
* @author Tom Tang (xmader@distributive.network)
* @brief Struct for representing ArrayBuffers
* @version 0.1
* @date 2023-04-27
*
* @copyright Copyright (c) 2023 Distributive Corp.
*
*/

#ifndef PythonMonkey_BufferType_
#define PythonMonkey_BufferType_

#include "PyType.hh"
#include "TypeEnum.hh"

#include <jsapi.h>
#include <js/ScalarType.h>

#include <Python.h>

struct BufferType : public PyType {
public:
BufferType(PyObject *object);

/**
* @brief Construct a new BufferType object from a JS TypedArray or ArrayBuffer, as a Python [memoryview](https://docs.python.org/3.9/c-api/memoryview.html) object
*
* @param cx - javascript context pointer
* @param bufObj - JS object to be coerced
*/
BufferType(JSContext *cx, JS::HandleObject bufObj);

const TYPE returnType = TYPE::BUFFER;

/**
* @brief Convert a Python object that [provides the buffer interface](https://docs.python.org/3.9/c-api/typeobj.html#buffer-object-structures) to JS TypedArray.
* The subtype (Uint8Array, Float64Array, ...) is automatically determined by the Python buffer's [format](https://docs.python.org/3.9/c-api/buffer.html#c.Py_buffer.format)
*
* @param cx - javascript context pointer
*/
JSObject *toJsTypedArray(JSContext *cx);

/**
* @returns Is the given JS object either a TypedArray or an ArrayBuffer?
*/
static bool isSupportedJsTypes(JSObject *obj);

protected:
virtual void print(std::ostream &os) const override;

static PyObject *fromJsTypedArray(JSContext *cx, JS::HandleObject typedArray);
static PyObject *fromJsArrayBuffer(JSContext *cx, JS::HandleObject arrayBuffer);

private:
static void _releasePyBuffer(Py_buffer *bufView);
static void _releasePyBuffer(void *, void *bufView); // JS::BufferContentsFreeFunc callback for JS::NewExternalArrayBuffer

static JS::Scalar::Type _getPyBufferType(Py_buffer *bufView);
static const char *_toPyBufferFormatCode(JS::Scalar::Type subtype);

/**
* @brief Create a new typed array using up the given ArrayBuffer or SharedArrayBuffer for storage.
* @see https://hg.mozilla.org/releases/mozilla-esr102/file/a03fde6/js/public/experimental/TypedData.h#l80
* There's no SpiderMonkey API to assign the subtype at execution time
*/
static JSObject *_newTypedArrayWithBuffer(JSContext *cx, JS::Scalar::Type subtype, JS::HandleObject arrayBuffer);
};

#endif
49 changes: 49 additions & 0 deletions include/ExceptionType.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @file ExceptionType.hh
* @author Tom Tang (xmader@distributive.network)
* @brief Struct for representing Python Exception objects from a corresponding JS Error object
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
*/

#ifndef PythonMonkey_ExceptionType_
#define PythonMonkey_ExceptionType_

#include "PyType.hh"
#include "TypeEnum.hh"

#include <jsapi.h>

#include <Python.h>

/**
* @brief This struct represents a Python Exception object from the corresponding JS Error object
*/
struct ExceptionType : public PyType {
public:
ExceptionType(PyObject *object);

/**
* @brief Construct a new SpiderMonkeyError from the JS Error object.
*
* @param cx - javascript context pointer
* @param error - JS Error object to be converted
*/
ExceptionType(JSContext *cx, JS::HandleObject error);

const TYPE returnType = TYPE::EXCEPTION;

/**
* @brief Convert a python [*Exception object](https://docs.python.org/3/c-api/exceptions.html#standard-exceptions) to JS Error object
*
* @param cx - javascript context pointer
*/
JSObject *toJsError(JSContext *cx);
protected:
virtual void print(std::ostream &os) const override;
};

#endif
2 changes: 1 addition & 1 deletion include/IntType.hh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public:
* @brief Construct a new IntType object from a JS::BigInt.
*
* @param cx - javascript context pointer
* @param str - JS::BigInt pointer
* @param bigint - JS::BigInt pointer
*/
IntType(JSContext *cx, JS::BigInt *bigint);

Expand Down
67 changes: 67 additions & 0 deletions include/JobQueue.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @file JobQueue.hh
* @author Tom Tang (xmader@distributive.network)
* @brief
* @version 0.1
* @date 2023-04-03
*
* @copyright Copyright (c) 2023
*
*/

#ifndef PythonMonkey_JobQueue_
#define PythonMonkey_JobQueue_

#include <jsapi.h>
#include <js/Promise.h>

#include <Python.h>

class JobQueue : public JS::JobQueue {
//
// JS::JobQueue methods.
// see also: js::InternalJobQueue https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/src/vm/JSContext.h#l88
//
public:
~JobQueue() = default;

JSObject *getIncumbentGlobal(JSContext *cx) override;

bool enqueuePromiseJob(JSContext *cx, JS::HandleObject promise,
JS::HandleObject job, JS::HandleObject allocationSite,
JS::HandleObject incumbentGlobal) override;

void runJobs(JSContext *cx) override;

/**
* @return true if the job queue is empty, false otherwise.
*/
bool empty() const override;

private:
js::UniquePtr<JS::JobQueue::SavedJobQueue> saveJobQueue(JSContext *) override;

//
// Custom methods
//
public:
/**
* @brief Initialize PythonMonkey's event-loop job queue
* @param cx - javascript context pointer
* @return success
*/
bool init(JSContext *cx);

private:
/**
* @brief The callback for dispatching an off-thread promise to the event loop
* see https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/public/Promise.h#l580
* https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/src/vm/OffThreadPromiseRuntimeState.cpp#l160
* @param closure - closure, currently the javascript context
* @param dispatchable - Pointer to the Dispatchable to be called
* @return not shutting down
*/
static bool dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable);
};

#endif
56 changes: 56 additions & 0 deletions include/PromiseType.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @file PromiseType.hh
* @author Tom Tang (xmader@distributive.network)
* @brief Struct for representing Promises
* @version 0.1
* @date 2023-03-29
*
* @copyright Copyright (c) 2023
*
*/

#ifndef PythonMonkey_PromiseType_
#define PythonMonkey_PromiseType_

#include "PyType.hh"
#include "TypeEnum.hh"

#include <jsapi.h>
#include <js/Promise.h>

#include <Python.h>

/**
* @brief This struct represents the JS Promise type in Python using our custom pythonmonkey.promise type. It inherits from the PyType struct
*/
struct PromiseType : public PyType {
public:
PromiseType(PyObject *object);

/**
* @brief Construct a new PromiseType object from a JS::PromiseObject.
*
* @param cx - javascript context pointer
* @param promise - JS::PromiseObject to be coerced
*/
PromiseType(JSContext *cx, JS::HandleObject promise);

const TYPE returnType = TYPE::PYTHONMONKEY_PROMISE;

/**
* @brief Convert a Python [awaitable](https://docs.python.org/3/library/asyncio-task.html#awaitables) object to JS Promise
*
* @param cx - javascript context pointer
*/
JSObject *toJsPromise(JSContext *cx);
protected:
virtual void print(std::ostream &os) const override;
};

/**
* @brief Check if the object can be used in Python await expression.
* `PyAwaitable_Check` hasn't been and has no plan to be added to the Python C API as of CPython 3.9
*/
bool PythonAwaitable_Check(PyObject *obj);

#endif
Loading