Skip to content

Commit

Permalink
[NTI] Migrate InlineProperties and test to NTI.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=155172489
  • Loading branch information
shicks authored and brad4d committed May 5, 2017
1 parent 7dd6165 commit 7056e8e
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 162 deletions.
1 change: 1 addition & 0 deletions src/com/google/javascript/jscomp/GlobalTypeInfo.java
Expand Up @@ -500,6 +500,7 @@ public void process(Node externs, Node root) {
// Type the global THIS as a loose object
globalThisType = this.commonTypes.getTopObject().withLoose();
}
commonTypes.setGlobalThis(globalThisType);
this.globalScope.setDeclaredType(
(new FunctionTypeBuilder(this.commonTypes)).
addReceiverType(globalThisType).buildDeclaration());
Expand Down
152 changes: 73 additions & 79 deletions src/com/google/javascript/jscomp/InlineProperties.java
Expand Up @@ -16,32 +16,30 @@
package com.google.javascript.jscomp;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableList;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.FunctionTypeI;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.ObjectTypeI;
import com.google.javascript.rhino.TypeI;
import com.google.javascript.rhino.TypeIRegistry;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.ObjectType;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* InlineProperties attempts to find references to properties that are known to
* be constants and inline the known value.
* InlineProperties attempts to find references to properties that are known to be constants and
* inline the known value.
*
* This pass relies on type information to find these property references and
* properties are assumed to be constant if either:
* - the property is assigned unconditionally in the instance constructor
* - the property is assigned unconditionally to the type's prototype
* <p>This pass relies on type information to find these property references and properties are
* assumed to be constant if they are assigned exactly once, unconditionally, in either of the
* following contexts: (1) statically on a constructor, or (2) on a class's prototype.
*
* The current implementation only inlines immutable values (as defined by
* <p>The current implementation only inlines immutable values (as defined by
* NodeUtil.isImmutableValue).
*
* @author johnlenz@google.com (John Lenz)
Expand All @@ -50,21 +48,20 @@ final class InlineProperties implements CompilerPass {

private final AbstractCompiler compiler;

static class PropertyInfo {
PropertyInfo(JSType type, Node value) {
private static class PropertyInfo {
PropertyInfo(TypeI type, Node value) {
this.type = type;
this.value = value;
}
final JSType type;
final TypeI type;
final Node value;
}

private static final PropertyInfo INVALIDATED = new PropertyInfo(
null, null);
private static final PropertyInfo INVALIDATED = new PropertyInfo(null, null);

private final Map<String, PropertyInfo> props = new HashMap<>();

private Set<TypeI> invalidatingTypes;
private final Set<TypeI> invalidatingTypes = new HashSet<>();

InlineProperties(AbstractCompiler compiler) {
this.compiler = compiler;
Expand All @@ -77,21 +74,22 @@ static class PropertyInfo {
// we should move it to a common location.
private void buildInvalidatingTypeSet() {
TypeIRegistry registry = compiler.getTypeIRegistry();
invalidatingTypes = new HashSet<>(ImmutableSet.<TypeI>of(
(JSType) registry.getNativeType(JSTypeNative.ALL_TYPE),
(JSType) registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE),
(JSType) registry.getNativeType(JSTypeNative.NO_TYPE),
(JSType) registry.getNativeType(JSTypeNative.NULL_TYPE),
(JSType) registry.getNativeType(JSTypeNative.VOID_TYPE),
(JSType) registry.getNativeType(JSTypeNative.FUNCTION_FUNCTION_TYPE),
(JSType) registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
(JSType) registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE),
(JSType) registry.getNativeType(JSTypeNative.GLOBAL_THIS),
(JSType) registry.getNativeType(JSTypeNative.OBJECT_TYPE),
(JSType) registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE),
(JSType) registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE),
(JSType) registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE),
(JSType) registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)));
invalidatingTypes.addAll(
ImmutableList.of(
registry.getNativeType(JSTypeNative.ALL_TYPE),
registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE),
registry.getNativeType(JSTypeNative.NO_TYPE),
registry.getNativeType(JSTypeNative.NULL_TYPE),
registry.getNativeType(JSTypeNative.VOID_TYPE),
registry.getNativeType(JSTypeNative.FUNCTION_FUNCTION_TYPE),
registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE),
registry.getNativeType(JSTypeNative.GLOBAL_THIS),
registry.getNativeType(JSTypeNative.OBJECT_TYPE),
registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE),
registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE),
registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE),
registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)));

for (TypeMismatch mis : compiler.getTypeMismatches()) {
addInvalidatingType(mis.typeA);
Expand All @@ -106,9 +104,7 @@ private void invalidateExternProperties() {
}
}

