970 lines
22 KiB
Go
970 lines
22 KiB
Go
package config
|
|
|
|
import (
|
|
"math"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/evanw/esbuild/internal/js_ast"
|
|
"github.com/evanw/esbuild/internal/logger"
|
|
)
|
|
|
|
var processedGlobalsMutex sync.Mutex
|
|
var processedGlobals *ProcessedDefines
|
|
|
|
// If something is in this list, then a direct identifier expression or property
|
|
// access chain matching this will be assumed to have no side effects and will
|
|
// be removed.
|
|
//
|
|
// This also means code is allowed to be reordered past things in this list. For
|
|
// example, if "console.log" is in this list, permitting reordering allows for
|
|
// "if (a) console.log(b); else console.log(c)" to be reordered and transformed
|
|
// into "console.log(a ? b : c)". Notice that "a" and "console.log" are in a
|
|
// different order, which can only happen if evaluating the "console.log"
|
|
// property access can be assumed to not change the value of "a".
|
|
//
|
|
// Note that membership in this list says nothing about whether calling any of
|
|
// these functions has any side effects. It only says something about
|
|
// referencing these function without calling them.
|
|
var knownGlobals = [][]string{
|
|
// These global identifiers should exist in all JavaScript environments. This
|
|
// deliberately omits "NaN", "Infinity", and "undefined" because these are
|
|
// treated as automatically-inlined constants instead of identifiers.
|
|
{"Array"},
|
|
{"Boolean"},
|
|
{"Function"},
|
|
{"Math"},
|
|
{"Number"},
|
|
{"Object"},
|
|
{"RegExp"},
|
|
{"String"},
|
|
|
|
// Object: Static methods
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Static_methods
|
|
{"Object", "assign"},
|
|
{"Object", "create"},
|
|
{"Object", "defineProperties"},
|
|
{"Object", "defineProperty"},
|
|
{"Object", "entries"},
|
|
{"Object", "freeze"},
|
|
{"Object", "fromEntries"},
|
|
{"Object", "getOwnPropertyDescriptor"},
|
|
{"Object", "getOwnPropertyDescriptors"},
|
|
{"Object", "getOwnPropertyNames"},
|
|
{"Object", "getOwnPropertySymbols"},
|
|
{"Object", "getPrototypeOf"},
|
|
{"Object", "is"},
|
|
{"Object", "isExtensible"},
|
|
{"Object", "isFrozen"},
|
|
{"Object", "isSealed"},
|
|
{"Object", "keys"},
|
|
{"Object", "preventExtensions"},
|
|
{"Object", "seal"},
|
|
{"Object", "setPrototypeOf"},
|
|
{"Object", "values"},
|
|
|
|
// Object: Instance methods
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Instance_methods
|
|
{"Object", "prototype", "__defineGetter__"},
|
|
{"Object", "prototype", "__defineSetter__"},
|
|
{"Object", "prototype", "__lookupGetter__"},
|
|
{"Object", "prototype", "__lookupSetter__"},
|
|
{"Object", "prototype", "hasOwnProperty"},
|
|
{"Object", "prototype", "isPrototypeOf"},
|
|
{"Object", "prototype", "propertyIsEnumerable"},
|
|
{"Object", "prototype", "toLocaleString"},
|
|
{"Object", "prototype", "toString"},
|
|
{"Object", "prototype", "unwatch"},
|
|
{"Object", "prototype", "valueOf"},
|
|
{"Object", "prototype", "watch"},
|
|
|
|
// Math: Static properties
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math#Static_properties
|
|
{"Math", "E"},
|
|
{"Math", "LN10"},
|
|
{"Math", "LN2"},
|
|
{"Math", "LOG10E"},
|
|
{"Math", "LOG2E"},
|
|
{"Math", "PI"},
|
|
{"Math", "SQRT1_2"},
|
|
{"Math", "SQRT2"},
|
|
|
|
// Math: Static methods
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math#Static_methods
|
|
{"Math", "abs"},
|
|
{"Math", "acos"},
|
|
{"Math", "acosh"},
|
|
{"Math", "asin"},
|
|
{"Math", "asinh"},
|
|
{"Math", "atan"},
|
|
{"Math", "atan2"},
|
|
{"Math", "atanh"},
|
|
{"Math", "cbrt"},
|
|
{"Math", "ceil"},
|
|
{"Math", "clz32"},
|
|
{"Math", "cos"},
|
|
{"Math", "cosh"},
|
|
{"Math", "exp"},
|
|
{"Math", "expm1"},
|
|
{"Math", "floor"},
|
|
{"Math", "fround"},
|
|
{"Math", "hypot"},
|
|
{"Math", "imul"},
|
|
{"Math", "log"},
|
|
{"Math", "log10"},
|
|
{"Math", "log1p"},
|
|
{"Math", "log2"},
|
|
{"Math", "max"},
|
|
{"Math", "min"},
|
|
{"Math", "pow"},
|
|
{"Math", "random"},
|
|
{"Math", "round"},
|
|
{"Math", "sign"},
|
|
{"Math", "sin"},
|
|
{"Math", "sinh"},
|
|
{"Math", "sqrt"},
|
|
{"Math", "tan"},
|
|
{"Math", "tanh"},
|
|
{"Math", "trunc"},
|
|
|
|
// Reflect: Static methods
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect#static_methods
|
|
{"Reflect", "apply"},
|
|
{"Reflect", "construct"},
|
|
{"Reflect", "defineProperty"},
|
|
{"Reflect", "deleteProperty"},
|
|
{"Reflect", "get"},
|
|
{"Reflect", "getOwnPropertyDescriptor"},
|
|
{"Reflect", "getPrototypeOf"},
|
|
{"Reflect", "has"},
|
|
{"Reflect", "isExtensible"},
|
|
{"Reflect", "ownKeys"},
|
|
{"Reflect", "preventExtensions"},
|
|
{"Reflect", "set"},
|
|
{"Reflect", "setPrototypeOf"},
|
|
|
|
// Other globals present in both the browser and node (except "eval" because
|
|
// it has special behavior)
|
|
{"AbortController"},
|
|
{"AbortSignal"},
|
|
{"AggregateError"},
|
|
{"ArrayBuffer"},
|
|
{"BigInt"},
|
|
{"DataView"},
|
|
{"Date"},
|
|
{"Error"},
|
|
{"EvalError"},
|
|
{"Event"},
|
|
{"EventTarget"},
|
|
{"Float32Array"},
|
|
{"Float64Array"},
|
|
{"Int16Array"},
|
|
{"Int32Array"},
|
|
{"Int8Array"},
|
|
{"Intl"},
|
|
{"JSON"},
|
|
{"Map"},
|
|
{"MessageChannel"},
|
|
{"MessageEvent"},
|
|
{"MessagePort"},
|
|
{"Promise"},
|
|
{"Proxy"},
|
|
{"RangeError"},
|
|
{"ReferenceError"},
|
|
{"Reflect"},
|
|
{"Set"},
|
|
{"Symbol"},
|
|
{"SyntaxError"},
|
|
{"TextDecoder"},
|
|
{"TextEncoder"},
|
|
{"TypeError"},
|
|
{"URIError"},
|
|
{"URL"},
|
|
{"URLSearchParams"},
|
|
{"Uint16Array"},
|
|
{"Uint32Array"},
|
|
{"Uint8Array"},
|
|
{"Uint8ClampedArray"},
|
|
{"WeakMap"},
|
|
{"WeakSet"},
|
|
{"WebAssembly"},
|
|
{"clearInterval"},
|
|
{"clearTimeout"},
|
|
{"console"},
|
|
{"decodeURI"},
|
|
{"decodeURIComponent"},
|
|
{"encodeURI"},
|
|
{"encodeURIComponent"},
|
|
{"escape"},
|
|
{"globalThis"},
|
|
{"isFinite"},
|
|
{"isNaN"},
|
|
{"parseFloat"},
|
|
{"parseInt"},
|
|
{"queueMicrotask"},
|
|
{"setInterval"},
|
|
{"setTimeout"},
|
|
{"unescape"},
|
|
|
|
// Console method references are assumed to have no side effects
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/console
|
|
{"console", "assert"},
|
|
{"console", "clear"},
|
|
{"console", "count"},
|
|
{"console", "countReset"},
|
|
{"console", "debug"},
|
|
{"console", "dir"},
|
|
{"console", "dirxml"},
|
|
{"console", "error"},
|
|
{"console", "group"},
|
|
{"console", "groupCollapsed"},
|
|
{"console", "groupEnd"},
|
|
{"console", "info"},
|
|
{"console", "log"},
|
|
{"console", "table"},
|
|
{"console", "time"},
|
|
{"console", "timeEnd"},
|
|
{"console", "timeLog"},
|
|
{"console", "trace"},
|
|
{"console", "warn"},
|
|
|
|
// CSSOM APIs
|
|
{"CSSAnimation"},
|
|
{"CSSFontFaceRule"},
|
|
{"CSSImportRule"},
|
|
{"CSSKeyframeRule"},
|
|
{"CSSKeyframesRule"},
|
|
{"CSSMediaRule"},
|
|
{"CSSNamespaceRule"},
|
|
{"CSSPageRule"},
|
|
{"CSSRule"},
|
|
{"CSSRuleList"},
|
|
{"CSSStyleDeclaration"},
|
|
{"CSSStyleRule"},
|
|
{"CSSStyleSheet"},
|
|
{"CSSSupportsRule"},
|
|
{"CSSTransition"},
|
|
|
|
// SVG DOM
|
|
{"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"},
|
|
|
|
// Other browser APIs
|
|
//
|
|
// This list contains all globals present in modern versions of Chrome, Safari,
|
|
// and Firefox except for the following properties, since they have a side effect
|
|
// of triggering layout (https://gist.github.com/paulirish/5d52fb081b3570c81e3a):
|
|
//
|
|
// - scrollX
|
|
// - scrollY
|
|
// - innerWidth
|
|
// - innerHeight
|
|
// - pageXOffset
|
|
// - pageYOffset
|
|
//
|
|
// The following globals have also been removed since they sometimes throw an
|
|
// exception when accessed, which is a side effect (for more information see
|
|
// https://stackoverflow.com/a/33047477):
|
|
//
|
|
// - localStorage
|
|
// - sessionStorage
|
|
//
|
|
{"AnalyserNode"},
|
|
{"Animation"},
|
|
{"AnimationEffect"},
|
|
{"AnimationEvent"},
|
|
{"AnimationPlaybackEvent"},
|
|
{"AnimationTimeline"},
|
|
{"Attr"},
|
|
{"Audio"},
|
|
{"AudioBuffer"},
|
|
{"AudioBufferSourceNode"},
|
|
{"AudioDestinationNode"},
|
|
{"AudioListener"},
|
|
{"AudioNode"},
|
|
{"AudioParam"},
|
|
{"AudioProcessingEvent"},
|
|
{"AudioScheduledSourceNode"},
|
|
{"BarProp"},
|
|
{"BeforeUnloadEvent"},
|
|
{"BiquadFilterNode"},
|
|
{"Blob"},
|
|
{"BlobEvent"},
|
|
{"ByteLengthQueuingStrategy"},
|
|
{"CDATASection"},
|
|
{"CSS"},
|
|
{"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"},
|
|
{"DelayNode"},
|
|
{"Document"},
|
|
{"DocumentFragment"},
|
|
{"DocumentTimeline"},
|
|
{"DocumentType"},
|
|
{"DragEvent"},
|
|
{"DynamicsCompressorNode"},
|
|
{"Element"},
|
|
{"ErrorEvent"},
|
|
{"EventSource"},
|
|
{"File"},
|
|
{"FileList"},
|
|
{"FileReader"},
|
|
{"FocusEvent"},
|
|
{"FontFace"},
|
|
{"FormData"},
|
|
{"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"},
|
|
{"IntersectionObserver"},
|
|
{"IntersectionObserverEntry"},
|
|
{"KeyboardEvent"},
|
|
{"KeyframeEffect"},
|
|
{"Location"},
|
|
{"MediaCapabilities"},
|
|
{"MediaElementAudioSourceNode"},
|
|
{"MediaEncryptedEvent"},
|
|
{"MediaError"},
|
|
{"MediaList"},
|
|
{"MediaQueryList"},
|
|
{"MediaQueryListEvent"},
|
|
{"MediaRecorder"},
|
|
{"MediaSource"},
|
|
{"MediaStream"},
|
|
{"MediaStreamAudioDestinationNode"},
|
|
{"MediaStreamAudioSourceNode"},
|
|
{"MediaStreamTrack"},
|
|
{"MediaStreamTrackEvent"},
|
|
{"MimeType"},
|
|
{"MimeTypeArray"},
|
|
{"MouseEvent"},
|
|
{"MutationEvent"},
|
|
{"MutationObserver"},
|
|
{"MutationRecord"},
|
|
{"NamedNodeMap"},
|
|
{"Navigator"},
|
|
{"Node"},
|
|
{"NodeFilter"},
|
|
{"NodeIterator"},
|
|
{"NodeList"},
|
|
{"Notification"},
|
|
{"OfflineAudioCompletionEvent"},
|
|
{"Option"},
|
|
{"OscillatorNode"},
|
|
{"PageTransitionEvent"},
|
|
{"Path2D"},
|
|
{"Performance"},
|
|
{"PerformanceEntry"},
|
|
{"PerformanceMark"},
|
|
{"PerformanceMeasure"},
|
|
{"PerformanceNavigation"},
|
|
{"PerformanceObserver"},
|
|
{"PerformanceObserverEntryList"},
|
|
{"PerformanceResourceTiming"},
|
|
{"PerformanceTiming"},
|
|
{"PeriodicWave"},
|
|
{"Plugin"},
|
|
{"PluginArray"},
|
|
{"PointerEvent"},
|
|
{"PopStateEvent"},
|
|
{"ProcessingInstruction"},
|
|
{"ProgressEvent"},
|
|
{"PromiseRejectionEvent"},
|
|
{"RTCCertificate"},
|
|
{"RTCDTMFSender"},
|
|
{"RTCDTMFToneChangeEvent"},
|
|
{"RTCDataChannel"},
|
|
{"RTCDataChannelEvent"},
|
|
{"RTCIceCandidate"},
|
|
{"RTCPeerConnection"},
|
|
{"RTCPeerConnectionIceEvent"},
|
|
{"RTCRtpReceiver"},
|
|
{"RTCRtpSender"},
|
|
{"RTCRtpTransceiver"},
|
|
{"RTCSessionDescription"},
|
|
{"RTCStatsReport"},
|
|
{"RTCTrackEvent"},
|
|
{"RadioNodeList"},
|
|
{"Range"},
|
|
{"ReadableStream"},
|
|
{"Request"},
|
|
{"ResizeObserver"},
|
|
{"ResizeObserverEntry"},
|
|
{"Response"},
|
|
{"Screen"},
|
|
{"ScriptProcessorNode"},
|
|
{"SecurityPolicyViolationEvent"},
|
|
{"Selection"},
|
|
{"ShadowRoot"},
|
|
{"SourceBuffer"},
|
|
{"SourceBufferList"},
|
|
{"SpeechSynthesisEvent"},
|
|
{"SpeechSynthesisUtterance"},
|
|
{"StaticRange"},
|
|
{"Storage"},
|
|
{"StorageEvent"},
|
|
{"StyleSheet"},
|
|
{"StyleSheetList"},
|
|
{"Text"},
|
|
{"TextMetrics"},
|
|
{"TextTrack"},
|
|
{"TextTrackCue"},
|
|
{"TextTrackCueList"},
|
|
{"TextTrackList"},
|
|
{"TimeRanges"},
|
|
{"TrackEvent"},
|
|
{"TransitionEvent"},
|
|
{"TreeWalker"},
|
|
{"UIEvent"},
|
|
{"VTTCue"},
|
|
{"ValidityState"},
|
|
{"VisualViewport"},
|
|
{"WaveShaperNode"},
|
|
{"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"},
|
|
{"close"},
|
|
{"closed"},
|
|
{"confirm"},
|
|
{"customElements"},
|
|
{"devicePixelRatio"},
|
|
{"document"},
|
|
{"event"},
|
|
{"fetch"},
|
|
{"find"},
|
|
{"focus"},
|
|
{"frameElement"},
|
|
{"frames"},
|
|
{"getComputedStyle"},
|
|
{"getSelection"},
|
|
{"history"},
|
|
{"indexedDB"},
|
|
{"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"},
|
|
{"performance"},
|
|
{"personalbar"},
|
|
{"postMessage"},
|
|
{"print"},
|
|
{"prompt"},
|
|
{"releaseEvents"},
|
|
{"requestAnimationFrame"},
|
|
{"resizeBy"},
|
|
{"resizeTo"},
|
|
{"screen"},
|
|
{"screenLeft"},
|
|
{"screenTop"},
|
|
{"screenX"},
|
|
{"screenY"},
|
|
{"scroll"},
|
|
{"scrollBy"},
|
|
{"scrollTo"},
|
|
{"scrollbars"},
|
|
{"self"},
|
|
{"speechSynthesis"},
|
|
{"status"},
|
|
{"statusbar"},
|
|
{"stop"},
|
|
{"toolbar"},
|
|
{"top"},
|
|
{"webkitURL"},
|
|
{"window"},
|
|
}
|
|
|
|
type DefineArgs struct {
|
|
Loc logger.Loc
|
|
FindSymbol func(logger.Loc, string) js_ast.Ref
|
|
SymbolForDefine func(int) js_ast.Ref
|
|
}
|
|
|
|
type DefineFunc func(DefineArgs) js_ast.E
|
|
|
|
type DefineData struct {
|
|
DefineFunc DefineFunc
|
|
|
|
// True if accessing this value is known to not have any side effects. For
|
|
// example, a bare reference to "Object.create" can be removed because it
|
|
// does not have any observable side effects.
|
|
CanBeRemovedIfUnused bool
|
|
|
|
// True if a call to this value is known to not have any side effects. For
|
|
// example, a bare call to "Object()" can be removed because it does not
|
|
// have any observable side effects.
|
|
CallCanBeUnwrappedIfUnused bool
|
|
}
|
|
|
|
func mergeDefineData(old DefineData, new DefineData) DefineData {
|
|
if old.CanBeRemovedIfUnused {
|
|
new.CanBeRemovedIfUnused = true
|
|
}
|
|
if old.CallCanBeUnwrappedIfUnused {
|
|
new.CallCanBeUnwrappedIfUnused = true
|
|
}
|
|
return new
|
|
}
|
|
|
|
type DotDefine struct {
|
|
Parts []string
|
|
Data DefineData
|
|
}
|
|
|
|
type ProcessedDefines struct {
|
|
IdentifierDefines map[string]DefineData
|
|
DotDefines map[string][]DotDefine
|
|
}
|
|
|
|
// This transformation is expensive, so we only want to do it once. Make sure
|
|
// to only call processDefines() once per compilation. Unfortunately Golang
|
|
// doesn't have an efficient way to copy a map and the overhead of copying
|
|
// all of the properties into a new map once for every new parser noticeably
|
|
// slows down our benchmarks.
|
|
func ProcessDefines(userDefines map[string]DefineData) ProcessedDefines {
|
|
// Optimization: reuse known globals if there are no user-specified defines
|
|
hasUserDefines := len(userDefines) != 0
|
|
if !hasUserDefines {
|
|
processedGlobalsMutex.Lock()
|
|
if processedGlobals != nil {
|
|
defer processedGlobalsMutex.Unlock()
|
|
return *processedGlobals
|
|
}
|
|
processedGlobalsMutex.Unlock()
|
|
}
|
|
|
|
result := ProcessedDefines{
|
|
IdentifierDefines: make(map[string]DefineData),
|
|
DotDefines: make(map[string][]DotDefine),
|
|
}
|
|
|
|
// Mark these property accesses as free of side effects. That means they can
|
|
// be removed if their result is unused. We can't just remove all unused
|
|
// property accesses since property accesses can have side effects. For
|
|
// example, the property access "a.b.c" has the side effect of throwing an
|
|
// exception if "a.b" is undefined.
|
|
for _, parts := range knownGlobals {
|
|
tail := parts[len(parts)-1]
|
|
if len(parts) == 1 {
|
|
result.IdentifierDefines[tail] = DefineData{CanBeRemovedIfUnused: true}
|
|
} else {
|
|
result.DotDefines[tail] = append(result.DotDefines[tail], DotDefine{Parts: parts, Data: DefineData{CanBeRemovedIfUnused: true}})
|
|
}
|
|
}
|
|
|
|
// Swap in certain literal values because those can be constant folded
|
|
result.IdentifierDefines["undefined"] = DefineData{
|
|
DefineFunc: func(DefineArgs) js_ast.E { return js_ast.EUndefinedShared },
|
|
}
|
|
result.IdentifierDefines["NaN"] = DefineData{
|
|
DefineFunc: func(DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.NaN()} },
|
|
}
|
|
result.IdentifierDefines["Infinity"] = DefineData{
|
|
DefineFunc: func(DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.Inf(1)} },
|
|
}
|
|
|
|
// Then copy the user-specified defines in afterwards, which will overwrite
|
|
// any known globals above.
|
|
for key, data := range userDefines {
|
|
parts := strings.Split(key, ".")
|
|
|
|
// Identifier defines are special-cased
|
|
if len(parts) == 1 {
|
|
result.IdentifierDefines[key] = mergeDefineData(result.IdentifierDefines[key], data)
|
|
continue
|
|
}
|
|
|
|
tail := parts[len(parts)-1]
|
|
dotDefines := result.DotDefines[tail]
|
|
found := false
|
|
|
|
// Try to merge with existing dot defines first
|
|
for i, define := range dotDefines {
|
|
if arePartsEqual(parts, define.Parts) {
|
|
define := &dotDefines[i]
|
|
define.Data = mergeDefineData(define.Data, data)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
dotDefines = append(dotDefines, DotDefine{Parts: parts, Data: data})
|
|
}
|
|
result.DotDefines[tail] = dotDefines
|
|
}
|
|
|
|
// Potentially cache the result for next time
|
|
if !hasUserDefines {
|
|
processedGlobalsMutex.Lock()
|
|
defer processedGlobalsMutex.Unlock()
|
|
if processedGlobals == nil {
|
|
processedGlobals = &result
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func arePartsEqual(a []string, b []string) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i := range a {
|
|
if a[i] != b[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|