Module refinery.lib.scripts.js.analysis.environment

The host environment a script is assumed to run in, and the set of global names that environment guarantees a bare read resolves rather than throwing a ReferenceError.

A bare read of a name the program neither declares nor assigns reaches the host's global object. Which names are there is not a language fact but a host one: window exists in a browser and throws under Node, global the other way around. The deobfuscator's sound default asserts only what every host shares — GUARANTEED_GLOBALS, the names the ECMAScript specification mandates — and answers every other bare read may-throw, so no pass drops the ReferenceError an absent host raises. An analyst who knows where a sample runs pins the host with the djs unit's -e switch, and the reads that host guarantees become certain, recovering the folds the sound default refuses.

This module is the single datum those decisions read. HostEnvironment.provides() answers, from data alone, whether a host resolves a name; SemanticModel.read_may_throw is the one seam that consults it. The data is host-partitioned and, per host, built from the language floor (GUARANTEED_GLOBALS) plus a conservative set of host-conditional globals: a name is listed only where its presence is certain, since under-listing costs at most a fold a wider table would recover while over-listing would drop a real throw. The node set was established by reading the globals out of Node v24 (benign introspection, no sample executed); the browser and worker sets are the web-platform globals every implementation of those scopes exposes.

Each pinned host models its current mainstream version — the analyst asserting -e node/-e browser is asserting a present-day runtime, so recently standardized globals (fetch, structuredClone, navigator under Node) are treated as present; a sample targeting an older engine is not the case a pin serves. worker models a dedicated worker: its present set carries the dedicated-worker surface (XMLHttpRequest, the nested Worker constructor), which a service worker does not share, so -e worker is the pin for a dedicated worker rather than a service worker.

Expand source code Browse git
"""
The host environment a script is assumed to run in, and the set of global names that environment
guarantees a bare read resolves rather than throwing a `ReferenceError`.

A bare read of a name the program neither declares nor assigns reaches the host's global object.
Which names are there is not a language fact but a host one: `window` exists in a browser and throws
under Node, `global` the other way around. The deobfuscator's sound default asserts only what every
host shares — `GUARANTEED_GLOBALS`, the names the ECMAScript specification mandates — and answers
every other bare read may-throw, so no pass drops the `ReferenceError` an absent host raises. An
analyst who knows where a sample runs pins the host with the `djs` unit's `-e` switch, and the reads
that host guarantees become certain, recovering the folds the sound default refuses.

This module is the single datum those decisions read. `HostEnvironment.provides` answers, from data
alone, whether a host resolves a name; `SemanticModel.read_may_throw` is the one seam that consults it.
The data is host-partitioned and, per host, built from the language floor (`GUARANTEED_GLOBALS`) plus a
conservative set of host-conditional globals: a name is listed only where its presence is certain, since
under-listing costs at most a fold a wider table would recover while over-listing would drop a real
throw. The `node` set was established by reading the globals out of Node v24 (benign introspection, no
sample executed); the `browser` and `worker` sets are the web-platform globals every implementation of
those scopes exposes.

Each pinned host models its *current mainstream* version — the analyst asserting `-e node`/`-e browser`
is asserting a present-day runtime, so recently standardized globals (`fetch`, `structuredClone`,
`navigator` under Node) are treated as present; a sample targeting an older engine is not the case a pin
serves. `worker` models a *dedicated* worker: its present set carries the dedicated-worker surface
(`XMLHttpRequest`, the nested `Worker` constructor), which a service worker does not share, so `-e worker`
is the pin for a dedicated worker rather than a service worker.
"""
from __future__ import annotations

import enum
import operator

from dataclasses import dataclass
from typing import Callable, NamedTuple

from refinery.lib.scripts.js.builtins import ITERATOR_METHODS


class Presence(enum.Enum):
    """
    What a pinned host knows about whether a global name resolves. `PRESENT` and `ABSENT` are the two
    certain answers a `typeof` fold may act on — present names fold to their type, absent names to
    `'undefined'`; `UNKNOWN` means the host neither guarantees nor rules out the name, so a fold that
    would assert either abstains. The default `universal` host answers `PRESENT` only for the language
    floor and `UNKNOWN` for every other name, since some host defines it and some does not.
    """
    PRESENT = 'present'
    ABSENT = 'absent'
    UNKNOWN = 'unknown'


GUARANTEED_GLOBAL_TYPEOF: dict[str, str] = {
    'globalThis': 'object',
    'NaN': 'number',
    'Infinity': 'number',
    'undefined': 'undefined',
    'eval': 'function',
    'isFinite': 'function',
    'isNaN': 'function',
    'parseFloat': 'function',
    'parseInt': 'function',
    'decodeURI': 'function',
    'decodeURIComponent': 'function',
    'encodeURI': 'function',
    'encodeURIComponent': 'function',
    'Object': 'function',
    'Function': 'function',
    'Boolean': 'function',
    'Symbol': 'function',
    'BigInt': 'function',
    'Error': 'function',
    'AggregateError': 'function',
    'EvalError': 'function',
    'RangeError': 'function',
    'ReferenceError': 'function',
    'SyntaxError': 'function',
    'TypeError': 'function',
    'URIError': 'function',
    'Number': 'function',
    'Math': 'object',
    'Date': 'function',
    'String': 'function',
    'RegExp': 'function',
    'Array': 'function',
    'Int8Array': 'function',
    'Uint8Array': 'function',
    'Uint8ClampedArray': 'function',
    'Int16Array': 'function',
    'Uint16Array': 'function',
    'Int32Array': 'function',
    'Uint32Array': 'function',
    'Float32Array': 'function',
    'Float64Array': 'function',
    'BigInt64Array': 'function',
    'BigUint64Array': 'function',
    'Map': 'function',
    'Set': 'function',
    'WeakMap': 'function',
    'WeakSet': 'function',
    'WeakRef': 'function',
    'FinalizationRegistry': 'function',
    'ArrayBuffer': 'function',
    'DataView': 'function',
    'JSON': 'object',
    'Promise': 'function',
    'Reflect': 'object',
    'Proxy': 'function',
}
"""
Maps each `GUARANTEED_GLOBALS` name to the string its `typeof` yields. Because the name resolves in
every host to a value of a fixed type — a constructor or built-in function (`'function'`), a namespace
object such as `Math`/`JSON`/`Reflect` or `globalThis` itself (`'object'`), the two numeric constants
(`'number'`), or `undefined` — the operator's result is host-independent, unlike a host-conditional
alias (`window`, `self`, …) whose `typeof` is `'undefined'` where the host omits the name. This is what
lets a simplification fold `typeof globalThis !== 'undefined'` to `true` for a name it can prove
pristine; the values were established under Node 24. `GUARANTEED_GLOBALS` is the key set of this map, so
the two cannot drift.
"""

