All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.RewritePolyfills Maven / Gradle / Ivy

/*
 * Copyright 2015 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import com.google.common.annotations.VisibleForTesting;
import com.google.javascript.jscomp.PolyfillUsageFinder.Polyfill;
import com.google.javascript.jscomp.PolyfillUsageFinder.PolyfillUsage;
import com.google.javascript.jscomp.PolyfillUsageFinder.Polyfills;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.resources.ResourceLoader;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * Injects polyfill libraries to ensure that ES6+ library functions are available.
 *
 * 

Also runs if polyfill isolation is enabled, even if polyfill injection is disabled, in order * to prevent deletion of a required library function by dead code elimination. * *

TODO(b/120486392): consider merging this pass with {@link InjectRuntimeLibraries} and {@link * InjectTranspilationRuntimeLibraries}. */ public class RewritePolyfills implements HotSwapCompilerPass { static final DiagnosticType INSUFFICIENT_OUTPUT_VERSION_ERROR = DiagnosticType.disabled( "JSC_INSUFFICIENT_OUTPUT_VERSION", "Built-in ''{0}'' not supported in output version {1}"); private final AbstractCompiler compiler; private final Polyfills polyfills; private final boolean injectPolyfills; private final boolean isolatePolyfills; private Set libraries; /** * @param injectPolyfills if true, injects $jscomp.polyfill initializations into the first input. * if false, no polyfills are injected. * @param isolatePolyfills if true, adds externs for library functions used by {@link * IsolatePolyfills} to prevent their deletion. */ public RewritePolyfills( AbstractCompiler compiler, boolean injectPolyfills, boolean isolatePolyfills) { this( compiler, Polyfills.fromTable( ResourceLoader.loadTextResource(RewritePolyfills.class, "js/polyfills.txt")), injectPolyfills, isolatePolyfills); } @VisibleForTesting RewritePolyfills( AbstractCompiler compiler, Polyfills polyfills, boolean injectPolyfills, boolean isolatePolyfills) { this.compiler = compiler; this.polyfills = polyfills; this.injectPolyfills = injectPolyfills; this.isolatePolyfills = isolatePolyfills; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { if (this.isolatePolyfills) { // Polyfill isolation requires a pass to run near the end of optimizations. That pass may call // into a library method injected in this pass. Adding an externs declaration of that library // method prevents it from being dead-code-elimiated before polyfill isolation runs. Node jscompLookupMethodDecl = IR.var(IR.name("$jscomp$lookupPolyfilledValue")); compiler .getSynthesizedExternsInputAtEnd() .getAstRoot(compiler) .addChildToBack(jscompLookupMethodDecl); compiler.reportChangeToEnclosingScope(jscompLookupMethodDecl); } if (!this.injectPolyfills) { // Nothing left to do. Probably this pass only needed to run because --isolate_polyfills is // enabled but not --rewrite_polyfills. return; } this.libraries = new LinkedHashSet<>(); new PolyfillUsageFinder(compiler, polyfills).traverseExcludingGuarded(scriptRoot, this::inject); if (libraries.isEmpty()) { return; } Node lastNode = null; for (String library : libraries) { lastNode = compiler.ensureLibraryInjected(library, false); } if (lastNode != null) { Node parent = lastNode.getParent(); removeUnneededPolyfills(parent, lastNode.getNext()); compiler.reportChangeToEnclosingScope(parent); } } // Remove any $jscomp.polyfill calls whose 3rd parameter (the language version // that already contains the library) is the same or lower than languageOut. private void removeUnneededPolyfills(Node parent, Node runtimeEnd) { Node node = parent.getFirstChild(); while (node != null && node != runtimeEnd) { Node next = node.getNext(); if (NodeUtil.isExprCall(node)) { Node call = node.getFirstChild(); Node name = call.getFirstChild(); if (name.matchesQualifiedName("$jscomp.polyfill")) { final String nativeVersionStr = name.getNext().getNext().getNext().getString(); final FeatureSet outputFeatureSet = compiler.getOptions().getOutputFeatureSet(); if (outputFeatureSet.contains(FeatureSet.valueOf(nativeVersionStr))) { NodeUtil.removeChild(parent, node); NodeUtil.markFunctionsDeleted(node, compiler); } } } node = next; } } @Override public void process(Node externs, Node root) { hotSwapScript(root, null); } private void inject(PolyfillUsage polyfillUsage) { Polyfill polyfill = polyfillUsage.polyfill(); final FeatureSet outputFeatureSet = compiler.getOptions().getOutputFeatureSet(); final FeatureSet featuresRequiredByPolyfill = FeatureSet.valueOf(polyfill.polyfillVersion); if (polyfill.kind.equals(Polyfill.Kind.STATIC) && !outputFeatureSet.contains(featuresRequiredByPolyfill)) { compiler.report( JSError.make( polyfillUsage.node(), INSUFFICIENT_OUTPUT_VERSION_ERROR, polyfillUsage.name(), outputFeatureSet.version())); } // The question we want to ask here is: // "Does the target platform already have the symbol this polyfill provides?" // We approximate it by asking instead: // "Does the target platform support all of the features that existed in the language // version that introduced this symbol?" if (!outputFeatureSet.contains(FeatureSet.valueOf(polyfill.nativeVersion)) && !polyfill.library.isEmpty()) { libraries.add(polyfill.library); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy