com.google.javascript.jscomp.GenerateExports Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.frontend.js.minifier
Show all versions of com.liferay.frontend.js.minifier
Liferay Frontend JS Minifier
/*
* Copyright 2008 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.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.TypeI;
import com.google.javascript.rhino.jstype.JSTypeNative;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Generates goog.exportSymbol/goog.exportProperty for the @export annotation.
*
*/
class GenerateExports implements CompilerPass {
private static final String PROTOTYPE_PROPERTY = "prototype";
private final AbstractCompiler compiler;
private final String exportSymbolFunction;
private final String exportPropertyFunction;
private final boolean allowNonGlobalExports;
private final Set exportedVariables = new HashSet<>();
/**
* Creates a new generate exports compiler pass.
* @param compiler JS compiler.
* @param exportSymbolFunction function used for exporting symbols.
* @param exportPropertyFunction function used for exporting property names.
*/
GenerateExports(
AbstractCompiler compiler,
boolean allowNonGlobalExports,
String exportSymbolFunction,
String exportPropertyFunction) {
checkNotNull(compiler);
checkNotNull(exportSymbolFunction);
checkNotNull(exportPropertyFunction);
this.compiler = compiler;
this.allowNonGlobalExports = allowNonGlobalExports;
this.exportSymbolFunction = exportSymbolFunction;
this.exportPropertyFunction = exportPropertyFunction;
}
Set getExportedVariableNames() {
return exportedVariables;
}
@Override
public void process(Node externs, Node root) {
FindExportableNodes findExportableNodes = new FindExportableNodes(
compiler, allowNonGlobalExports);
NodeTraversal.traverseEs6(compiler, root, findExportableNodes);
Map exports = findExportableNodes.getExports();
Set localExports = findExportableNodes.getLocalExports();
for (Map.Entry entry : exports.entrySet()) {
String export = entry.getKey();
Node context = entry.getValue();
addExportMethod(exports, export, context);
}
for (String export : localExports) {
addExtern(export);
}
}
private void addExtern(String export) {
Node objectPrototype = NodeUtil.newQName(compiler, "Object.prototype");
TypeI objCtor = compiler.getTypeIRegistry().getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE);
objectPrototype.getFirstChild().setTypeI(objCtor);
Node propstmt = IR.exprResult(IR.getprop(objectPrototype, IR.string(export)));
propstmt.useSourceInfoFromForTree(getSynthesizedExternsRoot());
propstmt.setOriginalName(export);
getSynthesizedExternsRoot().addChildToBack(propstmt);
compiler.reportChangeToEnclosingScope(propstmt);
}
private void recordExportSymbol(String qname) {
int dot = qname.indexOf('.');
if (dot == -1) {
exportedVariables.add(qname);
} else {
exportedVariables.add(qname.substring(0, dot));
}
}
private void addExportMethod(Map exports, String export, Node context) {
// Emit the proper CALL expression.
// This is an optimization to avoid exporting everything as a symbol
// because exporting a property is significantly simpler/faster.
// Only export the property if the parent is being exported or
// if the parent is "prototype" and the grandparent is being exported.
String parent = null;
String grandparent = null;
Node node = context.getFirstChild();
if (node.isGetProp()) {
Node parentNode = node.getFirstChild();
parent = parentNode.getQualifiedName();
if (parentNode.isGetProp()
&& parentNode.getLastChild().getString().equals(PROTOTYPE_PROPERTY)) {
grandparent = parentNode.getFirstChild().getQualifiedName();
}
} else if (node.getParent().isMemberFunctionDef()) {
Node classNode = node.getGrandparent().getParent();
parent = NodeUtil.getName(classNode);
parent += node.getParent().isStaticMember() ? "" : ".prototype";
export = parent + "." + export;
}
boolean useExportSymbol = true;
if (grandparent != null) {
// grandparent is only set for properties exported off a prototype obj.
useExportSymbol = false;
} else if (parent != null && exports.containsKey(parent)) {
useExportSymbol = false;
} else if (node.getParent().isMemberFunctionDef()) {
useExportSymbol = false;
}
Node call;
if (useExportSymbol) {
recordExportSymbol(export);
// exportSymbol(publicPath, object);
call = IR.call(
NodeUtil.newQName(
compiler, exportSymbolFunction,
context, export),
IR.string(export),
NodeUtil.newQName(
compiler, export,
context, export));
} else {
// exportProperty(object, publicName, symbol);
String property = getPropertyName(node);
call = IR.call(
NodeUtil.newQName(
compiler, exportPropertyFunction,
context, exportPropertyFunction),
NodeUtil.newQName(
compiler, parent,
context, exportPropertyFunction),
IR.string(property),
NodeUtil.newQName(
compiler, export,
context, exportPropertyFunction));
}
Node expression = IR.exprResult(call).useSourceInfoIfMissingFromForTree(node);
annotate(expression);
addStatement(context, expression);
}
private void addStatement(Node context, Node stmt) {
CodingConvention convention = compiler.getCodingConvention();
Node n = context;
Node exprRoot = n;
while (!NodeUtil.isStatementBlock(exprRoot.getParent())) {
exprRoot = exprRoot.getParent();
}
// It's important that any class-building calls (goog.inherits)
// come right after the class definition, so move the export after that.
while (true) {
Node next = exprRoot.getNext();
if (next != null
&& NodeUtil.isExprCall(next)
&& convention.getClassesDefinedByCall(next.getFirstChild()) != null) {
exprRoot = next;
} else {
break;
}
}
Node block = exprRoot.getParent();
block.addChildAfter(stmt, exprRoot);
compiler.reportChangeToEnclosingScope(stmt);
}
private void annotate(Node node) {
NodeTraversal.traverseEs6(
compiler, node, new PrepareAst.PrepareAnnotations());
}
/**
* Assumes the node type is correct and returns the property name
* (not fully qualified).
* @param node node
* @return property name.
*/
private static String getPropertyName(Node node) {
checkArgument(node.isGetProp() || node.getParent().isMemberFunctionDef());
if (node.isGetProp()) {
return node.getLastChild().getString();
} else {
return node.getParent().getString();
}
}
/** Lazily create a "new" externs root for undeclared variables. */
private Node getSynthesizedExternsRoot() {
return compiler.getSynthesizedExternsInput().getAstRoot(compiler);
}
}