GUARANTEED_GLOBALS = frozenset(GUARANTEED_GLOBAL_TYPEOF)
"""
Names the ECMAScript specification mandates as properties of the global object and that every mainstream
engine exposes unconditionally, so a bare read of one is guaranteed to resolve rather than throw a
`ReferenceError`. This is an *existence* allowlist — distinct from the getter-purity and host-presence
sets in `refinery.lib.scripts.js.analysis.effects` — used to decide whether `<global-alias>.name` may be
collapsed to the bare `name` without turning the member read's `undefined` into a throw. It excludes host
and alias names (`window`, `self`, `global`, `top`, `frames`, `console`, timers, `Buffer`, …) that are not
universal, and `SharedArrayBuffer`/`Atomics`, which a conformant host may withhold outside a
cross-origin-isolated context. `GUARANTEED_GLOBAL_TYPEOF` additionally records the `typeof` of each. It is
the provide-set of the `UNIVERSAL` host and the language floor every pinned host builds on.
"""

UNIVERSAL_TYPEOF: dict[str, str] = {**GUARANTEED_GLOBAL_TYPEOF, 'escape': 'function', 'unescape': 'function'}
"""
The `typeof` of every name whose result is the same in every host, so a `typeof` of one folds without a
host pin. It is `GUARANTEED_GLOBAL_TYPEOF` plus `escape` and `unescape`, the Annex B pair every mainstream
engine defines but the existence floor omits: their `typeof` is `'function'` everywhere even though a bare
read of one is not part of the language-mandated `GUARANTEED_GLOBALS` set.
"""

_HOST_CONDITIONAL_TYPEOF: dict[str, str] = {
    'global': 'object',
    'process': 'object',
    'console': 'object',
    'window': 'object',
    'self': 'object',
    'top': 'object',
    'parent': 'object',
    'frames': 'object',
    'document': 'object',
    'location': 'object',
    'navigator': 'object',
    'history': 'object',
    'screen': 'object',
    'localStorage': 'object',
    'sessionStorage': 'object',
    'performance': 'object',
    'crypto': 'object',
    'Atomics': 'object',
    'WebAssembly': 'object',
    'Intl': 'object',
    'Buffer': 'function',
    'importScripts': 'function',
    'setTimeout': 'function',
    'setInterval': 'function',
    'setImmediate': 'function',
    'clearTimeout': 'function',
    'clearInterval': 'function',
    'clearImmediate': 'function',
    'queueMicrotask': 'function',
    'requestAnimationFrame': 'function',
    'cancelAnimationFrame': 'function',
    'btoa': 'function',
    'atob': 'function',
    'fetch': 'function',
    'structuredClone': 'function',
    'SharedArrayBuffer': 'function',
    'TextEncoder': 'function',
    'TextDecoder': 'function',
    'URL': 'function',
    'URLSearchParams': 'function',
    'XMLHttpRequest': 'function',
    'WebSocket': 'function',
    'Blob': 'function',
    'File': 'function',
    'FileReader': 'function',
    'FormData': 'function',
    'Headers': 'function',
    'Request': 'function',
    'Response': 'function',
    'Event': 'function',
    'CustomEvent': 'function',
    'MessageChannel': 'function',
    'AbortController': 'function',
    'Worker': 'function',
    'Image': 'function',
    'alert': 'function',
    'confirm': 'function',
    'prompt': 'function',
}
"""
The `typeof` of each host-conditional global when the host defines it: a `typeof` of one folds only under
a pin that answers `Presence.PRESENT` for it, to the string recorded here, or `Presence.ABSENT`, to
`'undefined'`. A namespace or DOM object is `'object'`; a constructor, timer, or plain function is
`'function'`. A present name absent from this map has an unrecorded type, so its `typeof` abstains rather
than guess.
"""

_NODE_GLOBALS = frozenset({
    'global',
    'Buffer',
    'process',
    'navigator',
    'console',
    'setTimeout',
    'setInterval',
    'setImmediate',
    'clearTimeout',
    'clearInterval',
    'clearImmediate',
    'queueMicrotask',
    'TextEncoder',
    'TextDecoder',
    'atob',
    'btoa',
    'URL',
    'URLSearchParams',
    'fetch',
    'structuredClone',
    'performance',
    'crypto',
    'SharedArrayBuffer',
    'Atomics',
    'WebAssembly',
    'Intl',
})
"""
Global names a current Node exposes beyond `GUARANTEED_GLOBALS`, read out of Node v24. `global` is Node's
same-realm global-object alias. The CommonJS wrapper locals `require`, `module`, `exports`, `__dirname`,
and `__filename` are deliberately absent: they are injected into a CommonJS file's function scope rather
than being properties of the global object, and an ES module does not see them at all, so whether a bare
read of one resolves is the question `refinery.lib.scripts.js.options.ExecutionModel` answers, not a
host-existence one.
"""

_BROWSER_GLOBALS = frozenset({
    'window',
    'self',
    'top',
    'parent',
    'frames',
    'document',
    'location',
    'navigator',
    'history',
    'screen',
    'localStorage',
    'sessionStorage',
    'console',
    'alert',
    'confirm',
    'prompt',
    'setTimeout',
    'setInterval',
    'clearTimeout',
    'clearInterval',
    'requestAnimationFrame',
    'cancelAnimationFrame',
    'queueMicrotask',
    'fetch',
    'XMLHttpRequest',
    'WebSocket',
    'URL',
    'URLSearchParams',
    'btoa',
    'atob',
    'TextEncoder',
    'TextDecoder',
    'performance',
    'crypto',
    'structuredClone',
    'Blob',
    'File',
    'FileReader',
    'FormData',
    'Headers',
    'Request',
    'Response',
    'Event',
    'CustomEvent',
    'MessageChannel',
    'AbortController',
    'Worker',
    'Image',
    'Intl',
    'WebAssembly',
})
"""
Global names a browser window exposes beyond `GUARANTEED_GLOBALS`. `window` and `self` are the same-realm
aliases of the window's own global object; `top`, `parent`, and `frames` resolve too, but in a framed
document they name another realm's global object, which is why the global-object finder keys its
substitution to `refinery.lib.scripts.js.analysis.model.SAME_REALM_GLOBAL_OBJECT_ALIASES` — which admits
`window` and `self` but not `top`, `parent`, or `frames` — rather than to mere existence. The set is
conservative: a name is listed only where every current browser provides it.
"""

_WORKER_GLOBALS = frozenset({
    'self',
    'location',
    'navigator',
    'console',
    'setTimeout',
    'setInterval',
    'clearTimeout',
    'clearInterval',
    'queueMicrotask',
    'fetch',
    'XMLHttpRequest',
    'WebSocket',
    'URL',
    'URLSearchParams',
    'btoa',
    'atob',
    'TextEncoder',
    'TextDecoder',
    'performance',
    'crypto',
    'structuredClone',
    'Blob',
    'File',
    'FileReader',
    'FormData',
    'Headers',
    'Request',
    'Response',
    'Event',
    'CustomEvent',
    'MessageChannel',
    'AbortController',
    'Worker',
    'importScripts',
    'Intl',
    'WebAssembly',
})
"""
Global names a worker global scope exposes beyond `GUARANTEED_GLOBALS`. `self` is the worker's own
same-realm global-object alias; the window-only names `window`, `top`, `parent`, `frames`, `document`,
and the DOM and storage surface are absent, because a worker has no document and no window to frame.
"""

