/* * YUI Compressor * Author: Julien Lecomte <jlecomte@yahoo-inc.com> * Copyright (c) 2007, Yahoo! Inc. All rights reserved. * Code licensed under the BSD License: * http://developer.yahoo.net/yui/license.txt */ package com.yahoo.platform.yui.compressor; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; class ScriptOrFnScope { private int braceNesting; private ScriptOrFnScope parentScope; private ArrayList subScopes; private Hashtable identifiers = new Hashtable(); private Hashtable hints = new Hashtable(); private boolean markedForMunging = true; private int varcount = 0; ScriptOrFnScope(int braceNesting, ScriptOrFnScope parentScope) { this.braceNesting = braceNesting; this.parentScope = parentScope; this.subScopes = new ArrayList(); if (parentScope != null) { parentScope.subScopes.add(this); } } int getBraceNesting() { return braceNesting; } ScriptOrFnScope getParentScope() { return parentScope; } JavaScriptIdentifier declareIdentifier(String symbol) { JavaScriptIdentifier identifier = (JavaScriptIdentifier) identifiers.get(symbol); if (identifier == null) { identifier = new JavaScriptIdentifier(symbol, this); identifiers.put(symbol, identifier); } return identifier; } JavaScriptIdentifier getIdentifier(String symbol) { return (JavaScriptIdentifier) identifiers.get(symbol); } void addHint(String variableName, String variableType) { hints.put(variableName, variableType); } void preventMunging() { if (parentScope != null) { // The symbols in the global scope don't get munged, // but the sub-scopes it contains do get munged. markedForMunging = false; } } private ArrayList getUsedSymbols() { ArrayList result = new ArrayList(); Enumeration elements = identifiers.elements(); while (elements.hasMoreElements()) { JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.nextElement(); String mungedValue = identifier.getMungedValue(); if (mungedValue == null) { mungedValue = identifier.getValue(); } result.add(mungedValue); } return result; } private ArrayList getAllUsedSymbols() { ArrayList result = new ArrayList(); ScriptOrFnScope scope = this; while (scope != null) { result.addAll(scope.getUsedSymbols()); scope = scope.parentScope; } return result; } int incrementVarCount() { varcount++; return varcount; } void munge() { if (!markedForMunging) { // Stop right here if this scope was flagged as unsafe for munging. return; } int pickFromSet = 1; // Do not munge symbols in the global scope! if (parentScope != null) { ArrayList freeSymbols = new ArrayList(); freeSymbols.addAll(JavaScriptCompressor.ones); freeSymbols.removeAll(getAllUsedSymbols()); if (freeSymbols.size() == 0) { pickFromSet = 2; freeSymbols.addAll(JavaScriptCompressor.twos); freeSymbols.removeAll(getAllUsedSymbols()); } if (freeSymbols.size() == 0) { pickFromSet = 3; freeSymbols.addAll(JavaScriptCompressor.threes); freeSymbols.removeAll(getAllUsedSymbols()); } if (freeSymbols.size() == 0) { throw new IllegalStateException("The YUI Compressor ran out of symbols. Aborting..."); } Enumeration elements = identifiers.elements(); while (elements.hasMoreElements()) { if (freeSymbols.size() == 0) { pickFromSet++; if (pickFromSet == 2) { freeSymbols.addAll(JavaScriptCompressor.twos); } else if (pickFromSet == 3) { freeSymbols.addAll(JavaScriptCompressor.threes); } else { throw new IllegalStateException("The YUI Compressor ran out of symbols. Aborting..."); } // It is essential to remove the symbols already used in // the containing scopes, or some of the variables declared // in the containing scopes will be redeclared, which can // lead to errors. freeSymbols.removeAll(getAllUsedSymbols()); } String mungedValue; JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.nextElement(); if (identifier.isMarkedForMunging()) { mungedValue = (String) freeSymbols.remove(0); } else { mungedValue = identifier.getValue(); } identifier.setMungedValue(mungedValue); } } for (int i = 0; i < subScopes.size(); i++) { ScriptOrFnScope scope = (ScriptOrFnScope) subScopes.get(i); scope.munge(); } } }