/**
* Invalidates the given type, so that no properties on it will be renamed.
*/
/** Invalidates the given type, so that no properties on it will be inlined. */
private void addInvalidatingType(TypeI type) {
type = type.restrictByNotNullOrUndefined();
if (type.isUnionType()) {
Expand All @@ -118,45 +114,43 @@ private void addInvalidatingType(TypeI type) {
}

invalidatingTypes.add(type);
ObjectType objType = (ObjectType) type.toMaybeObjectType();
ObjectTypeI objType = type.toMaybeObjectType();
if (objType != null && objType.isInstanceType()) {
invalidatingTypes.add(objType.getImplicitPrototype());
invalidatingTypes.add(objType.getPrototypeObject());
}
}

/** Returns true if properties on this type should not be renamed. */
private boolean isInvalidatingType(JSType type) {
/** Returns true if properties on this type should not be inlined. */
private boolean isInvalidatingType(TypeI type) {
if (type.isUnionType()) {
type = type.restrictByNotNullOrUndefined();
if (type.isUnionType()) {
for (JSType alt : type.toMaybeUnionType().getAlternatesWithoutStructuralTyping()) {
for (TypeI alt : type.getUnionMembers()) {
if (isInvalidatingType(alt)) {
return true;
}
}
return false;
}
}
ObjectType objType = ObjectType.cast(type);
ObjectTypeI objType = type.toMaybeObjectType();
return objType == null
|| invalidatingTypes.contains(objType)
|| !objType.hasReferenceName()
|| objType.isUnknownObject()
|| objType.isUnknownType()
|| objType.isEmptyType() /* unresolved types */
|| objType.isEnumType()
|| objType.autoboxesTo() != null;
|| objType.isBottom()
|| objType.isEnumObject()
|| objType.isBoxableScalar()
|| !(type.isConstructor() || objType.isInstanceType());
}

/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
/** This method gets the JSType from the Node argument and verifies that it is present. */
private TypeI getTypeI(Node n) {
TypeI type = n.getTypeI();
if (type == null) {
return compiler.getTypeIRegistry().getNativeType(JSTypeNative.UNKNOWN_TYPE);
} else {
return jsType;
return type;
}
}

Expand All @@ -176,7 +170,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isGetProp()) {
propName = n.getLastChild().getString();
if (parent.isAssign()) {
invalidatingPropRef = !maybeCandidateDefinition(t, n, parent);
invalidatingPropRef = !isValidCandidateDefinition(t, n, parent);
} else if (NodeUtil.isLValue(n)) {
// Other LValue references invalidate
invalidatingPropRef = true;
Expand All @@ -202,13 +196,9 @@ public void visit(NodeTraversal t, Node n, Node parent) {
}
}

/**
* @return Whether this is a valid definition for a candidate property.
*/
private boolean maybeCandidateDefinition(
NodeTraversal t, Node n, Node parent) {
/** @return Whether this is a valid definition for a candidate property. */
private boolean isValidCandidateDefinition(NodeTraversal t, Node n, Node parent) {
Preconditions.checkState(n.isGetProp() && parent.isAssign(), n);
boolean isCandidate = false;
Node src = n.getFirstChild();
String propName = n.getLastChild().getString();

Expand All @@ -218,32 +208,32 @@ private boolean maybeCandidateDefinition(
// this.foo = 1;
if (inConstructor(t)) {
// This maybe a valid assignment.
isCandidate = maybeStoreCandidateValue(
getJSType(src), propName, value);
return maybeStoreCandidateValue(getTypeI(src), propName, value);
}
} else if (t.inGlobalHoistScope()
&& src.isGetProp()
&& src.getLastChild().getString().equals("prototype")) {
// This is a prototype assignment like:
// x.prototype.foo = 1;
JSType instanceType = maybeGetInstanceTypeFromPrototypeRef(src);
TypeI instanceType = maybeGetInstanceTypeFromPrototypeRef(src);
if (instanceType != null) {
isCandidate = maybeStoreCandidateValue(
instanceType, propName, value);
return maybeStoreCandidateValue(instanceType, propName, value);
}
} else if (t.inGlobalHoistScope()) {
JSType targetType = getJSType(src);
// This is a static assignment like:
// x.foo = 1;
TypeI targetType = getTypeI(src);
if (targetType != null && targetType.isConstructor()) {
isCandidate = maybeStoreCandidateValue(targetType, propName, value);
return maybeStoreCandidateValue(targetType, propName, value);
}
}
return isCandidate;
return false;
}

private JSType maybeGetInstanceTypeFromPrototypeRef(Node src) {
JSType ownerType = getJSType(src.getFirstChild());
if (ownerType.isFunctionType() && ownerType.isConstructor()) {
FunctionType functionType = ((FunctionType) ownerType);
private TypeI maybeGetInstanceTypeFromPrototypeRef(Node src) {
TypeI ownerType = getTypeI(src.getFirstChild());
if (ownerType.isConstructor()) {
FunctionTypeI functionType = ownerType.toMaybeFunctionType();
return functionType.getInstanceType();
}
return null;
Expand All @@ -253,8 +243,12 @@ private void invalidateProperty(String propName) {
props.put(propName, INVALIDATED);
}

private boolean maybeStoreCandidateValue(
JSType type, String propName, Node value) {
/**
* Adds the candidate property to the map if it meets all constness and immutability criteria,
* and is not already present in the map. If the property was already present, it is
* invalidated. Returns true if the property was successfully added.
*/
private boolean maybeStoreCandidateValue(TypeI type, String propName, Node value) {
Preconditions.checkNotNull(value);
if (!props.containsKey(propName)
&& !isInvalidatingType(type)
Expand Down Expand Up @@ -296,19 +290,19 @@ && isMatchingType(target, info.type)) {
}
}

private boolean isMatchingType(Node n, JSType src) {
private boolean isMatchingType(Node n, TypeI src) {
src = src.restrictByNotNullOrUndefined();
JSType dest = getJSType(n).restrictByNotNullOrUndefined();
TypeI dest = getTypeI(n).restrictByNotNullOrUndefined();
if (!isInvalidatingType(dest)) {
if (dest.isConstructor()) {
if (dest.isConstructor() || src.isConstructor()) {
// Don't inline constructor properties referenced from
// subclass constructor references. This would be appropriate
// for ES6 class with Class-side inheritence but not
// traditional Closure classes from which subclass constructor
// don't inherit the super-classes constructor properties.
return dest.isEquivalentTo(src);
return dest.equals(src);
} else {
return dest.isSubtype(src);
return dest.isSubtypeOf(src);
}
}
return false;
Expand Down
5 changes: 4 additions & 1 deletion src/com/google/javascript/jscomp/newtypes/JSType.java
Expand Up @@ -1970,7 +1970,10 @@ public final ObjectTypeI normalizeObjectForCheckAccessControls() {

@Override
public final boolean isBoxableScalar() {
return isNumber() || isString() || isBoolean();
return isNumber()
|| isString()
|| isBoolean()
|| (isEnumElement() && getEnumeratedTypeOfEnumElement().isBoxableScalar());
}

@Override
Expand Down
15 changes: 15 additions & 0 deletions src/com/google/javascript/jscomp/newtypes/JSTypes.java
Expand Up @@ -123,6 +123,7 @@ public final class JSTypes {
private JSType stringOrString;
private JSType anyNumOrStr;

private JSType globalThis;
private JSType regexpInstance;
private RawNominalType arrayType;
private RawNominalType builtinObject;
Expand Down Expand Up @@ -359,6 +360,10 @@ public JSType getRegexpType() {
return regexpInstance != null ? regexpInstance : this.UNKNOWN;
}

public JSType getGlobalThis() {
return globalThis != null ? globalThis : UNKNOWN;
}

public JSType getNumberInstance() {
return numberInstance != null ? numberInstance : this.NUMBER;
}
Expand Down Expand Up @@ -424,9 +429,13 @@ public JSType getNativeType(JSTypeNative typeId) {
return getFunctionType().getPrototypeObject();
case FUNCTION_INSTANCE_TYPE:
return fromFunctionType(QMARK_FUNCTION);
case FUNCTION_FUNCTION_TYPE:
return builtinFunction.toJSType();
case OBJECT_PROTOTYPE:
case TOP_LEVEL_PROTOTYPE:
return getTopObject().getNominalTypeIfSingletonObj().getPrototypePropertyOfCtor();
case GLOBAL_THIS:
return getGlobalThis();
default:
throw new RuntimeException("Native type " + typeId.name() + " not found");
}
Expand Down Expand Up @@ -477,6 +486,12 @@ public void setRegexpInstance(JSType regexpInstance) {
this.regexpInstance = regexpInstance;
}

public void setGlobalThis(JSType globalThis) {
Preconditions.checkState(this.globalThis == null,
"Tried to reassign globalThis from %s to %s", this.globalThis, globalThis);
this.globalThis = globalThis;
}

public void setNumberInstance(JSType t) {
Preconditions.checkState(numberInstance == null);
Preconditions.checkNotNull(t);
Expand Down
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/newtypes/ObjectType.java
Expand Up @@ -337,7 +337,7 @@ private ObjectType withPropertyHelper(QualifiedName qname, JSType type,
}
}

if (type == null && declType == null) {
if (type == null) {
newProps = newProps.without(pname);
} else if (!type.equals(declType) && !type.equals(inferred)) {
if (isDeclared && declType == null) {
Expand Down
4 changes: 4 additions & 0 deletions src/com/google/javascript/rhino/ObjectTypeI.java
Expand Up @@ -110,6 +110,10 @@ public interface ObjectTypeI extends TypeI {
*/
ObjectTypeI normalizeObjectForCheckAccessControls();

/**
* Returns true if this object is an anonymous object type (i.e. the builtin Object type, or an
* object literal). Everything else has a named reference type and returns false.
*/
boolean isUnknownObject();

/**
Expand Down

0 comments on commit 7056e8e

Please sign in to comment.