_NODE_ABSENT = frozenset({
    'window',
    'self',
    'top',
    'parent',
    'frames',
    'document',
    'location',
    'history',
    'screen',
    'localStorage',
    'sessionStorage',
    'alert',
    'confirm',
    'prompt',
    'XMLHttpRequest',
    'requestAnimationFrame',
    'Image',
    'define',
    'angular',
})
"""
Well-known browser globals a bare read of which is certain to throw under Node, read out of Node
v24 as `typeof` `'undefined'`, and two a page gets from a library it loads: the `define` of an AMD
loader and AngularJS's `angular`, which code built to run anywhere probes before it hands itself to
them. `navigator` is deliberately absent from this set: Node exposes it, so it is a present name,
not an absent one — the trap a `typeof` fold must not fall into. The set is used only to fold
`typeof <name>` to `'undefined'` under `-e node`; a name neither provided nor listed here stays
unknown.
"""

NODE_BUILTIN_MODULES = frozenset({
    '_http_agent',
    '_http_client',
    '_http_common',
    '_http_incoming',
    '_http_outgoing',
    '_http_server',
    '_stream_duplex',
    '_stream_passthrough',
    '_stream_readable',
    '_stream_transform',
    '_stream_wrap',
    '_stream_writable',
    '_tls_common',
    '_tls_wrap',
    'assert',
    'assert/strict',
    'async_hooks',
    'buffer',
    'child_process',
    'cluster',
    'console',
    'constants',
    'crypto',
    'dgram',
    'diagnostics_channel',
    'dns',
    'dns/promises',
    'domain',
    'events',
    'fs',
    'fs/promises',
    'http',
    'http2',
    'https',
    'inspector',
    'inspector/promises',
    'module',
    'net',
    'os',
    'path',
    'path/posix',
    'path/win32',
    'perf_hooks',
    'process',
    'punycode',
    'querystring',
    'readline',
    'readline/promises',
    'repl',
    'stream',
    'stream/consumers',
    'stream/promises',
    'stream/web',
    'string_decoder',
    'sys',
    'timers',
    'timers/promises',
    'tls',
    'trace_events',
    'tty',
    'url',
    'util',
    'util/types',
    'v8',
    'vm',
    'wasi',
    'worker_threads',
    'zlib',
    'node:sea',
    'node:sqlite',
    'node:test',
    'node:test/reporters',
})
"""
The modules Node's `require` loads from itself rather than from a file, read out of Node v24 as
`require('module').builtinModules`. A name with the `node:` prefix is one Node loads only under that
prefix; every other one it also loads with the prefix.
"""

NODE_MODULES_THAT_RUN_CODE = frozenset({
    'inspector',
    'inspector/promises',
    'module',
    'repl',
    'vm',
})
"""
The built-in modules through which a program has code run that its text does not hold: `vm` and
`repl` evaluate source text, the inspector evaluates it through a session, and `module` loads any
file and compiles source into a module.
"""


def node_require_runs_no_code(specifier: str) -> bool:
    """
    Whether Node's `require` of *specifier* runs no code the file's text does not hold: it names a
    built-in module (`NODE_BUILTIN_MODULES`), with or without the `node:` prefix it allows, outside
    `NODE_MODULES_THAT_RUN_CODE`. Any other specifier names a file or a package, whose code runs
    when it is loaded.
    """
    name = specifier
    if name.startswith('node:'):
        if specifier in NODE_BUILTIN_MODULES:
            return True
        name = name[5:]
    elif F'node:{name}' in NODE_BUILTIN_MODULES:
        return False
    return name in NODE_BUILTIN_MODULES and name not in NODE_MODULES_THAT_RUN_CODE


_BROWSER_ABSENT = frozenset({
    'global',
    'Buffer',
    'process',
    'require',
    'module',
    'exports',
    '__dirname',
    '__filename',
    'setImmediate',
    'clearImmediate',
    'importScripts',
})
"""
The Node and worker globals a browser window is certain to lack. These are the names that separate a
Node target from a browser one, so `typeof global`/`typeof Buffer`/`typeof require` fold to `'undefined'`
under `-e browser`.
"""

_WORKER_ABSENT = _BROWSER_ABSENT - frozenset({'importScripts'}) | frozenset({
    'window',
    'top',
    'parent',
    'frames',
    'document',
    'history',
    'screen',
    'localStorage',
    'sessionStorage',
    'alert',
    'confirm',
    'prompt',
    'Image',
})
"""
The window-only and Node-only globals a dedicated worker global scope is certain to lack: it has no
document and no window to frame, and it is not Node. `importScripts` is a worker global, so it is excluded
from the Node/browser-absent base this builds on. `requestAnimationFrame` is deliberately *not* listed: a
dedicated worker exposes it through the `AnimationFrameProvider` mixin, so it is left `UNKNOWN` rather than
asserted absent — which would fold `typeof requestAnimationFrame` to `'undefined'` where the host in fact
yields `'function'`.
"""

_WINDOW_EVENT_HANDLERS = frozenset({
    'onabort',
    'onafterprint',
    'onanimationcancel',
    'onanimationend',
    'onanimationiteration',
    'onanimationstart',
    'onappinstalled',
    'onauxclick',
    'onbeforeinput',
    'onbeforeinstallprompt',
    'onbeforematch',
    'onbeforeprint',
    'onbeforetoggle',
    'onbeforeunload',
    'onbeforexrselect',
    'onblur',
    'oncancel',
    'oncanplay',
    'oncanplaythrough',
    'onchange',
    'onclick',
    'onclose',
    'oncommand',
    'oncontentvisibilityautostatechange',
    'oncontextlost',
    'oncontextmenu',
    'oncontextrestored',
    'oncuechange',
    'ondblclick',
    'ondevicemotion',
    'ondeviceorientation',
    'ondeviceorientationabsolute',
    'ondrag',
    'ondragend',
    'ondragenter',
    'ondragleave',
    'ondragover',
    'ondragstart',
    'ondrop',
    'ondurationchange',
    'onemptied',
    'onended',
    'onerror',
    'onfocus',
    'onformdata',
    'ongamepadconnected',
    'ongamepaddisconnected',
    'ongotpointercapture',
    'onhashchange',
    'oninput',
    'oninvalid',
    'onkeydown',
    'onkeypress',
    'onkeyup',
    'onlanguagechange',
    'onload',
    'onloadeddata',
    'onloadedmetadata',
    'onloadstart',
    'onlostpointercapture',
    'onmessage',
    'onmessageerror',
    'onmousedown',
    'onmouseenter',
    'onmouseleave',
    'onmousemove',
    'onmouseout',
    'onmouseover',
    'onmouseup',
    'onmousewheel',
    'onoffline',
    'ononline',
    'onpagehide',
    'onpagereveal',
    'onpageshow',
    'onpageswap',
    'onpause',
    'onplay',
    'onplaying',
    'onpointercancel',
    'onpointerdown',
    'onpointerenter',
    'onpointerleave',
    'onpointermove',
    'onpointerout',
    'onpointerover',
    'onpointerrawupdate',
    'onpointerup',
    'onpopstate',
    'onprogress',
    'onratechange',
    'onrejectionhandled',
    'onreset',
    'onresize',
    'onscroll',
    'onscrollend',
    'onscrollsnapchange',
    'onscrollsnapchanging',
    'onsearch',
    'onsecuritypolicyviolation',
    'onseeked',
    'onseeking',
    'onselect',
    'onselectionchange',
    'onselectstart',
    'onslotchange',
    'onstalled',
    'onstorage',
    'onsubmit',
    'onsuspend',
    'ontimeupdate',
    'ontoggle',
    'ontransitioncancel',
    'ontransitionend',
    'ontransitionrun',
    'ontransitionstart',
    'onunhandledrejection',
    'onunload',
    'onvolumechange',
    'onwaiting',
    'onwebkitanimationend',
    'onwebkitanimationiteration',
    'onwebkitanimationstart',
    'onwebkittransitionend',
    'onwheel',
})
"""
The event handler attributes of a browser window: properties of the global object whose value the
window calls when the event they name fires, so a function a script stores in one runs with no call
in the file. The set is every name starting with `on` on a window of a current Chromium browser and
its prototype chain. A name only another browser defines is missing, which keeps a store into it
dead.
"""

