char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somport web extension to typescript44fe2c6

main
8.6 KiB276 linesraw
1import {
2  ArrayPrototypePush as push,
3  TypeError,
4  WeakMap,
5  WeakMapPrototypeGet as get,
6  WeakMapPrototypeSet as set,
7} from "ext:core/primordials";
8import { array } from "./idl.js";
9
10interface TransferEntry {
11  states: WeakMap<object, unknown>;
12  transferable: boolean;
13  clone?: ((state: unknown, value: unknown) => object) | null;
14  prepare?: (state: unknown, value: unknown) => unknown;
15  validate?: (state: unknown, source: unknown, value?: unknown) => void;
16  commit?: (state: unknown, source: unknown, value?: unknown) => void;
17  rollback?: (state: unknown, source: unknown, value?: unknown) => void;
18}
19
20interface WireEntry {
21  states?: WeakMap<object, unknown>;
22  name: string;
23  transferable: boolean;
24  accepts?: (value: unknown) => boolean;
25  encode: (state: unknown, value: unknown, transfer?: unknown) => unknown;
26  decode: (data: unknown) => object;
27  prepare?: (state: unknown, value: unknown) => unknown;
28  validate?: (state: unknown, source: unknown, value?: unknown) => void;
29  commit?: (state: unknown, source: unknown, value?: unknown) => void;
30  rollback?: (state: unknown, source: unknown, value?: unknown) => void;
31}
32
33type MarkedEntry = Omit<TransferEntry, "states" | "clone"> & { states?: undefined };
34
35interface PreparedTransfer {
36  source: unknown;
37  entry: TransferEntry | WireEntry | MarkedEntry;
38  state: unknown;
39  value: unknown;
40  committed?: boolean;
41}
42
43interface WireCallbacks<State, Packet, Prepared> {
44  name: string;
45  encode: (state: State, value: object, prepared: Prepared) => Packet;
46  decode: (data: NoInfer<Packet>) => object;
47  prepare?: (state: State, value: object) => Prepared;
48  validate?: (
49    state: State,
50    source: object,
51    prepared: NoInfer<Prepared>,
52  ) => void;
53  commit?: (state: State, source: object, prepared: NoInfer<Prepared>) => void;
54  rollback?: (
55    state: State,
56    source: object,
57    prepared: NoInfer<Prepared>,
58  ) => void;
59}
60
61type WireRegistration<State, Packet, Prepared> =
62  | (
63    & { states: WeakMap<object, State>; accepts?: never }
64    & WireCallbacks<NoInfer<State>, Packet, Prepared>
65  )
66  | (
67    & { states?: undefined; accepts: (value: unknown) => boolean }
68    & WireCallbacks<undefined, Packet, Prepared>
69  );
70
71const types: TransferEntry[] = array(),
72  marked = new WeakMap<object, 1 | 2>(),
73  wireTypes: WireEntry[] = array();
74
75export function registerWire<
76  State = unknown,
77  Packet = unknown,
78  Prepared = undefined,
79>(
80  entry: WireRegistration<State, Packet, Prepared>,
81): void {
82  push(
83    wireTypes,
84    { ...entry, transferable: !!entry.prepare } as unknown as WireEntry,
85  );
86}
87function wireType(value: unknown): WireEntry | undefined {
88  // Newest first so specific types win (File over Blob).
89  for (let i = wireTypes.length - 1; i >= 0; i--) {
90    const entry = wireTypes[i];
91    if (
92      entry.states
93        ? get(entry.states, value) !== undefined
94        : entry.accepts!(value)
95    ) return entry;
96  }
97}
98export function decodeWire(packet: [string, unknown]): object {
99  for (let i = 0; i < wireTypes.length; i++) {
100    if (wireTypes[i].name === packet[0]) return wireTypes[i].decode(packet[1]);
101  }
102  throw new TypeError("Unknown serialized host object");
103}
104let markedTransferable: MarkedEntry | undefined,
105  structuredCloneImpl:
106    | ((value: unknown, options?: unknown) => unknown)
107    | undefined;
108
109export function setStructuredClone(
110  callback: (value: unknown, options?: unknown) => unknown,
111): void {
112  structuredCloneImpl = callback;
113}
114export function cloneValue(
115  value: unknown,
116  options: unknown = undefined,
117): unknown {
118  if (structuredCloneImpl === undefined) {
119    throw new TypeError("structuredClone is unavailable");
120  }
121  return structuredCloneImpl(value, options);
122}
123export function registerSerializable<State>(
124  states: WeakMap<object, State>,
125  clone: (state: State, value: object) => object,
126): void {
127  push(types, {
128    states,
129    clone: clone as unknown as TransferEntry["clone"],
130    transferable: false,
131  } as unknown as TransferEntry);
132}
133export function registerUncloneable<State>(
134  states: WeakMap<object, State>,
135  transferable = false,
136): void {
137  push(types, { states, clone: null, transferable });
138}
139export function registerTransferable<State, Prepared extends object>(
140  states: WeakMap<object, State>,
141  prepare: (state: State, value: object) => Prepared,
142  validate: (state: State, source: object, value: Prepared) => void,
143  commit: (state: State, source: object, value: Prepared) => void,
144): void {
145  push(types, {
146    states,
147    clone: null,
148    transferable: true,
149    prepare: prepare as unknown as TransferEntry["prepare"],
150    validate: validate as unknown as TransferEntry["validate"],
151    commit: commit as unknown as TransferEntry["commit"],
152  } as unknown as TransferEntry);
153}
154export function registerMarkedTransferable<Prepared extends object>(
155  prepare: (state: unknown, value: object) => Prepared,
156  validate: (state: unknown, source: object, value: Prepared) => void,
157  commit: (state: unknown, source: object, value: Prepared) => void,
158): void {
159  markedTransferable = {
160    transferable: true,
161    prepare: prepare as unknown as TransferEntry["prepare"],
162    validate: validate as unknown as TransferEntry["validate"],
163    commit: commit as unknown as TransferEntry["commit"],
164  } as unknown as MarkedEntry;
165}
166export function markUncloneable(value: object, transferable = false): void {
167  set(marked, value, transferable ? 2 : 1);
168}
169function type(value: unknown): TransferEntry | undefined {
170  // Derived interfaces register after their bases.
171  for (let i = types.length - 1; i >= 0; i--) {
172    if (get(types[i].states, value) !== undefined) return types[i];
173  }
174}
175export function classify(value: unknown): 0 | 1 | 2 {
176  const kind = get(marked, value);
177  if (kind) return kind;
178  const entry = type(value);
179  return entry ? (entry.transferable ? 2 : 1) : 0;
180}
181export function transferCallbacks(
182  transfer: unknown[],
183  dataCloneError: (message: string) => unknown,
184  wire = false,
185): {
186  encodeHost(value: unknown): [name: string, data: unknown];
187  prepare(): void;
188  validate(): void;
189  commit(): void;
190  cloneHost(value: unknown): object | undefined;
191  clear(): void;
192} {
193  let prepared: PreparedTransfer[] = array();
194  function find(value: unknown): PreparedTransfer | undefined {
195    for (let i = 0; i < prepared.length; i++) {
196      if (prepared[i].source === value) return prepared[i];
197    }
198  }
199  function prepare(): void {
200    for (let i = 0; i < transfer.length; i++) {
201      const value = transfer[i];
202      const wireEntry = wire ? wireType(value) : undefined;
203      const entry = wireEntry?.prepare ? wireEntry : type(value) ??
204        (get(marked, value) === 2 ? markedTransferable : undefined);
205      if (entry?.transferable && entry.prepare) {
206        const state = entry.states ? get(entry.states, value) : undefined;
207        push(prepared, {
208          source: value,
209          entry,
210          state,
211          value: entry.prepare(state, value),
212        });
213      } else if (classify(value) === 2) {
214        throw dataCloneError("not yet implemented: transferring this object");
215      }
216    }
217  }
218  function validate(): void {
219    for (let i = 0; i < prepared.length; i++) {
220      const { source, entry, state, value } = prepared[i];
221      entry.validate!(state, source, value);
222    }
223  }
224  function commit(): void {
225    for (let i = 0; i < prepared.length; i++) {
226      const { source, entry, state, value } = prepared[i];
227      entry.commit!(state, source, value);
228      prepared[i].committed = true;
229    }
230  }
231  function cloneHost(value: unknown): object | undefined {
232    const record = find(value);
233    if (record) return record.value as object;
234    const entry = type(value);
235    if (entry?.transferable || classify(value) === 2) {
236      throw dataCloneError("Transferable value is not in the transfer list");
237    }
238    return entry?.clone
239      ? entry.clone(get(entry.states, value), value)
240      : undefined;
241  }
242  function encodeHost(value: unknown): [string, unknown] {
243    const record = find(value);
244    if (!record && classify(value) === 2) {
245      throw dataCloneError("Transferable value is not in the transfer list");
246    }
247    const entry = wireType(value);
248    if (!entry) {
249      throw dataCloneError("This object cannot be sent between isolates");
250    }
251    return [
252      entry.name,
253      entry.encode(
254        entry.states ? get(entry.states, value) : undefined,
255        value,
256        record?.value,
257      ),
258    ];
259  }
260  return {
261    encodeHost,
262    prepare,
263    validate,
264    commit,
265    cloneHost,
266    clear() {
267      for (let i = 0; i < prepared.length; i++) {
268        const record = prepared[i];
269        if (!record.committed) {
270          record.entry.rollback?.(record.state, record.source, record.value);
271        }
272      }
273      prepared = array();
274    },
275  };
276}