Closed
Description
Search Terms
- postMessage
- Transferable
- structuredserialize, structured serialize
- use-after-transfer
- service worker
Suggestion
The Service Worker APIs come with a mthod postMessage
(e.g. window.postMessage). Three things:
-
The parameter
message
is processed using the structured clone algorithm, which requires the value to
satisfy certain constrains: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm- Current type of
message
:any
- The algorithm prohibits passing these values for
message
(or an object containing these values):Error
s andFunction
s- DOM nodes
- There is a list of all allowed types. However:
- No symbols
- only plain objects (e.g. from object literals)
- While I think that it would be possible to constrain the
message
type fromany
to something more specific, I don't know if there is a way to constrain the object requirements. any
allows passing not-serializable data- Suggestion: Constrain the type of
message
- Additional suggestion: Constrain the type of
MessageEvent#data
- Current type of
-
There is a
Transferable
interface which is used to provide transfering objects instead of copying them.- It has no specific properties or methods
- It is currently implemented by:
ArrayBuffer
,MessagePort
,ImageBitmap
- Tricky: As
Transferable
is basically an empty interface any
allows passing not-transferable data- Suggestion: Constrain the type of
transfer
/transferList
toArrayBuffer | MessagePort | ImageBitmap
(or find another way of constraining the type)
-
A transfered object cannot be used after it was transferred (e.g. using
postMessage()
)- Suggestion: Introduce a diagnostic for use-after-transfer of a variable to warn the user
Use Cases
- For 1: Prevents accidental passing of an object containing not-serializable data. Prevents an exception during runtime (
DATA_CLONE_ERR
). - For 2: Prevents passing a value of the wrong type that does not implement the interface.
- For 3: Prevents accidental use of value after it was transfered.
Examples
1.:
These errors could be prevented (all valid according to the current definitions):
window.postMessage({
f() {
alert("hi!");
}
}, "*");
window.postMessage({
e: document.createElement("div"),
}, "*");
window.postMessage({
s: Symbol(":("),
}, "*");
window.postMessage(class foo { }, "*");
2.:
This error could be prevented:
window.postMessage({ }, "*", [ { foo: 42 } /* objects are not transferable */]);
// -> Uncaught TypeError: Failed to execute 'postMessage' on 'Window':
// Value at index 0 does not have a transferable type.
3.:
This unintentional use-after-transfer could be prevented / pointed at:
const bar = new ArrayBuffer(42);
window.postMessage({a: 13}, "*", [bar]);
// Prints 0, because it was used after transfer (presumably not the intention of the user)
console.log(bar.byteLength);
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript / JavaScript code
- Well, it would break if it surfaces previously undetected type errors
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. new expression-level syntax)