_WORKER_EVENT_HANDLERS = frozenset({
    'onerror',
    'onlanguagechange',
    'onmessage',
    'onmessageerror',
    'onrejectionhandled',
    'onrtctransform',
    'onunhandledrejection',
})
"""
The event handler attributes of a dedicated worker global scope, in the sense of
`_WINDOW_EVENT_HANDLERS`, read out of the same browser.
"""


class JsEngine(enum.Enum):
    """
    The engine a host runs scripts in, where every engine the host covers is that one engine. It
    answers what the language leaves to the engine: the text a function the language installs
    converts to, the text a regular expression converts to, and the characters `Date()` writes at
    fixed positions. A host whose engines differ has none, and a fold of any of these refuses there.
    The answers of `v8` were read out of Node 24.
    """
    v8 = 'v8'

    def function_text(self, name: str) -> str:
        """
        The text a function the language installs under *name* converts to, which reads as follows
        for `[].at`:

            function at() { [native code] }
        """
        return F'function {name}() {{ [native code] }}'

    def regexp_text(self, source: str) -> str:
        """
        The text a regular expression without flags converts to, whose pattern *source* is made of
        characters standing for themselves: the engine writes an empty pattern as `(?:)` and a slash
        as an escaped one, so that the text reads back as the same expression.
        """
        escaped = source.replace('/', '\\/') if source else '(?:)'
        return F'/{escaped}/'

    def date_character(self, index: int) -> str | None:
        """
        The character the string `Date()` returns holds at *index* whatever the time, or `None`
        where it depends on the time. The string reads as follows and then holds the name of the
        time zone, which fixes these positions where the year has four digits:

            Www Mmm DD YYYY HH:MM:SS GMT+hhmm
        """
        return _V8_DATE_CHARACTERS.get(index)


_V8_DATE_CHARACTERS = {
    3  : ' ',
    7  : ' ',
    10 : ' ',
    15 : ' ',
    18 : ':',
    21 : ':',
    24 : ' ',
    25 : 'G',
    26 : 'M',
    27 : 'T',
}


ENGINE_FUNCTIONS = frozenset(('Array', method) for method in ITERATOR_METHODS)
"""
The functions the language installs, by the owner of the prototype holding them and their key,
that a host covering the Windows Script Host does not have: JScript, its engine, has no iterators.
Only a host with one engine is known to have them (`HostEnvironment.has_function`).
"""


@dataclass(frozen=True)
class _EnvironmentSpec:
    """
    The existence answer for one host: *provided* is every name a bare read resolves in it, and
    *mandated* is the subset the language guarantees, which the delete guard never retracts. A
    host-conditional global is a configurable, deletable property of the global object, so a program
    that deletes one makes a later bare read of it throw; a language-mandated name is treated as present
    regardless, preserving the sound default's behavior exactly where the host adds nothing.
    *engine* is the engine every engine the host covers is, where there is one. *handlers* are the
    properties of the global object whose value the host calls.
    """
    provided: frozenset[str]
    mandated: frozenset[str]
    absent: frozenset[str] = frozenset()
    engine: JsEngine | None = None
    handlers: frozenset[str] = frozenset()

    def provides(self, name: str) -> bool:
        return name in self.provided

    def withholds_on_delete(self, name: str) -> bool:
        return name in self.provided and name not in self.mandated

    def presence(self, name: str) -> Presence:
        if name in self.provided:
            return Presence.PRESENT
        if name in self.absent:
            return Presence.ABSENT
        return Presence.UNKNOWN


COMMONJS_WRAPPER_PARAMETERS = ('exports', 'require', 'module', '__filename', '__dirname')
"""
The parameters of the function a CommonJS host wraps around a file's text, in their order. Where the
file declares none of these names itself, a bare read of one reads the value the host passed and a
bare assignment writes it; the global scope holds none of them. Code run by a `Function`
construction, an indirect `eval` or a string timer is global code outside that function and does not
see them. A `let`, `const` or `class` declaration of one at the top of the file redeclares a
parameter, which Node refuses to compile as CommonJS; it runs such a file as an ES module.
"""


class FileKind(enum.Enum):
    """
    What the code of one file is, decided for a run from the execution model and the file's own text
    (`refinery.lib.scripts.js.options.file_kind`):

    - `script`: a classic script, whose top-level `var` and function declarations are properties of
      the global object.
    - `commonjs`: the body of the function a CommonJS host wraps around the text.
    - `es_module`: an ES module, strict code throughout.
    - `commonjs_or_es_module`: one of the two without saying which; an answer must hold for both.
    """
    script = 'script'
    commonjs = 'commonjs'
    es_module = 'es-module'
    commonjs_or_es_module = 'commonjs-or-es-module'

    @property
    def scopes_declarations_to_the_file(self) -> bool:
        return self is not FileKind.script

    @property
    def may_be_an_es_module(self) -> bool:
        return self is FileKind.es_module or self is FileKind.commonjs_or_es_module

    @property
    def may_be_commonjs(self) -> bool:
        return self is FileKind.commonjs or self is FileKind.commonjs_or_es_module


