use oxc_ast::ast::*;
use oxc_compat::ESFeature;
use oxc_regular_expression::{
LiteralParser, Options, RegexUnsupportedFlags, RegexUnsupportedPatterns, ast::Pattern,
has_unsupported_regular_expression_flags, has_unsupported_regular_expression_pattern,
};
use super::context::MayHaveSideEffectsContext;
pub fn is_valid_regexp<'a>(
args: &[Argument<'a>],
ctx: &impl MayHaveSideEffectsContext<'a>,
) -> bool {
let (pattern, is_regexp_literal) = match args.first() {
None => ("", false),
Some(arg) => match arg.as_expression() {
Some(Expression::RegExpLiteral(_)) => ("", true),
Some(Expression::StringLiteral(s)) if !s.lone_surrogates => (s.value.as_str(), false),
_ => return false,
},
};
let flags = match args.get(1) {
None => None,
Some(arg) => match arg.as_expression() {
Some(Expression::StringLiteral(s)) => Some(s.value.as_str()),
_ => return false,
},
};
if is_regexp_literal {
return regexp_flags_are_supported(flags.unwrap_or_default(), ctx)
&& (flags.is_none()
|| supports_es_feature(ctx, ESFeature::ES2015RegExpConstructorCanAlterFlags));
}
let allocator = oxc_allocator::Allocator::default();
LiteralParser::new(&allocator, pattern, flags, Options::default())
.parse()
.is_ok_and(|pattern| is_regexp_syntax_supported(&pattern, flags.unwrap_or_default(), ctx))
}
pub fn is_regexp_syntax_supported<'a>(
pattern: &Pattern<'_>,
flags: &str,
ctx: &impl MayHaveSideEffectsContext<'a>,
) -> bool {
regexp_flags_are_supported(flags, ctx)
&& !has_unsupported_regular_expression_pattern(pattern, &unsupported_regexp_patterns(ctx))
}
fn regexp_flags_are_supported<'a>(flags: &str, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
!has_unsupported_regular_expression_flags(flags, &unsupported_regexp_flags(ctx))
}
fn unsupported_regexp_flags<'a>(ctx: &impl MayHaveSideEffectsContext<'a>) -> RegexUnsupportedFlags {
RegexUnsupportedFlags {
sticky: !supports_es_feature(ctx, ESFeature::ES2015StickyRegex),
unicode: !supports_es_feature(ctx, ESFeature::ES2015UnicodeRegex),
dot_all: !supports_es_feature(ctx, ESFeature::ES2018DotallRegex),
match_indices: !supports_es_feature(ctx, ESFeature::ES2022MatchIndicesRegex),
unicode_sets: !supports_es_feature(ctx, ESFeature::ES2024UnicodeSetsRegex),
}
}
fn unsupported_regexp_patterns<'a>(
ctx: &impl MayHaveSideEffectsContext<'a>,
) -> RegexUnsupportedPatterns {
RegexUnsupportedPatterns {
named_capture_groups: !supports_es_feature(ctx, ESFeature::ES2018NamedCapturingGroupsRegex),
duplicate_named_capture_groups: !supports_es_feature(
ctx,
ESFeature::ES2025DuplicateNamedCapturingGroupsRegex,
),
unicode_property_escapes: !supports_es_feature(ctx, ESFeature::ES2018UnicodePropertyRegex),
look_behind_assertions: !supports_es_feature(ctx, ESFeature::ES2018LookbehindRegex),
pattern_modifiers: !supports_es_feature(ctx, ESFeature::ES2025RegexpModifiers),
}
}
fn supports_es_feature<'a>(ctx: &impl MayHaveSideEffectsContext<'a>, feature: ESFeature) -> bool {
ctx.engine_targets().is_some_and(|target| target.supports_es_feature(feature))
}
#[rustfmt::skip]
pub(super) fn is_pure_global_function(name: &str) -> bool {
matches!(name, "decodeURI" | "decodeURIComponent" | "encodeURI" | "encodeURIComponent"
| "escape" | "isFinite" | "isNaN" | "parseFloat" | "parseInt")
}
#[rustfmt::skip]
pub(super) fn is_pure_callable_constructor(name: &str) -> bool {
matches!(name, "Date" | "Boolean" | "Object" | "String")
}
#[rustfmt::skip]
pub(super) fn is_unconditionally_pure_constructor(name: &str) -> bool {
matches!(name, "Object" | "Boolean")
}
#[rustfmt::skip]
pub(super) fn is_error_constructor(name: &str) -> bool {
matches!(name, "Error" | "EvalError" | "RangeError" | "ReferenceError"
| "SyntaxError" | "TypeError" | "URIError")
}
#[rustfmt::skip]
pub fn is_typed_array_constructor(name: &str) -> bool {
matches!(name, "Int8Array" | "Uint8Array" | "Uint8ClampedArray"
| "Int16Array" | "Uint16Array" | "Int32Array" | "Uint32Array"
| "Float32Array" | "Float64Array" | "BigInt64Array" | "BigUint64Array")
}
pub(super) fn is_pure_collection_constructor<'a>(
name: &str,
args: &[Argument<'a>],
ctx: &impl MayHaveSideEffectsContext<'a>,
) -> bool {
if !matches!(name, "Set" | "Map" | "WeakSet" | "WeakMap") {
return false;
}
match args.first() {
None => true,
Some(arg) => match arg.as_expression() {
Some(Expression::NullLiteral(_)) => true,
Some(Expression::Identifier(id))
if id.name == "undefined" && ctx.is_global_reference(id) =>
{
true
}
Some(Expression::ArrayExpression(arr)) => {
if matches!(name, "Map" | "WeakMap") {
arr.elements
.iter()
.all(|el| matches!(el, ArrayExpressionElement::ArrayExpression(_)))
} else {
true
}
}
_ => false,
},
}
}
pub(super) fn is_known_global_constructor(name: &str) -> bool {
matches!(
name,
"AggregateError"
| "Array"
| "ArrayBuffer"
| "BigInt"
| "BigInt64Array"
| "BigUint64Array"
| "Boolean"
| "DataView"
| "Date"
| "Error"
| "EvalError"
| "FinalizationRegistry"
| "Float32Array"
| "Float64Array"
| "Function"
| "Int8Array"
| "Int16Array"
| "Int32Array"
| "Iterator"
| "Map"
| "Number"
| "Object"
| "Promise"
| "Proxy"
| "RangeError"
| "ReferenceError"
| "RegExp"
| "Set"
| "SharedArrayBuffer"
| "String"
| "Symbol"
| "SyntaxError"
| "TypeError"
| "Uint8Array"
| "Uint8ClampedArray"
| "Uint16Array"
| "Uint32Array"
| "URIError"
| "WeakMap"
| "WeakSet"
)
}
pub(super) fn is_known_global_identifier(name: &str) -> bool {
KNOWN_GLOBAL_IDENTIFIERS.binary_search(&name).is_ok()
}
#[rustfmt::skip]
static KNOWN_GLOBAL_IDENTIFIERS: &[&str] = &[
"AbortController", "AbortSignal", "AggregateError", "AnalyserNode", "Animation",
"AnimationEffect", "AnimationEvent", "AnimationPlaybackEvent", "AnimationTimeline", "Array",
"ArrayBuffer", "Attr", "Audio", "AudioBuffer", "AudioBufferSourceNode", "AudioDestinationNode",
"AudioListener", "AudioNode", "AudioParam", "AudioProcessingEvent", "AudioScheduledSourceNode",
"BarProp", "BeforeUnloadEvent", "BigInt", "BiquadFilterNode", "Blob", "BlobEvent", "Boolean",
"ByteLengthQueuingStrategy", "CDATASection", "CSS", "CSSAnimation", "CSSFontFaceRule",
"CSSImportRule", "CSSKeyframeRule", "CSSKeyframesRule", "CSSMediaRule", "CSSNamespaceRule",
"CSSPageRule", "CSSRule", "CSSRuleList", "CSSStyleDeclaration", "CSSStyleRule",
"CSSStyleSheet", "CSSSupportsRule", "CSSTransition", "CanvasGradient", "CanvasPattern",
"CanvasRenderingContext2D", "ChannelMergerNode", "ChannelSplitterNode", "CharacterData",
"ClipboardEvent", "CloseEvent", "Comment", "CompositionEvent", "ConvolverNode",
"CountQueuingStrategy", "Crypto", "CustomElementRegistry", "CustomEvent", "DOMException",
"DOMImplementation", "DOMMatrix", "DOMMatrixReadOnly", "DOMParser", "DOMPoint",
"DOMPointReadOnly", "DOMQuad", "DOMRect", "DOMRectList", "DOMRectReadOnly", "DOMStringList",
"DOMStringMap", "DOMTokenList", "DataTransfer", "DataTransferItem", "DataTransferItemList",
"DataView", "Date", "DelayNode", "Document", "DocumentFragment", "DocumentTimeline",
"DocumentType", "DragEvent", "DynamicsCompressorNode", "Element", "Error", "ErrorEvent",
"EvalError", "Event", "EventSource", "EventTarget", "File", "FileList", "FileReader",
"Float32Array", "Float64Array", "FocusEvent", "FontFace", "FormData", "Function", "GainNode",
"Gamepad", "GamepadButton", "GamepadEvent", "Geolocation", "GeolocationPositionError",
"HTMLAllCollection", "HTMLAnchorElement", "HTMLAreaElement", "HTMLAudioElement",
"HTMLBRElement", "HTMLBaseElement", "HTMLBodyElement", "HTMLButtonElement",
"HTMLCanvasElement", "HTMLCollection", "HTMLDListElement", "HTMLDataElement",
"HTMLDataListElement", "HTMLDetailsElement", "HTMLDirectoryElement", "HTMLDivElement",
"HTMLDocument", "HTMLElement", "HTMLEmbedElement", "HTMLFieldSetElement", "HTMLFontElement",
"HTMLFormControlsCollection", "HTMLFormElement", "HTMLFrameElement", "HTMLFrameSetElement",
"HTMLHRElement", "HTMLHeadElement", "HTMLHeadingElement", "HTMLHtmlElement",
"HTMLIFrameElement", "HTMLImageElement", "HTMLInputElement", "HTMLLIElement",
"HTMLLabelElement", "HTMLLegendElement", "HTMLLinkElement", "HTMLMapElement",
"HTMLMarqueeElement", "HTMLMediaElement", "HTMLMenuElement", "HTMLMetaElement",
"HTMLMeterElement", "HTMLModElement", "HTMLOListElement", "HTMLObjectElement",
"HTMLOptGroupElement", "HTMLOptionElement", "HTMLOptionsCollection", "HTMLOutputElement",
"HTMLParagraphElement", "HTMLParamElement", "HTMLPictureElement", "HTMLPreElement",
"HTMLProgressElement", "HTMLQuoteElement", "HTMLScriptElement", "HTMLSelectElement",
"HTMLSlotElement", "HTMLSourceElement", "HTMLSpanElement", "HTMLStyleElement",
"HTMLTableCaptionElement", "HTMLTableCellElement", "HTMLTableColElement", "HTMLTableElement",
"HTMLTableRowElement", "HTMLTableSectionElement", "HTMLTemplateElement", "HTMLTextAreaElement",
"HTMLTimeElement", "HTMLTitleElement", "HTMLTrackElement", "HTMLUListElement",
"HTMLUnknownElement", "HTMLVideoElement", "HashChangeEvent", "Headers", "History", "IDBCursor",
"IDBCursorWithValue", "IDBDatabase", "IDBFactory", "IDBIndex", "IDBKeyRange", "IDBObjectStore",
"IDBOpenDBRequest", "IDBRequest", "IDBTransaction", "IDBVersionChangeEvent", "Image",
"ImageData", "InputEvent", "Int16Array", "Int32Array", "Int8Array", "IntersectionObserver",
"IntersectionObserverEntry", "Intl", "JSON", "KeyboardEvent", "KeyframeEffect", "Location",
"Map", "Math", "MediaCapabilities", "MediaElementAudioSourceNode", "MediaEncryptedEvent",
"MediaError", "MediaList", "MediaQueryList", "MediaQueryListEvent", "MediaRecorder",
"MediaSource", "MediaStream", "MediaStreamAudioDestinationNode", "MediaStreamAudioSourceNode",
"MediaStreamTrack", "MediaStreamTrackEvent", "MessageChannel", "MessageEvent", "MessagePort",
"MimeType", "MimeTypeArray", "MouseEvent", "MutationEvent", "MutationObserver",
"MutationRecord", "NamedNodeMap", "Navigator", "Node", "NodeFilter", "NodeIterator",
"NodeList", "Notification", "Number", "Object", "OfflineAudioCompletionEvent", "Option",
"OscillatorNode", "PageTransitionEvent", "Path2D", "Performance", "PerformanceEntry",
"PerformanceMark", "PerformanceMeasure", "PerformanceNavigation", "PerformanceObserver",
"PerformanceObserverEntryList", "PerformanceResourceTiming", "PerformanceTiming",
"PeriodicWave", "Plugin", "PluginArray", "PointerEvent", "PopStateEvent",
"ProcessingInstruction", "ProgressEvent", "Promise", "PromiseRejectionEvent", "Proxy",
"RTCCertificate", "RTCDTMFSender", "RTCDTMFToneChangeEvent", "RTCDataChannel",
"RTCDataChannelEvent", "RTCIceCandidate", "RTCPeerConnection", "RTCPeerConnectionIceEvent",
"RTCRtpReceiver", "RTCRtpSender", "RTCRtpTransceiver", "RTCSessionDescription",
"RTCStatsReport", "RTCTrackEvent", "RadioNodeList", "Range", "RangeError", "ReadableStream",
"ReferenceError", "Reflect", "RegExp", "Request", "ResizeObserver", "ResizeObserverEntry",
"Response", "SVGAElement", "SVGAngle", "SVGAnimateElement", "SVGAnimateMotionElement",
"SVGAnimateTransformElement", "SVGAnimatedAngle", "SVGAnimatedBoolean",
"SVGAnimatedEnumeration", "SVGAnimatedInteger", "SVGAnimatedLength", "SVGAnimatedLengthList",
"SVGAnimatedNumber", "SVGAnimatedNumberList", "SVGAnimatedPreserveAspectRatio",
"SVGAnimatedRect", "SVGAnimatedString", "SVGAnimatedTransformList", "SVGAnimationElement",
"SVGCircleElement", "SVGClipPathElement", "SVGComponentTransferFunctionElement",
"SVGDefsElement", "SVGDescElement", "SVGElement", "SVGEllipseElement", "SVGFEBlendElement",
"SVGFEColorMatrixElement", "SVGFEComponentTransferElement", "SVGFECompositeElement",
"SVGFEConvolveMatrixElement", "SVGFEDiffuseLightingElement", "SVGFEDisplacementMapElement",
"SVGFEDistantLightElement", "SVGFEDropShadowElement", "SVGFEFloodElement", "SVGFEFuncAElement",
"SVGFEFuncBElement", "SVGFEFuncGElement", "SVGFEFuncRElement", "SVGFEGaussianBlurElement",
"SVGFEImageElement", "SVGFEMergeElement", "SVGFEMergeNodeElement", "SVGFEMorphologyElement",
"SVGFEOffsetElement", "SVGFEPointLightElement", "SVGFESpecularLightingElement",
"SVGFESpotLightElement", "SVGFETileElement", "SVGFETurbulenceElement", "SVGFilterElement",
"SVGForeignObjectElement", "SVGGElement", "SVGGeometryElement", "SVGGradientElement",
"SVGGraphicsElement", "SVGImageElement", "SVGLength", "SVGLengthList", "SVGLineElement",
"SVGLinearGradientElement", "SVGMPathElement", "SVGMarkerElement", "SVGMaskElement",
"SVGMatrix", "SVGMetadataElement", "SVGNumber", "SVGNumberList", "SVGPathElement",
"SVGPatternElement", "SVGPoint", "SVGPointList", "SVGPolygonElement", "SVGPolylineElement",
"SVGPreserveAspectRatio", "SVGRadialGradientElement", "SVGRect", "SVGRectElement",
"SVGSVGElement", "SVGScriptElement", "SVGSetElement", "SVGStopElement", "SVGStringList",
"SVGStyleElement", "SVGSwitchElement", "SVGSymbolElement", "SVGTSpanElement",
"SVGTextContentElement", "SVGTextElement", "SVGTextPathElement", "SVGTextPositioningElement",
"SVGTitleElement", "SVGTransform", "SVGTransformList", "SVGUnitTypes", "SVGUseElement",
"SVGViewElement", "Screen", "ScriptProcessorNode", "SecurityPolicyViolationEvent", "Selection",
"Set", "ShadowRoot", "SourceBuffer", "SourceBufferList", "SpeechSynthesisEvent",
"SpeechSynthesisUtterance", "StaticRange", "Storage", "StorageEvent", "String", "StyleSheet",
"StyleSheetList", "Symbol", "SyntaxError", "Text", "TextDecoder", "TextEncoder", "TextMetrics",
"TextTrack", "TextTrackCue", "TextTrackCueList", "TextTrackList", "TimeRanges", "TrackEvent",
"TransitionEvent", "TreeWalker", "TypeError", "UIEvent", "URIError", "URL", "URLSearchParams",
"Uint16Array", "Uint32Array", "Uint8Array", "Uint8ClampedArray", "VTTCue", "ValidityState",
"VisualViewport", "WaveShaperNode", "WeakMap", "WeakSet", "WebAssembly", "WebGLActiveInfo",
"WebGLBuffer", "WebGLContextEvent", "WebGLFramebuffer", "WebGLProgram", "WebGLQuery",
"WebGLRenderbuffer", "WebGLRenderingContext", "WebGLSampler", "WebGLShader",
"WebGLShaderPrecisionFormat", "WebGLSync", "WebGLTexture", "WebGLUniformLocation",
"WebKitCSSMatrix", "WebSocket", "WheelEvent", "Window", "Worker", "XMLDocument",
"XMLHttpRequest", "XMLHttpRequestEventTarget", "XMLHttpRequestUpload", "XMLSerializer",
"XPathEvaluator", "XPathExpression", "XPathResult", "XSLTProcessor", "alert", "atob", "blur",
"btoa", "cancelAnimationFrame", "captureEvents", "clearInterval", "clearTimeout", "close",
"closed", "confirm", "console", "customElements", "decodeURI", "decodeURIComponent",
"devicePixelRatio", "document", "encodeURI", "encodeURIComponent", "escape", "event", "fetch",
"find", "focus", "frameElement", "frames", "getComputedStyle", "getSelection", "globalThis",
"history", "indexedDB", "isFinite", "isNaN", "isSecureContext", "length", "location",
"locationbar", "matchMedia", "menubar", "moveBy", "moveTo", "name", "navigator", "onabort",
"onafterprint", "onanimationend", "onanimationiteration", "onanimationstart", "onbeforeprint",
"onbeforeunload", "onblur", "oncanplay", "oncanplaythrough", "onchange", "onclick",
"oncontextmenu", "oncuechange", "ondblclick", "ondrag", "ondragend", "ondragenter",
"ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied",
"onended", "onerror", "onfocus", "ongotpointercapture", "onhashchange", "oninput", "oninvalid",
"onkeydown", "onkeypress", "onkeyup", "onlanguagechange", "onload", "onloadeddata",
"onloadedmetadata", "onloadstart", "onlostpointercapture", "onmessage", "onmousedown",
"onmouseenter", "onmouseleave", "onmousemove", "onmouseout", "onmouseover", "onmouseup",
"onoffline", "ononline", "onpagehide", "onpageshow", "onpause", "onplay", "onplaying",
"onpointercancel", "onpointerdown", "onpointerenter", "onpointerleave", "onpointermove",
"onpointerout", "onpointerover", "onpointerup", "onpopstate", "onprogress", "onratechange",
"onrejectionhandled", "onreset", "onresize", "onscroll", "onseeked", "onseeking", "onselect",
"onstalled", "onstorage", "onsubmit", "onsuspend", "ontimeupdate", "ontoggle",
"ontransitioncancel", "ontransitionend", "ontransitionrun", "ontransitionstart",
"onunhandledrejection", "onunload", "onvolumechange", "onwaiting", "onwebkitanimationend",
"onwebkitanimationiteration", "onwebkitanimationstart", "onwebkittransitionend", "onwheel",
"open", "opener", "origin", "outerHeight", "outerWidth", "parent", "parseFloat", "parseInt",
"performance", "personalbar", "postMessage", "print", "prompt", "queueMicrotask",
"releaseEvents", "requestAnimationFrame", "resizeBy", "resizeTo", "screen", "screenLeft",
"screenTop", "screenX", "screenY", "scroll", "scrollBy", "scrollTo", "scrollbars", "self",
"setInterval", "setTimeout", "speechSynthesis", "status", "statusbar", "stop", "toolbar",
"top", "unescape", "webkitURL", "window",
];
#[rustfmt::skip]
fn is_pure_math_method(method: &str) -> bool {
matches!(method,
"abs" | "acos" | "acosh" | "asin" | "asinh" | "atan" | "atan2" | "atanh"
| "cbrt" | "ceil" | "clz32" | "cos" | "cosh" | "exp" | "expm1" | "floor"
| "fround" | "hypot" | "imul" | "log" | "log10" | "log1p" | "log2" | "max"
| "min" | "pow" | "random" | "round" | "sign" | "sin" | "sinh" | "sqrt"
| "tan" | "tanh" | "trunc"
)
}
#[rustfmt::skip]
pub(super) fn is_pure_global_method_call(object: &str, method: &str) -> bool {
match object {
"Array" => matches!(method, "isArray" | "of"),
"ArrayBuffer" => method == "isView",
"Date" => matches!(method, "now" | "parse" | "UTC"),
"Math" => is_pure_math_method(method),
"Number" => matches!(method, "isFinite" | "isInteger" | "isNaN" | "isSafeInteger" | "parseFloat" | "parseInt"),
"Object" => method == "is",
"String" => matches!(method, "fromCharCode" | "fromCodePoint"),
"Symbol" => method == "for",
"URL" => method == "canParse",
_ if is_typed_array_constructor(object) => method == "of",
_ => false,
}
}
#[rustfmt::skip]
pub(super) fn is_known_global_property(global: &str, property: &str) -> bool {
match global {
"Math" => matches!(property, "E" | "LN10" | "LN2" | "LOG10E" | "LOG2E" | "PI" | "SQRT1_2" | "SQRT2")
|| is_pure_math_method(property),
"Number" => matches!(property,
"POSITIVE_INFINITY" | "NEGATIVE_INFINITY" | "EPSILON" | "NaN"
| "MAX_VALUE" | "MIN_VALUE" | "MAX_SAFE_INTEGER" | "MIN_SAFE_INTEGER"),
"console" => matches!(property,
"assert" | "clear" | "count" | "countReset" | "debug" | "dir" | "dirxml"
| "error" | "group" | "groupCollapsed" | "groupEnd" | "info" | "log"
| "table" | "time" | "timeEnd" | "timeLog" | "trace" | "warn"
),
"Object" => matches!(property,
"assign" | "create" | "defineProperties" | "defineProperty" | "entries"
| "freeze" | "fromEntries" | "getOwnPropertyDescriptor"
| "getOwnPropertyDescriptors" | "getOwnPropertyNames"
| "getOwnPropertySymbols" | "getPrototypeOf" | "is" | "isExtensible"
| "isFrozen" | "isSealed" | "keys" | "preventExtensions" | "prototype"
| "seal" | "setPrototypeOf" | "values"
),
"Reflect" => matches!(property,
"apply" | "construct" | "defineProperty" | "deleteProperty" | "get"
| "getOwnPropertyDescriptor" | "getPrototypeOf" | "has" | "isExtensible"
| "ownKeys" | "preventExtensions" | "set" | "setPrototypeOf"
),
"Symbol" => matches!(property,
"asyncDispose" | "asyncIterator" | "dispose" | "hasInstance"
| "isConcatSpreadable" | "iterator" | "match" | "matchAll" | "replace"
| "search" | "species" | "split" | "toPrimitive" | "toStringTag"
| "unscopables"
),
"JSON" => matches!(property, "parse" | "stringify"),
_ => false,
}
}
#[rustfmt::skip]
pub(super) fn is_known_global_property_deep(global: &str, middle: &str, property: &str) -> bool {
global == "Object" && middle == "prototype" && matches!(property,
"__defineGetter__" | "__defineSetter__" | "__lookupGetter__" | "__lookupSetter__"
| "hasOwnProperty" | "isPrototypeOf" | "propertyIsEnumerable" | "toLocaleString"
| "toString" | "unwatch" | "valueOf" | "watch"
)
}
#[cfg(test)]
mod test {
use super::{KNOWN_GLOBAL_IDENTIFIERS, is_known_global_identifier};
#[test]
fn known_global_identifiers_table_is_sorted_and_unique() {
for pair in KNOWN_GLOBAL_IDENTIFIERS.windows(2) {
assert!(pair[0] < pair[1], "`{}` must sort before `{}`", pair[0], pair[1]);
}
}
#[test]
fn known_global_identifiers_lookup() {
assert!(is_known_global_identifier("Array"));
assert!(is_known_global_identifier("window"));
assert!(is_known_global_identifier("AbortController"));
assert!(is_known_global_identifier("webkitURL"));
assert!(!is_known_global_identifier("NaN"));
assert!(!is_known_global_identifier("foo"));
assert!(!is_known_global_identifier(""));
}
}