com.google.javascript.jscomp.JSChunk Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of closure-compiler-unshaded Show documentation
Show all versions of closure-compiler-unshaded Show documentation
Closure Compiler is a JavaScript optimizing compiler. It parses your
JavaScript, analyzes it, removes dead code and rewrites and minimizes
what's left. It also checks syntax, variable references, and types, and
warns about common JavaScript pitfalls. It is used in many of Google's
JavaScript apps, including Gmail, Google Web Search, Google Maps, and
Google Docs.
/*
* Copyright 2005 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 static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.deps.DependencyInfo;
import com.google.javascript.jscomp.deps.SortedDependencies;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A JavaScript chunk has a unique name, consists of a list of compiler inputs, and can depend on
* other chunks.
*/
public final class JSChunk extends DependencyInfo.Base implements Serializable {
// The name of the artificial chunk containing all strong sources when there is no chunk spec.
// If there is a chunk spec, strong sources go in their respective chunks, and this chunk does
// not exist.
public static final String STRONG_CHUNK_NAME = "$strong$";
// The name of the artificial chunk containing all weak sources. Regardless of the chunk spec,
// weak sources are moved into this chunk, which is made to depend on every other chunk. This is
// necessary so that removing weak sources (as an optimization) does not accidentally remove
// namespace declarations whose existence strong sources rely upon.
public static final String WEAK_CHUNK_NAME = "$weak$";
private static final long serialVersionUID = 1;
/** Chunk name */
private final String name;
/** Source code inputs */
// non-final for deserilaization
// CompilerInputs must be explicitly added to the JSChunk again after deserialization
// A map keyed by the {@code CompilerInput.getName()} to speed up getByName and removeByName.
private transient Map inputs = new LinkedHashMap<>();
/** Chunks that this chunk depends on */
private final List deps = new ArrayList<>();
/** The length of the longest path starting from this chunk */
private int depth;
/** The position of this chunk relative to all others in the AST. */
private int index;
/**
* Creates an instance.
*
* @param name A unique name for the chunk
*/
public JSChunk(String name) {
this.name = name;
// Depth and index will be set to their correct values by the JSChunkGraph into which they
// are placed.
this.depth = -1;
this.index = -1;
}
/** Gets the chunk name. */
@Override
public String getName() {
return name;
}
@Override
public ImmutableList getProvides() {
return ImmutableList.of(name);
}
@Override
public boolean getHasExternsAnnotation() {
return false;
}
@Override
public boolean getHasNoCompileAnnotation() {
return false;
}
@Override
public ImmutableList getRequires() {
ImmutableList.Builder builder = ImmutableList.builder();
for (JSChunk m : deps) {
builder.add(Require.compilerModule(m.getName()));
}
return builder.build();
}
@Override
public ImmutableList getTypeRequires() {
// TODO(blickly): Actually allow weak chunk deps
return ImmutableList.of();
}
@Override
public String getPathRelativeToClosureBase() {
throw new UnsupportedOperationException();
}
@Override
public ImmutableMap getLoadFlags() {
throw new UnsupportedOperationException();
}
@Override
public boolean isModule() {
// NOTE: The meaning of "module" has changed over time. A "JsChunk" is
// a collection of inputs that are loaded together. A "module" file,
// is a CommonJs module, ES6 module, goog.module or other file whose
// top level symbols are not in global scope.
throw new UnsupportedOperationException();
}
/** Adds a source file input to this chunk. */
public void add(SourceFile file) {
add(new CompilerInput(file));
}
/** Adds a source code input to this chunk. */
public void add(CompilerInput input) {
String inputName = input.getName();
checkArgument(
!inputs.containsKey(inputName), "%s already exist in chunk %s", inputName, this.getName());
inputs.put(inputName, input);
input.setModule(this);
}
/**
* Adds a source code input to this chunk. Call only if the input might already be associated with
* a chunk. Otherwise, use add(CompilerInput input).
*/
void addAndOverrideChunk(CompilerInput input) {
String inputName = input.getName();
checkArgument(
!inputs.containsKey(inputName), "%s already exist in chunk %s", inputName, this.getName());
inputs.put(inputName, input);
input.overrideModule(this);
}
/** Adds a dependency on another chunk. */
public void addDependency(JSChunk dep) {
checkNotNull(dep);
Preconditions.checkState(dep != this, "Cannot add dependency on self (%s)", this);
deps.add(dep);
}
/** Removes an input from this chunk. */
public void remove(CompilerInput input) {
input.setModule(null);
inputs.remove(input.getName());
}
/** Removes all of the inputs from this chunk. */
public void removeAll() {
for (CompilerInput input : inputs.values()) {
input.setModule(null);
}
inputs.clear();
}
/**
* Gets the list of chunks that this chunk depends on.
*
* @return A list that may be empty but not null
*/
public ImmutableList getDependencies() {
return ImmutableList.copyOf(deps);
}
/** Gets the names of the chunks that this chunk depends on, sorted alphabetically. */
List getSortedDependencyNames() {
List names = new ArrayList<>();
for (JSChunk chunk : getDependencies()) {
names.add(chunk.getName());
}
Collections.sort(names);
return names;
}
/**
* Returns the transitive closure of dependencies starting from the dependencies of this chunk.
*/
public Set getAllDependencies() {
// JSChunk uses identity semantics
Set allDeps = Sets.newIdentityHashSet();
allDeps.addAll(deps);
ArrayDeque stack = new ArrayDeque<>(deps);
while (!stack.isEmpty()) {
JSChunk chunk = stack.pop();
List chunkDeps = chunk.deps;
for (JSChunk dep : chunkDeps) {
if (allDeps.add(dep)) {
stack.push(dep);
}
}
}
return allDeps;
}
/** Returns this chunk and all of its dependencies in one list. */
public Set getThisAndAllDependencies() {
Set deps = getAllDependencies();
deps.add(this);
return deps;
}
/** Returns the number of source code inputs. */
public int getInputCount() {
return inputs.size();
}
/** Returns the first source code input. */
public CompilerInput getFirst() {
return inputs.values().iterator().next();
}
/**
* Gets this chunk's list of source code inputs.
*
* @return A list that may be empty but not null
*/
public ImmutableList getInputs() {
return ImmutableList.copyOf(inputs.values());
}
/** Returns the input with the given name or null if none. */
public CompilerInput getByName(String name) {
return inputs.get(name);
}
/**
* Removes any input with the given name. Returns whether any were removed.
*/
public boolean removeByName(String name) {
CompilerInput value = inputs.remove(name);
return value != null;
}
/**
* Returns whether this chunk is synthetic (i.e. one of the special strong or weak chunks created
* by the compiler.
*/
public boolean isSynthetic() {
return name.equals(STRONG_CHUNK_NAME) || name.equals(WEAK_CHUNK_NAME);
}
public boolean isWeak() {
return name.equals(WEAK_CHUNK_NAME);
}
/** Returns the chunk name (primarily for debugging). */
@Override
public String toString() {
return name;
}
/**
* Puts the JS files into a topologically sorted order by their dependencies.
*/
public void sortInputsByDeps(AbstractCompiler compiler) {
// Set the compiler, so that we can parse requires/provides and report
// errors properly.
for (CompilerInput input : inputs.values()) {
input.setCompiler(compiler);
}
// Sort the JSChunk in this order.
List sortedList =
new SortedDependencies(getInputs()).getSortedList();
inputs.clear();
for (CompilerInput input : sortedList) {
inputs.put(input.getName(), input);
}
}
/**
* @param dep the depth to set
*/
public void setDepth(int dep) {
checkArgument(dep >= 0, "invalid depth: %s", dep);
this.depth = dep;
}
/**
* @return the depth
*/
public int getDepth() {
return depth;
}
public void setIndex(int index) {
checkArgument(index >= 0, "Invalid chunk index: %s", index);
this.index = index;
}
public int getIndex() {
return index;
}
@GwtIncompatible("ObjectinputStream")
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
this.inputs = new LinkedHashMap<>();
}
}