class HostEnvironment(enum.Enum):
    """
    The host a script is assumed to run in, selected by the `djs` unit's `-e` switch. `universal` is
    the default and asserts only `GUARANTEED_GLOBALS`, so deobfuscation stays byte-identical to an
    unpinned run; each other member adds the globals that host guarantees, recovering the folds the
    sound default refuses for a name no universal host defines.
    """
    universal = 'universal'
    node = 'node'
    browser = 'browser'
    worker = 'worker'

    def provides(self, name: str) -> bool:
        """
        Whether a bare read of *name* is guaranteed to resolve in this host rather than throw a
        `ReferenceError`, judged from the host's global-name data alone.
        """
        return _SPECS[self].provides(name)

    def withholds_on_delete(self, name: str) -> bool:
        """
        Whether *name* is a host-conditional global whose presence a `delete` of it retracts, as opposed
        to a language-mandated name the guard leaves present. A name this answers `True` for is withheld
        program-wide once the program deletes it off a same-realm global alias anywhere, which over-keeps
        a read's throw rather than dropping one, and so stays sound.
        """
        return _SPECS[self].withholds_on_delete(name)

    def presence(self, name: str) -> Presence:
        """
        Whether this host is certain *name* resolves (`PRESENT`), certain it does not (`ABSENT`), or
        neither (`UNKNOWN`). `provides` is the `PRESENT` half; the `ABSENT` half names the well-known
        globals of *other* hosts this one is characterized to lack, so that `typeof <name>` folds to
        `'undefined'` under a pin without asserting absence for a name merely left off the present set.
        """
        return _SPECS[self].presence(name)

    def has_function(self, owner: str | None, key: str) -> bool:
        """
        Whether a call of the function the language installs under *key* on the prototype of the
        constructor *owner*, or as a global where *owner* is `None`, finds that function in this
        host. Every host is taken to have each such function but those of `ENGINE_FUNCTIONS`, which
        only a host with one engine is known to have.
        """
        return self.engine is not None or (owner, key) not in ENGINE_FUNCTIONS

    def calls_handler(self, name: str) -> bool:
        """
        Whether some host this one covers calls the function a script stores in the property *name*
        of the global object, an event handler attribute such as the `onload` of a browser window.
        The host calls it with no call in the file, so a store there is never dead. The default
        `universal` covers browser windows and workers, so their handlers are called there too, also
        in a Windows Script Host, where the name is an ordinary global.
        """
        return name in _SPECS[self].handlers

    @property
    def engine(self) -> JsEngine | None:
        """
        The engine every engine this host covers is, or `None` where they may differ. Only `node`
        has one. The Windows Script Host, which `universal` covers, writes the text of a function
        the language installs with a line break before `function`, before `[native code]`, and on
        either side of the closing brace, so it shares no character with what V8 writes; the
        engines of the browsers are not measured.
        """
        return _SPECS[self].engine


def typeof_of_global(name: str, environment: HostEnvironment) -> str | None:
    """
    The string `typeof name` yields for a global *name* under *environment*, or `None` when the answer is
    host-dependent and the host is not pinned to one that settles it. A name whose `typeof` is the same in
    every host (`UNIVERSAL_TYPEOF`) folds always; a host-conditional name folds only where the pin makes
    it certainly present — to its recorded type — or certainly absent — to `'undefined'`. The default
    `universal` host settles only the universal names, so `typeof Buffer` and `typeof window` abstain
    unpinned, exactly as a bare read of either is answered may-throw.
    """
    if name in UNIVERSAL_TYPEOF:
        return UNIVERSAL_TYPEOF[name]
    presence = environment.presence(name)
    if presence is Presence.ABSENT:
        return 'undefined'
    if presence is Presence.PRESENT:
        return _HOST_CONDITIONAL_TYPEOF.get(name)
    return None


_CLOCK_COMPARISONS: dict[str, Callable[[float, float], bool]] = {
    '<'   : operator.lt,
    '<='  : operator.le,
    '>'   : operator.gt,
    '>='  : operator.ge,
    '=='  : operator.eq,
    '===' : operator.eq,
    '!='  : operator.ne,
    '!==' : operator.ne,
}


class ClockWindow(NamedTuple):
    """
    The times between which a run reads the host's clock, in milliseconds since the epoch, both
    included.
    """
    earliest: float
    latest: float

    def comparison(self, comparison: str, time: float) -> bool | None:
        """
        What `clock <comparison> time` yields for a number *time* where every reading of the clock
        inside this window yields the same, or `None`: a time inside the window, `NaN`, or an
        operator that is not a comparison.
        """
        compare = _CLOCK_COMPARISONS.get(comparison)
        if compare is None or time != time or self.earliest <= time <= self.latest:
            return None
        return compare(self.earliest, time)


ASSUMED_CLOCK = ClockWindow(946771200000, 4102358400000)
"""
The window a run is assumed to read the host's clock in: from the start of 2000-01-02 to the start
of 2099-12-31, both in UTC, a day after 2000 and a day before 2100. A lock that hangs or breaks the
script outside a window of dates compares the clock with the first day of 2000 or of 2100, moved by
some seconds; that defense is not the program's behavior, and the comparison answers the same for
every reading inside the window. A date inside it, an expiry or a time the program waits for, is
the program's own and stays.
"""


_SPECS: dict[HostEnvironment, _EnvironmentSpec] = {
    HostEnvironment.universal: _EnvironmentSpec(
        GUARANTEED_GLOBALS,
        GUARANTEED_GLOBALS,
        handlers=_WINDOW_EVENT_HANDLERS | _WORKER_EVENT_HANDLERS,
    ),
    HostEnvironment.node: _EnvironmentSpec(
        GUARANTEED_GLOBALS | _NODE_GLOBALS,
        GUARANTEED_GLOBALS,
        _NODE_ABSENT,
        engine=JsEngine.v8,
    ),
    HostEnvironment.browser: _EnvironmentSpec(
        GUARANTEED_GLOBALS | _BROWSER_GLOBALS,
        GUARANTEED_GLOBALS,
        _BROWSER_ABSENT,
        handlers=_WINDOW_EVENT_HANDLERS,
    ),
    HostEnvironment.worker: _EnvironmentSpec(
        GUARANTEED_GLOBALS | _WORKER_GLOBALS,
        GUARANTEED_GLOBALS,
        _WORKER_ABSENT,
        handlers=_WORKER_EVENT_HANDLERS,
    ),
}

Global variables

var GUARANTEED_GLOBAL_TYPEOF

Maps each GUARANTEED_GLOBALS name to the string its typeof yields. Because the name resolves in every host to a value of a fixed type — a constructor or built-in function ('function'), a namespace object such as Math/JSON/Reflect or globalThis itself ('object'), the two numeric constants ('number'), or undefined — the operator's result is host-independent, unlike a host-conditional alias (window, self, …) whose typeof is 'undefined' where the host omits the name. This is what lets a simplification fold typeof globalThis !== 'undefined' to true for a name it can prove pristine; the values were established under Node 24. GUARANTEED_GLOBALS is the key set of this map, so the two cannot drift.

var GUARANTEED_GLOBALS

Names the ECMAScript specification mandates as properties of the global object and that every mainstream engine exposes unconditionally, so a bare read of one is guaranteed to resolve rather than throw a ReferenceError. This is an existence allowlist — distinct from the getter-purity and host-presence sets in refinery.lib.scripts.js.analysis.effects — used to decide whether <global-alias>.name may be collapsed to the bare name without turning the member read's undefined into a throw. It excludes host and alias names (window, self, global, top, frames, console, timers, Buffer, …) that are not universal, and SharedArrayBuffer/Atomics, which a conformant host may withhold outside a cross-origin-isolated context. GUARANTEED_GLOBAL_TYPEOF additionally records the typeof of each. It is the provide-set of the UNIVERSAL host and the language floor every pinned host builds on.

var UNIVERSAL_TYPEOF

