gowebbuild/vendor/github.com/evanw/esbuild/internal/config/globals.go

970 lines
22 KiB
Go
Raw Normal View History

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
}