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 // Type the global THIS as a loose object
globalThisType = this.commonTypes.getTopObject().withLoose(); globalThisType = this.commonTypes.getTopObject().withLoose();
} }
commonTypes.setGlobalThis(globalThisType);
this.globalScope.setDeclaredType( this.globalScope.setDeclaredType(
(new FunctionTypeBuilder(this.commonTypes)). (new FunctionTypeBuilder(this.commonTypes)).
addReceiverType(globalThisType).buildDeclaration()); 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; package com.google.javascript.jscomp;


import com.google.common.base.Preconditions; 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.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.FunctionTypeI;
import com.google.javascript.rhino.IR; import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.ObjectTypeI;
import com.google.javascript.rhino.TypeI; import com.google.javascript.rhino.TypeI;
import com.google.javascript.rhino.TypeIRegistry; 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.JSTypeNative;
import com.google.javascript.rhino.jstype.ObjectType;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;


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


private final AbstractCompiler compiler; private final AbstractCompiler compiler;


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


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


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


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


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


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


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


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


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


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


Expand All @@ -176,7 +170,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isGetProp()) { if (n.isGetProp()) {
propName = n.getLastChild().getString(); propName = n.getLastChild().getString();
if (parent.isAssign()) { if (parent.isAssign()) {
invalidatingPropRef = !maybeCandidateDefinition(t, n, parent); invalidatingPropRef = !isValidCandidateDefinition(t, n, parent);
} else if (NodeUtil.isLValue(n)) { } else if (NodeUtil.isLValue(n)) {
// Other LValue references invalidate // Other LValue references invalidate
invalidatingPropRef = true; 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. */
* @return Whether this is a valid definition for a candidate property. private boolean isValidCandidateDefinition(NodeTraversal t, Node n, Node parent) {
*/
private boolean maybeCandidateDefinition(
NodeTraversal t, Node n, Node parent) {
Preconditions.checkState(n.isGetProp() && parent.isAssign(), n); Preconditions.checkState(n.isGetProp() && parent.isAssign(), n);
boolean isCandidate = false;
Node src = n.getFirstChild(); Node src = n.getFirstChild();
String propName = n.getLastChild().getString(); String propName = n.getLastChild().getString();


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


private JSType maybeGetInstanceTypeFromPrototypeRef(Node src) { private TypeI maybeGetInstanceTypeFromPrototypeRef(Node src) {
JSType ownerType = getJSType(src.getFirstChild()); TypeI ownerType = getTypeI(src.getFirstChild());
if (ownerType.isFunctionType() && ownerType.isConstructor()) { if (ownerType.isConstructor()) {
FunctionType functionType = ((FunctionType) ownerType); FunctionTypeI functionType = ownerType.toMaybeFunctionType();
return functionType.getInstanceType(); return functionType.getInstanceType();
} }
return null; return null;
Expand All @@ -253,8 +243,12 @@ private void invalidateProperty(String propName) {
props.put(propName, INVALIDATED); 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); Preconditions.checkNotNull(value);
if (!props.containsKey(propName) if (!props.containsKey(propName)
&& !isInvalidatingType(type) && !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(); src = src.restrictByNotNullOrUndefined();
JSType dest = getJSType(n).restrictByNotNullOrUndefined(); TypeI dest = getTypeI(n).restrictByNotNullOrUndefined();
if (!isInvalidatingType(dest)) { if (!isInvalidatingType(dest)) {
if (dest.isConstructor()) { if (dest.isConstructor() || src.isConstructor()) {
// Don't inline constructor properties referenced from // Don't inline constructor properties referenced from
// subclass constructor references. This would be appropriate // subclass constructor references. This would be appropriate
// for ES6 class with Class-side inheritence but not // for ES6 class with Class-side inheritence but not
// traditional Closure classes from which subclass constructor // traditional Closure classes from which subclass constructor
// don't inherit the super-classes constructor properties. // don't inherit the super-classes constructor properties.
return dest.isEquivalentTo(src); return dest.equals(src);
} else { } else {
return dest.isSubtype(src); return dest.isSubtypeOf(src);
} }
} }
return false; 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 @Override
public final boolean isBoxableScalar() { public final boolean isBoxableScalar() {
return isNumber() || isString() || isBoolean(); return isNumber()
|| isString()
|| isBoolean()
|| (isEnumElement() && getEnumeratedTypeOfEnumElement().isBoxableScalar());
} }


@Override @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 stringOrString;
private JSType anyNumOrStr; private JSType anyNumOrStr;


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


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

public JSType getNumberInstance() { public JSType getNumberInstance() {
return numberInstance != null ? numberInstance : this.NUMBER; return numberInstance != null ? numberInstance : this.NUMBER;
} }
Expand Down Expand Up @@ -424,9 +429,13 @@ public JSType getNativeType(JSTypeNative typeId) {
return getFunctionType().getPrototypeObject(); return getFunctionType().getPrototypeObject();
case FUNCTION_INSTANCE_TYPE: case FUNCTION_INSTANCE_TYPE:
return fromFunctionType(QMARK_FUNCTION); return fromFunctionType(QMARK_FUNCTION);
case FUNCTION_FUNCTION_TYPE:
return builtinFunction.toJSType();
case OBJECT_PROTOTYPE: case OBJECT_PROTOTYPE:
case TOP_LEVEL_PROTOTYPE: case TOP_LEVEL_PROTOTYPE:
return getTopObject().getNominalTypeIfSingletonObj().getPrototypePropertyOfCtor(); return getTopObject().getNominalTypeIfSingletonObj().getPrototypePropertyOfCtor();
case GLOBAL_THIS:
return getGlobalThis();
default: default:
throw new RuntimeException("Native type " + typeId.name() + " not found"); 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; 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) { public void setNumberInstance(JSType t) {
Preconditions.checkState(numberInstance == null); Preconditions.checkState(numberInstance == null);
Preconditions.checkNotNull(t); 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); newProps = newProps.without(pname);
} else if (!type.equals(declType) && !type.equals(inferred)) { } else if (!type.equals(declType) && !type.equals(inferred)) {
if (isDeclared && declType == null) { 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(); 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(); boolean isUnknownObject();


/** /**
Expand Down

0 comments on commit 7056e8e

Please sign in to comment.