All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.InvalidatingTypes Maven / Gradle / Ivy

Go to download

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.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2017 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 com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
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.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;

/**
 * Keeps track of "invalidating types" that force type-based optimizations to back off.
 *
 * 

Specifically for {@link InlineProperties}, {@link * com.google.javascript.jscomp.disambiguate.AmbiguateProperties}, and {@link * com.google.javascript.jscomp.disambiguate.DisambiguateProperties}. */ public final class InvalidatingTypes { private final ImmutableSetMultimap typeToLocation; private InvalidatingTypes(ImmutableSetMultimap typeToLocation) { this.typeToLocation = typeToLocation; } public boolean isInvalidating(JSType type) { if (type == null || type.isUnknownType() || type.isEmptyType()) { return true; } // A union type is invalidating if any one of its members is invalidating if (type.isUnionType()) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.getUnionMembers()) { if (isInvalidating(alt)) { return true; } } return false; } } ObjectType objType = type.toMaybeObjectType(); if (objType == null) { // TODO(b/174534994): why can't scalars be invalidating? return false; } if (objType.isTemplatizedType()) { objType = objType.toMaybeTemplatizedType().getReferencedType(); } return this.typeToLocation.containsKey(objType) // Don't disambiguate properties on object types that are structurally compared or that // don't come from a literal class or function definition || isAmbiguousOrStructuralType(objType); } public ImmutableSetMultimap getMismatchLocations() { return this.typeToLocation; } // Returns true if any of the following hold: // - this type obeys structural subtyping rules, as opposed to nominal subtyping // - this type is some JSDoc-only or anonymous type like a mixin, as opposed to a class or // function literal private static boolean isAmbiguousOrStructuralType(ObjectType type) { if (type.isEnumType()) { // enum types are created via object literals, which are normally structural, but Closure // special-cases them to behave as if nominal. return false; } else if (type.isEnumElementType()) { ObjectType primitive = type.toMaybeEnumElementType().getPrimitiveType().toMaybeObjectType(); // Treat an Enum identically to a Foo return primitive == null || isAmbiguousOrStructuralType(primitive); } else if (type.isFunctionType()) { return !type.isNominalConstructorOrInterface() || type.toMaybeFunctionType().isAmbiguousConstructor(); } else if (type.isFunctionPrototypeType()) { FunctionType ownerFunction = type.getOwnerFunction(); return ownerFunction == null || !ownerFunction.isNominalConstructorOrInterface() || ownerFunction.isAmbiguousConstructor() || ownerFunction.isStructuralInterface(); } else if (type.isInstanceType()) { FunctionType ctor = type.getConstructor(); return ctor == null || ctor.isAmbiguousConstructor() || ctor.isStructuralInterface(); } return true; } /** Builder */ public static final class Builder { private final JSTypeRegistry registry; private final ImmutableSetMultimap.Builder typeToLocation = ImmutableSetMultimap.builder(); public Builder(JSTypeRegistry registry) { this.registry = registry; } public InvalidatingTypes build() { for (JSTypeNative t : ALWAYS_INVALIDATING_TYPES) { this.typeToLocation.put(registry.getNativeType(t), ALWAYS_INVALIDATING_LOCATION); } return new InvalidatingTypes(this.typeToLocation.build()); } @CanIgnoreReturnValue public Builder addAllTypeMismatches(Iterable mismatches) { for (TypeMismatch mismatch : mismatches) { this.addTypeWithReason(mismatch.getFound(), mismatch.getLocation()); this.addTypeWithReason(mismatch.getRequired(), mismatch.getLocation()); } return this; } /** Invalidates the given type, so that no properties on it will be inlined or renamed. */ private void addTypeWithReason(JSType type, Node location) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.getUnionMembers()) { this.addTypeWithReason(alt, location); } return; } if (type.isEnumElementType()) { this.addTypeWithReason(type.getEnumeratedTypeOfEnumElement(), location); return; } ObjectType objType = type.toMaybeObjectType(); if (objType == null) { return; } this.recordTypeWithReason(objType, location); this.recordTypeWithReason(objType.getImplicitPrototype(), location); if (objType.isConstructor()) { // TODO(b/142431852): This should never be null but it is possible. // Case: `function(new:T)`, `T = number`. ObjectType instanceType = objType.toMaybeFunctionType().getInstanceType(); this.recordTypeWithReason(instanceType, location); } else if (objType.isInstanceType()) { this.recordTypeWithReason(objType.getConstructor(), location); } } private void recordTypeWithReason(ObjectType type, Node location) { if (type == null) { return; } if (type.isTemplatizedType()) { type = type.toMaybeTemplatizedType().getReferencedType(); } if (isAmbiguousOrStructuralType(type)) { // This type is inherently invalidating. Putting it in the map wastes memory. // This also fixes a performance regression: previously we saw ~4k structural types // hash to the same bucket in the "typeToLocation" map, causing >100 seconds spent in // hash map lookups for some builds. return; } this.typeToLocation.put(type, location); } } private static final ImmutableList ALWAYS_INVALIDATING_TYPES = ImmutableList.of( JSTypeNative.FUNCTION_FUNCTION_TYPE, JSTypeNative.FUNCTION_TYPE, JSTypeNative.FUNCTION_PROTOTYPE, JSTypeNative.FUNCTION_INSTANCE_PROTOTYPE, JSTypeNative.OBJECT_TYPE, JSTypeNative.OBJECT_PROTOTYPE, JSTypeNative.OBJECT_FUNCTION_TYPE); private static final Node ALWAYS_INVALIDATING_LOCATION = IR.name("alwaysInvalidatingLocation") .setStaticSourceFile( SourceFile.fromCode("InvalidatingTypes_alwaysInvalidatingLocation", "")); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy