// Copyright (C) 2008 Google Inc. // // 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.caja.ancillary.linter; import com.google.caja.parser.AncestorChain; import com.google.caja.parser.js.CatchStmt; import com.google.caja.parser.js.FunctionConstructor; import com.google.caja.parser.js.WithStmt; import com.google.caja.util.Lists; import java.util.List; /** * A set of adjacent AST nodes that share a common ancestor which is in the set. * * <h2>Glossary</h2> * <dl> * <dt>Scope-Chain</dt> * <dd>A stack-like structure used to resolve references in an EcmaScript * interpreter as defined in * <a href= * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf * >EcmScript 262</a> section 10.1.4.</dd> * <dt>Lexical Scope</dt> * <dd>A contiguous range of source code elements that, when control enters * them, cause an element to be added to the scope-chain; and that, when * control leaves them, cause that element to be removed from the * scope-chain.</dd> * <dt>Lexical Scope Root</dt> * <dd>The common ancestor of all source code elements in a lexical scope, and * which contains no elements not in the lexical scope.</dd> * <dt>Symbol</dt> * <dd>A named property in a scope-chain frame. * Symbols correspond to properties of the global object, function * parameters and local variables, members of an object in a {@code with} * block, etc.</dd> * <dt>Lexical Scope Ancestor</dt> * <dd>A lexical scope that is always on the scope-chain when this lexical scope * is on the scope-chain. * </dl> * * @author mikesamuel@gmail.com */ final class LexicalScope { final AncestorChain<?> root; final LexicalScope parent; final SymbolTable symbols; final List<LexicalScope> innerScopes = Lists.newArrayList(); LexicalScope(AncestorChain<?> root, LexicalScope parent) { this.root = root; this.parent = parent; this.symbols = new SymbolTable(); if (parent != null) { parent.innerScopes.add(this); } } LexicalScope declaringScope(String symbolName) { if (symbols.getSymbol(symbolName) != null) { return this; } if (parent == null) { return null; } return parent.declaringScope(symbolName); } /** * True if both scopes occur in the same function, or if neither are in a * function, in the same global scope. */ boolean inSameProgramUnit(LexicalScope that) { LexicalScope shallow, deep; if (this.root.depth >= that.root.depth) { deep = this; shallow = that; } else { deep = that; shallow = this; } while (shallow.parent != null && !shallow.isFunctionScope()) { shallow = shallow.parent; } for (LexicalScope s = deep; s != null; s = s.parent) { if (s == shallow) { return true; } // shallow contains deep if (s.isFunctionScope()) { return false; } } return false; // disjoint } boolean isWithScope() { return root.node instanceof WithStmt; } boolean isCatchScope() { return root.node instanceof CatchStmt; } boolean isFunctionScope() { return root.node instanceof FunctionConstructor; } boolean isGlobal() { return parent == null; } }