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

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

There is a newer version: 9.0.8
Show newest version
/*
 * Copyright 2009 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.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.TypeI;
import java.util.HashSet;
import java.util.Set;

/**
 * Checks that the code obeys the static restrictions of strict mode:
 * 
    *
  1. No use of "with". *
  2. No deleting variables, functions, or arguments. *
  3. No re-declarations or assignments of "eval" or arguments. *
  4. No use of arguments.callee *
  5. No use of arguments.caller *
  6. Class: Always under strict mode *
  7. In addition, no duplicate class method names *
* */ class StrictModeCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType USE_OF_WITH = DiagnosticType.warning( "JSC_USE_OF_WITH", "The 'with' statement cannot be used in strict mode."); static final DiagnosticType EVAL_DECLARATION = DiagnosticType.warning( "JSC_EVAL_DECLARATION", "\"eval\" cannot be redeclared in strict mode"); static final DiagnosticType EVAL_ASSIGNMENT = DiagnosticType.warning( "JSC_EVAL_ASSIGNMENT", "the \"eval\" object cannot be reassigned in strict mode"); static final DiagnosticType ARGUMENTS_DECLARATION = DiagnosticType.warning( "JSC_ARGUMENTS_DECLARATION", "\"arguments\" cannot be redeclared in strict mode"); static final DiagnosticType ARGUMENTS_ASSIGNMENT = DiagnosticType.warning( "JSC_ARGUMENTS_ASSIGNMENT", "the \"arguments\" object cannot be reassigned in strict mode"); static final DiagnosticType ARGUMENTS_CALLEE_FORBIDDEN = DiagnosticType.warning( "JSC_ARGUMENTS_CALLEE_FORBIDDEN", "\"arguments.callee\" cannot be used in strict mode"); static final DiagnosticType ARGUMENTS_CALLER_FORBIDDEN = DiagnosticType.warning( "JSC_ARGUMENTS_CALLER_FORBIDDEN", "\"arguments.caller\" cannot be used in strict mode"); static final DiagnosticType FUNCTION_CALLER_FORBIDDEN = DiagnosticType.warning( "JSC_FUNCTION_CALLER_FORBIDDEN", "A function''s \"caller\" property cannot be used in strict mode"); static final DiagnosticType FUNCTION_ARGUMENTS_PROP_FORBIDDEN = DiagnosticType.warning( "JSC_FUNCTION_ARGUMENTS_PROP_FORBIDDEN", "A function''s \"arguments\" property cannot be used in strict mode"); static final DiagnosticType BAD_FUNCTION_DECLARATION = DiagnosticType.warning( "JSC_BAD_FUNCTION_DECLARATION", "functions can only be declared at top level or immediately within" + " another function in ES5 strict mode"); static final DiagnosticType DELETE_VARIABLE = DiagnosticType.warning( "JSC_DELETE_VARIABLE", "variables, functions, and arguments cannot be deleted in strict mode"); static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.error( "JSC_DUPLICATE_OBJECT_KEY", "Object literal contains illegal duplicate key \"{0}\", disallowed in strict mode"); static final DiagnosticType DUPLICATE_CLASS_METHODS = DiagnosticType.error( "JSC_DUPLICATE_CLASS_METHODS", "Class contains duplicate method name \"{0}\""); private final AbstractCompiler compiler; StrictModeCheck(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRootsEs6(compiler, this, externs, root); NodeTraversal.traverseEs6(compiler, root, new NonExternChecks()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isAssign()) { checkAssignment(t, n); } else if (n.isDelProp()) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteralOrClass(t, n); } else if (n.isClass()) { checkObjectLiteralOrClass(t, n.getLastChild()); } else if (n.isWith()) { checkWith(t, n); } } /** Reports a warning for with statements. */ private static void checkWith(NodeTraversal t, Node n) { JSDocInfo info = n.getJSDocInfo(); boolean allowWith = info != null && info.getSuppressions().contains("with"); if (!allowWith) { t.report(n, USE_OF_WITH); } } /** * Determines if the given name is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getToken()) { case LET: case CONST: case VAR: case CATCH: return true; case FUNCTION: return n == n.getParent().getFirstChild(); case PARAM_LIST: return n.getGrandparent().isFunction(); default: return false; } } /** Checks that an assignment is not to the "arguments" object. */ private static void checkAssignment(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { if ("arguments".equals(n.getFirstChild().getString())) { t.report(n, ARGUMENTS_ASSIGNMENT); } else if ("eval".equals(n.getFirstChild().getString())) { // Note that assignment to eval is already illegal because any use of // that name is illegal. t.report(n, EVAL_ASSIGNMENT); } } } /** Checks that variables, functions, and arguments are not deleted. */ private static void checkDelete(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { Var v = t.getScope().getVar(n.getFirstChild().getString()); if (v != null) { t.report(n, DELETE_VARIABLE); } } } /** Checks that object literal keys or class method names are valid. */ private static void checkObjectLiteralOrClass(NodeTraversal t, Node n) { Set getters = new HashSet<>(); Set setters = new HashSet<>(); Set staticGetters = new HashSet<>(); Set staticSetters = new HashSet<>(); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (key.isEmpty() || key.isComputedProp()) { continue; } if (key.isSpread()) { continue; } String keyName = key.getString(); if (!key.isSetterDef()) { // normal property and getter cases Set set = key.isStaticMember() ? staticGetters : getters; if (!set.add(keyName)) { if (n.isClassMembers()) { t.report(key, DUPLICATE_CLASS_METHODS, keyName); } else { t.report(key, DUPLICATE_OBJECT_KEY, keyName); } } } if (!key.isGetterDef()) { // normal property and setter cases Set set = key.isStaticMember() ? staticSetters : setters; if (!set.add(keyName)) { if (n.isClassMembers()) { t.report(key, DUPLICATE_CLASS_METHODS, keyName); } else { t.report(key, DUPLICATE_OBJECT_KEY, keyName); } } } } } /** Checks that are performed on non-extern code only. */ private static class NonExternChecks extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName() && isDeclaration(n)) { checkDeclaration(t, n); } else if (n.isGetProp()) { checkGetProp(t, n); } } /** Checks for illegal declarations. */ private void checkDeclaration(NodeTraversal t, Node n) { if ("eval".equals(n.getString())) { t.report(n, EVAL_DECLARATION); } else if ("arguments".equals(n.getString())) { t.report(n, ARGUMENTS_DECLARATION); } } /** Checks that the arguments.callee is not used. */ private void checkGetProp(NodeTraversal t, Node n) { Node target = n.getFirstChild(); Node prop = n.getLastChild(); if (prop.getString().equals("callee")) { if (target.isName() && target.getString().equals("arguments")) { t.report(n, ARGUMENTS_CALLEE_FORBIDDEN); } } else if (prop.getString().equals("caller")) { if (target.isName() && target.getString().equals("arguments")) { t.report(n, ARGUMENTS_CALLER_FORBIDDEN); } else if (isFunctionType(target)) { t.report(n, FUNCTION_CALLER_FORBIDDEN); } } else if (prop.getString().equals("arguments") && isFunctionType(target)) { t.report(n, FUNCTION_ARGUMENTS_PROP_FORBIDDEN); } } } private static boolean isFunctionType(Node n) { TypeI type = n.getTypeI(); return (type != null && type.isFunctionType()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy