Skip to content

Commit

Permalink
Support partial compilation/analysis of programs using JSCompiler.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=152923137
  • Loading branch information
mprobst authored and brad4d committed Apr 12, 2017
1 parent 8aedea7 commit 697e52a
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 26 deletions.
29 changes: 28 additions & 1 deletion src/com/google/javascript/jscomp/Compiler.java
Expand Up @@ -54,10 +54,12 @@
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.FileSystems;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -222,7 +224,7 @@ public SourceFile apply(String filename) {
private DefinitionUseSiteFinder defFinder = null;

// Types that have been forward declared
private final Set<String> forwardDeclaredTypes = new HashSet<>();
private Set<String> forwardDeclaredTypes = new HashSet<>();

// For use by the new type inference
private GlobalTypeInfo symbolTable;
Expand Down Expand Up @@ -363,6 +365,31 @@ public void initOptions(CompilerOptions options) {
options.useNonStrictWarningsGuard();
}

if (options.assumeForwardDeclaredForMissingTypes) {
this.forwardDeclaredTypes =
new AbstractSet<String>() {
@Override
public boolean contains(Object o) {
return true; // Report all types as forward declared types.
}

@Override
public boolean add(String e) {
return false;
}

@Override
public Iterator<String> iterator() {
return Collections.<String>emptySet().iterator();
}

@Override
public int size() {
return 0;
}
};
}

initWarningsGuard(options.getWarningsGuard());
}

