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

com.google.javascript.jscomp.TypedScope 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 2015 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.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.StaticTypedScope;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * TypedScope contains information about variables and their types.
 * Scopes can be nested, a scope points back to its parent scope.
 * 

* TypedScope is also used as a lattice element for flow-sensitive type inference. * As a lattice element, a scope is viewed as a map from names to types. A name * not in the map is considered to have the bottom type. The join of two maps m1 * and m2 is the map of the union of names with {@link JSType#getLeastSupertype} * to meet the m1 type and m2 type. * * @see NodeTraversal * @see DataFlowAnalysis * * Several methods in this class, such as {@code isBlockScope} throw an exception when called. * The reason for this is that we want to shadow methods from the parent class, to avoid calling * them accidentally. */ public class TypedScope extends Scope implements StaticTypedScope { private final Map vars = new LinkedHashMap<>(); private final TypedScope parent; /** Whether this is a bottom scope for the purposes of type inference. */ private final boolean isBottom; // Scope.java contains an arguments field. // We haven't added it here because it's unused by the passes that need typed scopes. private static final Predicate DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES = new Predicate() { @Override public boolean apply(TypedVar var) { return var.getParentNode() != null && var.getType() == null && var.getParentNode().isVar() && !var.isExtern(); } }; TypedScope(TypedScope parent, Node rootNode) { super(parent, rootNode); this.parent = parent; this.isBottom = false; } /** * Creates a empty Scope (bottom of the lattice). * @param rootNode Typically a FUNCTION node or the global BLOCK node. * @param isBottom Whether this is the bottom of a lattice. Otherwise, * it must be a global scope. */ private TypedScope(Node rootNode, boolean isBottom) { super(rootNode); this.parent = null; this.isBottom = isBottom; } static TypedScope createGlobalScope(Node rootNode) { return new TypedScope(rootNode, false); } static TypedScope createLatticeBottom(Node rootNode) { return new TypedScope(rootNode, true); } /** Whether this is the bottom of the lattice. */ boolean isBottom() { return isBottom; } @Override public int getDepth() { return depth; } @Override public Node getRootNode() { return rootNode; } @Override public TypedScope getParent() { return parent; } @Override TypedScope getGlobalScope() { TypedScope result = this; while (result.getParent() != null) { result = result.getParent(); } return result; } @Override public StaticTypedScope getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope. */ @Override public JSType getTypeOfThis() { if (isGlobal()) { return ObjectType.cast(rootNode.getJSType()); } Preconditions.checkState(rootNode.isFunction()); JSType nodeType = rootNode.getJSType(); if (nodeType != null && nodeType.isFunctionType()) { return nodeType.toMaybeFunctionType().getTypeOfThis(); } else { // Executed when the current scope has not been typechecked. return null; } } @Override Var declare(String name, Node nameNode, CompilerInput input) { throw new IllegalStateException( "Method declare(untyped) cannot be called on typed scopes."); } TypedVar declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } TypedVar declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && !name.isEmpty()); TypedVar var = new TypedVar(inferred, name, nameNode, type, this, vars.size(), input); vars.put(name, var); return var; } @Override void undeclare(Var var) { TypedVar tvar = (TypedVar) var; Preconditions.checkState(tvar.scope == this); Preconditions.checkState(vars.get(tvar.name) == tvar); vars.remove(tvar.name); } @Override public TypedVar getSlot(String name) { return getVar(name); } @Override public TypedVar getOwnSlot(String name) { return vars.get(name); } @Override public TypedVar getVar(String name) { TypedScope scope = this; while (scope != null) { TypedVar var = scope.vars.get(name); if (var != null) { return var; } // Recurse up the parent Scope scope = scope.parent; } return null; } @Override public Var getArgumentsVar() { throw new IllegalStateException("Method getArgumentsVar cannot be called on typed scopes."); } @Override public boolean isDeclared(String name, boolean recurse) { TypedScope scope = this; while (true) { if (scope.vars.containsKey(name)) { return true; } if (scope.parent != null && recurse) { scope = scope.parent; continue; } return false; } } @Override public Iterable getVarIterable() { return vars.values(); } @Override public Iterable getAllSymbols() { return Collections.unmodifiableCollection(vars.values()); } @Override public int getVarCount() { return vars.size(); } @Override public boolean isGlobal() { return parent == null; } @Override public boolean isLocal() { return parent != null; } public Iterable getDeclarativelyUnboundVarsWithoutTypes() { return Iterables.filter(getVarIterable(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES); } static interface TypeResolver { void resolveTypes(); } private TypeResolver typeResolver; /** Resolve all type references. Only used on typed scopes. */ void resolveTypes() { if (typeResolver != null) { typeResolver.resolveTypes(); typeResolver = null; } } void setTypeResolver(TypeResolver resolver) { this.typeResolver = resolver; } @Override public boolean isBlockScope() { // TypedScope is not ES6 compatible yet, so always return false for now. return false; } @Override public boolean isFunctionBlockScope() { throw new IllegalStateException( "Method isFunctionBlockScope cannot be called on typed scopes."); } @Override public Scope getClosestHoistScope() { throw new IllegalStateException( "Method getClosestHoistScope cannot be called on typed scopes."); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy