summaryrefslogtreecommitdiff
path: root/core/core.js
blob: 7c39c1baeda2a7debe919976c46d3b3c33718db6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";

((window) => {
  // Available on start due to bindings.
  const core = window.Deno.core;
  const { recv, send } = core;

  let opsCache = {};
  const errorMap = {};
  let nextPromiseId = 1;
  const promiseTable = new Map();

  function init() {
    recv(handleAsyncMsgFromRust);
  }

  function ops() {
    // op id 0 is a special value to retrieve the map of registered ops.
    const newOpsCache = Object.fromEntries(send(0));
    opsCache = Object.freeze(newOpsCache);
    return opsCache;
  }

  function handleAsyncMsgFromRust() {
    for (let i = 0; i < arguments.length; i += 2) {
      opAsyncHandler(arguments[i], arguments[i + 1]);
    }
  }

  function dispatch(opName, promiseId, control, zeroCopy) {
    return send(opsCache[opName], promiseId, control, zeroCopy);
  }

  function registerErrorClass(errorName, className, args) {
    if (typeof errorMap[errorName] !== "undefined") {
      throw new TypeError(`Error class for "${errorName}" already registered`);
    }
    errorMap[errorName] = [className, args ?? []];
  }

  function getErrorClassAndArgs(errorName) {
    return errorMap[errorName] ?? [undefined, []];
  }

  function processResponse(res) {
    // const [ok, err] = res;
    if (res[1] === null) {
      return res[0];
    }
    throw processErr(res[1]);
  }

  function processErr(err) {
    const [ErrorClass, args] = getErrorClassAndArgs(err.className);
    if (!ErrorClass) {
      return new Error(
        `Unregistered error class: "${err.className}"\n  ${err.message}\n  Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
      );
    }
    return new ErrorClass(err.message, ...args);
  }

  function jsonOpAsync(opName, args = null, zeroCopy = null) {
    const promiseId = nextPromiseId++;
    const maybeError = dispatch(opName, promiseId, args, zeroCopy);
    // Handle sync error (e.g: error parsing args)
    if (maybeError) processResponse(maybeError);
    let resolve, reject;
    const promise = new Promise((resolve_, reject_) => {
      resolve = resolve_;
      reject = reject_;
    });
    promise.resolve = resolve;
    promise.reject = reject;
    promiseTable.set(promiseId, promise);
    return promise;
  }

  function jsonOpSync(opName, args = null, zeroCopy = null) {
    return processResponse(dispatch(opName, null, args, zeroCopy));
  }

  function opAsyncHandler(promiseId, res) {
    // const [ok, err] = res;
    const promise = promiseTable.get(promiseId);
    promiseTable.delete(promiseId);
    if (!res[1]) {
      promise.resolve(res[0]);
    } else {
      promise.reject(processErr(res[1]));
    }
  }

  function binOpSync(opName, args = null, zeroCopy = null) {
    return jsonOpSync(opName, args, zeroCopy);
  }

  function binOpAsync(opName, args = null, zeroCopy = null) {
    return jsonOpAsync(opName, args, zeroCopy);
  }

  function resources() {
    return jsonOpSync("op_resources");
  }

  function close(rid) {
    jsonOpSync("op_close", { rid });
  }

  Object.assign(window.Deno.core, {
    binOpAsync,
    binOpSync,
    jsonOpAsync,
    jsonOpSync,
    dispatch: send,
    dispatchByName: dispatch,
    ops,
    close,
    resources,
    registerErrorClass,
    init,
  });
})(this);