The typeof of every name whose result is the same in every host, so a typeof of one folds without a host pin. It is GUARANTEED_GLOBAL_TYPEOF plus escape and unescape, the Annex B pair every mainstream engine defines but the existence floor omits: their typeof is 'function' everywhere even though a bare read of one is not part of the language-mandated GUARANTEED_GLOBALS set.

var NODE_BUILTIN_MODULES

The modules Node's require loads from itself rather than from a file, read out of Node v24 as require('module').builtinModules. A name with the node: prefix is one Node loads only under that prefix; every other one it also loads with the prefix.

var NODE_MODULES_THAT_RUN_CODE

The built-in modules through which a program has code run that its text does not hold: vm and repl evaluate source text, the inspector evaluates it through a session, and module loads any file and compiles source into a module.

var ENGINE_FUNCTIONS

The functions the language installs, by the owner of the prototype holding them and their key, that a host covering the Windows Script Host does not have: JScript, its engine, has no iterators. Only a host with one engine is known to have them (HostEnvironment.has_function()).

var COMMONJS_WRAPPER_PARAMETERS

The parameters of the function a CommonJS host wraps around a file's text, in their order. Where the file declares none of these names itself, a bare read of one reads the value the host passed and a bare assignment writes it; the global scope holds none of them. Code run by a Function construction, an indirect eval or a string timer is global code outside that function and does not see them. A let, const or class declaration of one at the top of the file redeclares a parameter, which Node refuses to compile as CommonJS; it runs such a file as an ES module.

var ASSUMED_CLOCK

The window a run is assumed to read the host's clock in: from the start of 2000-01-02 to the start of 2099-12-31, both in UTC, a day after 2000 and a day before 2100. A lock that hangs or breaks the script outside a window of dates compares the clock with the first day of 2000 or of 2100, moved by some seconds; that defense is not the program's behavior, and the comparison answers the same for every reading inside the window. A date inside it, an expiry or a time the program waits for, is the program's own and stays.

Functions

def node_require_runs_no_code(specifier)

Whether Node's require of specifier runs no code the file's text does not hold: it names a built-in module (NODE_BUILTIN_MODULES), with or without the node: prefix it allows, outside NODE_MODULES_THAT_RUN_CODE. Any other specifier names a file or a package, whose code runs when it is loaded.

Expand source code Browse git
def node_require_runs_no_code(specifier: str) -> bool:
    """
    Whether Node's `require` of *specifier* runs no code the file's text does not hold: it names a
    built-in module (`NODE_BUILTIN_MODULES`), with or without the `node:` prefix it allows, outside
    `NODE_MODULES_THAT_RUN_CODE`. Any other specifier names a file or a package, whose code runs
    when it is loaded.
    """
    name = specifier
    if name.startswith('node:'):
        if specifier in NODE_BUILTIN_MODULES:
            return True
        name = name[5:]
    elif F'node:{name}' in NODE_BUILTIN_MODULES:
        return False
    return name in NODE_BUILTIN_MODULES and name not in NODE_MODULES_THAT_RUN_CODE
def typeof_of_global(name, environment)

The string typeof name yields for a global name under environment, or None when the answer is host-dependent and the host is not pinned to one that settles it. A name whose typeof is the same in every host (UNIVERSAL_TYPEOF) folds always; a host-conditional name folds only where the pin makes it certainly present — to its recorded type — or certainly absent — to 'undefined'. The default universal host settles only the universal names, so typeof Buffer and typeof window abstain unpinned, exactly as a bare read of either is answered may-throw.

Expand source code Browse git
def typeof_of_global(name: str, environment: HostEnvironment) -> str | None:
    """
    The string `typeof name` yields for a global *name* under *environment*, or `None` when the answer is
    host-dependent and the host is not pinned to one that settles it. A name whose `typeof` is the same in
    every host (`UNIVERSAL_TYPEOF`) folds always; a host-conditional name folds only where the pin makes
    it certainly present — to its recorded type — or certainly absent — to `'undefined'`. The default
    `universal` host settles only the universal names, so `typeof Buffer` and `typeof window` abstain
    unpinned, exactly as a bare read of either is answered may-throw.
    """
    if name in UNIVERSAL_TYPEOF:
        return UNIVERSAL_TYPEOF[name]
    presence = environment.presence(name)
    if presence is Presence.ABSENT:
        return 'undefined'
    if presence is Presence.PRESENT:
        return _HOST_CONDITIONAL_TYPEOF.get(name)
    return None

Classes

class Presence (*args, **kwds)

What a pinned host knows about whether a global name resolves. PRESENT and ABSENT are the two certain answers a typeof fold may act on — present names fold to their type, absent names to 'undefined'; UNKNOWN means the host neither guarantees nor rules out the name, so a fold that would assert either abstains. The default universal host answers PRESENT only for the language floor and UNKNOWN for every other name, since some host defines it and some does not.

Expand source code Browse git
class Presence(enum.Enum):
    """
    What a pinned host knows about whether a global name resolves. `PRESENT` and `ABSENT` are the two
    certain answers a `typeof` fold may act on — present names fold to their type, absent names to
    `'undefined'`; `UNKNOWN` means the host neither guarantees nor rules out the name, so a fold that
    would assert either abstains. The default `universal` host answers `PRESENT` only for the language
    floor and `UNKNOWN` for every other name, since some host defines it and some does not.
    """
    PRESENT = 'present'
    ABSENT = 'absent'
    UNKNOWN = 'unknown'

Ancestors

  • enum.Enum

Class variables

var PRESENT

The type of the None singleton.

var ABSENT

The type of the None singleton.

var UNKNOWN

The type of the None singleton.

class JsEngine (*args, **kwds)

The engine a host runs scripts in, where every engine the host covers is that one engine. It answers what the language leaves to the engine: the text a function the language installs converts to, the text a regular expression converts to, and the characters Date() writes at fixed positions. A host whose engines differ has none, and a fold of any of these refuses there. The answers of v8 were read out of Node 24.

Expand source code Browse git
class JsEngine(enum.Enum):
    """
    The engine a host runs scripts in, where every engine the host covers is that one engine. It
    answers what the language leaves to the engine: the text a function the language installs
    converts to, the text a regular expression converts to, and the characters `Date()` writes at
    fixed positions. A host whose engines differ has none, and a fold of any of these refuses there.
    The answers of `v8` were read out of Node 24.
    """
    v8 = 'v8'

    def function_text(self, name: str) -> str:
        """
        The text a function the language installs under *name* converts to, which reads as follows
        for `[].at`:

            function at() { [native code] }
        """
        return F'function {name}() {{ [native code] }}'

    def regexp_text(self, source: str) -> str:
        """
        The text a regular expression without flags converts to, whose pattern *source* is made of
        characters standing for themselves: the engine writes an empty pattern as `(?:)` and a slash
        as an escaped one, so that the text reads back as the same expression.
        """
        escaped = source.replace('/', '\\/') if source else '(?:)'
        return F'/{escaped}/'

    def date_character(self, index: int) -> str | None:
        """
        The character the string `Date()` returns holds at *index* whatever the time, or `None`
        where it depends on the time. The string reads as follows and then holds the name of the
        time zone, which fixes these positions where the year has four digits:

            Www Mmm DD YYYY HH:MM:SS GMT+hhmm
        """
        return _V8_DATE_CHARACTERS.get(index)