Expand Down
10 changes: 10 additions & 0 deletions src/com/google/javascript/jscomp/CompilerOptions.java
Expand Up @@ -895,6 +895,16 @@ static enum OutputJs {
/** Runtime libraries to never inject. */
boolean preventLibraryInjection = false;

boolean assumeForwardDeclaredForMissingTypes = false;

/**
* If {@code true}, considers all missing types to be forward declared (useful for partial
* compilation).
*/
public void setAssumeForwardDeclaredForMissingTypes(
boolean assumeForwardDeclaredForMissingTypes) {
this.assumeForwardDeclaredForMissingTypes = assumeForwardDeclaredForMissingTypes;
}

//--------------------------------
// Output options
Expand Down
33 changes: 30 additions & 3 deletions src/com/google/javascript/jscomp/DiagnosticGroups.java
Expand Up @@ -46,9 +46,15 @@ public class DiagnosticGroups {
static final DiagnosticType UNUSED =
DiagnosticType.warning("JSC_UNUSED", "{0}");

public static final Set<String> wildcardExcludedGroups = ImmutableSet.of(
"reportUnknownTypes", "analyzerChecks", "oldReportUnknownTypes",
"newCheckTypes", "newCheckTypesCompatibility", "newCheckTypesExtraChecks");
public static final Set<String> wildcardExcludedGroups =
ImmutableSet.of(
"reportUnknownTypes",
"analyzerChecks",
"oldReportUnknownTypes",
"newCheckTypes",
"newCheckTypesCompatibility",
"newCheckTypesExtraChecks",
"missingSourcesWarnings");

public DiagnosticGroups() {}

Expand Down Expand Up @@ -128,6 +134,7 @@ public DiagnosticGroup forName(String name) {
+ "msgDescriptions, "
+ "newCheckTypes, "
+ "nonStandardJsDocs, "
+ "missingSourcesWarnings, "
+ "reportUnknownTypes, "
+ "suspiciousCode, "
+ "strictModuleDepCheck, "
Expand Down Expand Up @@ -482,6 +489,26 @@ public DiagnosticGroup forName(String name) {
DiagnosticGroups.registerGroup("missingRequire",
CheckRequiresForConstructors.MISSING_REQUIRE_WARNING);

/**
* A set of diagnostics expected when parsing and type checking partial programs. Useful for clutz
* (tool that extracts TypeScript definitions from JS code).
*/
public static final DiagnosticGroup MISSING_SOURCES_WARNINGS =
DiagnosticGroups.registerGroup(
"missingSourcesWarnings",
REPORT_UNKNOWN_TYPES,
UNDEFINED_NAMES,
UNDEFINED_VARIABLES,
MISSING_PROVIDE,
DiagnosticGroup.forType(FunctionTypeBuilder.RESOLVED_TAG_EMPTY),
DiagnosticGroup.forType(ProcessClosurePrimitives.MISSING_PROVIDE_ERROR),
MISSING_PROPERTIES,
// triggered by typedefs with missing types
DUPLICATE_VARS,
// caused by a define depending on another define that's missing
DiagnosticGroup.forType(ProcessDefines.INVALID_DEFINE_INIT_ERROR),
DiagnosticGroup.forType(Es6ExternsCheck.MISSING_ES6_EXTERNS));

public static final DiagnosticGroup STRICT_MISSING_REQUIRE =
DiagnosticGroups.registerGroup("strictMissingRequire",
CheckRequiresForConstructors.MISSING_REQUIRE_WARNING,
Expand Down
3 changes: 3 additions & 0 deletions src/com/google/javascript/rhino/jstype/JSType.java
Expand Up @@ -689,6 +689,9 @@ boolean checkEquivalenceHelper(final JSType that, EquivalenceMethod eqMethod,
if (this == that) {
return true;
}
if (this.isNoResolvedType() && that.isNoResolvedType()) {
return true;
}

boolean thisUnknown = isUnknownType();
boolean thatUnknown = that.isUnknownType();
Expand Down
80 changes: 62 additions & 18 deletions src/com/google/javascript/rhino/jstype/JSTypeRegistry.java
Expand Up @@ -1142,6 +1142,21 @@ public FunctionType getNativeFunctionType(JSTypeNative typeId) {
*/
public JSType getType(StaticTypedScope<JSType> scope, String jsTypeName,
String sourceName, int lineno, int charno) {
return getType(scope, jsTypeName, sourceName, lineno, charno, true);
}

/**
* @param recordUnresolvedTypes record unresolved named types and resolve
* them later. Set to false if types should be ignored for backwards
* compatibility (i.e. previously unparsed template type args).
*/
private JSType getType(
StaticTypedScope<JSType> scope,
String jsTypeName,
String sourceName,
int lineno,
int charno,
boolean recordUnresolvedTypes) {
switch (jsTypeName) {
case "boolean":
return getNativeType(JSTypeNative.BOOLEAN_TYPE);
Expand Down Expand Up @@ -1172,7 +1187,9 @@ public JSType getType(StaticTypedScope<JSType> scope, String jsTypeName,
// TODO(user): Each instance should support named type creation using
// interning.
NamedType namedType = createNamedType(jsTypeName, sourceName, lineno, charno);
unresolvedNamedTypes.put(scope, namedType);
if (recordUnresolvedTypes) {
unresolvedNamedTypes.put(scope, namedType);
}
type = namedType;
}
return type;
Expand Down Expand Up @@ -1687,19 +1704,19 @@ public JSType createTypeFromCommentNode(Node n) {
*/
public JSType createTypeFromCommentNode(
Node n, String sourceName, StaticTypedScope<? extends TypeI> scope) {
return createFromTypeNodesInternal(n, sourceName, (StaticTypedScope<JSType>) scope);
return createFromTypeNodesInternal(n, sourceName, (StaticTypedScope<JSType>) scope, true);
}

private JSType createFromTypeNodesInternal(Node n, String sourceName,
StaticTypedScope<JSType> scope) {
StaticTypedScope<JSType> scope, boolean recordUnresolvedTypes) {
switch (n.getToken()) {
case LC: // Record type.
return createRecordTypeFromNodes(
n.getFirstChild(), sourceName, scope);

case BANG: // Not nullable
return createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope)
n.getFirstChild(), sourceName, scope, recordUnresolvedTypes)
.restrictByNotNullOrUndefined();

case QMARK: // Nullable or unknown
Expand All @@ -1709,17 +1726,17 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
}
return createNullableType(
createFromTypeNodesInternal(
firstChild, sourceName, scope));
firstChild, sourceName, scope, recordUnresolvedTypes));

case EQUALS: // Optional
return createOptionalType(
createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope));
n.getFirstChild(), sourceName, scope, recordUnresolvedTypes));

case ELLIPSIS: // Var args
return createOptionalType(
createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope));
n.getFirstChild(), sourceName, scope, recordUnresolvedTypes));

case STAR: // The AllType
return getNativeType(ALL_TYPE);
Expand All @@ -1729,7 +1746,7 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
builder.addAlternate(
createFromTypeNodesInternal(child, sourceName, scope));
createFromTypeNodesInternal(child, sourceName, scope, recordUnresolvedTypes));
}
return builder.build();

