1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-26 16:09:27 -05:00
denoland-deno/ext/web/01_dom_exception.js
Marcos Casagrande de25c81fd0
perf(ext/web): optimize DOMException (#20715)
This PR optimizes `DOMException` constructor increasing performance of
all Web APIs that throw a `DOMException` (ie: `AbortSignal`)

**main**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.37.1 (x86_64-unknown-linux-gnu)

new DOMException()            9.66 µs/iter     103,476.8   (8.47 µs … 942.71 µs)   9.62 µs  11.29 µs  14.04 µs
abort writeTextFileSync      16.45 µs/iter      60,775.5    (13.65 µs … 1.33 ms)  16.39 µs  20.59 µs  24.12 µs
abort readFile               16.25 µs/iter      61,542.2  (15.12 µs … 621.14 µs)  16.18 µs  19.59 µs  22.33 µs
```

**this PR**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.37.1 (x86_64-unknown-linux-gnu)

benchmark                    time (avg)        iter/s             (min … max)       p75       p99      p995
----------------------------------------------------------------------------- -----------------------------
new DOMException()            2.37 µs/iter     421,657.0     (2.33 µs … 2.58 µs)   2.37 µs   2.58 µs   2.58 µs
abort writeTextFileSync        7.1 µs/iter     140,760.1     (6.94 µs … 7.68 µs)   7.13 µs   7.68 µs   7.68 µs
abort readFile                5.48 µs/iter     182,648.2      (5.3 µs … 5.69 µs)   5.56 µs   5.69 µs   5.69 µ
```

```js
Deno.bench("new DOMException()", () => {
  new DOMException();
});

Deno.bench("abort writeTextFileSync", () => {
  const ac = new AbortController();
  ac.abort();
  try {
    Deno.writeTextFileSync("/tmp/out", "x", { signal: ac.signal });
  } catch {}
});

Deno.bench("abort readFile", async () => {
  const ac = new AbortController();
  ac.abort();
  try {
    await Deno.readFile("/tmp/out", { signal: ac.signal });
  } catch {}
});
```
2023-10-02 02:18:34 +02:00

210 lines
5.9 KiB
JavaScript

// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// @ts-check
/// <reference path="../../core/internal.d.ts" />
/// <reference path="../../core/lib.deno_core.d.ts" />
/// <reference path="../webidl/internal.d.ts" />
/// <reference path="../web/internal.d.ts" />
/// <reference path="../web/lib.deno_web.d.ts" />
const primordials = globalThis.__bootstrap.primordials;
const {
ArrayPrototypeSlice,
Error,
ErrorPrototype,
ObjectDefineProperty,
ObjectCreate,
ObjectEntries,
ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf,
Symbol,
SymbolFor,
} = primordials;
import * as webidl from "ext:deno_webidl/00_webidl.js";
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
const _name = Symbol("name");
const _message = Symbol("message");
const _code = Symbol("code");
const _error = Symbol("error");
// Defined in WebIDL 4.3.
// https://webidl.spec.whatwg.org/#idl-DOMException
const INDEX_SIZE_ERR = 1;
const DOMSTRING_SIZE_ERR = 2;
const HIERARCHY_REQUEST_ERR = 3;
const WRONG_DOCUMENT_ERR = 4;
const INVALID_CHARACTER_ERR = 5;
const NO_DATA_ALLOWED_ERR = 6;
const NO_MODIFICATION_ALLOWED_ERR = 7;
const NOT_FOUND_ERR = 8;
const NOT_SUPPORTED_ERR = 9;
const INUSE_ATTRIBUTE_ERR = 10;
const INVALID_STATE_ERR = 11;
const SYNTAX_ERR = 12;
const INVALID_MODIFICATION_ERR = 13;
const NAMESPACE_ERR = 14;
const INVALID_ACCESS_ERR = 15;
const VALIDATION_ERR = 16;
const TYPE_MISMATCH_ERR = 17;
const SECURITY_ERR = 18;
const NETWORK_ERR = 19;
const ABORT_ERR = 20;
const URL_MISMATCH_ERR = 21;
const QUOTA_EXCEEDED_ERR = 22;
const TIMEOUT_ERR = 23;
const INVALID_NODE_TYPE_ERR = 24;
const DATA_CLONE_ERR = 25;
// Defined in WebIDL 2.8.1.
// https://webidl.spec.whatwg.org/#dfn-error-names-table
/** @type {Record<string, number>} */
// the prototype should be null, to prevent user code from looking
// up Object.prototype properties, such as "toString"
const nameToCodeMapping = ObjectCreate(null, {
IndexSizeError: { value: INDEX_SIZE_ERR },
HierarchyRequestError: { value: HIERARCHY_REQUEST_ERR },
WrongDocumentError: { value: WRONG_DOCUMENT_ERR },
InvalidCharacterError: { value: INVALID_CHARACTER_ERR },
NoModificationAllowedError: { value: NO_MODIFICATION_ALLOWED_ERR },
NotFoundError: { value: NOT_FOUND_ERR },
NotSupportedError: { value: NOT_SUPPORTED_ERR },
InUseAttributeError: { value: INUSE_ATTRIBUTE_ERR },
InvalidStateError: { value: INVALID_STATE_ERR },
SyntaxError: { value: SYNTAX_ERR },
InvalidModificationError: { value: INVALID_MODIFICATION_ERR },
NamespaceError: { value: NAMESPACE_ERR },
InvalidAccessError: { value: INVALID_ACCESS_ERR },
TypeMismatchError: { value: TYPE_MISMATCH_ERR },
SecurityError: { value: SECURITY_ERR },
NetworkError: { value: NETWORK_ERR },
AbortError: { value: ABORT_ERR },
URLMismatchError: { value: URL_MISMATCH_ERR },
QuotaExceededError: { value: QUOTA_EXCEEDED_ERR },
TimeoutError: { value: TIMEOUT_ERR },
InvalidNodeTypeError: { value: INVALID_NODE_TYPE_ERR },
DataCloneError: { value: DATA_CLONE_ERR },
});
// Defined in WebIDL 4.3.
// https://webidl.spec.whatwg.org/#idl-DOMException
class DOMException {
[_message];
[_name];
[_code];
// https://webidl.spec.whatwg.org/#dom-domexception-domexception
constructor(message = "", name = "Error") {
message = webidl.converters.DOMString(
message,
"Failed to construct 'DOMException'",
"Argument 1",
);
name = webidl.converters.DOMString(
name,
"Failed to construct 'DOMException'",
"Argument 2",
);
const code = nameToCodeMapping[name] ?? 0;
this[_message] = message;
this[_name] = name;
this[_code] = code;
this[webidl.brand] = webidl.brand;
this[_error] = new Error(message);
this[_error].name = "DOMException";
}
get message() {
webidl.assertBranded(this, DOMExceptionPrototype);
return this[_message];
}
get name() {
webidl.assertBranded(this, DOMExceptionPrototype);
return this[_name];
}
get code() {
webidl.assertBranded(this, DOMExceptionPrototype);
return this[_code];
}
[SymbolFor("Deno.customInspect")](inspect) {
if (ObjectPrototypeIsPrototypeOf(DOMExceptionPrototype, this)) {
return `DOMException: ${this[_message]}`;
} else {
return inspect(createFilteredInspectProxy({
object: this,
evaluate: false,
keys: [
"message",
"name",
"code",
],
}));
}
}
}
ObjectDefineProperty(DOMException.prototype, "stack", {
get() {
return this[_error].stack;
},
set(value) {
this[_error].stack = value;
},
configurable: true,
});
// `DOMException` isn't a native error, so `Error.prepareStackTrace()` is
// not called when accessing `.stack`, meaning our structured stack trace
// hack doesn't apply. This patches it in.
ObjectDefineProperty(DOMException.prototype, "__callSiteEvals", {
get() {
return ArrayPrototypeSlice(this[_error].__callSiteEvals, 1);
},
configurable: true,
});
ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype);
webidl.configurePrototype(DOMException);
const DOMExceptionPrototype = DOMException.prototype;
const entries = ObjectEntries({
INDEX_SIZE_ERR,
DOMSTRING_SIZE_ERR,
HIERARCHY_REQUEST_ERR,
WRONG_DOCUMENT_ERR,
INVALID_CHARACTER_ERR,
NO_DATA_ALLOWED_ERR,
NO_MODIFICATION_ALLOWED_ERR,
NOT_FOUND_ERR,
NOT_SUPPORTED_ERR,
INUSE_ATTRIBUTE_ERR,
INVALID_STATE_ERR,
SYNTAX_ERR,
INVALID_MODIFICATION_ERR,
NAMESPACE_ERR,
INVALID_ACCESS_ERR,
VALIDATION_ERR,
TYPE_MISMATCH_ERR,
SECURITY_ERR,
NETWORK_ERR,
ABORT_ERR,
URL_MISMATCH_ERR,
QUOTA_EXCEEDED_ERR,
TIMEOUT_ERR,
INVALID_NODE_TYPE_ERR,
DATA_CLONE_ERR,
});
for (let i = 0; i < entries.length; ++i) {
const { 0: key, 1: value } = entries[i];
const desc = { value, enumerable: true };
ObjectDefineProperty(DOMException, key, desc);
ObjectDefineProperty(DOMException.prototype, key, desc);
}
export default DOMException;