Ancestors

  • enum.Enum

Class variables

var v8

The type of the None singleton.

Methods

def function_text(self, name)

The text a function the language installs under name converts to, which reads as follows for [].at:

function at() { [native code] }
Expand source code Browse git
def function_text(self, name: str) -> str:
    """
    The text a function the language installs under *name* converts to, which reads as follows
    for `[].at`:

        function at() { [native code] }
    """
    return F'function {name}() {{ [native code] }}'
def regexp_text(self, source)

The text a regular expression without flags converts to, whose pattern source is made of characters standing for themselves: the engine writes an empty pattern as (?:) and a slash as an escaped one, so that the text reads back as the same expression.

Expand source code Browse git
def regexp_text(self, source: str) -> str:
    """
    The text a regular expression without flags converts to, whose pattern *source* is made of
    characters standing for themselves: the engine writes an empty pattern as `(?:)` and a slash
    as an escaped one, so that the text reads back as the same expression.
    """
    escaped = source.replace('/', '\\/') if source else '(?:)'
    return F'/{escaped}/'
def date_character(self, index)

The character the string Date() returns holds at index whatever the time, or None where it depends on the time. The string reads as follows and then holds the name of the time zone, which fixes these positions where the year has four digits:

Www Mmm DD YYYY HH:MM:SS GMT+hhmm
Expand source code Browse git
def date_character(self, index: int) -> str | None:
    """
    The character the string `Date()` returns holds at *index* whatever the time, or `None`
    where it depends on the time. The string reads as follows and then holds the name of the
    time zone, which fixes these positions where the year has four digits:

        Www Mmm DD YYYY HH:MM:SS GMT+hhmm
    """
    return _V8_DATE_CHARACTERS.get(index)
class FileKind (*args, **kwds)

What the code of one file is, decided for a run from the execution model and the file's own text (file_kind()):

  • script: a classic script, whose top-level var and function declarations are properties of the global object.
  • commonjs: the body of the function a CommonJS host wraps around the text.
  • es_module: an ES module, strict code throughout.
  • commonjs_or_es_module: one of the two without saying which; an answer must hold for both.
Expand source code Browse git
class FileKind(enum.Enum):
    """
    What the code of one file is, decided for a run from the execution model and the file's own text
    (`refinery.lib.scripts.js.options.file_kind`):

    - `script`: a classic script, whose top-level `var` and function declarations are properties of
      the global object.
    - `commonjs`: the body of the function a CommonJS host wraps around the text.
    - `es_module`: an ES module, strict code throughout.
    - `commonjs_or_es_module`: one of the two without saying which; an answer must hold for both.
    """
    script = 'script'
    commonjs = 'commonjs'
    es_module = 'es-module'
    commonjs_or_es_module = 'commonjs-or-es-module'

    @property
    def scopes_declarations_to_the_file(self) -> bool:
        return self is not FileKind.script

    @property
    def may_be_an_es_module(self) -> bool:
        return self is FileKind.es_module or self is FileKind.commonjs_or_es_module

    @property
    def may_be_commonjs(self) -> bool:
        return self is FileKind.commonjs or self is FileKind.commonjs_or_es_module

Ancestors

  • enum.Enum

Class variables

var script

The type of the None singleton.

var commonjs

The type of the None singleton.

var es_module

The type of the None singleton.

var commonjs_or_es_module

The type of the None singleton.

Instance variables

var scopes_declarations_to_the_file
Expand source code Browse git
@property
def scopes_declarations_to_the_file(self) -> bool:
    return self is not FileKind.script
var may_be_an_es_module
Expand source code Browse git
@property
def may_be_an_es_module(self) -> bool:
    return self is FileKind.es_module or self is FileKind.commonjs_or_es_module
var may_be_commonjs
Expand source code Browse git
@property
def may_be_commonjs(self) -> bool:
    return self is FileKind.commonjs or self is FileKind.commonjs_or_es_module
class HostEnvironment (*args, **kwds)

The host a script is assumed to run in, selected by the djs unit's -e switch. universal is the default and asserts only GUARANTEED_GLOBALS, so deobfuscation stays byte-identical to an unpinned run; each other member adds the globals that host guarantees, recovering the folds the sound default refuses for a name no universal host defines.

Expand source code Browse git
class HostEnvironment(enum.Enum):
    """
    The host a script is assumed to run in, selected by the `djs` unit's `-e` switch. `universal` is
    the default and asserts only `GUARANTEED_GLOBALS`, so deobfuscation stays byte-identical to an
    unpinned run; each other member adds the globals that host guarantees, recovering the folds the
    sound default refuses for a name no universal host defines.
    """
    universal = 'universal'
    node = 'node'
    browser = 'browser'
    worker = 'worker'

    def provides(self, name: str) -> bool:
        """
        Whether a bare read of *name* is guaranteed to resolve in this host rather than throw a
        `ReferenceError`, judged from the host's global-name data alone.
        """
        return _SPECS[self].provides(name)

    def withholds_on_delete(self, name: str) -> bool:
        """
        Whether *name* is a host-conditional global whose presence a `delete` of it retracts, as opposed
        to a language-mandated name the guard leaves present. A name this answers `True` for is withheld
        program-wide once the program deletes it off a same-realm global alias anywhere, which over-keeps
        a read's throw rather than dropping one, and so stays sound.
        """
        return _SPECS[self].withholds_on_delete(name)

    def presence(self, name: str) -> Presence:
        """
        Whether this host is certain *name* resolves (`PRESENT`), certain it does not (`ABSENT`), or
        neither (`UNKNOWN`). `provides` is the `PRESENT` half; the `ABSENT` half names the well-known
        globals of *other* hosts this one is characterized to lack, so that `typeof <name>` folds to
        `'undefined'` under a pin without asserting absence for a name merely left off the present set.
        """
        return _SPECS[self].presence(name)

    def has_function(self, owner: str | None, key: str) -> bool:
        """
        Whether a call of the function the language installs under *key* on the prototype of the
        constructor *owner*, or as a global where *owner* is `None`, finds that function in this
        host. Every host is taken to have each such function but those of `ENGINE_FUNCTIONS`, which
        only a host with one engine is known to have.
        """
        return self.engine is not None or (owner, key) not in ENGINE_FUNCTIONS

    def calls_handler(self, name: str) -> bool:
        """
        Whether some host this one covers calls the function a script stores in the property *name*
        of the global object, an event handler attribute such as the `onload` of a browser window.
        The host calls it with no call in the file, so a store there is never dead. The default
        `universal` covers browser windows and workers, so their handlers are called there too, also
        in a Windows Script Host, where the name is an ordinary global.
        """
        return name in _SPECS[self].handlers

    @property
    def engine(self) -> JsEngine | None:
        """
        The engine every engine this host covers is, or `None` where they may differ. Only `node`
        has one. The Windows Script Host, which `universal` covers, writes the text of a function
        the language installs with a line break before `function`, before `[native code]`, and on
        either side of the closing brace, so it shares no character with what V8 writes; the
        engines of the browsers are not measured.
        """
        return _SPECS[self].engine