Expand All @@ -1743,12 +1760,21 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
// TODO(martinprobst): The new type syntax resolution should be separate.
// Remove the NAME case then.
case NAME:
JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno());
JSType namedType =
getType(
scope,
n.getString(),
sourceName,
n.getLineno(),
n.getCharno(),
recordUnresolvedTypes);
if ((namedType instanceof ObjectType)
&& !(namedType instanceof NamespaceType)
&& !(nonNullableTypeNames.contains(n.getString()))) {
Node typeList = n.getFirstChild();
if (!namedType.isUnknownType() && typeList != null) {
boolean isUnknownForwardDeclared =
namedType.isUnknownType() && isForwardDeclaredType(n.getString());
if ((!namedType.isUnknownType() || isUnknownForwardDeclared) && typeList != null) {
// Templatized types.
ImmutableList.Builder<JSType> templateTypes = ImmutableList.builder();

Expand All @@ -1758,7 +1784,11 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
templateTypes.add(getNativeType(UNKNOWN_TYPE));
}

int nAllowedTypes = namedType.getTemplateTypeMap().numUnfilledTemplateKeys();
int nAllowedTypes =
isUnknownForwardDeclared
? Integer.MAX_VALUE
: namedType.getTemplateTypeMap().numUnfilledTemplateKeys();
boolean recordTemplateArgs = recordUnresolvedTypes && !isUnknownForwardDeclared;
int templateNodeIndex = 0;
for (Node templateNode : typeList.children()) {
// Don't parse more templatized type nodes than the type can
Expand All @@ -1777,9 +1807,23 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
sourceName, templateNode.getLineno(), templateNode.getCharno());
break;
}
templateTypes.add(createFromTypeNodesInternal(templateNode, sourceName, scope));
templateTypes.add(
createFromTypeNodesInternal(templateNode, sourceName, scope, recordTemplateArgs));
}
if (isUnknownForwardDeclared) {
// For backwards compatibility, construct a TemplatizedType but "hide" the template
// arguments from further resolution.
namedType =
new NamedType(
this,
n.getString(),
sourceName,
n.getLineno(),
n.getCharno(),
templateTypes.build());
} else {
namedType = createTemplatizedType((ObjectType) namedType, templateTypes.build());
}
namedType = createTemplatizedType((ObjectType) namedType, templateTypes.build());
Preconditions.checkNotNull(namedType);
}
return createDefaultObjectUnion(namedType);
Expand All @@ -1795,7 +1839,7 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
Node contextNode = current.getFirstChild();

JSType candidateThisType = createFromTypeNodesInternal(
contextNode, sourceName, scope);
contextNode, sourceName, scope, recordUnresolvedTypes);

// Allow null/undefined 'this' types to indicate that
// the function is not called in a deliberate context,
Expand Down Expand Up @@ -1832,11 +1876,11 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
} else {
paramBuilder.addVarArgs(
createFromTypeNodesInternal(
arg.getFirstChild(), sourceName, scope));
arg.getFirstChild(), sourceName, scope, recordUnresolvedTypes));
}
} else {
JSType type = createFromTypeNodesInternal(
arg, sourceName, scope);
arg, sourceName, scope, recordUnresolvedTypes);
if (arg.getToken() == Token.EQUALS) {
boolean addSuccess = paramBuilder.addOptionalParams(type);
if (!addSuccess) {
Expand All @@ -1854,7 +1898,7 @@ private JSType createFromTypeNodesInternal(Node n, String sourceName,
}

JSType returnType =
createFromTypeNodesInternal(current, sourceName, scope);
createFromTypeNodesInternal(current, sourceName, scope, recordUnresolvedTypes);

return new FunctionBuilder(this)
.withParamsNode(paramBuilder.build())
Expand Down Expand Up @@ -1909,7 +1953,7 @@ private JSType createRecordTypeFromNodes(Node n, String sourceName,
if (hasType) {
// We have a declared type.
fieldType = createFromTypeNodesInternal(
fieldTypeNode.getLastChild(), sourceName, scope);
fieldTypeNode.getLastChild(), sourceName, scope, true);
} else {
// Otherwise, the type is UNKNOWN.
fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
Expand Down
30 changes: 26 additions & 4 deletions src/com/google/javascript/rhino/jstype/NamedType.java
Expand Up @@ -41,10 +41,12 @@

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

/**
* A {@code NamedType} is a named reference to some other type. This provides
Expand Down Expand Up @@ -96,6 +98,13 @@ public class NamedType extends ProxyObjectType {
*/
private List<PropertyContinuation> propertyContinuations = null;

/**
* Template types defined on a named, not yet resolved type, or {@code null} if none. These are
* ignored during resolution, for backwards compatibility with existing usage.
* This field is not used for JSCompiler's type checking; it is only needed by Clutz.
*/
@Nullable private ImmutableList<JSType> templateTypes;

/**
* Create a named type based on the reference.
*/
Expand All @@ -110,6 +119,22 @@ public class NamedType extends ProxyObjectType {
this.charno = charno;
}

NamedType(
JSTypeRegistry registry,
String reference,
String sourceName,
int lineno,
int charno,
ImmutableList<JSType> templateTypes) {
this(registry, reference, sourceName, lineno, charno);
this.templateTypes = templateTypes;
}

@Override
public ImmutableList<JSType> getTemplateTypes() {
return templateTypes;
}

@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
Expand Down Expand Up @@ -336,10 +361,7 @@ private void handleUnresolvedType(
if (!isForwardDeclared) {
warning(t, "Bad type annotation. Unknown type " + reference);
} else {
setReferencedType(
registry.getNativeObjectType(
JSTypeNative.NO_RESOLVED_TYPE));

setReferencedType(new NoResolvedType(registry, getReferenceName(), getTemplateTypes()));
if (validator != null) {
validator.apply(getReferencedType());
}
Expand Down

0 comments on commit 697e52a

Please sign in to comment.