Mercurial > hg > icedtea9-forest > nashorn
view src/jdk/nashorn/internal/ir/Symbol.java @ 661:ae5f2130c3b9
8027700: function redeclaration checks missing for declaration binding instantiation
Reviewed-by: jlaskey, lagergren
author | sundar |
---|---|
date | Fri, 01 Nov 2013 19:54:48 +0530 |
parents | 2a25917777f7 |
children |
line wrap: on
line source
/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.nashorn.internal.ir; import java.io.PrintWriter; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; import jdk.nashorn.internal.codegen.types.Range; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Debug; import jdk.nashorn.internal.runtime.options.Options; import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; /** * Maps a name to specific data. */ public final class Symbol implements Comparable<Symbol> { /** Symbol kinds. Kind ordered by precedence. */ public static final int IS_TEMP = 1; /** Is this Global */ public static final int IS_GLOBAL = 2; /** Is this a variable */ public static final int IS_VAR = 3; /** Is this a parameter */ public static final int IS_PARAM = 4; /** Is this a constant */ public static final int IS_CONSTANT = 5; /** Mask for kind flags */ public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits /** Is this scope */ public static final int IS_SCOPE = 1 << 4; /** Is this a this symbol */ public static final int IS_THIS = 1 << 5; /** Can this symbol ever be undefined */ public static final int CAN_BE_UNDEFINED = 1 << 6; /** Is this symbol always defined? */ public static final int IS_ALWAYS_DEFINED = 1 << 8; /** Can this symbol ever have primitive types */ public static final int CAN_BE_PRIMITIVE = 1 << 9; /** Is this a let */ public static final int IS_LET = 1 << 10; /** Is this an internal symbol, never represented explicitly in source code */ public static final int IS_INTERNAL = 1 << 11; /** Is this a function self-reference symbol */ public static final int IS_FUNCTION_SELF = 1 << 12; /** Is this a specialized param? */ public static final int IS_SPECIALIZED_PARAM = 1 << 13; /** Is this symbol a shared temporary? */ public static final int IS_SHARED = 1 << 14; /** Is this a function declaration? */ public static final int IS_FUNCTION_DECLARATION = 1 << 15; /** Null or name identifying symbol. */ private final String name; /** Symbol flags. */ private int flags; /** Type of symbol. */ private Type type; /** Local variable slot. -1 indicates external property. */ private int slot; /** Field number in scope or property; array index in varargs when not using arguments object. */ private int fieldIndex; /** Number of times this symbol is used in code */ private int useCount; /** Range for symbol */ private Range range; /** Debugging option - dump info and stack trace when symbols with given names are manipulated */ private static final Set<String> TRACE_SYMBOLS; private static final Set<String> TRACE_SYMBOLS_STACKTRACE; static { final String stacktrace = Options.getStringProperty("nashorn.compiler.symbol.stacktrace", null); final String trace; if (stacktrace != null) { trace = stacktrace; //stacktrace always implies trace as well TRACE_SYMBOLS_STACKTRACE = new HashSet<>(); for (StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) { TRACE_SYMBOLS_STACKTRACE.add(st.nextToken()); } } else { trace = Options.getStringProperty("nashorn.compiler.symbol.trace", null); TRACE_SYMBOLS_STACKTRACE = null; } if (trace != null) { TRACE_SYMBOLS = new HashSet<>(); for (StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) { TRACE_SYMBOLS.add(st.nextToken()); } } else { TRACE_SYMBOLS = null; } } /** * Constructor * * @param name name of symbol * @param flags symbol flags * @param type type of this symbol * @param slot bytecode slot for this symbol */ protected Symbol(final String name, final int flags, final Type type, final int slot) { this.name = name; this.flags = flags; this.type = type; this.slot = slot; this.fieldIndex = -1; this.range = Range.createUnknownRange(); trace("CREATE SYMBOL"); } /** * Constructor * * @param name name of symbol * @param flags symbol flags */ public Symbol(final String name, final int flags) { this(name, flags, Type.UNKNOWN, -1); } /** * Constructor * * @param name name of symbol * @param flags symbol flags * @param type type of this symbol */ public Symbol(final String name, final int flags, final Type type) { this(name, flags, type, -1); } private Symbol(final Symbol base, final String name, final int flags) { this.flags = flags; this.name = name; this.fieldIndex = base.fieldIndex; this.slot = base.slot; this.type = base.type; this.useCount = base.useCount; this.range = base.range; } private static String align(final String string, final int max) { final StringBuilder sb = new StringBuilder(); sb.append(string.substring(0, Math.min(string.length(), max))); while (sb.length() < max) { sb.append(' '); } return sb.toString(); } /** * Return the type for this symbol. Normally, if there is no type override, * this is where any type for any node is stored. If the node has a TypeOverride, * it may override this, e.g. when asking for a scoped field as a double * * @return symbol type */ public final Type getSymbolType() { return type; } /** * Debugging . * * @param stream Stream to print to. */ void print(final PrintWriter stream) { final String printName = align(name, 20); final String printType = align(type.toString(), 10); final String printSlot = align(slot == -1 ? "none" : "" + slot, 10); String printFlags = ""; switch (flags & KINDMASK) { case IS_TEMP: printFlags = "temp " + printFlags; break; case IS_GLOBAL: printFlags = "global " + printFlags; break; case IS_VAR: printFlags = "var " + printFlags; break; case IS_PARAM: printFlags = "param " + printFlags; break; case IS_CONSTANT: printFlags = "CONSTANT " + printFlags; break; default: break; } if (isScope()) { printFlags += "scope "; } if (isInternal()) { printFlags += "internal "; } if (isLet()) { printFlags += "let "; } if (isThis()) { printFlags += "this "; } if (!canBeUndefined()) { printFlags += "always_def "; } if (canBePrimitive()) { printFlags += "can_be_prim "; } stream.print(printName + ": " + printType + ", " + printSlot + ", " + printFlags); stream.println(); } /** * Compare the the symbol kind with another. * * @param other Other symbol's flags. * @return True if symbol has less kind. */ public boolean less(final int other) { return (flags & KINDMASK) < (other & KINDMASK); } /** * Allocate a slot for this symbol. * * @param needsSlot True if symbol needs a slot. */ public void setNeedsSlot(final boolean needsSlot) { setSlot(needsSlot ? 0 : -1); } /** * Return the number of slots required for the symbol. * * @return Number of slots. */ public int slotCount() { return type.isCategory2() ? 2 : 1; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(name). append(' '). append('('). append(getSymbolType().getTypeClass().getSimpleName()). append(')'); if (hasSlot()) { sb.append(' '). append('('). append("slot="). append(slot). append(')'); } if (isScope()) { if(isGlobal()) { sb.append(" G"); } else { sb.append(" S"); } } if (canBePrimitive()) { sb.append(" P?"); } return sb.toString(); } @Override public int compareTo(final Symbol other) { return name.compareTo(other.name); } /** * Does this symbol have an allocated bytecode slot. If not, it is scope * and must be loaded from memory upon access * * @return true if this symbol has a local bytecode slot */ public boolean hasSlot() { return slot >= 0; } /** * Check if this is a temporary symbol * @return true if temporary */ public boolean isTemp() { return (flags & KINDMASK) == IS_TEMP; } /** * Check if this is a symbol in scope. Scope symbols cannot, for obvious reasons * be stored in byte code slots on the local frame * * @return true if this is scoped */ public boolean isScope() { assert ((flags & KINDMASK) != IS_GLOBAL) || ((flags & IS_SCOPE) == IS_SCOPE) : "global without scope flag"; return (flags & IS_SCOPE) == IS_SCOPE; } /** * Returns true if this symbol is a temporary that is being shared across expressions. * @return true if this symbol is a temporary that is being shared across expressions. */ public boolean isShared() { return (flags & IS_SHARED) == IS_SHARED; } /** * Check if this symbol is a function declaration * @return true if a function declaration */ public boolean isFunctionDeclaration() { return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION; } /** * Creates an unshared copy of a symbol. The symbol must be currently shared. * @param newName the name for the new symbol. * @return a new, unshared symbol. */ public Symbol createUnshared(final String newName) { assert isShared(); return new Symbol(this, newName, flags & ~IS_SHARED); } /** * Flag this symbol as scope as described in {@link Symbol#isScope()} */ /** * Flag this symbol as scope as described in {@link Symbol#isScope()} */ public void setIsScope() { if (!isScope()) { trace("SET IS SCOPE"); assert !isShared(); flags |= IS_SCOPE; } } /** * Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary. */ public void setIsShared() { if (!isShared()) { assert isTemp(); trace("SET IS SHARED"); flags |= IS_SHARED; } } /** * Mark this symbol as a function declaration. */ public void setIsFunctionDeclaration() { if (!isFunctionDeclaration()) { trace("SET IS FUNCTION DECLARATION"); flags |= IS_FUNCTION_DECLARATION; } } /** * Check if this symbol is a variable * @return true if variable */ public boolean isVar() { return (flags & KINDMASK) == IS_VAR; } /** * Check if this symbol is a global (undeclared) variable * @return true if global */ public boolean isGlobal() { return (flags & KINDMASK) == IS_GLOBAL; } /** * Check if this symbol is a function parameter * @return true if parameter */ public boolean isParam() { return (flags & KINDMASK) == IS_PARAM; } /** * Check if this symbol is always defined, which overrides all canBeUndefined tags * @return true if always defined */ public boolean isAlwaysDefined() { return isParam() || (flags & IS_ALWAYS_DEFINED) == IS_ALWAYS_DEFINED; } /** * Get the range for this symbol * @return range for symbol */ public Range getRange() { return range; } /** * Set the range for this symbol * @param range range */ public void setRange(final Range range) { this.range = range; } /** * Check if this symbol represents a return value with a known non-generic type. * @return true if specialized return value */ public boolean isNonGenericReturn() { return getName().equals(RETURN.symbolName()) && type != Type.OBJECT; } /** * Check if this symbol is a function parameter of known * narrowest type * @return true if parameter */ public boolean isSpecializedParam() { return (flags & IS_SPECIALIZED_PARAM) == IS_SPECIALIZED_PARAM; } /** * Check whether this symbol ever has primitive assignments. Conservative * @return true if primitive assignments exist */ public boolean canBePrimitive() { return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE; } /** * Check if this symbol can ever be undefined * @return true if can be undefined */ public boolean canBeUndefined() { return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED; } /** * Flag this symbol as potentially undefined in parts of the program */ public void setCanBeUndefined() { assert type.isObject() : type; if (isAlwaysDefined()) { return; } else if (!canBeUndefined()) { assert !isShared(); flags |= CAN_BE_UNDEFINED; } } /** * Flag this symbol as potentially primitive * @param type the primitive type it occurs with, currently unused but can be used for width guesses */ public void setCanBePrimitive(final Type type) { if(!canBePrimitive()) { assert !isShared(); flags |= CAN_BE_PRIMITIVE; } } /** * Check if this symbol is a constant * @return true if a constant */ public boolean isConstant() { return (flags & KINDMASK) == IS_CONSTANT; } /** * Check if this is an internal symbol, without an explicit JavaScript source * code equivalent * @return true if internal */ public boolean isInternal() { return (flags & IS_INTERNAL) != 0; } /** * Check if this symbol represents {@code this} * @return true if this */ public boolean isThis() { return (flags & IS_THIS) != 0; } /** * Check if this symbol is a let * @return true if let */ public boolean isLet() { return (flags & IS_LET) == IS_LET; } /** * Flag this symbol as a let */ public void setIsLet() { if(!isLet()) { assert !isShared(); flags |= IS_LET; } } /** * Flag this symbol as a function's self-referencing symbol. * @return true if this symbol as a function's self-referencing symbol. */ public boolean isFunctionSelf() { return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF; } /** * Get the index of the field used to store this symbol, should it be an AccessorProperty * and get allocated in a JO-prefixed ScriptObject subclass. * * @return field index */ public int getFieldIndex() { assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex; return fieldIndex; } /** * Set the index of the field used to store this symbol, should it be an AccessorProperty * and get allocated in a JO-prefixed ScriptObject subclass. * * @param fieldIndex field index - a positive integer */ public void setFieldIndex(final int fieldIndex) { if(this.fieldIndex != fieldIndex) { assert !isShared(); this.fieldIndex = fieldIndex; } } /** * Get the symbol flags * @return flags */ public int getFlags() { return flags; } /** * Set the symbol flags * @param flags flags */ public void setFlags(final int flags) { if(this.flags != flags) { assert !isShared(); this.flags = flags; } } /** * Get the name of this symbol * @return symbol name */ public String getName() { return name; } /** * Get the byte code slot for this symbol * @return byte code slot, or -1 if no slot allocated/possible */ public int getSlot() { return slot; } /** * Increase the symbol's use count by one. */ public void increaseUseCount() { useCount++; } /** * Get the symbol's use count * @return the number of times the symbol is used in code. */ public int getUseCount() { return useCount; } /** * Set the bytecode slot for this symbol * @param slot valid bytecode slot, or -1 if not available */ public void setSlot(final int slot) { if (slot != this.slot) { assert !isShared(); trace("SET SLOT " + slot); this.slot = slot; } } /** * Assign a specific subclass of Object to the symbol * * @param type the type */ public void setType(final Class<?> type) { assert !type.isPrimitive() && !Number.class.isAssignableFrom(type) : "Class<?> types can only be subclasses of object"; setType(Type.typeFor(type)); } /** * Assign a type to the symbol * * @param type the type */ public void setType(final Type type) { setTypeOverride(Type.widest(this.type, type)); } /** * Returns true if calling {@link #setType(Type)} on this symbol would effectively change its type. * @param newType the new type to test for * @return true if setting this symbols type to a new value would effectively change its type. */ public boolean wouldChangeType(final Type newType) { return Type.widest(this.type, newType) != this.type; } /** * Only use this if you know about an existing type * constraint - otherwise a type can only be * widened * * @param type the type */ public void setTypeOverride(final Type type) { final Type old = this.type; if (old != type) { assert !isShared(); trace("TYPE CHANGE: " + old + "=>" + type + " == " + type); this.type = type; } } /** * Sets the type of the symbol to the specified type. If the type would be changed, but this symbol is a shared * temporary, it will instead return a different temporary symbol of the requested type from the passed temporary * symbols. That way, it never mutates the type of a shared temporary. * @param type the new type for the symbol * @param ts a holder of temporary symbols * @return either this symbol, or a different symbol if this symbol is a shared temporary and it type would have to * be changed. */ public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) { if(getSymbolType() != type) { if(isShared()) { assert !hasSlot(); return ts.getTypedTemporarySymbol(type); } setTypeOverride(type); } return this; } /** * From a lexical context, set this symbol as needing scope, which * will set flags for the defining block that will be written when * block is popped from the lexical context stack, used by codegen * when flags need to be tagged, but block is in the * middle of evaluation and cannot be modified. * * @param lc lexical context * @param symbol symbol */ public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) { symbol.setIsScope(); if (!symbol.isGlobal()) { lc.setBlockNeedsScope(lc.getDefiningBlock(symbol)); } } private void trace(final String desc) { if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) { Context.err(Debug.id(this) + " SYMBOL: '" + name + "' " + desc); if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) { new Throwable().printStackTrace(Context.getCurrentErr()); } } } }