Ancestors

  • enum.Enum

Class variables

var universal

The type of the None singleton.

var node

The type of the None singleton.

var browser

The type of the None singleton.

var worker

The type of the None singleton.

Instance variables

var engine

The engine every engine this host covers is, or None where they may differ. Only node has one. The Windows Script Host, which universal covers, writes the text of a function the language installs with a line break before function, before [native code], and on either side of the closing brace, so it shares no character with what V8 writes; the engines of the browsers are not measured.

Expand source code Browse git
@property
def engine(self) -> JsEngine | None:
    """
    The engine every engine this host covers is, or `None` where they may differ. Only `node`
    has one. The Windows Script Host, which `universal` covers, writes the text of a function
    the language installs with a line break before `function`, before `[native code]`, and on
    either side of the closing brace, so it shares no character with what V8 writes; the
    engines of the browsers are not measured.
    """
    return _SPECS[self].engine

Methods

def provides(self, name)

Whether a bare read of name is guaranteed to resolve in this host rather than throw a ReferenceError, judged from the host's global-name data alone.

Expand source code Browse git
def provides(self, name: str) -> bool:
    """
    Whether a bare read of *name* is guaranteed to resolve in this host rather than throw a
    `ReferenceError`, judged from the host's global-name data alone.
    """
    return _SPECS[self].provides(name)
def withholds_on_delete(self, name)

Whether name is a host-conditional global whose presence a delete of it retracts, as opposed to a language-mandated name the guard leaves present. A name this answers True for is withheld program-wide once the program deletes it off a same-realm global alias anywhere, which over-keeps a read's throw rather than dropping one, and so stays sound.

Expand source code Browse git
def withholds_on_delete(self, name: str) -> bool:
    """
    Whether *name* is a host-conditional global whose presence a `delete` of it retracts, as opposed
    to a language-mandated name the guard leaves present. A name this answers `True` for is withheld
    program-wide once the program deletes it off a same-realm global alias anywhere, which over-keeps
    a read's throw rather than dropping one, and so stays sound.
    """
    return _SPECS[self].withholds_on_delete(name)
def presence(self, name)

Whether this host is certain name resolves (PRESENT), certain it does not (ABSENT), or neither (UNKNOWN). provides is the PRESENT half; the ABSENT half names the well-known globals of other hosts this one is characterized to lack, so that typeof <name> folds to 'undefined' under a pin without asserting absence for a name merely left off the present set.

Expand source code Browse git
def presence(self, name: str) -> Presence:
    """
    Whether this host is certain *name* resolves (`PRESENT`), certain it does not (`ABSENT`), or
    neither (`UNKNOWN`). `provides` is the `PRESENT` half; the `ABSENT` half names the well-known
    globals of *other* hosts this one is characterized to lack, so that `typeof <name>` folds to
    `'undefined'` under a pin without asserting absence for a name merely left off the present set.
    """
    return _SPECS[self].presence(name)
def has_function(self, owner, key)

Whether a call of the function the language installs under key on the prototype of the constructor owner, or as a global where owner is None, finds that function in this host. Every host is taken to have each such function but those of ENGINE_FUNCTIONS, which only a host with one engine is known to have.

Expand source code Browse git
def has_function(self, owner: str | None, key: str) -> bool:
    """
    Whether a call of the function the language installs under *key* on the prototype of the
    constructor *owner*, or as a global where *owner* is `None`, finds that function in this
    host. Every host is taken to have each such function but those of `ENGINE_FUNCTIONS`, which
    only a host with one engine is known to have.
    """
    return self.engine is not None or (owner, key) not in ENGINE_FUNCTIONS
def calls_handler(self, name)

Whether some host this one covers calls the function a script stores in the property name of the global object, an event handler attribute such as the onload of a browser window. The host calls it with no call in the file, so a store there is never dead. The default universal covers browser windows and workers, so their handlers are called there too, also in a Windows Script Host, where the name is an ordinary global.

Expand source code Browse git
def calls_handler(self, name: str) -> bool:
    """
    Whether some host this one covers calls the function a script stores in the property *name*
    of the global object, an event handler attribute such as the `onload` of a browser window.
    The host calls it with no call in the file, so a store there is never dead. The default
    `universal` covers browser windows and workers, so their handlers are called there too, also
    in a Windows Script Host, where the name is an ordinary global.
    """
    return name in _SPECS[self].handlers
class ClockWindow (earliest, latest)

The times between which a run reads the host's clock, in milliseconds since the epoch, both included.

Expand source code Browse git
class ClockWindow(NamedTuple):
    """
    The times between which a run reads the host's clock, in milliseconds since the epoch, both
    included.
    """
    earliest: float
    latest: float

    def comparison(self, comparison: str, time: float) -> bool | None:
        """
        What `clock <comparison> time` yields for a number *time* where every reading of the clock
        inside this window yields the same, or `None`: a time inside the window, `NaN`, or an
        operator that is not a comparison.
        """
        compare = _CLOCK_COMPARISONS.get(comparison)
        if compare is None or time != time or self.earliest <= time <= self.latest:
            return None
        return compare(self.earliest, time)

Ancestors

  • builtins.tuple

Instance variables

var earliest

Alias for field number 0

Expand source code Browse git
class ClockWindow(NamedTuple):
    """
    The times between which a run reads the host's clock, in milliseconds since the epoch, both
    included.
    """
    earliest: float
    latest: float

    def comparison(self, comparison: str, time: float) -> bool | None:
        """
        What `clock <comparison> time` yields for a number *time* where every reading of the clock
        inside this window yields the same, or `None`: a time inside the window, `NaN`, or an
        operator that is not a comparison.
        """
        compare = _CLOCK_COMPARISONS.get(comparison)
        if compare is None or time != time or self.earliest <= time <= self.latest:
            return None
        return compare(self.earliest, time)
var latest

Alias for field number 1

Expand source code Browse git
class ClockWindow(NamedTuple):
    """
    The times between which a run reads the host's clock, in milliseconds since the epoch, both
    included.
    """
    earliest: float
    latest: float

    def comparison(self, comparison: str, time: float) -> bool | None:
        """
        What `clock <comparison> time` yields for a number *time* where every reading of the clock
        inside this window yields the same, or `None`: a time inside the window, `NaN`, or an
        operator that is not a comparison.
        """
        compare = _CLOCK_COMPARISONS.get(comparison)
        if compare is None or time != time or self.earliest <= time <= self.latest:
            return None
        return compare(self.earliest, time)

Methods

def comparison(self, comparison, time)

What clock <comparison> time yields for a number time where every reading of the clock inside this window yields the same, or None: a time inside the window, NaN, or an operator that is not a comparison.

Expand source code Browse git
def comparison(self, comparison: str, time: float) -> bool | None:
    """
    What `clock <comparison> time` yields for a number *time* where every reading of the clock
    inside this window yields the same, or `None`: a time inside the window, `NaN`, or an
    operator that is not a comparison.
    """
    compare = _CLOCK_COMPARISONS.get(comparison)
    if compare is None or time != time or self.earliest <= time <= self.latest:
        return None
    return compare(self.earliest, time)